193 files changed, 80411 insertions, 44563 deletions
diff --git a/libntfs-3g/win32_io.c b/libntfs-3g/win32_io.c index ed9fa52..437a3fc 100755 --- a/libntfs-3g/win32_io.c +++ b/libntfs-3g/win32_io.c @@ -6,6 +6,7 @@ * Copyright (c) 2003-2004 Lode Leroy * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2004-2005 Yuval Fledel + * Copyright (c) 2012-2013 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 @@ -26,8 +27,33 @@ #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 @@ -42,6 +68,11 @@ #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 @@ -51,6 +82,10 @@ 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 @@ -61,10 +96,26 @@ typedef struct ntfs_volume ntfs_volume; #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); @@ -83,6 +134,146 @@ static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL; #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 - */ @@ -93,8 +284,11 @@ typedef struct { 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; /** @@ -146,6 +340,26 @@ static int ntfs_w32error_to_errno(unsigned int w32error) } } +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() * @@ -156,10 +370,11 @@ static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) { - liDistanceToMove.LowPart = SetFilePointer(hFile, - liDistanceToMove.LowPart, &liDistanceToMove.HighPart, - dwMoveMethod); - if (liDistanceToMove.LowPart == INVALID_SET_FILE_POINTER && + 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; @@ -199,7 +414,7 @@ static void ntfs_device_win32_init_imports(void) * SetFilePointerEx(). */ if (!fnSetFilePointerEx) { - ntfs_log_debug("SetFilePonterEx() not found in " + ntfs_log_debug("SetFilePointerEx() not found in " "kernel32.dll: Enabling emulation.\n"); fnSetFilePointerEx = libntfs_SetFilePointerEx; } @@ -232,13 +447,13 @@ static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags) switch (flags & O_ACCMODE) { case O_RDONLY: - win_mode = FILE_READ_DATA; + win_mode = GENERIC_READ; break; case O_WRONLY: - win_mode = FILE_WRITE_DATA; + win_mode = GENERIC_WRITE; break; case O_RDWR: - win_mode = FILE_READ_DATA | FILE_WRITE_DATA; + win_mode = GENERIC_READ | GENERIC_WRITE; break; default: /* error */ @@ -267,7 +482,8 @@ static int ntfs_device_win32_simple_open_file(const char *filename, *handle = CreateFile(filename, ntfs_device_unix_status_flags_to_win32(flags), locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), - NULL, OPEN_EXISTING, 0, NULL); + 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); @@ -321,6 +537,22 @@ static int ntfs_device_win32_unlock(HANDLE handle) 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 @@ -359,15 +591,18 @@ static int ntfs_device_win32_dismount(HANDLE handle) */ static s64 ntfs_device_win32_getsize(HANDLE handle) { - DWORD loword, hiword; + LONG loword, hiword; - loword = GetFileSize(handle, &hiword); - if (loword == INVALID_FILE_SIZE) { + 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) + loword; + return ((s64)hiword << 32) + (ULONG)loword; } /** @@ -467,6 +702,7 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) 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; @@ -493,6 +729,7 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) 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()); @@ -500,9 +737,51 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) 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 @@ -516,12 +795,22 @@ 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; @@ -530,6 +819,8 @@ static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, 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; } @@ -561,6 +852,90 @@ static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, 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; @@ -694,7 +1069,7 @@ static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id, do { buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) + part_count * sizeof(PARTITION_INFORMATION); - drive_layout = malloc(buf_size); + drive_layout = (DRIVE_LAYOUT_INFORMATION*)ntfs_malloc(buf_size); if (!drive_layout) { errno = ENOMEM; return FALSE; @@ -775,6 +1150,8 @@ static int ntfs_device_win32_open_partition(int drive_id, 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; @@ -837,7 +1214,13 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) } ntfs_device_win32_init_imports(); numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part); - drive_id = toupper(drive_char) - 'A'; + 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); @@ -854,6 +1237,12 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) 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); @@ -866,7 +1255,7 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) /* Setup our read-only flag. */ if ((flags & O_RDWR) != O_RDWR) NDevSetReadOnly(dev); - dev->d_private = malloc(sizeof(win32_fd)); + dev->d_private = (win32_fd*)ntfs_malloc(sizeof(win32_fd)); memcpy(dev->d_private, &fd, sizeof(win32_fd)); NDevSetOpen(dev); NDevClearDirty(dev); @@ -916,7 +1305,8 @@ static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, errno = EINVAL; return -1; } - if (abs_ofs < 0 || abs_ofs > fd->part_length) { + if ((abs_ofs < 0) + || (fd->ntdll && (abs_ofs > fd->part_length))) { ntfs_log_trace("Seeking outsize seekable area.\n"); errno = EINVAL; return -1; @@ -937,18 +1327,19 @@ static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, * error returns -1 and errno set. Transfer starts from position @pos on @fd. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @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 *b, const BOOL write) + 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" : @@ -962,21 +1353,57 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, handle = fd->handle; li.QuadPart += fd->part_start; } - if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("SetFilePointer failed.\n"); - return -1; - } - if (write) - res = WriteFile(handle, b, count, &bt, NULL); - else - res = ReadFile(handle, b, count, &bt, NULL); - if (!res) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("%sFile() failed.\n", write ? "Write" : "Read"); - return -1; + + 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 bt; + return bytes; } /** @@ -990,7 +1417,7 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, * returns -1 and errno set. Read starts from position @pos. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @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. @@ -998,7 +1425,7 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 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, FALSE); + return ntfs_device_win32_pio(fd, pos, count, b, (void*)NULL); } /** @@ -1018,9 +1445,9 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) int old_ofs, ofs; old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_read = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); + 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; @@ -1031,8 +1458,8 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) "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 & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) + 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, @@ -1047,7 +1474,7 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) s64 vol_to_read = fd->geo_size - old_pos; if (count > vol_to_read) { br = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), + old_pos & ~(s64)(fd->geo_sector_size - 1), ofs + vol_to_read, alignedbuffer); if (br == -1) goto read_error; @@ -1058,13 +1485,13 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) } br -= ofs; fd->pos += br; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); + 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)(NTFS_BLOCK_SIZE - 1), to_read, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_read, alignedbuffer + br); if (i == -1) { if (br) @@ -1115,11 +1542,18 @@ static int ntfs_device_win32_close(struct ntfs_device *dev) if (!CloseHandle(fd->vol_handle)) ntfs_log_trace("CloseHandle() failed for volume.\n"); } - rvl = CloseHandle(fd->handle); + if (fd->ntdll) { + ntfs_device_win32_setlock(fd->handle,FSCTL_UNLOCK_VOLUME); + rvl = NtClose(fd->handle) == STATUS_SUCCESS; + } else + rvl = CloseHandle(fd->handle); free(fd); if (!rvl) { errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("CloseHandle() failed.\n"); + if (fd->ntdll) + ntfs_log_trace("NtClose() failed.\n"); + else + ntfs_log_trace("CloseHandle() failed.\n"); return -1; } return 0; @@ -1174,7 +1608,7 @@ static int ntfs_device_win32_sync(struct ntfs_device *dev) * errno set. Write starts from position @pos. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @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. @@ -1182,7 +1616,7 @@ static int ntfs_device_win32_sync(struct ntfs_device *dev) 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 *)b, TRUE); + return ntfs_device_win32_pio(fd, pos, count, (void*)NULL, b); } /** @@ -1203,9 +1637,9 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, int old_ofs, ofs; old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_write = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); + 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; @@ -1224,8 +1658,8 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, if (!count) return 0; NDevSetDirty(dev); - if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) + if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && + !(count & (fd->geo_sector_size - 1))) alignedbuffer = (BYTE *)b; else { s64 end; @@ -1240,9 +1674,9 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, /* Read first sector if start of write not sector aligned. */ if (ofs) { i = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer); - if (i != NTFS_BLOCK_SIZE) { + old_pos & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, alignedbuffer); + if (i != fd->geo_sector_size) { if (i >= 0) errno = EIO; goto write_error; @@ -1255,13 +1689,13 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, * yet, i.e. the start of the write is sector aligned. */ end = old_pos + count; - if ((end & (NTFS_BLOCK_SIZE - 1)) && - ((to_write > NTFS_BLOCK_SIZE) || !ofs)) { + if ((end & (fd->geo_sector_size - 1)) && + ((to_write > fd->geo_sector_size) || !ofs)) { i = ntfs_device_win32_pread_simple(fd, - end & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer + - to_write - NTFS_BLOCK_SIZE); - if (i != NTFS_BLOCK_SIZE) { + end & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, alignedbuffer + + to_write - fd->geo_sector_size); + if (i != fd->geo_sector_size) { if (i >= 0) errno = EIO; goto write_error; @@ -1274,7 +1708,7 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, s64 vol_to_write = fd->geo_size - old_pos; if (count > vol_to_write) { bw = ntfs_device_win32_pwrite_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), + old_pos & ~(s64)(fd->geo_sector_size - 1), ofs + vol_to_write, alignedbuffer); if (bw == -1) goto write_error; @@ -1285,13 +1719,13 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, } bw -= ofs; fd->pos += bw; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); + 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)(NTFS_BLOCK_SIZE - 1), to_write, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_write, alignedbuffer + bw); if (i == -1) { if (bw) @@ -1329,19 +1763,22 @@ 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; - switch (GetFileType(fd->handle)) { - case FILE_TYPE_CHAR: - st_mode = S_IFCHR; - break; - case FILE_TYPE_DISK: + if ((dev->d_name[1] == ':') && (dev->d_name[2] == '\0')) st_mode = S_IFBLK; - break; - case FILE_TYPE_PIPE: - st_mode = S_IFIFO; - break; - default: - st_mode = 0; - } + 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; @@ -1352,6 +1789,7 @@ static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) return 0; } +#ifdef HDIO_GETGEO /** * ntfs_win32_hdio_getgeo - get drive geometry * @dev: ntfs device obtained via ->open @@ -1373,6 +1811,7 @@ static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev, argp->start = fd->part_hidden_sectors; return 0; } +#endif /** * ntfs_win32_blksszget - get block device sector size @@ -1404,7 +1843,9 @@ static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) 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) { @@ -1454,13 +1895,40 @@ static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b, s64 count, s64 offset) { - return ntfs_pread(dev, offset, count, b); + 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) { - return ntfs_pwrite(dev, offset, count, b); + 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 = { @@ -1475,3 +1943,87 @@ struct ntfs_device_operations ntfs_device_win32_io_ops = { .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); +} |