| /* |
| * win32_io.c - A stdio-like disk I/O implementation for low-level disk access |
| * on Win32. Can access an NTFS volume while it is mounted. |
| * Originated from the Linux-NTFS project. |
| * |
| * Copyright (c) 2003-2004 Lode Leroy |
| * Copyright (c) 2003-2006 Anton Altaparmakov |
| * Copyright (c) 2004-2005 Yuval Fledel |
| * Copyright (c) 2012-2014 Jean-Pierre Andre |
| * |
| * This program/include file is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as published |
| * by the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program/include file is distributed in the hope that it will be |
| * useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program (in the main directory of the NTFS-3G |
| * distribution in the file COPYING); if not, write to the Free Software |
| * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "config.h" |
| |
| #ifdef HAVE_WINDOWS_H |
| #define BOOL WINBOOL /* avoid conflicting definitions of BOOL */ |
| #include <windows.h> |
| #undef BOOL |
| #endif |
| |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| |
| /* |
| * Definitions needed for <winioctl.h> |
| */ |
| #ifndef _ANONYMOUS_UNION |
| #define _ANONYMOUS_UNION |
| #define _ANONYMOUS_STRUCT |
| typedef unsigned long long DWORD64; |
| #endif |
| |
| typedef struct { |
| DWORD data1; /* The first eight hexadecimal digits of the GUID. */ |
| WORD data2; /* The first group of four hexadecimal digits. */ |
| WORD data3; /* The second group of four hexadecimal digits. */ |
| char data4[8]; /* The first two bytes are the third group of four |
| hexadecimal digits. The remaining six bytes are the |
| final 12 hexadecimal digits. */ |
| } GUID; |
| |
| #include <winioctl.h> |
| |
| #ifdef HAVE_STDIO_H |
| #include <stdio.h> |
| #endif |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #define stat stat64 |
| #define st_blocks st_rdev /* emulate st_blocks, missing in Windows */ |
| #endif |
| |
| /* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */ |
| #define _NTFS_VOLUME_H |
| struct ntfs_volume; |
| typedef struct ntfs_volume ntfs_volume; |
| |
| #include "debug.h" |
| #include "types.h" |
| #include "device.h" |
| #include "misc.h" |
| |
| #define cpu_to_le16(x) (x) |
| #define const_cpu_to_le16(x) (x) |
| |
| #ifndef MAX_PATH |
| #define MAX_PATH 1024 |
| #endif |
| |
| #ifndef NTFS_BLOCK_SIZE |
| #define NTFS_BLOCK_SIZE 512 |
| #define NTFS_BLOCK_SIZE_BITS 9 |
| #endif |
| |
| #ifndef INVALID_SET_FILE_POINTER |
| #define INVALID_SET_FILE_POINTER ((DWORD)-1) |
| #endif |
| |
| #ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS |
| #define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096 |
| #endif |
| |
| #ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY |
| #define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x70000 |
| #endif |
| |
| #ifndef IOCTL_GET_DISK_LENGTH_INFO |
| #define IOCTL_GET_DISK_LENGTH_INFO 0x7405c |
| #endif |
| |
| #ifndef FSCTL_ALLOW_EXTENDED_DASD_IO |
| #define FSCTL_ALLOW_EXTENDED_DASD_IO 0x90083 |
| #endif |
| |
| /* Windows 2k+ imports. */ |
| typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD); |
| typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD); |
| typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE); |
| typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER, |
| PLARGE_INTEGER, DWORD); |
| |
| static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL; |
| static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL; |
| static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL; |
| static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL; |
| |
| #ifdef UNICODE |
| #define FNPOSTFIX "W" |
| #else |
| #define FNPOSTFIX "A" |
| #endif |
| |
| enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx */ |
| STATUS_UNKNOWN = -1, |
| STATUS_SUCCESS = 0x00000000, |
| STATUS_BUFFER_OVERFLOW = 0x80000005, |
| STATUS_INVALID_HANDLE = 0xC0000008, |
| STATUS_INVALID_PARAMETER = 0xC000000D, |
| STATUS_INVALID_DEVICE_REQUEST = 0xC0000010, |
| STATUS_END_OF_FILE = 0xC0000011, |
| STATUS_CONFLICTING_ADDRESSES = 0xC0000018, |
| STATUS_NO_MATCH = 0xC000001E, |
| STATUS_ACCESS_DENIED = 0xC0000022, |
| STATUS_BUFFER_TOO_SMALL = 0xC0000023, |
| STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024, |
| STATUS_FILE_NOT_FOUND = 0xC0000028, |
| STATUS_OBJECT_NAME_INVALID = 0xC0000033, |
| STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, |
| STATUS_INVALID_PARAMETER_1 = 0xC00000EF, |
| STATUS_IO_DEVICE_ERROR = 0xC0000185, |
| STATUS_GUARD_PAGE_VIOLATION = 0x80000001 |
| } ; |
| |
| typedef u32 NTSTATUS; /* do not let the compiler choose the size */ |
| #ifdef __x86_64__ |
| typedef unsigned long long ULONG_PTR; /* an integer the same size as a pointer */ |
| #else |
| typedef unsigned long ULONG_PTR; /* an integer the same size as a pointer */ |
| #endif |
| |
| HANDLE get_osfhandle(int); /* from msvcrt.dll */ |
| |
| /* |
| * A few needed definitions not included in <windows.h> |
| */ |
| |
| typedef struct _IO_STATUS_BLOCK { |
| union { |
| NTSTATUS Status; |
| PVOID Pointer; |
| }; |
| ULONG_PTR Information; |
| } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; |
| |
| typedef struct _UNICODE_STRING { |
| USHORT Length; |
| USHORT MaximumLength; |
| #ifdef __x86_64__ |
| u32 padding; |
| #endif |
| PWSTR Buffer; |
| } UNICODE_STRING, *PUNICODE_STRING; |
| |
| typedef struct _OBJECT_ATTRIBUTES { |
| ULONG Length; |
| #ifdef __x86_64__ |
| u32 padding1; |
| HANDLE RootDirectory; |
| PUNICODE_STRING ObjectName; |
| ULONG Attributes; |
| u32 padding2; |
| #else |
| HANDLE RootDirectory; |
| PUNICODE_STRING ObjectName; |
| ULONG Attributes; |
| #endif |
| PVOID SecurityDescriptor; |
| PVOID SecurityQualityOfService; |
| } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; |
| |
| #define FILE_OPEN 1 |
| #define FILE_CREATE 2 |
| #define FILE_OVERWRITE 4 |
| #define FILE_SYNCHRONOUS_IO_ALERT 0x10 |
| #define FILE_SYNCHRONOUS_IO_NONALERT 0x20 |
| #define OBJ_CASE_INSENSITIVE 0x40 |
| |
| typedef void (WINAPI *PIO_APC_ROUTINE)(void*, PIO_STATUS_BLOCK, ULONG); |
| |
| extern WINAPI NTSTATUS NtOpenFile( |
| PHANDLE FileHandle, |
| ACCESS_MASK DesiredAccess, |
| POBJECT_ATTRIBUTES ObjectAttributes, |
| PIO_STATUS_BLOCK IoStatusBlock, |
| ULONG ShareAccess, |
| ULONG OpenOptions |
| ); |
| |
| extern WINAPI NTSTATUS NtReadFile( |
| HANDLE FileHandle, |
| HANDLE Event, |
| PIO_APC_ROUTINE ApcRoutine, |
| PVOID ApcContext, |
| PIO_STATUS_BLOCK IoStatusBlock, |
| PVOID Buffer, |
| ULONG Length, |
| PLARGE_INTEGER ByteOffset, |
| PULONG Key |
| ); |
| |
| extern WINAPI NTSTATUS NtWriteFile( |
| HANDLE FileHandle, |
| HANDLE Event, |
| PIO_APC_ROUTINE ApcRoutine, |
| PVOID ApcContext, |
| PIO_STATUS_BLOCK IoStatusBlock, |
| LPCVOID Buffer, |
| ULONG Length, |
| PLARGE_INTEGER ByteOffset, |
| PULONG Key |
| ); |
| |
| extern NTSTATUS WINAPI NtClose( |
| HANDLE Handle |
| ); |
| |
| extern NTSTATUS WINAPI NtDeviceIoControlFile( |
| HANDLE FileHandle, |
| HANDLE Event, |
| PIO_APC_ROUTINE ApcRoutine, |
| PVOID ApcContext, |
| PIO_STATUS_BLOCK IoStatusBlock, |
| ULONG IoControlCode, |
| PVOID InputBuffer, |
| ULONG InputBufferLength, |
| PVOID OutputBuffer, |
| ULONG OutputBufferLength |
| ); |
| |
| extern NTSTATUS WINAPI NtFsControlFile( |
| HANDLE FileHandle, |
| HANDLE Event, |
| PIO_APC_ROUTINE ApcRoutine, |
| PVOID ApcContext, |
| PIO_STATUS_BLOCK IoStatusBlock, |
| ULONG FsControlCode, |
| PVOID InputBuffer, |
| ULONG InputBufferLength, |
| PVOID OutputBuffer, |
| ULONG OutputBufferLength |
| ); |
| |
| /** |
| * struct win32_fd - |
| */ |
| typedef struct { |
| HANDLE handle; |
| s64 pos; /* Logical current position on the volume. */ |
| s64 part_start; |
| s64 part_length; |
| int part_hidden_sectors; |
| s64 geo_size, geo_cylinders; |
| s32 geo_sector_size; |
| s64 volume_size; |
| DWORD geo_sectors, geo_heads; |
| HANDLE vol_handle; |
| BOOL ntdll; |
| } win32_fd; |
| |
| /** |
| * ntfs_w32error_to_errno - convert a win32 error code to the unix one |
| * @w32error: the win32 error code |
| * |
| * Limited to a relatively small but useful number of codes. |
| */ |
| static int ntfs_w32error_to_errno(unsigned int w32error) |
| { |
| ntfs_log_trace("Converting w32error 0x%x.\n",w32error); |
| switch (w32error) { |
| case ERROR_INVALID_FUNCTION: |
| return EBADRQC; |
| case ERROR_FILE_NOT_FOUND: |
| case ERROR_PATH_NOT_FOUND: |
| case ERROR_INVALID_NAME: |
| return ENOENT; |
| case ERROR_TOO_MANY_OPEN_FILES: |
| return EMFILE; |
| case ERROR_ACCESS_DENIED: |
| return EACCES; |
| case ERROR_INVALID_HANDLE: |
| return EBADF; |
| case ERROR_NOT_ENOUGH_MEMORY: |
| return ENOMEM; |
| case ERROR_OUTOFMEMORY: |
| return ENOSPC; |
| case ERROR_INVALID_DRIVE: |
| case ERROR_BAD_UNIT: |
| return ENODEV; |
| case ERROR_WRITE_PROTECT: |
| return EROFS; |
| case ERROR_NOT_READY: |
| case ERROR_SHARING_VIOLATION: |
| return EBUSY; |
| case ERROR_BAD_COMMAND: |
| return EINVAL; |
| case ERROR_SEEK: |
| case ERROR_NEGATIVE_SEEK: |
| return ESPIPE; |
| case ERROR_NOT_SUPPORTED: |
| return EOPNOTSUPP; |
| case ERROR_BAD_NETPATH: |
| return ENOSHARE; |
| default: |
| /* generic message */ |
| return ENOMSG; |
| } |
| } |
| |
| static int ntfs_ntstatus_to_errno(NTSTATUS status) |
| { |
| ntfs_log_trace("Converting w32error 0x%x.\n",w32error); |
| switch (status) { |
| case STATUS_INVALID_HANDLE : |
| case STATUS_INVALID_PARAMETER : |
| case STATUS_OBJECT_NAME_INVALID : |
| case STATUS_INVALID_DEVICE_REQUEST : |
| return (EINVAL); |
| case STATUS_ACCESS_DENIED : |
| return (EACCES); |
| case STATUS_IO_DEVICE_ERROR : |
| case STATUS_END_OF_FILE : |
| return (EIO); |
| default: |
| /* generic message */ |
| return ENOMSG; |
| } |
| } |
| |
| /** |
| * libntfs_SetFilePointerEx - emulation for SetFilePointerEx() |
| * |
| * We use this to emulate SetFilePointerEx() when it is not present. This can |
| * happen since SetFilePointerEx() only exists in Win2k+. |
| */ |
| static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile, |
| LARGE_INTEGER liDistanceToMove, |
| PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) |
| { |
| liDistanceToMove.u.LowPart = SetFilePointer(hFile, |
| liDistanceToMove.u.LowPart, |
| &liDistanceToMove.u.HighPart, dwMoveMethod); |
| SetLastError(NO_ERROR); |
| if (liDistanceToMove.u.LowPart == INVALID_SET_FILE_POINTER && |
| GetLastError() != NO_ERROR) { |
| if (lpNewFilePointer) |
| lpNewFilePointer->QuadPart = -1; |
| return FALSE; |
| } |
| if (lpNewFilePointer) |
| lpNewFilePointer->QuadPart = liDistanceToMove.QuadPart; |
| return TRUE; |
| } |
| |
| /** |
| * ntfs_device_win32_init_imports - initialize the function pointers |
| * |
| * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such |
| * we cannot just staticly import them. |
| * |
| * This function initializes the imports if the functions do exist and in the |
| * SetFilePointerEx case, we emulate the function ourselves if it is not |
| * present. |
| * |
| * Note: The values are cached, do be afraid to run it more than once. |
| */ |
| static void ntfs_device_win32_init_imports(void) |
| { |
| HMODULE kernel32 = GetModuleHandle("kernel32"); |
| if (!kernel32) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("kernel32.dll could not be imported.\n"); |
| } |
| if (!fnSetFilePointerEx) { |
| if (kernel32) |
| fnSetFilePointerEx = (LPFN_SETFILEPOINTEREX) |
| GetProcAddress(kernel32, |
| "SetFilePointerEx"); |
| /* |
| * If we did not get kernel32.dll or it is not Win2k+, emulate |
| * SetFilePointerEx(). |
| */ |
| if (!fnSetFilePointerEx) { |
| ntfs_log_debug("SetFilePointerEx() not found in " |
| "kernel32.dll: Enabling emulation.\n"); |
| fnSetFilePointerEx = libntfs_SetFilePointerEx; |
| } |
| } |
| /* Cannot do lookups if we could not get kernel32.dll... */ |
| if (!kernel32) |
| return; |
| if (!fnFindFirstVolume) |
| fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME) |
| GetProcAddress(kernel32, "FindFirstVolume" |
| FNPOSTFIX); |
| if (!fnFindNextVolume) |
| fnFindNextVolume = (LPFN_FINDNEXTVOLUME) |
| GetProcAddress(kernel32, "FindNextVolume" |
| FNPOSTFIX); |
| if (!fnFindVolumeClose) |
| fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE) |
| GetProcAddress(kernel32, "FindVolumeClose"); |
| } |
| |
| /** |
| * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags |
| * @flags: unix open status flags |
| * |
| * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. |
| */ |
| static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags) |
| { |
| int win_mode; |
| |
| switch (flags & O_ACCMODE) { |
| case O_RDONLY: |
| win_mode = GENERIC_READ; |
| break; |
| case O_WRONLY: |
| win_mode = GENERIC_WRITE; |
| break; |
| case O_RDWR: |
| win_mode = GENERIC_READ | GENERIC_WRITE; |
| break; |
| default: |
| /* error */ |
| ntfs_log_trace("Unknown status flags.\n"); |
| win_mode = 0; |
| } |
| return win_mode; |
| } |
| |
| |
| /** |
| * ntfs_device_win32_simple_open_file - just open a file via win32 API |
| * @filename: name of the file to open |
| * @handle: pointer the a HANDLE in which to put the result |
| * @flags: unix open status flags |
| * @locking: will the function gain an exclusive lock on the file? |
| * |
| * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. In this case handle is trashed. |
| */ |
| static int ntfs_device_win32_simple_open_file(const char *filename, |
| HANDLE *handle, int flags, BOOL locking) |
| { |
| *handle = CreateFile(filename, |
| ntfs_device_unix_status_flags_to_win32(flags), |
| locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), |
| NULL, (flags & O_CREAT ? OPEN_ALWAYS : OPEN_EXISTING), |
| 0, NULL); |
| if (*handle == INVALID_HANDLE_VALUE) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("CreateFile(%s) failed.\n", filename); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_lock - lock the volume |
| * @handle: a win32 HANDLE for a volume to lock |
| * |
| * Locking a volume means no one can access its contents. |
| * Exiting the process automatically unlocks the volume, except in old NT4s. |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static int ntfs_device_win32_lock(HANDLE handle) |
| { |
| DWORD i; |
| |
| if (!DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &i, |
| NULL)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't lock volume.\n"); |
| return -1; |
| } |
| ntfs_log_debug("Volume locked.\n"); |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_unlock - unlock the volume |
| * @handle: the win32 HANDLE which the volume was locked with |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static int ntfs_device_win32_unlock(HANDLE handle) |
| { |
| DWORD i; |
| |
| if (!DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &i, |
| NULL)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't unlock volume.\n"); |
| return -1; |
| } |
| ntfs_log_debug("Volume unlocked.\n"); |
| return 0; |
| } |
| |
| static int ntfs_device_win32_setlock(HANDLE handle, ULONG code) |
| { |
| IO_STATUS_BLOCK io_status; |
| NTSTATUS res; |
| |
| io_status.Status = STATUS_SUCCESS; |
| io_status.Information = 0; |
| res = NtFsControlFile(handle,(HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL,(void*)NULL, |
| &io_status, code, |
| (char*)NULL,0,(char*)NULL,0); |
| if (res != STATUS_SUCCESS) |
| errno = ntfs_ntstatus_to_errno(res); |
| return (res == STATUS_SUCCESS ? 0 : -1); |
| } |
| |
| /** |
| * ntfs_device_win32_dismount - dismount a volume |
| * @handle: a win32 HANDLE for a volume to dismount |
| * |
| * Dismounting means the system will refresh the volume in the first change it |
| * gets. Usefull after altering the file structures. |
| * The volume must be locked by the current process while dismounting. |
| * A side effect is that the volume is also unlocked, but you must not rely om |
| * this. |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static int ntfs_device_win32_dismount(HANDLE handle) |
| { |
| DWORD i; |
| |
| if (!DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, |
| &i, NULL)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't dismount volume.\n"); |
| return -1; |
| } |
| ntfs_log_debug("Volume dismounted.\n"); |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_getsize - get file size via win32 API |
| * @handle: pointer the file HANDLE obtained via open |
| * |
| * Only works on ordinary files. |
| * |
| * Return The file size if o.k. |
| * -1 if not, and errno set. |
| */ |
| static s64 ntfs_device_win32_getsize(HANDLE handle) |
| { |
| LONG loword, hiword; |
| |
| SetLastError(NO_ERROR); |
| hiword = 0; |
| loword = SetFilePointer(handle, 0, &hiword, 2); |
| if ((loword == INVALID_SET_FILE_POINTER) |
| && (GetLastError() != NO_ERROR)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't get file size.\n"); |
| return -1; |
| } |
| return ((s64)hiword << 32) + (ULONG)loword; |
| } |
| |
| /** |
| * ntfs_device_win32_getdisklength - get disk size via win32 API |
| * @handle: pointer the file HANDLE obtained via open |
| * @argp: pointer to result buffer |
| * |
| * Only works on PhysicalDriveX type handles. |
| * |
| * Return The disk size if o.k. |
| * -1 if not, and errno set. |
| */ |
| static s64 ntfs_device_win32_getdisklength(HANDLE handle) |
| { |
| GET_LENGTH_INFORMATION buf; |
| DWORD i; |
| |
| if (!DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, |
| sizeof(buf), &i, NULL)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't get disk length.\n"); |
| return -1; |
| } |
| ntfs_log_debug("Disk length: %lld.\n", buf.Length.QuadPart); |
| return buf.Length.QuadPart; |
| } |
| |
| /** |
| * ntfs_device_win32_getntfssize - get NTFS volume size via win32 API |
| * @handle: pointer the file HANDLE obtained via open |
| * @argp: pointer to result buffer |
| * |
| * Only works on NTFS volume handles. |
| * An annoying bug in windows is that an NTFS volume does not occupy the entire |
| * partition, namely not the last sector (which holds the backup boot sector, |
| * and normally not interesting). |
| * Use this function to get the length of the accessible space through a given |
| * volume handle. |
| * |
| * Return The volume size if o.k. |
| * -1 if not, and errno set. |
| */ |
| static s64 ntfs_device_win32_getntfssize(HANDLE handle) |
| { |
| s64 rvl; |
| #ifdef FSCTL_GET_NTFS_VOLUME_DATA |
| DWORD i; |
| NTFS_VOLUME_DATA_BUFFER buf; |
| |
| if (!DeviceIoControl(handle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &buf, |
| sizeof(buf), &i, NULL)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't get NTFS volume length.\n"); |
| return -1; |
| } |
| rvl = buf.NumberSectors.QuadPart * buf.BytesPerSector; |
| ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl); |
| #else |
| errno = EINVAL; |
| rvl = -1; |
| #endif |
| return rvl; |
| } |
| |
| /** |
| * ntfs_device_win32_getgeo - get CHS information of a drive |
| * @handle: an open handle to the PhysicalDevice |
| * @fd: a win_fd structure that will be filled |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| * |
| * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1. |
| * In Windows XP+: fills size, sectors, cylinders, and heads. |
| * |
| * Note: In pre XP, this requires write permission, even though nothing is |
| * actually written. |
| * |
| * If fails, sets sectors, cylinders, heads, and size to -1. |
| */ |
| static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) |
| { |
| DWORD i; |
| BOOL rvl; |
| BYTE b[sizeof(DISK_GEOMETRY) + sizeof(DISK_PARTITION_INFO) + |
| sizeof(DISK_DETECTION_INFO) + 512]; |
| |
| rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, |
| 0, &b, sizeof(b), &i, NULL); |
| if (rvl) { |
| ntfs_log_debug("GET_DRIVE_GEOMETRY_EX detected.\n"); |
| DISK_DETECTION_INFO *ddi = (PDISK_DETECTION_INFO) |
| (((PBYTE)(&((PDISK_GEOMETRY_EX)b)->Data)) + |
| (((PDISK_PARTITION_INFO) |
| (&((PDISK_GEOMETRY_EX)b)->Data))-> |
| SizeOfPartitionInfo)); |
| fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; |
| fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; |
| fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart; |
| fd->geo_sector_size = NTFS_BLOCK_SIZE; |
| switch (ddi->DetectionType) { |
| case DetectInt13: |
| fd->geo_cylinders = ddi->Int13.MaxCylinders; |
| fd->geo_sectors = ddi->Int13.SectorsPerTrack; |
| fd->geo_heads = ddi->Int13.MaxHeads; |
| return 0; |
| case DetectExInt13: |
| fd->geo_cylinders = ddi->ExInt13.ExCylinders; |
| fd->geo_sectors = ddi->ExInt13.ExSectorsPerTrack; |
| fd->geo_heads = ddi->ExInt13.ExHeads; |
| return 0; |
| case DetectNone: |
| default: |
| break; |
| } |
| } else |
| fd->geo_heads = -1; |
| rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, |
| &b, sizeof(b), &i, NULL); |
| if (rvl) { |
| ntfs_log_debug("GET_DRIVE_GEOMETRY detected.\n"); |
| fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; |
| fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; |
| fd->geo_size = fd->geo_cylinders * fd->geo_sectors * |
| ((DISK_GEOMETRY*)&b)->TracksPerCylinder * |
| ((DISK_GEOMETRY*)&b)->BytesPerSector; |
| fd->geo_sector_size = ((DISK_GEOMETRY*)&b)->BytesPerSector; |
| return 0; |
| } |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("Couldn't retrieve disk geometry.\n"); |
| fd->geo_cylinders = -1; |
| fd->geo_sectors = -1; |
| fd->geo_size = -1; |
| fd->geo_sector_size = NTFS_BLOCK_SIZE; |
| return -1; |
| } |
| |
| static int ntfs_device_win32_getntgeo(HANDLE handle, win32_fd *fd) |
| { |
| DISK_GEOMETRY geo; |
| NTSTATUS st; |
| IO_STATUS_BLOCK status; |
| u64 bytes; |
| int res; |
| |
| res = -1; |
| fd->geo_cylinders = 0; |
| fd->geo_sectors = 0; |
| fd->geo_size = 1073741824; |
| fd->geo_sectors = fd->geo_size >> 9; |
| fd->geo_sector_size = NTFS_BLOCK_SIZE; |
| |
| st = NtDeviceIoControlFile(handle, (HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL, (void*)NULL, |
| &status, IOCTL_DISK_GET_DRIVE_GEOMETRY, (void*)NULL, 0, |
| (void*)&geo, sizeof(geo)); |
| if (st == STATUS_SUCCESS) { |
| /* over-estimate the (rounded) number of cylinders */ |
| fd->geo_cylinders = geo.Cylinders.QuadPart + 1; |
| fd->geo_sectors = fd->geo_cylinders |
| *geo.TracksPerCylinder*geo.SectorsPerTrack; |
| fd->geo_size = fd->geo_sectors*geo.BytesPerSector; |
| fd->geo_sector_size = geo.BytesPerSector; |
| res = 0; |
| /* try to get the exact sector count */ |
| st = NtDeviceIoControlFile(handle, (HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL, (void*)NULL, |
| &status, IOCTL_GET_DISK_LENGTH_INFO, |
| (void*)NULL, 0, |
| (void*)&bytes, sizeof(bytes)); |
| if (st == STATUS_SUCCESS) { |
| fd->geo_size = bytes; |
| fd->geo_sectors = bytes/geo.BytesPerSector; |
| } |
| } |
| return (res); |
| } |
| |
| /** |
| * ntfs_device_win32_open_file - open a file via win32 API |
| * @filename: name of the file to open |
| * @fd: pointer to win32 file device in which to put the result |
| * @flags: unix open status flags |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, |
| int flags) |
| { |
| HANDLE handle; |
| int mode; |
| |
| if (ntfs_device_win32_simple_open_file(filename, &handle, flags, |
| FALSE)) { |
| /* open error */ |
| return -1; |
| } |
| mode = flags & O_ACCMODE; |
| if ((mode == O_RDWR) || (mode == O_WRONLY)) { |
| DWORD bytes; |
| |
| /* try making sparse (but ignore errors) */ |
| DeviceIoControl(handle, FSCTL_SET_SPARSE, |
| (void*)NULL, 0, (void*)NULL, 0, |
| &bytes, (LPOVERLAPPED)NULL); |
| } |
| /* fill fd */ |
| fd->handle = handle; |
| fd->part_start = 0; |
| fd->part_length = ntfs_device_win32_getsize(handle); |
| fd->pos = 0; |
| fd->part_hidden_sectors = -1; |
| fd->geo_size = -1; /* used as a marker that this is a file */ |
| fd->vol_handle = INVALID_HANDLE_VALUE; |
| fd->geo_sector_size = 512; /* will be adjusted from the boot sector */ |
| fd->ntdll = FALSE; |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_open_drive - open a drive via win32 API |
| * @drive_id: drive to open |
| * @fd: pointer to win32 file device in which to put the result |
| * @flags: unix open status flags |
| * |
| * return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, |
| int flags) |
| { |
| HANDLE handle; |
| int err; |
| char filename[MAX_PATH]; |
| |
| sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id); |
| if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags, |
| TRUE))) { |
| /* open error */ |
| return err; |
| } |
| /* store the drive geometry */ |
| ntfs_device_win32_getgeo(handle, fd); |
| /* Just to be sure */ |
| if (fd->geo_size == -1) |
| fd->geo_size = ntfs_device_win32_getdisklength(handle); |
| /* fill fd */ |
| fd->ntdll = FALSE; |
| fd->handle = handle; |
| fd->part_start = 0; |
| fd->part_length = fd->geo_size; |
| fd->pos = 0; |
| fd->part_hidden_sectors = -1; |
| fd->vol_handle = INVALID_HANDLE_VALUE; |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_open_lowlevel - open a drive via low level win32 API |
| * @drive_id: drive to open |
| * @fd: pointer to win32 file device in which to put the result |
| * @flags: unix open status flags |
| * |
| * return 0 if o.k. |
| * -1 if not, and errno set. |
| */ |
| static __inline__ int ntfs_device_win32_open_lowlevel(int drive_id, |
| win32_fd *fd, int flags) |
| { |
| HANDLE handle; |
| NTSTATUS st; |
| ACCESS_MASK access; |
| ULONG share; |
| OBJECT_ATTRIBUTES attr; |
| IO_STATUS_BLOCK io_status; |
| UNICODE_STRING unicode_name; |
| ntfschar unicode_buffer[7]; |
| int mode; |
| static const ntfschar unicode_init[] = { |
| const_cpu_to_le16('\\'), const_cpu_to_le16('?'), |
| const_cpu_to_le16('?'), const_cpu_to_le16('\\'), |
| const_cpu_to_le16(' '), const_cpu_to_le16(':'), |
| const_cpu_to_le16(0) |
| }; |
| |
| memcpy(unicode_buffer, unicode_init, sizeof(unicode_buffer)); |
| unicode_buffer[4] = cpu_to_le16(drive_id + 'A'); |
| unicode_name.Buffer = unicode_buffer; |
| unicode_name.Length = 6*sizeof(ntfschar); |
| unicode_name.MaximumLength = 6*sizeof(ntfschar); |
| |
| attr.Length = sizeof(OBJECT_ATTRIBUTES); |
| attr.RootDirectory = (HANDLE*)NULL; |
| attr.ObjectName = &unicode_name; |
| attr.Attributes = OBJ_CASE_INSENSITIVE; |
| attr.SecurityDescriptor = (void*)NULL; |
| attr.SecurityQualityOfService = (void*)NULL; |
| |
| io_status.Status = 0; |
| io_status.Information = 0; |
| mode = flags & O_ACCMODE; |
| share = (mode == O_RDWR ? |
| 0 : FILE_SHARE_READ | FILE_SHARE_WRITE); |
| access = (mode == O_RDWR ? |
| FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE |
| : FILE_READ_DATA | SYNCHRONIZE); |
| |
| st = NtOpenFile(&handle, access, |
| &attr, &io_status, |
| share, |
| FILE_SYNCHRONOUS_IO_ALERT); |
| if (st != STATUS_SUCCESS) { |
| errno = ntfs_ntstatus_to_errno(st); |
| return (-1); |
| } |
| ntfs_device_win32_setlock(handle,FSCTL_LOCK_VOLUME); |
| /* store the drive geometry */ |
| ntfs_device_win32_getntgeo(handle, fd); |
| fd->ntdll = TRUE; |
| /* allow accessing the full partition */ |
| st = NtFsControlFile(handle, (HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL, |
| (PVOID)NULL, &io_status, |
| FSCTL_ALLOW_EXTENDED_DASD_IO, |
| NULL, 0, NULL, 0); |
| if (st != STATUS_SUCCESS) { |
| errno = ntfs_ntstatus_to_errno(st); |
| NtClose(handle); |
| return (-1); |
| } |
| /* fill fd */ |
| fd->handle = handle; |
| fd->part_start = 0; |
| fd->part_length = fd->geo_size; |
| fd->pos = 0; |
| fd->part_hidden_sectors = -1; |
| fd->vol_handle = INVALID_HANDLE_VALUE; |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_open_volume_for_partition - find and open a volume |
| * |
| * Windows NT/2k/XP handles volumes instead of partitions. |
| * This function gets the partition details and return an open volume handle. |
| * That volume is the one whose only physical location on disk is the described |
| * partition. |
| * |
| * The function required Windows 2k/XP, otherwise it fails (gracefully). |
| * |
| * Return success: a valid open volume handle. |
| * fail : INVALID_HANDLE_VALUE |
| */ |
| static HANDLE ntfs_device_win32_open_volume_for_partition(unsigned int drive_id, |
| s64 part_offset, s64 part_length, int flags) |
| { |
| HANDLE vol_find_handle; |
| TCHAR vol_name[MAX_PATH]; |
| |
| /* Make sure all the required imports exist. */ |
| if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) { |
| ntfs_log_trace("Required dll imports not found.\n"); |
| return INVALID_HANDLE_VALUE; |
| } |
| /* Start iterating through volumes. */ |
| ntfs_log_trace("Entering with drive_id=%d, part_offset=%lld, " |
| "path_length=%lld, flags=%d.\n", drive_id, |
| (unsigned long long)part_offset, |
| (unsigned long long)part_length, flags); |
| vol_find_handle = fnFindFirstVolume(vol_name, MAX_PATH); |
| /* If a valid handle could not be aquired, reply with "don't know". */ |
| if (vol_find_handle == INVALID_HANDLE_VALUE) { |
| ntfs_log_trace("FindFirstVolume failed.\n"); |
| return INVALID_HANDLE_VALUE; |
| } |
| do { |
| int vol_name_length; |
| HANDLE handle; |
| |
| /* remove trailing '/' from vol_name */ |
| #ifdef UNICODE |
| vol_name_length = wcslen(vol_name); |
| #else |
| vol_name_length = strlen(vol_name); |
| #endif |
| if (vol_name_length>0) |
| vol_name[vol_name_length-1]=0; |
| |
| ntfs_log_debug("Processing %s.\n", vol_name); |
| /* open the file */ |
| handle = CreateFile(vol_name, |
| ntfs_device_unix_status_flags_to_win32(flags), |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| OPEN_EXISTING, 0, NULL); |
| if (handle != INVALID_HANDLE_VALUE) { |
| DWORD bytesReturned; |
| #define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT) |
| char extents[EXTENTS_SIZE]; |
| |
| /* Check physical locations. */ |
| if (DeviceIoControl(handle, |
| IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, |
| NULL, 0, extents, EXTENTS_SIZE, |
| &bytesReturned, NULL)) { |
| if (((VOLUME_DISK_EXTENTS *)extents)-> |
| NumberOfDiskExtents == 1) { |
| DISK_EXTENT *extent = &(( |
| VOLUME_DISK_EXTENTS *) |
| extents)->Extents[0]; |
| if ((extent->DiskNumber==drive_id) && |
| (extent->StartingOffset. |
| QuadPart==part_offset) |
| && (extent-> |
| ExtentLength.QuadPart |
| == part_length)) { |
| /* |
| * Eureka! (Archimedes, 287 BC, |
| * "I have found it!") |
| */ |
| fnFindVolumeClose( |
| vol_find_handle); |
| return handle; |
| } |
| } |
| } |
| } else |
| ntfs_log_trace("getExtents() Failed.\n"); |
| } while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH)); |
| /* End of iteration through volumes. */ |
| ntfs_log_trace("Closing, volume was not found.\n"); |
| fnFindVolumeClose(vol_find_handle); |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| /** |
| * ntfs_device_win32_find_partition - locates partition details by id. |
| * @handle: HANDLE to the PhysicalDrive |
| * @partition_id: the partition number to locate |
| * @part_offset: pointer to where to put the offset to the partition |
| * @part_length: pointer to where to put the length of the partition |
| * @hidden_sectors: pointer to where to put the hidden sectors |
| * |
| * This function requires an open PhysicalDrive handle and a partition_id. |
| * If a partition with the required id is found on the supplied device, |
| * the partition attributes are returned back. |
| * |
| * Returns: TRUE if found, and sets the output parameters. |
| * FALSE if not and errno is set to the error code. |
| */ |
| static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id, |
| s64 *part_offset, s64 *part_length, int *hidden_sectors) |
| { |
| DRIVE_LAYOUT_INFORMATION *drive_layout; |
| unsigned int err, buf_size, part_count; |
| DWORD i; |
| |
| /* |
| * There is no way to know the required buffer, so if the ioctl fails, |
| * try doubling the buffer size each time until the ioctl succeeds. |
| */ |
| part_count = 8; |
| do { |
| buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) + |
| part_count * sizeof(PARTITION_INFORMATION); |
| drive_layout = (DRIVE_LAYOUT_INFORMATION*)ntfs_malloc(buf_size); |
| if (!drive_layout) { |
| errno = ENOMEM; |
| return FALSE; |
| } |
| if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, |
| 0, (BYTE*)drive_layout, buf_size, &i, NULL)) |
| break; |
| err = GetLastError(); |
| free(drive_layout); |
| if (err != ERROR_INSUFFICIENT_BUFFER) { |
| ntfs_log_trace("GetDriveLayout failed.\n"); |
| errno = ntfs_w32error_to_errno(err); |
| return FALSE; |
| } |
| ntfs_log_debug("More than %u partitions.\n", part_count); |
| part_count <<= 1; |
| if (part_count > 512) { |
| ntfs_log_trace("GetDriveLayout failed: More than 512 " |
| "partitions?\n"); |
| errno = ENOBUFS; |
| return FALSE; |
| } |
| } while (1); |
| for (i = 0; i < drive_layout->PartitionCount; i++) { |
| if (drive_layout->PartitionEntry[i].PartitionNumber == |
| partition_id) { |
| *part_offset = drive_layout->PartitionEntry[i]. |
| StartingOffset.QuadPart; |
| *part_length = drive_layout->PartitionEntry[i]. |
| PartitionLength.QuadPart; |
| *hidden_sectors = drive_layout->PartitionEntry[i]. |
| HiddenSectors; |
| free(drive_layout); |
| return TRUE; |
| } |
| } |
| free(drive_layout); |
| errno = ENOENT; |
| return FALSE; |
| } |
| |
| /** |
| * ntfs_device_win32_open_partition - open a partition via win32 API |
| * @drive_id: drive to open |
| * @partition_id: partition to open |
| * @fd: win32 file device to return |
| * @flags: unix open status flags |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| * |
| * When fails, fd contents may have not been preserved. |
| */ |
| static int ntfs_device_win32_open_partition(int drive_id, |
| unsigned int partition_id, win32_fd *fd, int flags) |
| { |
| s64 part_start, part_length; |
| HANDLE handle; |
| int err, hidden_sectors; |
| char drive_name[MAX_PATH]; |
| |
| sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id); |
| /* Open the entire device without locking, ask questions later */ |
| if ((err = ntfs_device_win32_simple_open_file(drive_name, &handle, |
| flags, FALSE))) { |
| /* error */ |
| return err; |
| } |
| if (ntfs_device_win32_find_partition(handle, partition_id, &part_start, |
| &part_length, &hidden_sectors)) { |
| s64 tmp; |
| HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition( |
| drive_id, part_start, part_length, flags); |
| /* Store the drive geometry. */ |
| ntfs_device_win32_getgeo(handle, fd); |
| fd->handle = handle; |
| fd->pos = 0; |
| fd->part_start = part_start; |
| fd->part_length = part_length; |
| fd->part_hidden_sectors = hidden_sectors; |
| fd->geo_sector_size = 512; |
| fd->ntdll = FALSE; |
| tmp = ntfs_device_win32_getntfssize(vol_handle); |
| if (tmp > 0) |
| fd->geo_size = tmp; |
| else |
| fd->geo_size = fd->part_length; |
| if (vol_handle != INVALID_HANDLE_VALUE) { |
| if (((flags & O_RDWR) == O_RDWR) && |
| ntfs_device_win32_lock(vol_handle)) { |
| CloseHandle(vol_handle); |
| CloseHandle(handle); |
| return -1; |
| } |
| fd->vol_handle = vol_handle; |
| } else { |
| if ((flags & O_RDWR) == O_RDWR) { |
| /* Access if read-write, no volume found. */ |
| ntfs_log_trace("Partitions containing Spanned/" |
| "Mirrored volumes are not " |
| "supported in R/W status " |
| "yet.\n"); |
| CloseHandle(handle); |
| errno = EOPNOTSUPP; |
| return -1; |
| } |
| fd->vol_handle = INVALID_HANDLE_VALUE; |
| } |
| return 0; |
| } else { |
| ntfs_log_debug("Partition %u not found on drive %d.\n", |
| partition_id, drive_id); |
| CloseHandle(handle); |
| errno = ENODEV; |
| return -1; |
| } |
| } |
| |
| /** |
| * ntfs_device_win32_open - open a device |
| * @dev: a pointer to the NTFS_DEVICE to open |
| * @flags: unix open status flags |
| * |
| * @dev->d_name must hold the device name, the rest is ignored. |
| * Supported flags are O_RDONLY, O_WRONLY and O_RDWR. |
| * |
| * If name is in format "(hd[0-9],[0-9])" then open a partition. |
| * If name is in format "(hd[0-9])" then open a volume. |
| * Otherwise open a file. |
| */ |
| static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) |
| { |
| int drive_id = 0, numparams; |
| unsigned int part = 0; |
| char drive_char; |
| win32_fd fd; |
| int err; |
| |
| if (NDevOpen(dev)) { |
| errno = EBUSY; |
| return -1; |
| } |
| ntfs_device_win32_init_imports(); |
| numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part); |
| if (!numparams |
| && (dev->d_name[1] == ':') |
| && (dev->d_name[2] == '\0')) { |
| drive_char = dev->d_name[0]; |
| numparams = 3; |
| drive_id = toupper(drive_char) - 'A'; |
| } |
| switch (numparams) { |
| case 0: |
| ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name); |
| err = ntfs_device_win32_open_file(dev->d_name, &fd, flags); |
| break; |
| case 1: |
| ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev->d_name, |
| drive_id); |
| err = ntfs_device_win32_open_drive(drive_id, &fd, flags); |
| break; |
| case 2: |
| ntfs_log_debug("win32_open(%s) -> drive %d, part %u.\n", |
| dev->d_name, drive_id, part); |
| err = ntfs_device_win32_open_partition(drive_id, part, &fd, |
| flags); |
| break; |
| case 3: |
| ntfs_log_debug("win32_open(%s) -> drive %c:\n", |
| dev->d_name, drive_char); |
| err = ntfs_device_win32_open_lowlevel(drive_id, &fd, |
| flags); |
| break; |
| default: |
| ntfs_log_debug("win32_open(%s) -> unknwon file format.\n", |
| dev->d_name); |
| err = -1; |
| } |
| if (err) |
| return err; |
| ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev->d_name, |
| dev, fd.part_start); |
| /* Setup our read-only flag. */ |
| if ((flags & O_RDWR) != O_RDWR) |
| NDevSetReadOnly(dev); |
| dev->d_private = (win32_fd*)ntfs_malloc(sizeof(win32_fd)); |
| memcpy(dev->d_private, &fd, sizeof(win32_fd)); |
| NDevSetOpen(dev); |
| NDevClearDirty(dev); |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_seek - change current logical file position |
| * @dev: ntfs device obtained via ->open |
| * @offset: required offset from the whence anchor |
| * @whence: whence anchor specifying what @offset is relative to |
| * |
| * Return the new position on the volume on success and -1 on error with errno |
| * set to the error code. |
| * |
| * @whence may be one of the following: |
| * SEEK_SET - Offset is relative to file start. |
| * SEEK_CUR - Offset is relative to current position. |
| * SEEK_END - Offset is relative to end of file. |
| */ |
| static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, |
| int whence) |
| { |
| s64 abs_ofs; |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| |
| ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence); |
| switch (whence) { |
| case SEEK_SET: |
| abs_ofs = offset; |
| break; |
| case SEEK_CUR: |
| abs_ofs = fd->pos + offset; |
| break; |
| case SEEK_END: |
| /* End of partition != end of disk. */ |
| if (fd->part_length == -1) { |
| ntfs_log_trace("Position relative to end of disk not " |
| "implemented.\n"); |
| errno = EOPNOTSUPP; |
| return -1; |
| } |
| abs_ofs = fd->part_length + offset; |
| break; |
| default: |
| ntfs_log_trace("Wrong mode %d.\n", whence); |
| errno = EINVAL; |
| return -1; |
| } |
| if ((abs_ofs < 0) |
| || (fd->ntdll && (abs_ofs > fd->part_length))) { |
| ntfs_log_trace("Seeking outsize seekable area.\n"); |
| errno = EINVAL; |
| return -1; |
| } |
| fd->pos = abs_ofs; |
| return abs_ofs; |
| } |
| |
| /** |
| * ntfs_device_win32_pio - positioned low level i/o |
| * @fd: win32 device descriptor obtained via ->open |
| * @pos: at which position to do i/o from/to |
| * @count: how many bytes should be transfered |
| * @b: source/destination buffer |
| * @write: TRUE if write transfer and FALSE if read transfer |
| * |
| * On success returns the number of bytes transfered (can be < @count) and on |
| * error returns -1 and errno set. Transfer starts from position @pos on @fd. |
| * |
| * Notes: |
| * - @pos, @buf, and @count must be aligned to geo_sector_size |
| * - When dealing with volumes, a single call must not span both volume |
| * and disk extents. |
| * - Does not use/set @fd->pos. |
| */ |
| static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, |
| const s64 count, void *rbuf, const void *wbuf) |
| { |
| LARGE_INTEGER li; |
| HANDLE handle; |
| DWORD bt; |
| BOOL res; |
| s64 bytes; |
| |
| ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n", |
| (long long)pos, (long long)count, write ? "write" : |
| "read"); |
| li.QuadPart = pos; |
| if (fd->vol_handle != INVALID_HANDLE_VALUE && pos < fd->geo_size) { |
| ntfs_log_debug("Transfering via vol_handle.\n"); |
| handle = fd->vol_handle; |
| } else { |
| ntfs_log_debug("Transfering via handle.\n"); |
| handle = fd->handle; |
| li.QuadPart += fd->part_start; |
| } |
| |
| if (fd->ntdll) { |
| IO_STATUS_BLOCK io_status; |
| NTSTATUS res; |
| LARGE_INTEGER offset; |
| |
| io_status.Status = STATUS_SUCCESS; |
| io_status.Information = 0; |
| offset.QuadPart = pos; |
| if (wbuf) { |
| res = NtWriteFile(fd->handle,(HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL,(void*)NULL, |
| &io_status, wbuf, count, |
| &offset, (PULONG)NULL); |
| } else { |
| res = NtReadFile(fd->handle,(HANDLE)NULL, |
| (PIO_APC_ROUTINE)NULL,(void*)NULL, |
| &io_status, rbuf, count, |
| &offset, (PULONG)NULL); |
| } |
| if (res == STATUS_SUCCESS) { |
| bytes = io_status.Information; |
| } else { |
| bytes = -1; |
| errno = ntfs_ntstatus_to_errno(res); |
| } |
| } else { |
| if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("SetFilePointer failed.\n"); |
| return -1; |
| } |
| if (wbuf) |
| res = WriteFile(handle, wbuf, count, &bt, NULL); |
| else |
| res = ReadFile(handle, rbuf, count, &bt, NULL); |
| bytes = bt; |
| if (!res) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("%sFile() failed.\n", write ? |
| "Write" : "Read"); |
| return -1; |
| } |
| if (rbuf && !pos) { |
| /* get the sector size from the boot sector */ |
| char *boot = (char*)rbuf; |
| fd->geo_sector_size = (boot[11] & 255) |
| + ((boot[12] & 255) << 8); |
| } |
| } |
| return bytes; |
| } |
| |
| /** |
| * ntfs_device_win32_pread_simple - positioned simple read |
| * @fd: win32 device descriptor obtained via ->open |
| * @pos: at which position to read from |
| * @count: how many bytes should be read |
| * @b: a pointer to where to put the contents |
| * |
| * On success returns the number of bytes read (can be < @count) and on error |
| * returns -1 and errno set. Read starts from position @pos. |
| * |
| * Notes: |
| * - @pos, @buf, and @count must be aligned to geo_sector_size. |
| * - When dealing with volumes, a single call must not span both volume |
| * and disk extents. |
| * - Does not use/set @fd->pos. |
| */ |
| static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos, |
| const s64 count, void *b) |
| { |
| return ntfs_device_win32_pio(fd, pos, count, b, (void*)NULL); |
| } |
| |
| /** |
| * ntfs_device_win32_read - read bytes from an ntfs device |
| * @dev: ntfs device obtained via ->open |
| * @b: pointer to where to put the contents |
| * @count: how many bytes should be read |
| * |
| * On success returns the number of bytes actually read (can be < @count). |
| * On error returns -1 with errno set. |
| */ |
| static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) |
| { |
| s64 old_pos, to_read, i, br = 0; |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| BYTE *alignedbuffer; |
| int old_ofs, ofs; |
| |
| old_pos = fd->pos; |
| old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); |
| to_read = (ofs + count + fd->geo_sector_size - 1) & |
| ~(s64)(fd->geo_sector_size - 1); |
| /* Impose maximum of 2GB to be on the safe side. */ |
| if (to_read > 0x80000000) { |
| int delta = to_read - count; |
| to_read = 0x80000000; |
| count = to_read - delta; |
| } |
| ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " |
| "ofs = %i, to_read = 0x%llx.\n", fd, b, |
| (long long)count, (long long)old_pos, ofs, |
| (long long)to_read); |
| if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && |
| !(count & (fd->geo_sector_size - 1))) |
| alignedbuffer = b; |
| else { |
| alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT, |
| PAGE_READWRITE); |
| if (!alignedbuffer) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("VirtualAlloc failed for read.\n"); |
| return -1; |
| } |
| } |
| if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { |
| s64 vol_to_read = fd->geo_size - old_pos; |
| if (count > vol_to_read) { |
| br = ntfs_device_win32_pread_simple(fd, |
| old_pos & ~(s64)(fd->geo_sector_size - 1), |
| ofs + vol_to_read, alignedbuffer); |
| if (br == -1) |
| goto read_error; |
| to_read -= br; |
| if (br < ofs) { |
| br = 0; |
| goto read_partial; |
| } |
| br -= ofs; |
| fd->pos += br; |
| ofs = fd->pos & (fd->geo_sector_size - 1); |
| if (br != vol_to_read) |
| goto read_partial; |
| } |
| } |
| i = ntfs_device_win32_pread_simple(fd, |
| fd->pos & ~(s64)(fd->geo_sector_size - 1), to_read, |
| alignedbuffer + br); |
| if (i == -1) { |
| if (br) |
| goto read_partial; |
| goto read_error; |
| } |
| if (i < ofs) |
| goto read_partial; |
| i -= ofs; |
| br += i; |
| if (br > count) |
| br = count; |
| fd->pos = old_pos + br; |
| read_partial: |
| if (alignedbuffer != b) { |
| memcpy((void*)b, alignedbuffer + old_ofs, br); |
| VirtualFree(alignedbuffer, 0, MEM_RELEASE); |
| } |
| return br; |
| read_error: |
| if (alignedbuffer != b) |
| VirtualFree(alignedbuffer, 0, MEM_RELEASE); |
| return -1; |
| } |
| |
| /** |
| * ntfs_device_win32_close - close an open ntfs deivce |
| * @dev: ntfs device obtained via ->open |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. Note if error fd->vol_handle is trashed. |
| */ |
| static int ntfs_device_win32_close(struct ntfs_device *dev) |
| { |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| BOOL rvl; |
| |
| ntfs_log_trace("Closing device %p.\n", dev); |
| if (!NDevOpen(dev)) { |
| errno = EBADF; |
| return -1; |
| } |
| if (fd->vol_handle != INVALID_HANDLE_VALUE) { |
| if (!NDevReadOnly(dev)) { |
| ntfs_device_win32_dismount(fd->vol_handle); |
| ntfs_device_win32_unlock(fd->vol_handle); |
| } |
| if (!CloseHandle(fd->vol_handle)) |
| ntfs_log_trace("CloseHandle() failed for volume.\n"); |
| } |
| if (fd->ntdll) { |
| ntfs_device_win32_setlock(fd->handle,FSCTL_UNLOCK_VOLUME); |
| rvl = NtClose(fd->handle) == STATUS_SUCCESS; |
| } else |
| rvl = CloseHandle(fd->handle); |
| NDevClearOpen(dev); |
| free(fd); |
| if (!rvl) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| if (fd->ntdll) |
| ntfs_log_trace("NtClose() failed.\n"); |
| else |
| ntfs_log_trace("CloseHandle() failed.\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_sync - flush write buffers to disk |
| * @dev: ntfs device obtained via ->open |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. |
| * |
| * Note: Volume syncing works differently in windows. |
| * Disk cannot be synced in windows. |
| */ |
| static int ntfs_device_win32_sync(struct ntfs_device *dev) |
| { |
| int err = 0; |
| BOOL to_clear = TRUE; |
| |
| if (!NDevReadOnly(dev) && NDevDirty(dev)) { |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| |
| if ((fd->vol_handle != INVALID_HANDLE_VALUE) && |
| !FlushFileBuffers(fd->vol_handle)) { |
| to_clear = FALSE; |
| err = ntfs_w32error_to_errno(GetLastError()); |
| } |
| if (!FlushFileBuffers(fd->handle)) { |
| to_clear = FALSE; |
| if (!err) |
| err = ntfs_w32error_to_errno(GetLastError()); |
| } |
| if (!to_clear) { |
| ntfs_log_trace("Could not sync.\n"); |
| errno = err; |
| return -1; |
| } |
| NDevClearDirty(dev); |
| } |
| return 0; |
| } |
| |
| /** |
| * ntfs_device_win32_pwrite_simple - positioned simple write |
| * @fd: win32 device descriptor obtained via ->open |
| * @pos: at which position to write to |
| * @count: how many bytes should be written |
| * @b: a pointer to the data to write |
| * |
| * On success returns the number of bytes written and on error returns -1 and |
| * errno set. Write starts from position @pos. |
| * |
| * Notes: |
| * - @pos, @buf, and @count must be aligned to geo_sector_size. |
| * - When dealing with volumes, a single call must not span both volume |
| * and disk extents. |
| * - Does not use/set @fd->pos. |
| */ |
| static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos, |
| const s64 count, const void *b) |
| { |
| return ntfs_device_win32_pio(fd, pos, count, (void*)NULL, b); |
| } |
| |
| /** |
| * ntfs_device_win32_write - write bytes to an ntfs device |
| * @dev: ntfs device obtained via ->open |
| * @b: pointer to the data to write |
| * @count: how many bytes should be written |
| * |
| * On success returns the number of bytes actually written. |
| * On error returns -1 with errno set. |
| */ |
| static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, |
| s64 count) |
| { |
| s64 old_pos, to_write, i, bw = 0; |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| const BYTE *alignedbuffer; |
| BYTE *readbuffer; |
| int old_ofs, ofs; |
| |
| old_pos = fd->pos; |
| old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); |
| to_write = (ofs + count + fd->geo_sector_size - 1) & |
| ~(s64)(fd->geo_sector_size - 1); |
| /* Impose maximum of 2GB to be on the safe side. */ |
| if (to_write > 0x80000000) { |
| int delta = to_write - count; |
| to_write = 0x80000000; |
| count = to_write - delta; |
| } |
| ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, " |
| "ofs = %i, to_write = 0x%llx.\n", fd, b, |
| (long long)count, (long long)old_pos, ofs, |
| (long long)to_write); |
| if (NDevReadOnly(dev)) { |
| ntfs_log_trace("Can't write on a R/O device.\n"); |
| errno = EROFS; |
| return -1; |
| } |
| if (!count) |
| return 0; |
| NDevSetDirty(dev); |
| readbuffer = (BYTE*)NULL; |
| if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && |
| !(count & (fd->geo_sector_size - 1))) |
| alignedbuffer = (const BYTE *)b; |
| else { |
| s64 end; |
| |
| readbuffer = (BYTE *)VirtualAlloc(NULL, to_write, |
| MEM_COMMIT, PAGE_READWRITE); |
| if (!readbuffer) { |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("VirtualAlloc failed for write.\n"); |
| return -1; |
| } |
| /* Read first sector if start of write not sector aligned. */ |
| if (ofs) { |
| i = ntfs_device_win32_pread_simple(fd, |
| old_pos & ~(s64)(fd->geo_sector_size - 1), |
| fd->geo_sector_size, readbuffer); |
| if (i != fd->geo_sector_size) { |
| if (i >= 0) |
| errno = EIO; |
| goto write_error; |
| } |
| } |
| /* |
| * Read last sector if end of write not sector aligned and last |
| * sector is either not the same as the first sector or it is |
| * the same as the first sector but this has not been read in |
| * yet, i.e. the start of the write is sector aligned. |
| */ |
| end = old_pos + count; |
| if ((end & (fd->geo_sector_size - 1)) && |
| ((to_write > fd->geo_sector_size) || !ofs)) { |
| i = ntfs_device_win32_pread_simple(fd, |
| end & ~(s64)(fd->geo_sector_size - 1), |
| fd->geo_sector_size, readbuffer + |
| to_write - fd->geo_sector_size); |
| if (i != fd->geo_sector_size) { |
| if (i >= 0) |
| errno = EIO; |
| goto write_error; |
| } |
| } |
| /* Copy the data to be written into @readbuffer. */ |
| memcpy(readbuffer + ofs, b, count); |
| alignedbuffer = readbuffer; |
| } |
| if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) { |
| s64 vol_to_write = fd->geo_size - old_pos; |
| if (count > vol_to_write) { |
| bw = ntfs_device_win32_pwrite_simple(fd, |
| old_pos & ~(s64)(fd->geo_sector_size - 1), |
| ofs + vol_to_write, alignedbuffer); |
| if (bw == -1) |
| goto write_error; |
| to_write -= bw; |
| if (bw < ofs) { |
| bw = 0; |
| goto write_partial; |
| } |
| bw -= ofs; |
| fd->pos += bw; |
| ofs = fd->pos & (fd->geo_sector_size - 1); |
| if (bw != vol_to_write) |
| goto write_partial; |
| } |
| } |
| i = ntfs_device_win32_pwrite_simple(fd, |
| fd->pos & ~(s64)(fd->geo_sector_size - 1), to_write, |
| alignedbuffer + bw); |
| if (i == -1) { |
| if (bw) |
| goto write_partial; |
| goto write_error; |
| } |
| if (i < ofs) |
| goto write_partial; |
| i -= ofs; |
| bw += i; |
| if (bw > count) |
| bw = count; |
| fd->pos = old_pos + bw; |
| write_partial: |
| if (readbuffer) |
| VirtualFree(readbuffer, 0, MEM_RELEASE); |
| return bw; |
| write_error: |
| bw = -1; |
| goto write_partial; |
| } |
| |
| /** |
| * ntfs_device_win32_stat - get a unix-like stat structure for an ntfs device |
| * @dev: ntfs device obtained via ->open |
| * @buf: pointer to the stat structure to fill |
| * |
| * Note: Only st_mode, st_size, and st_blocks are filled. |
| * |
| * Return 0 if o.k. |
| * -1 if not and errno set. in this case handle is trashed. |
| */ |
| static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) |
| { |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| mode_t st_mode; |
| |
| if ((dev->d_name[1] == ':') && (dev->d_name[2] == '\0')) |
| st_mode = S_IFBLK; |
| else |
| switch (GetFileType(fd->handle)) { |
| case FILE_TYPE_CHAR: |
| st_mode = S_IFCHR; |
| break; |
| case FILE_TYPE_DISK: |
| st_mode = S_IFREG; |
| break; |
| case FILE_TYPE_PIPE: |
| st_mode = S_IFIFO; |
| break; |
| default: |
| st_mode = 0; |
| } |
| memset(buf, 0, sizeof(struct stat)); |
| buf->st_mode = st_mode; |
| buf->st_size = fd->part_length; |
| if (buf->st_size != -1) |
| buf->st_blocks = buf->st_size >> 9; |
| else |
| buf->st_size = 0; |
| return 0; |
| } |
| |
| #ifdef HDIO_GETGEO |
| /** |
| * ntfs_win32_hdio_getgeo - get drive geometry |
| * @dev: ntfs device obtained via ->open |
| * @argp: pointer to where to put the output |
| * |
| * Note: Works on windows NT/2k/XP only. |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. Note if error fd->handle is trashed. |
| */ |
| static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev, |
| struct hd_geometry *argp) |
| { |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| |
| argp->heads = fd->geo_heads; |
| argp->sectors = fd->geo_sectors; |
| argp->cylinders = fd->geo_cylinders; |
| argp->start = fd->part_hidden_sectors; |
| return 0; |
| } |
| #endif |
| |
| /** |
| * ntfs_win32_blksszget - get block device sector size |
| * @dev: ntfs device obtained via ->open |
| * @argp: pointer to where to put the output |
| * |
| * Note: Works on windows NT/2k/XP only. |
| * |
| * Return 0 if o.k. |
| * -1 if not, and errno set. Note if error fd->handle is trashed. |
| */ |
| static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) |
| { |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| DWORD bytesReturned; |
| DISK_GEOMETRY dg; |
| |
| if (DeviceIoControl(fd->handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, |
| &dg, sizeof(DISK_GEOMETRY), &bytesReturned, NULL)) { |
| /* success */ |
| *argp = dg.BytesPerSector; |
| return 0; |
| } |
| errno = ntfs_w32error_to_errno(GetLastError()); |
| ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n"); |
| return -1; |
| } |
| |
| static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, |
| void *argp) |
| { |
| #if defined(BLKGETSIZE) | defined(BLKGETSIZE64) |
| win32_fd *fd = (win32_fd *)dev->d_private; |
| #endif |
| |
| ntfs_log_trace("win32_ioctl(%d) called.\n", request); |
| switch (request) { |
| #if defined(BLKGETSIZE) |
| case BLKGETSIZE: |
| ntfs_log_debug("BLKGETSIZE detected.\n"); |
| if (fd->part_length >= 0) { |
| *(int *)argp = (int)(fd->part_length / 512); |
| return 0; |
| } |
| errno = EOPNOTSUPP; |
| return -1; |
| #endif |
| #if defined(BLKGETSIZE64) |
| case BLKGETSIZE64: |
| ntfs_log_debug("BLKGETSIZE64 detected.\n"); |
| if (fd->part_length >= 0) { |
| *(s64 *)argp = fd->part_length; |
| return 0; |
| } |
| errno = EOPNOTSUPP; |
| return -1; |
| #endif |
| #ifdef HDIO_GETGEO |
| case HDIO_GETGEO: |
| ntfs_log_debug("HDIO_GETGEO detected.\n"); |
| return ntfs_win32_hdio_getgeo(dev, (struct hd_geometry *)argp); |
| #endif |
| #ifdef BLKSSZGET |
| case BLKSSZGET: |
| ntfs_log_debug("BLKSSZGET detected.\n"); |
| if (fd && !fd->ntdll) { |
| *(int*)argp = fd->geo_sector_size; |
| return (0); |
| } else |
| return ntfs_win32_blksszget(dev, (int *)argp); |
| #endif |
| #ifdef BLKBSZSET |
| case BLKBSZSET: |
| ntfs_log_debug("BLKBSZSET detected.\n"); |
| /* Nothing to do on Windows. */ |
| return 0; |
| #endif |
| default: |
| ntfs_log_debug("unimplemented ioctl %d.\n", request); |
| errno = EOPNOTSUPP; |
| return -1; |
| } |
| } |
| |
| static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b, |
| s64 count, s64 offset) |
| { |
| s64 got; |
| win32_fd *fd; |
| |
| /* read the fast way if sector aligned */ |
| fd = (win32_fd*)dev->d_private; |
| if (!((count | offset) & (fd->geo_sector_size - 1))) { |
| got = ntfs_device_win32_pio(fd, offset, count, b, (void*)NULL); |
| } else { |
| if (ntfs_device_win32_seek(dev, offset, 0) == -1) |
| got = 0; |
| else |
| got = ntfs_device_win32_read(dev, b, count); |
| } |
| |
| return (got); |
| } |
| |
| static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b, |
| s64 count, s64 offset) |
| { |
| s64 put; |
| win32_fd *fd; |
| |
| /* write the fast way if sector aligned */ |
| fd = (win32_fd*)dev->d_private; |
| if (!((count | offset) & (fd->geo_sector_size - 1))) { |
| put = ntfs_device_win32_pio(fd, offset, count, (void*)NULL, b); |
| } else { |
| if (ntfs_device_win32_seek(dev, offset, 0) == -1) |
| put = 0; |
| else |
| put = ntfs_device_win32_write(dev, b, count); |
| } |
| return (put); |
| } |
| |
| struct ntfs_device_operations ntfs_device_win32_io_ops = { |
| .open = ntfs_device_win32_open, |
| .close = ntfs_device_win32_close, |
| .seek = ntfs_device_win32_seek, |
| .read = ntfs_device_win32_read, |
| .write = ntfs_device_win32_write, |
| .pread = ntfs_device_win32_pread, |
| .pwrite = ntfs_device_win32_pwrite, |
| .sync = ntfs_device_win32_sync, |
| .stat = ntfs_device_win32_stat, |
| .ioctl = ntfs_device_win32_ioctl |
| }; |
| |
| /* |
| * Mark an open file as sparse |
| * |
| * This is only called by ntfsclone when cloning a volume to a file. |
| * The argument is the target file, not a volume. |
| * |
| * Returns 0 if successful. |
| */ |
| |
| int ntfs_win32_set_sparse(int fd) |
| { |
| BOOL ok; |
| HANDLE handle; |
| DWORD bytes; |
| |
| handle = get_osfhandle(fd); |
| if (handle == INVALID_HANDLE_VALUE) |
| ok = FALSE; |
| else |
| ok = DeviceIoControl(handle, FSCTL_SET_SPARSE, |
| (void*)NULL, 0, (void*)NULL, 0, |
| &bytes, (LPOVERLAPPED)NULL); |
| return (!ok); |
| } |
| |
| /* |
| * Resize an open file |
| * |
| * This is only called by ntfsclone when cloning a volume to a file. |
| * The argument must designate a file, not a volume. |
| * |
| * Returns 0 if successful. |
| */ |
| |
| static int win32_ftruncate(HANDLE handle, s64 size) |
| { |
| BOOL ok; |
| LONG hsize, lsize; |
| LONG ohsize, olsize; |
| |
| if (handle == INVALID_HANDLE_VALUE) |
| ok = FALSE; |
| else { |
| SetLastError(NO_ERROR); |
| /* save original position */ |
| ohsize = 0; |
| olsize = SetFilePointer(handle, 0, &ohsize, 1); |
| hsize = size >> 32; |
| lsize = size & 0xffffffff; |
| ok = (SetFilePointer(handle, lsize, &hsize, 0) == (DWORD)lsize) |
| && (GetLastError() == NO_ERROR) |
| && SetEndOfFile(handle); |
| /* restore original position, even if above failed */ |
| SetFilePointer(handle, olsize, &ohsize, 0); |
| if (GetLastError() != NO_ERROR) |
| ok = FALSE; |
| } |
| if (!ok) |
| errno = EINVAL; |
| return (ok ? 0 : -1); |
| } |
| |
| int ntfs_device_win32_ftruncate(struct ntfs_device *dev, s64 size) |
| { |
| win32_fd *fd; |
| int ret; |
| |
| ret = -1; |
| fd = (win32_fd*)dev->d_private; |
| if (fd && !fd->ntdll) |
| ret = win32_ftruncate(fd->handle, size); |
| return (ret); |
| } |
| |
| int ntfs_win32_ftruncate(int fd, s64 size) |
| { |
| int ret; |
| HANDLE handle; |
| |
| handle = get_osfhandle(fd); |
| ret = win32_ftruncate(handle, size); |
| return (ret); |
| } |