mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 10:04:00 +08:00
31430ae01a
Translate to Windows error code STATUS_SHARING_VIOLATION to Unix error code EBUSY.
2041 lines
55 KiB
C
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, 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) {
|
|
#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 %d.\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);
|
|
}
|