ntfs-3g/libntfs-3g/win32_io.c
Jean-Pierre André 32c27a8a4f Defined the request argument of ioctl() as unsigned long
On linux the request argument of ioctl() is defined as an unsigned long,
but the fuse protocol squashes it into a signed int. As a consequence
the value received by ntfs-3g may appear as negative and different from
the value defined by the corresponding macro.
So define the request argument as unsigned long in ntfs-3g. It has
however to be fed as unsigned from fuse until the fuse protocol is
updated.
2020-03-07 10:31:12 +01:00

2041 lines
55 KiB
C

/*
* 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_SHARING_VIOLATION = 0xC0000043,
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);
case STATUS_SHARING_VIOLATION :
return (EBUSY);
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,
unsigned long request, void *argp)
{
#if defined(BLKGETSIZE) | defined(BLKGETSIZE64)
win32_fd *fd = (win32_fd *)dev->d_private;
#endif
ntfs_log_trace("win32_ioctl(0x%lx) 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 0x%lx.\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);
}