diff --git a/libntfs/win32_io.c b/libntfs/win32_io.c index 3e359f29..d61545e4 100644 --- a/libntfs/win32_io.c +++ b/libntfs/win32_io.c @@ -38,7 +38,6 @@ */ typedef long long int s64; typedef unsigned long int u32; -struct flock; struct stat; struct ntfs_volume; typedef struct ntfs_volume ntfs_volume; @@ -48,10 +47,26 @@ typedef struct ntfs_volume ntfs_volume; /* Need device, but prevent ../include/types.h to be loaded. */ #define _NTFS_TYPES_H -#define _NTFS_SUPPORT_H #define _NTFS_VOLUME_H #include "device.h" +#ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS +#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096 +#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); +static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL; +static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL; +static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL; +#ifdef UNICODE +#define FUNCTIONPOSTFIX "W" +#else +#define FUNCTIONPOSTFIX "A" +#endif + #define FORCE_ALIGNED_READ typedef struct win32_fd { @@ -127,49 +142,55 @@ static int ntfs_w32error_to_errno(DWORD w32error) } } +/** + * 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) +{ + switch (flags & O_ACCMODE) { + case O_RDONLY: + return FILE_READ_DATA; + break; + case O_WRONLY: + return FILE_WRITE_DATA; + break; + case O_RDWR: + return FILE_READ_DATA | FILE_WRITE_DATA; + break; + default: + /* error */ + Dputs("win32_unix_status_flags_to_win32: flags unknown"); + return 0; + } +} + + /** * ntfs_device_win32_simple_open_file - Just open a file via win32 API * @filename: Name of the file to open. * @handle: Pointer the a HADNLE 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. * -errno if not, in this case handle is trashed. */ -static int ntfs_device_win32_simple_open_file(char *filename, - HANDLE *handle, int flags) +static int ntfs_device_win32_simple_open_file(const char *filename, + HANDLE *handle, int flags, BOOL locking) { - int win32flags; - - switch (flags && O_ACCMODE) { - case O_RDONLY: - win32flags = FILE_READ_DATA; - break; - case O_WRONLY: -/* win32flags = FILE_WRITE_DATA; - break; */ - case O_RDWR: -/* win32flags = FILE_READ_DATA || FILE_WRITE_DATA; - break; */ - errno = ENOTSUP; - return -errno; - default: - /* error */ - return -EINVAL; - } - - *handle = CreateFile(filename, win32flags, FILE_SHARE_READ, - NULL, OPEN_EXISTING, 0, NULL); + *handle = CreateFile(filename, + ntfs_device_unix_status_flags_to_win32(flags), + locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), + NULL, OPEN_EXISTING, 0, NULL); if (*handle == INVALID_HANDLE_VALUE) { - char msg[1024]; - - sprintf(msg, "CreateFile(%s) failed", filename); - perror(msg); + Dprintf("CreateFile(%s) failed.\n", filename); errno = ntfs_w32error_to_errno(GetLastError()); - return -errno; } else { return 0; @@ -198,6 +219,30 @@ static int ntfs_win32_getsize(HANDLE handle,s64 *argp) return 0; } +/** + * ntfs_device_win32_init_imports - initialize the fnFind*Volume variables. + * + * The Find*Volume functions exist only on win2k+, as such we can't + * just staticly import it. + * This function initialize the imports if the function do exist. + * + * Note: The values are cached, do be afraid to run it more than once. + */ +static void ntfs_device_win32_init_imports(void) +{ + if (!fnFindFirstVolume) + fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME) + GetProcAddress(GetModuleHandle("kernel32"), + "FindFirstVolume"FUNCTIONPOSTFIX); + if (!fnFindNextVolume) + fnFindNextVolume = (LPFN_FINDNEXTVOLUME) + GetProcAddress(GetModuleHandle("kernel32"), + "FindNextVolume"FUNCTIONPOSTFIX); + if (!fnFindVolumeClose) + fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE) + GetProcAddress(GetModuleHandle("kernel32"), "FindVolumeClose"); +} + /** * ntfs_device_win32_open_file - Open a file via win32 API * @filename: Name of the file to open. @@ -214,25 +259,25 @@ static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, s64 size; int err; - if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags))) { + if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags, + TRUE))) { /* open error */ return err; } if ((err = ntfs_win32_getsize(handle, &size))) { /* error while getting the information */ - perror("ioctl failed"); - errno = err; - return -err; - } else { - /* success */ - fd->handle = handle; - fd->part_start = 0; - fd->part_end = size; - fd->current_pos.QuadPart = 0; - fd->part_hidden_sectors = -1; - return 0; + Dputs("win32_open_file(): getsize failed."); + size = -1; } + + /* fill fd */ + fd->handle = handle; + fd->part_start = 0; + fd->part_end = size; + fd->current_pos.QuadPart = 0; + fd->part_hidden_sectors = -1; + return 0; } /** @@ -254,25 +299,158 @@ static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id); - if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags))) { + if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags, + TRUE))) { /* open error */ return err; } if ((err = ntfs_win32_getsize(handle, &size))) { /* error while getting the information */ - perror("ioctl failed"); - errno = err; - return -err; - } else { - /* success */ - fd->handle = handle; - fd->part_start = 0; - fd->part_end = size; - fd->current_pos.QuadPart = 0; - fd->part_hidden_sectors = -1; - return 0; + Dputs("win32_open_drive(): getsize failed."); + size = -1; } + + /* fill fd */ + fd->handle = handle; + fd->part_start = 0; + fd->part_end = size; + fd->current_pos.QuadPart = 0; + fd->part_hidden_sectors = -1; + 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]; + + ntfs_device_win32_init_imports(); + /* make sure all the required imports exist */ + if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) { + Dputs("win32_is_mounted: Imports not found."); + return INVALID_HANDLE_VALUE; + } + + /* start iterating through volumes. */ + Dprintf("win32_open_volume_for_partition: Start\n"); + 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) { + Dprintf("win32_open_volume_for_partition: " + "FindFirstVolume failed."); + 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; + + Dprintf("win32_open_volume_for_partition: 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) { +#define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS)+9*sizeof(DISK_EXTENT) + char extents[EXTENTS_SIZE]; + DWORD bytesReturned; + + /* 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 + Dputs("win32_open_volume_for_partition: getExtents " + "Failed!" ); + } while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH)); + + /* end of iteration through volumes */ + Dprintf("win32_open_volume_for_partition: Closing.\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. + * + * 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. + * + * Return TRUE if found, and sets the output parameters. + * FALSE if not. + */ +static BOOL ntfs_device_win32_find_partition(HANDLE handle,DWORD partition_id, + s64 *part_offset, s64 *part_length, int *hidden_sectors) +{ + char buf[sizeof(DRIVE_LAYOUT_INFORMATION)+9*sizeof(PARTITION_INFORMATION)]; + DRIVE_LAYOUT_INFORMATION *drive_layout; + DWORD bytesReturned, i; + + if (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, + &buf, sizeof (buf), &bytesReturned, NULL)) { + Dputs("win32_find_partition(): GetDriveLayout failed."); + errno = ntfs_w32error_to_errno(GetLastError()); + return FALSE; + } + + drive_layout = (DRIVE_LAYOUT_INFORMATION *)buf; + 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; + return TRUE; + } + } + + return FALSE; } /** @@ -281,55 +459,93 @@ static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, * @fd: Win32 file device to return * @flags: Unix open status flags. * - * Return 0 if o.k. - * -errno if not + * Return 0 if o.k. + * -1 if not * * When fails, fd contents may have not been preserved. */ static __inline__ int ntfs_device_win32_open_partition(int drive_id, - unsigned int partition_id, win32_fd *fd, int flags) + DWORD partition_id, win32_fd *fd, int flags) { - DRIVE_LAYOUT_INFORMATION *drive_layout; - char buffer[10240]; - unsigned int i; - DWORD numread; + char drive_name[MAX_PATH]; HANDLE handle; int err; + s64 part_start, part_length; + int hidden_sectors; - sprintf(buffer, "\\\\.\\PhysicalDrive%d", drive_id); + sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id); - if ((err = ntfs_device_win32_simple_open_file(buffer, &handle, flags))) { - /* error */ + /* 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 (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, - &buffer, sizeof (buffer), &numread, NULL)) { - perror("ioctl failed"); - errno = ntfs_w32error_to_errno(GetLastError()); - return -errno; - } + if (ntfs_device_win32_find_partition(handle, partition_id, &part_start, + &part_length, &hidden_sectors)) { + HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition( + drive_id, part_start, part_length, flags); + if (vol_handle!=INVALID_HANDLE_VALUE) { + BOOL retVal; + DWORD bytesReturned; + + /* close the disk handle, we do not need it anymore */ + CloseHandle(handle); - drive_layout = (DRIVE_LAYOUT_INFORMATION *)buffer; - for (i = 0; i < drive_layout->PartitionCount; i++) { - if (drive_layout->PartitionEntry[i].PartitionNumber == partition_id) { - fd->handle = handle; - fd->part_start = - drive_layout->PartitionEntry[i].StartingOffset.QuadPart; - fd->part_end = - drive_layout->PartitionEntry[i].StartingOffset.QuadPart + - drive_layout->PartitionEntry[i].PartitionLength.QuadPart; + if ((flags & O_RDWR) == O_RDWR) { + /* lock the volume */ + Dputs("win32_open_partition: Locking volume"); + retVal = DeviceIoControl(vol_handle, FSCTL_LOCK_VOLUME, NULL, 0, + NULL, 0, &bytesReturned, NULL); + if (!retVal) { + Dputs("win32_open_partition: Couldn't lock volume"); + errno = ntfs_w32error_to_errno(GetLastError()); + return -1; + } else + Dputs("win32_open_partition: Lock O.k."); + + /* dismount volume */ + retVal = DeviceIoControl(vol_handle, FSCTL_DISMOUNT_VOLUME, + NULL, 0, NULL, 0, &bytesReturned, NULL); + if (!retVal) { + Dputs("win32_open_partition: Couldn't Dismount"); + errno = ntfs_w32error_to_errno(GetLastError()); + return -1; + } else + Dputs("win32_open_partition: Dismount O.k."); + } + + /* fill fd */ + fd->handle = vol_handle; + fd->part_start = 0; + fd->part_end = part_length; fd->current_pos.QuadPart = 0; - fd->part_hidden_sectors = - drive_layout->PartitionEntry[i].HiddenSectors; + fd->part_hidden_sectors = hidden_sectors; return 0; + } else { + if ((flags & O_RDWR) == O_RDWR) { + /* access if read-write, no volume found */ + Dputs("Partitions containing Spanned/Mirrored volumes are " + "not supported in R/W status yet"); + CloseHandle(handle); + return -1; + } else { + /* fill fd */ + fd->handle = handle; + fd->part_start = part_start; + fd->part_end = part_start + part_length; + fd->current_pos.QuadPart = 0; + fd->part_hidden_sectors = hidden_sectors; + return 0; + } } + } else { + Dprintf("partition %u not found on drive %d\n", partition_id, drive_id); + CloseHandle(handle); + errno = ENODEV; + return -1; } - - fprintf(stderr,"partition %u not found on drive %d\n", - partition_id, drive_id); - errno = ENODEV; - return -ENODEV; } /**