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
cantab.net!aia21 2004-08-12 08:06:25 +00:00
parent 1329505fd2
commit 1fd33a1326
1 changed files with 302 additions and 86 deletions

View File

@ -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;
}
/**