Change the win32_open function to open volumes
instead of partitions when possible, and use the windows volume locking/dismounting mechanism on R/W open to prevent data corruption. (Yuval) (Logical change 1.488)edge.strict_endians
parent
1329505fd2
commit
1fd33a1326
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue