1/*
2 * win32_io.c - A stdio-like disk I/O implementation for low-level disk access
3 *		on Win32.  Can access an NTFS volume while it is mounted.
4 *		Originated from the Linux-NTFS project.
5 *
6 * Copyright (c) 2003-2004 Lode Leroy
7 * Copyright (c) 2003-2006 Anton Altaparmakov
8 * Copyright (c) 2004-2005 Yuval Fledel
9 * Copyright (c) 2012-2014 Jean-Pierre Andre
10 *
11 * This program/include file is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program/include file is distributed in the hope that it will be
17 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
18 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program (in the main directory of the NTFS-3G
23 * distribution in the file COPYING); if not, write to the Free Software
24 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 */
26
27#include "config.h"
28
29#ifdef HAVE_WINDOWS_H
30#define BOOL WINBOOL /* avoid conflicting definitions of BOOL */
31#include <windows.h>
32#undef BOOL
33#endif
34
35#ifdef HAVE_STDLIB_H
36#include <stdlib.h>
37#endif
38
39/*
40 *		Definitions needed for <winioctl.h>
41 */
42#ifndef _ANONYMOUS_UNION
43#define _ANONYMOUS_UNION
44#define _ANONYMOUS_STRUCT
45typedef unsigned long long DWORD64;
46#endif
47
48typedef struct {
49        DWORD data1;     /* The first eight hexadecimal digits of the GUID. */
50        WORD data2;     /* The first group of four hexadecimal digits. */
51        WORD data3;     /* The second group of four hexadecimal digits. */
52        char data4[8];    /* The first two bytes are the third group of four
53                           hexadecimal digits. The remaining six bytes are the
54                           final 12 hexadecimal digits. */
55} GUID;
56
57#include <winioctl.h>
58
59#ifdef HAVE_STDIO_H
60#include <stdio.h>
61#endif
62#ifdef HAVE_CTYPE_H
63#include <ctype.h>
64#endif
65#ifdef HAVE_ERRNO_H
66#include <errno.h>
67#endif
68#ifdef HAVE_FCNTL_H
69#include <fcntl.h>
70#endif
71#ifdef HAVE_SYS_STAT_H
72#include <sys/stat.h>
73#define stat stat64
74#define st_blocks  st_rdev /* emulate st_blocks, missing in Windows */
75#endif
76
77/* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */
78#define _NTFS_VOLUME_H
79struct ntfs_volume;
80typedef struct ntfs_volume ntfs_volume;
81
82#include "debug.h"
83#include "types.h"
84#include "device.h"
85#include "misc.h"
86
87#define cpu_to_le16(x) (x)
88#define const_cpu_to_le16(x) (x)
89
90#ifndef MAX_PATH
91#define MAX_PATH 1024
92#endif
93
94#ifndef NTFS_BLOCK_SIZE
95#define NTFS_BLOCK_SIZE		512
96#define NTFS_BLOCK_SIZE_BITS	9
97#endif
98
99#ifndef INVALID_SET_FILE_POINTER
100#define INVALID_SET_FILE_POINTER ((DWORD)-1)
101#endif
102
103#ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
104#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096
105#endif
106
107#ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY
108#define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x70000
109#endif
110
111#ifndef IOCTL_GET_DISK_LENGTH_INFO
112#define IOCTL_GET_DISK_LENGTH_INFO 0x7405c
113#endif
114
115#ifndef FSCTL_ALLOW_EXTENDED_DASD_IO
116#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x90083
117#endif
118
119/* Windows 2k+ imports. */
120typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD);
121typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD);
122typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE);
123typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER,
124		PLARGE_INTEGER, DWORD);
125
126static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL;
127static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL;
128static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL;
129static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL;
130
131#ifdef UNICODE
132#define FNPOSTFIX "W"
133#else
134#define FNPOSTFIX "A"
135#endif
136
137enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx */
138   STATUS_UNKNOWN = -1,
139   STATUS_SUCCESS =              0x00000000,
140   STATUS_BUFFER_OVERFLOW =      0x80000005,
141   STATUS_INVALID_HANDLE =       0xC0000008,
142   STATUS_INVALID_PARAMETER =    0xC000000D,
143   STATUS_INVALID_DEVICE_REQUEST = 0xC0000010,
144   STATUS_END_OF_FILE =          0xC0000011,
145   STATUS_CONFLICTING_ADDRESSES = 0xC0000018,
146   STATUS_NO_MATCH =             0xC000001E,
147   STATUS_ACCESS_DENIED =        0xC0000022,
148   STATUS_BUFFER_TOO_SMALL =     0xC0000023,
149   STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024,
150   STATUS_FILE_NOT_FOUND =       0xC0000028,
151   STATUS_OBJECT_NAME_INVALID =  0xC0000033,
152   STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034,
153   STATUS_SHARING_VIOLATION =    0xC0000043,
154   STATUS_INVALID_PARAMETER_1 =  0xC00000EF,
155   STATUS_IO_DEVICE_ERROR =      0xC0000185,
156   STATUS_GUARD_PAGE_VIOLATION = 0x80000001
157 } ;
158
159typedef u32 NTSTATUS; /* do not let the compiler choose the size */
160#ifdef __x86_64__
161typedef unsigned long long ULONG_PTR; /* an integer the same size as a pointer */
162#else
163typedef unsigned long ULONG_PTR; /* an integer the same size as a pointer */
164#endif
165
166HANDLE get_osfhandle(int); /* from msvcrt.dll */
167
168/*
169 *		A few needed definitions not included in <windows.h>
170 */
171
172typedef struct _IO_STATUS_BLOCK {
173	union {
174		NTSTATUS Status;
175		PVOID    Pointer;
176	};
177  	ULONG_PTR Information;
178} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
179
180typedef struct _UNICODE_STRING {
181	USHORT Length;
182	USHORT MaximumLength;
183#ifdef __x86_64__
184	u32    padding;
185#endif
186	PWSTR  Buffer;
187} UNICODE_STRING, *PUNICODE_STRING;
188
189typedef struct _OBJECT_ATTRIBUTES {
190	ULONG           Length;
191#ifdef __x86_64__
192	u32		padding1;
193	HANDLE          RootDirectory;
194	PUNICODE_STRING ObjectName;
195	ULONG           Attributes;
196	u32		padding2;
197#else
198	HANDLE          RootDirectory;
199	PUNICODE_STRING ObjectName;
200	ULONG           Attributes;
201#endif
202	PVOID           SecurityDescriptor;
203	PVOID           SecurityQualityOfService;
204}  OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
205
206#define FILE_OPEN 1
207#define FILE_CREATE 2
208#define FILE_OVERWRITE 4
209#define FILE_SYNCHRONOUS_IO_ALERT    0x10
210#define FILE_SYNCHRONOUS_IO_NONALERT 0x20
211#define OBJ_CASE_INSENSITIVE 0x40
212
213typedef void (WINAPI *PIO_APC_ROUTINE)(void*, PIO_STATUS_BLOCK, ULONG);
214
215extern WINAPI NTSTATUS NtOpenFile(
216	PHANDLE FileHandle,
217	ACCESS_MASK DesiredAccess,
218	POBJECT_ATTRIBUTES ObjectAttributes,
219	PIO_STATUS_BLOCK IoStatusBlock,
220	ULONG ShareAccess,
221	ULONG OpenOptions
222);
223
224extern WINAPI NTSTATUS NtReadFile(
225	HANDLE FileHandle,
226	HANDLE Event,
227	PIO_APC_ROUTINE ApcRoutine,
228	PVOID ApcContext,
229	PIO_STATUS_BLOCK IoStatusBlock,
230	PVOID Buffer,
231	ULONG Length,
232	PLARGE_INTEGER ByteOffset,
233	PULONG Key
234);
235
236extern WINAPI NTSTATUS NtWriteFile(
237	HANDLE FileHandle,
238	HANDLE Event,
239	PIO_APC_ROUTINE ApcRoutine,
240	PVOID ApcContext,
241	PIO_STATUS_BLOCK IoStatusBlock,
242	LPCVOID Buffer,
243	ULONG Length,
244	PLARGE_INTEGER ByteOffset,
245	PULONG Key
246);
247
248extern NTSTATUS WINAPI NtClose(
249	HANDLE Handle
250);
251
252extern NTSTATUS WINAPI NtDeviceIoControlFile(
253	HANDLE FileHandle,
254	HANDLE Event,
255	PIO_APC_ROUTINE ApcRoutine,
256	PVOID ApcContext,
257	PIO_STATUS_BLOCK IoStatusBlock,
258	ULONG IoControlCode,
259	PVOID InputBuffer,
260	ULONG InputBufferLength,
261	PVOID OutputBuffer,
262	ULONG OutputBufferLength
263);
264
265extern NTSTATUS	WINAPI NtFsControlFile(
266	HANDLE FileHandle,
267	HANDLE Event,
268	PIO_APC_ROUTINE ApcRoutine,
269	PVOID ApcContext,
270	PIO_STATUS_BLOCK IoStatusBlock,
271	ULONG FsControlCode,
272	PVOID InputBuffer,
273	ULONG InputBufferLength,
274	PVOID OutputBuffer,
275	ULONG OutputBufferLength
276);
277
278/**
279 * struct win32_fd -
280 */
281typedef struct {
282	HANDLE handle;
283	s64 pos;		/* Logical current position on the volume. */
284	s64 part_start;
285	s64 part_length;
286	int part_hidden_sectors;
287	s64 geo_size, geo_cylinders;
288	s32 geo_sector_size;
289	s64 volume_size;
290	DWORD geo_sectors, geo_heads;
291	HANDLE vol_handle;
292	BOOL ntdll;
293} win32_fd;
294
295/**
296 * ntfs_w32error_to_errno - convert a win32 error code to the unix one
297 * @w32error:	the win32 error code
298 *
299 * Limited to a relatively small but useful number of codes.
300 */
301static int ntfs_w32error_to_errno(unsigned int w32error)
302{
303	ntfs_log_trace("Converting w32error 0x%x.\n",w32error);
304	switch (w32error) {
305		case ERROR_INVALID_FUNCTION:
306			return EBADRQC;
307		case ERROR_FILE_NOT_FOUND:
308		case ERROR_PATH_NOT_FOUND:
309		case ERROR_INVALID_NAME:
310			return ENOENT;
311		case ERROR_TOO_MANY_OPEN_FILES:
312			return EMFILE;
313		case ERROR_ACCESS_DENIED:
314			return EACCES;
315		case ERROR_INVALID_HANDLE:
316			return EBADF;
317		case ERROR_NOT_ENOUGH_MEMORY:
318			return ENOMEM;
319		case ERROR_OUTOFMEMORY:
320			return ENOSPC;
321		case ERROR_INVALID_DRIVE:
322		case ERROR_BAD_UNIT:
323			return ENODEV;
324		case ERROR_WRITE_PROTECT:
325			return EROFS;
326		case ERROR_NOT_READY:
327		case ERROR_SHARING_VIOLATION:
328			return EBUSY;
329		case ERROR_BAD_COMMAND:
330			return EINVAL;
331		case ERROR_SEEK:
332		case ERROR_NEGATIVE_SEEK:
333			return ESPIPE;
334		case ERROR_NOT_SUPPORTED:
335			return EOPNOTSUPP;
336		case ERROR_BAD_NETPATH:
337			return ENOSHARE;
338		default:
339			/* generic message */
340			return ENOMSG;
341	}
342}
343
344static int ntfs_ntstatus_to_errno(NTSTATUS status)
345{
346	ntfs_log_trace("Converting w32error 0x%x.\n",w32error);
347	switch (status) {
348		case STATUS_INVALID_HANDLE :
349		case STATUS_INVALID_PARAMETER :
350		case STATUS_OBJECT_NAME_INVALID :
351		case STATUS_INVALID_DEVICE_REQUEST :
352			return (EINVAL);
353		case STATUS_ACCESS_DENIED :
354			return (EACCES);
355		case STATUS_IO_DEVICE_ERROR :
356		case STATUS_END_OF_FILE :
357			return (EIO);
358		case STATUS_SHARING_VIOLATION :
359			return (EBUSY);
360		default:
361			/* generic message */
362			return ENOMSG;
363	}
364}
365
366/**
367 * libntfs_SetFilePointerEx - emulation for SetFilePointerEx()
368 *
369 * We use this to emulate SetFilePointerEx() when it is not present.  This can
370 * happen since SetFilePointerEx() only exists in Win2k+.
371 */
372static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile,
373		LARGE_INTEGER liDistanceToMove,
374		PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
375{
376	liDistanceToMove.u.LowPart = SetFilePointer(hFile,
377			liDistanceToMove.u.LowPart,
378			&liDistanceToMove.u.HighPart, dwMoveMethod);
379	SetLastError(NO_ERROR);
380	if (liDistanceToMove.u.LowPart == INVALID_SET_FILE_POINTER &&
381			GetLastError() != NO_ERROR) {
382		if (lpNewFilePointer)
383			lpNewFilePointer->QuadPart = -1;
384		return FALSE;
385	}
386	if (lpNewFilePointer)
387		lpNewFilePointer->QuadPart = liDistanceToMove.QuadPart;
388	return TRUE;
389}
390
391/**
392 * ntfs_device_win32_init_imports - initialize the function pointers
393 *
394 * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such
395 * we cannot just staticly import them.
396 *
397 * This function initializes the imports if the functions do exist and in the
398 * SetFilePointerEx case, we emulate the function ourselves if it is not
399 * present.
400 *
401 * Note: The values are cached, do be afraid to run it more than once.
402 */
403static void ntfs_device_win32_init_imports(void)
404{
405	HMODULE kernel32 = GetModuleHandle("kernel32");
406	if (!kernel32) {
407		errno = ntfs_w32error_to_errno(GetLastError());
408		ntfs_log_trace("kernel32.dll could not be imported.\n");
409	}
410	if (!fnSetFilePointerEx) {
411		if (kernel32)
412			fnSetFilePointerEx = (LPFN_SETFILEPOINTEREX)
413					GetProcAddress(kernel32,
414					"SetFilePointerEx");
415		/*
416		 * If we did not get kernel32.dll or it is not Win2k+, emulate
417		 * SetFilePointerEx().
418		 */
419		if (!fnSetFilePointerEx) {
420			ntfs_log_debug("SetFilePointerEx() not found in "
421					"kernel32.dll: Enabling emulation.\n");
422			fnSetFilePointerEx = libntfs_SetFilePointerEx;
423		}
424	}
425	/* Cannot do lookups if we could not get kernel32.dll... */
426	if (!kernel32)
427		return;
428	if (!fnFindFirstVolume)
429		fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME)
430				GetProcAddress(kernel32, "FindFirstVolume"
431				FNPOSTFIX);
432	if (!fnFindNextVolume)
433		fnFindNextVolume = (LPFN_FINDNEXTVOLUME)
434				GetProcAddress(kernel32, "FindNextVolume"
435				FNPOSTFIX);
436	if (!fnFindVolumeClose)
437		fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE)
438				GetProcAddress(kernel32, "FindVolumeClose");
439}
440
441/**
442 * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags
443 * @flags:	unix open status flags
444 *
445 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
446 */
447static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags)
448{
449	int win_mode;
450
451	switch (flags & O_ACCMODE) {
452	case O_RDONLY:
453		win_mode = GENERIC_READ;
454		break;
455	case O_WRONLY:
456		win_mode = GENERIC_WRITE;
457		break;
458	case O_RDWR:
459		win_mode = GENERIC_READ | GENERIC_WRITE;
460		break;
461	default:
462		/* error */
463		ntfs_log_trace("Unknown status flags.\n");
464		win_mode = 0;
465	}
466	return win_mode;
467}
468
469
470/**
471 * ntfs_device_win32_simple_open_file - just open a file via win32 API
472 * @filename:	name of the file to open
473 * @handle:	pointer the a HANDLE in which to put the result
474 * @flags:	unix open status flags
475 * @locking:	will the function gain an exclusive lock on the file?
476 *
477 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
478 *
479 * Return 0 if o.k.
480 *	 -1 if not, and errno set.  In this case handle is trashed.
481 */
482static int ntfs_device_win32_simple_open_file(const char *filename,
483		HANDLE *handle, int flags, BOOL locking)
484{
485	*handle = CreateFile(filename,
486			ntfs_device_unix_status_flags_to_win32(flags),
487			locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ),
488			NULL, (flags & O_CREAT ? OPEN_ALWAYS : OPEN_EXISTING),
489			0, NULL);
490	if (*handle == INVALID_HANDLE_VALUE) {
491		errno = ntfs_w32error_to_errno(GetLastError());
492		ntfs_log_trace("CreateFile(%s) failed.\n", filename);
493		return -1;
494	}
495	return 0;
496}
497
498/**
499 * ntfs_device_win32_lock - lock the volume
500 * @handle:	a win32 HANDLE for a volume to lock
501 *
502 * Locking a volume means no one can access its contents.
503 * Exiting the process automatically unlocks the volume, except in old NT4s.
504 *
505 * Return 0 if o.k.
506 *	 -1 if not, and errno set.
507 */
508static int ntfs_device_win32_lock(HANDLE handle)
509{
510	DWORD i;
511
512	if (!DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &i,
513			NULL)) {
514		errno = ntfs_w32error_to_errno(GetLastError());
515		ntfs_log_trace("Couldn't lock volume.\n");
516		return -1;
517	}
518	ntfs_log_debug("Volume locked.\n");
519	return 0;
520}
521
522/**
523 * ntfs_device_win32_unlock - unlock the volume
524 * @handle:	the win32 HANDLE which the volume was locked with
525 *
526 * Return 0 if o.k.
527 *	 -1 if not, and errno set.
528 */
529static int ntfs_device_win32_unlock(HANDLE handle)
530{
531	DWORD i;
532
533	if (!DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &i,
534			NULL)) {
535		errno = ntfs_w32error_to_errno(GetLastError());
536		ntfs_log_trace("Couldn't unlock volume.\n");
537		return -1;
538	}
539	ntfs_log_debug("Volume unlocked.\n");
540	return 0;
541}
542
543static int ntfs_device_win32_setlock(HANDLE handle, ULONG code)
544{
545	IO_STATUS_BLOCK io_status;
546	NTSTATUS res;
547
548	io_status.Status = STATUS_SUCCESS;
549	io_status.Information = 0;
550	res = NtFsControlFile(handle,(HANDLE)NULL,
551				(PIO_APC_ROUTINE)NULL,(void*)NULL,
552				&io_status, code,
553				(char*)NULL,0,(char*)NULL,0);
554	if (res != STATUS_SUCCESS)
555		errno = ntfs_ntstatus_to_errno(res);
556	return (res == STATUS_SUCCESS ? 0 : -1);
557}
558
559/**
560 * ntfs_device_win32_dismount - dismount a volume
561 * @handle:	a win32 HANDLE for a volume to dismount
562 *
563 * Dismounting means the system will refresh the volume in the first change it
564 * gets.  Usefull after altering the file structures.
565 * The volume must be locked by the current process while dismounting.
566 * A side effect is that the volume is also unlocked, but you must not rely om
567 * this.
568 *
569 * Return 0 if o.k.
570 *	 -1 if not, and errno set.
571 */
572static int ntfs_device_win32_dismount(HANDLE handle)
573{
574	DWORD i;
575
576	if (!DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0,
577			&i, NULL)) {
578		errno = ntfs_w32error_to_errno(GetLastError());
579		ntfs_log_trace("Couldn't dismount volume.\n");
580		return -1;
581	}
582	ntfs_log_debug("Volume dismounted.\n");
583	return 0;
584}
585
586/**
587 * ntfs_device_win32_getsize - get file size via win32 API
588 * @handle:	pointer the file HANDLE obtained via open
589 *
590 * Only works on ordinary files.
591 *
592 * Return The file size if o.k.
593 *	 -1 if not, and errno set.
594 */
595static s64 ntfs_device_win32_getsize(HANDLE handle)
596{
597	LONG loword, hiword;
598
599	SetLastError(NO_ERROR);
600	hiword = 0;
601	loword = SetFilePointer(handle, 0, &hiword, 2);
602	if ((loword == INVALID_SET_FILE_POINTER)
603	    && (GetLastError() != NO_ERROR)) {
604		errno = ntfs_w32error_to_errno(GetLastError());
605		ntfs_log_trace("Couldn't get file size.\n");
606		return -1;
607	}
608	return ((s64)hiword << 32) + (ULONG)loword;
609}
610
611/**
612 * ntfs_device_win32_getdisklength - get disk size via win32 API
613 * @handle:	pointer the file HANDLE obtained via open
614 * @argp:	pointer to result buffer
615 *
616 * Only works on PhysicalDriveX type handles.
617 *
618 * Return The disk size if o.k.
619 *	 -1 if not, and errno set.
620 */
621static s64 ntfs_device_win32_getdisklength(HANDLE handle)
622{
623	GET_LENGTH_INFORMATION buf;
624	DWORD i;
625
626	if (!DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf,
627			sizeof(buf), &i, NULL)) {
628		errno = ntfs_w32error_to_errno(GetLastError());
629		ntfs_log_trace("Couldn't get disk length.\n");
630		return -1;
631	}
632	ntfs_log_debug("Disk length: %lld.\n", buf.Length.QuadPart);
633	return buf.Length.QuadPart;
634}
635
636/**
637 * ntfs_device_win32_getntfssize - get NTFS volume size via win32 API
638 * @handle:	pointer the file HANDLE obtained via open
639 * @argp:	pointer to result buffer
640 *
641 * Only works on NTFS volume handles.
642 * An annoying bug in windows is that an NTFS volume does not occupy the entire
643 * partition, namely not the last sector (which holds the backup boot sector,
644 * and normally not interesting).
645 * Use this function to get the length of the accessible space through a given
646 * volume handle.
647 *
648 * Return The volume size if o.k.
649 *	 -1 if not, and errno set.
650 */
651static s64 ntfs_device_win32_getntfssize(HANDLE handle)
652{
653	s64 rvl;
654#ifdef FSCTL_GET_NTFS_VOLUME_DATA
655	DWORD i;
656	NTFS_VOLUME_DATA_BUFFER buf;
657
658	if (!DeviceIoControl(handle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &buf,
659			sizeof(buf), &i, NULL)) {
660		errno = ntfs_w32error_to_errno(GetLastError());
661		ntfs_log_trace("Couldn't get NTFS volume length.\n");
662		return -1;
663	}
664	rvl = buf.NumberSectors.QuadPart * buf.BytesPerSector;
665	ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl);
666#else
667	errno = EINVAL;
668	rvl = -1;
669#endif
670	return rvl;
671}
672
673/**
674 * ntfs_device_win32_getgeo - get CHS information of a drive
675 * @handle:	an open handle to the PhysicalDevice
676 * @fd:		a win_fd structure that will be filled
677 *
678 * Return 0 if o.k.
679 *	 -1 if not, and errno  set.
680 *
681 * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1.
682 * In Windows XP+: fills size, sectors, cylinders, and heads.
683 *
684 * Note: In pre XP, this requires write permission, even though nothing is
685 * actually written.
686 *
687 * If fails, sets sectors, cylinders, heads, and size to -1.
688 */
689static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd)
690{
691	DWORD i;
692	BOOL rvl;
693	BYTE b[sizeof(DISK_GEOMETRY) + sizeof(DISK_PARTITION_INFO) +
694			sizeof(DISK_DETECTION_INFO) + 512];
695
696	rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL,
697			0, &b, sizeof(b), &i, NULL);
698	if (rvl) {
699		ntfs_log_debug("GET_DRIVE_GEOMETRY_EX detected.\n");
700		DISK_DETECTION_INFO *ddi = (PDISK_DETECTION_INFO)
701				(((PBYTE)(&((PDISK_GEOMETRY_EX)b)->Data)) +
702				(((PDISK_PARTITION_INFO)
703				(&((PDISK_GEOMETRY_EX)b)->Data))->
704				SizeOfPartitionInfo));
705		fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart;
706		fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack;
707		fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart;
708		fd->geo_sector_size = NTFS_BLOCK_SIZE;
709		switch (ddi->DetectionType) {
710		case DetectInt13:
711			fd->geo_cylinders = ddi->Int13.MaxCylinders;
712			fd->geo_sectors = ddi->Int13.SectorsPerTrack;
713			fd->geo_heads = ddi->Int13.MaxHeads;
714			return 0;
715		case DetectExInt13:
716			fd->geo_cylinders = ddi->ExInt13.ExCylinders;
717			fd->geo_sectors = ddi->ExInt13.ExSectorsPerTrack;
718			fd->geo_heads = ddi->ExInt13.ExHeads;
719			return 0;
720		case DetectNone:
721		default:
722			break;
723		}
724	} else
725		fd->geo_heads = -1;
726	rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
727			&b, sizeof(b), &i, NULL);
728	if (rvl) {
729		ntfs_log_debug("GET_DRIVE_GEOMETRY detected.\n");
730		fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart;
731		fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack;
732		fd->geo_size = fd->geo_cylinders * fd->geo_sectors *
733				((DISK_GEOMETRY*)&b)->TracksPerCylinder *
734				((DISK_GEOMETRY*)&b)->BytesPerSector;
735		fd->geo_sector_size = ((DISK_GEOMETRY*)&b)->BytesPerSector;
736		return 0;
737	}
738	errno = ntfs_w32error_to_errno(GetLastError());
739	ntfs_log_trace("Couldn't retrieve disk geometry.\n");
740	fd->geo_cylinders = -1;
741	fd->geo_sectors = -1;
742	fd->geo_size = -1;
743	fd->geo_sector_size = NTFS_BLOCK_SIZE;
744	return -1;
745}
746
747static int ntfs_device_win32_getntgeo(HANDLE handle, win32_fd *fd)
748{
749	DISK_GEOMETRY geo;
750	NTSTATUS st;
751	IO_STATUS_BLOCK status;
752	u64 bytes;
753	int res;
754
755	res = -1;
756	fd->geo_cylinders = 0;
757	fd->geo_sectors = 0;
758	fd->geo_size = 1073741824;
759	fd->geo_sectors = fd->geo_size >> 9;
760	fd->geo_sector_size = NTFS_BLOCK_SIZE;
761
762	st = NtDeviceIoControlFile(handle, (HANDLE)NULL,
763			(PIO_APC_ROUTINE)NULL, (void*)NULL,
764			&status, IOCTL_DISK_GET_DRIVE_GEOMETRY, (void*)NULL, 0,
765			(void*)&geo, sizeof(geo));
766	if (st == STATUS_SUCCESS) {
767		/* over-estimate the (rounded) number of cylinders */
768		fd->geo_cylinders = geo.Cylinders.QuadPart + 1;
769		fd->geo_sectors = fd->geo_cylinders
770				*geo.TracksPerCylinder*geo.SectorsPerTrack;
771		fd->geo_size = fd->geo_sectors*geo.BytesPerSector;
772		fd->geo_sector_size = geo.BytesPerSector;
773		res = 0;
774			/* try to get the exact sector count */
775		st = NtDeviceIoControlFile(handle, (HANDLE)NULL,
776				(PIO_APC_ROUTINE)NULL, (void*)NULL,
777				&status, IOCTL_GET_DISK_LENGTH_INFO,
778				(void*)NULL, 0,
779				(void*)&bytes, sizeof(bytes));
780		if (st == STATUS_SUCCESS) {
781			fd->geo_size = bytes;
782			fd->geo_sectors = bytes/geo.BytesPerSector;
783		}
784	}
785	return (res);
786}
787
788/**
789 * ntfs_device_win32_open_file - open a file via win32 API
790 * @filename:	name of the file to open
791 * @fd:		pointer to win32 file device in which to put the result
792 * @flags:	unix open status flags
793 *
794 * Return 0 if o.k.
795 *	 -1 if not, and errno set.
796 */
797static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd,
798		int flags)
799{
800	HANDLE handle;
801	int mode;
802
803	if (ntfs_device_win32_simple_open_file(filename, &handle, flags,
804			FALSE)) {
805		/* open error */
806		return -1;
807	}
808	mode = flags & O_ACCMODE;
809	if ((mode == O_RDWR) || (mode == O_WRONLY)) {
810		DWORD bytes;
811
812		/* try making sparse (but ignore errors) */
813		DeviceIoControl(handle, FSCTL_SET_SPARSE,
814				(void*)NULL, 0, (void*)NULL, 0,
815				&bytes, (LPOVERLAPPED)NULL);
816	}
817	/* fill fd */
818	fd->handle = handle;
819	fd->part_start = 0;
820	fd->part_length = ntfs_device_win32_getsize(handle);
821	fd->pos = 0;
822	fd->part_hidden_sectors = -1;
823	fd->geo_size = -1;	/* used as a marker that this is a file */
824	fd->vol_handle = INVALID_HANDLE_VALUE;
825	fd->geo_sector_size = 512;  /* will be adjusted from the boot sector */
826	fd->ntdll = FALSE;
827	return 0;
828}
829
830/**
831 * ntfs_device_win32_open_drive - open a drive via win32 API
832 * @drive_id:	drive to open
833 * @fd:		pointer to win32 file device in which to put the result
834 * @flags:	unix open status flags
835 *
836 * return 0 if o.k.
837 *        -1 if not, and errno set.
838 */
839static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd,
840		int flags)
841{
842	HANDLE handle;
843	int err;
844	char filename[MAX_PATH];
845
846	sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id);
847	if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags,
848			TRUE))) {
849		/* open error */
850		return err;
851	}
852	/* store the drive geometry */
853	ntfs_device_win32_getgeo(handle, fd);
854	/* Just to be sure */
855	if (fd->geo_size == -1)
856		fd->geo_size = ntfs_device_win32_getdisklength(handle);
857	/* fill fd */
858	fd->ntdll = FALSE;
859	fd->handle = handle;
860	fd->part_start = 0;
861	fd->part_length = fd->geo_size;
862	fd->pos = 0;
863	fd->part_hidden_sectors = -1;
864	fd->vol_handle = INVALID_HANDLE_VALUE;
865	return 0;
866}
867
868/**
869 * ntfs_device_win32_open_lowlevel - open a drive via low level win32 API
870 * @drive_id:	drive to open
871 * @fd:		pointer to win32 file device in which to put the result
872 * @flags:	unix open status flags
873 *
874 * return 0 if o.k.
875 *        -1 if not, and errno set.
876 */
877static __inline__ int ntfs_device_win32_open_lowlevel(int drive_id,
878		win32_fd *fd, int flags)
879{
880	HANDLE handle;
881	NTSTATUS st;
882	ACCESS_MASK access;
883	ULONG share;
884	OBJECT_ATTRIBUTES attr;
885	IO_STATUS_BLOCK io_status;
886	UNICODE_STRING unicode_name;
887	ntfschar unicode_buffer[7];
888	int mode;
889	static const ntfschar unicode_init[] = {
890		const_cpu_to_le16('\\'), const_cpu_to_le16('?'),
891		const_cpu_to_le16('?'), const_cpu_to_le16('\\'),
892		const_cpu_to_le16(' '), const_cpu_to_le16(':'),
893		const_cpu_to_le16(0)
894	};
895
896	memcpy(unicode_buffer, unicode_init, sizeof(unicode_buffer));
897	unicode_buffer[4] = cpu_to_le16(drive_id + 'A');
898	unicode_name.Buffer = unicode_buffer;
899	unicode_name.Length = 6*sizeof(ntfschar);
900	unicode_name.MaximumLength = 6*sizeof(ntfschar);
901
902	attr.Length = sizeof(OBJECT_ATTRIBUTES);
903	attr.RootDirectory = (HANDLE*)NULL;
904	attr.ObjectName = &unicode_name;
905	attr.Attributes = OBJ_CASE_INSENSITIVE;
906	attr.SecurityDescriptor = (void*)NULL;
907	attr.SecurityQualityOfService = (void*)NULL;
908
909	io_status.Status = 0;
910	io_status.Information = 0;
911	mode = flags & O_ACCMODE;
912	share = (mode == O_RDWR ?
913			0 : FILE_SHARE_READ | FILE_SHARE_WRITE);
914	access = (mode == O_RDWR ?
915			 FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE
916			: FILE_READ_DATA | SYNCHRONIZE);
917
918	st = NtOpenFile(&handle, access,
919			&attr, &io_status,
920			share,
921			FILE_SYNCHRONOUS_IO_ALERT);
922	if (st != STATUS_SUCCESS) {
923		errno = ntfs_ntstatus_to_errno(st);
924		return (-1);
925	}
926	ntfs_device_win32_setlock(handle,FSCTL_LOCK_VOLUME);
927	/* store the drive geometry */
928	ntfs_device_win32_getntgeo(handle, fd);
929	fd->ntdll = TRUE;
930	/* allow accessing the full partition */
931	st = NtFsControlFile(handle, (HANDLE)NULL,
932			(PIO_APC_ROUTINE)NULL,
933			(PVOID)NULL, &io_status,
934			FSCTL_ALLOW_EXTENDED_DASD_IO,
935			NULL, 0, NULL, 0);
936	if (st != STATUS_SUCCESS) {
937		errno = ntfs_ntstatus_to_errno(st);
938		NtClose(handle);
939		return (-1);
940	}
941	/* fill fd */
942	fd->handle = handle;
943	fd->part_start = 0;
944	fd->part_length = fd->geo_size;
945	fd->pos = 0;
946	fd->part_hidden_sectors = -1;
947	fd->vol_handle = INVALID_HANDLE_VALUE;
948	return 0;
949}
950
951/**
952 * ntfs_device_win32_open_volume_for_partition - find and open a volume
953 *
954 * Windows NT/2k/XP handles volumes instead of partitions.
955 * This function gets the partition details and return an open volume handle.
956 * That volume is the one whose only physical location on disk is the described
957 * partition.
958 *
959 * The function required Windows 2k/XP, otherwise it fails (gracefully).
960 *
961 * Return success: a valid open volume handle.
962 *        fail   : INVALID_HANDLE_VALUE
963 */
964static HANDLE ntfs_device_win32_open_volume_for_partition(unsigned int drive_id,
965		s64 part_offset, s64 part_length, int flags)
966{
967	HANDLE vol_find_handle;
968	TCHAR vol_name[MAX_PATH];
969
970	/* Make sure all the required imports exist. */
971	if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) {
972		ntfs_log_trace("Required dll imports not found.\n");
973		return INVALID_HANDLE_VALUE;
974	}
975	/* Start iterating through volumes. */
976	ntfs_log_trace("Entering with drive_id=%d, part_offset=%lld, "
977			"path_length=%lld, flags=%d.\n", drive_id,
978			(unsigned long long)part_offset,
979			(unsigned long long)part_length, flags);
980	vol_find_handle = fnFindFirstVolume(vol_name, MAX_PATH);
981	/* If a valid handle could not be aquired, reply with "don't know". */
982	if (vol_find_handle == INVALID_HANDLE_VALUE) {
983		ntfs_log_trace("FindFirstVolume failed.\n");
984		return INVALID_HANDLE_VALUE;
985	}
986	do {
987		int vol_name_length;
988		HANDLE handle;
989
990		/* remove trailing '/' from vol_name */
991#ifdef UNICODE
992		vol_name_length = wcslen(vol_name);
993#else
994		vol_name_length = strlen(vol_name);
995#endif
996		if (vol_name_length>0)
997			vol_name[vol_name_length-1]=0;
998
999		ntfs_log_debug("Processing %s.\n", vol_name);
1000		/* open the file */
1001		handle = CreateFile(vol_name,
1002				ntfs_device_unix_status_flags_to_win32(flags),
1003				FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1004				OPEN_EXISTING, 0, NULL);
1005		if (handle != INVALID_HANDLE_VALUE) {
1006			DWORD bytesReturned;
1007#define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT)
1008			char extents[EXTENTS_SIZE];
1009
1010			/* Check physical locations. */
1011			if (DeviceIoControl(handle,
1012					IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
1013					NULL, 0, extents, EXTENTS_SIZE,
1014					&bytesReturned, NULL)) {
1015				if (((VOLUME_DISK_EXTENTS *)extents)->
1016						NumberOfDiskExtents == 1) {
1017					DISK_EXTENT *extent = &((
1018							VOLUME_DISK_EXTENTS *)
1019							extents)->Extents[0];
1020					if ((extent->DiskNumber==drive_id) &&
1021							(extent->StartingOffset.
1022							QuadPart==part_offset)
1023							&& (extent->
1024							ExtentLength.QuadPart
1025							== part_length)) {
1026						/*
1027						 * Eureka! (Archimedes, 287 BC,
1028						 * "I have found it!")
1029						 */
1030						fnFindVolumeClose(
1031							vol_find_handle);
1032						return handle;
1033					}
1034				}
1035			}
1036		} else
1037			ntfs_log_trace("getExtents() Failed.\n");
1038	} while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH));
1039	/* End of iteration through volumes. */
1040	ntfs_log_trace("Closing, volume was not found.\n");
1041	fnFindVolumeClose(vol_find_handle);
1042	return INVALID_HANDLE_VALUE;
1043}
1044
1045/**
1046 * ntfs_device_win32_find_partition - locates partition details by id.
1047 * @handle:		HANDLE to the PhysicalDrive
1048 * @partition_id:	the partition number to locate
1049 * @part_offset:	pointer to where to put the offset to the partition
1050 * @part_length:	pointer to where to put the length of the partition
1051 * @hidden_sectors:	pointer to where to put the hidden sectors
1052 *
1053 * This function requires an open PhysicalDrive handle and a partition_id.
1054 * If a partition with the required id is found on the supplied device,
1055 * the partition attributes are returned back.
1056 *
1057 * Returns: TRUE  if found, and sets the output parameters.
1058 *          FALSE if not and errno is set to the error code.
1059 */
1060static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id,
1061		s64 *part_offset, s64 *part_length, int *hidden_sectors)
1062{
1063	DRIVE_LAYOUT_INFORMATION *drive_layout;
1064	unsigned int err, buf_size, part_count;
1065	DWORD i;
1066
1067	/*
1068	 * There is no way to know the required buffer, so if the ioctl fails,
1069	 * try doubling the buffer size each time until the ioctl succeeds.
1070	 */
1071	part_count = 8;
1072	do {
1073		buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) +
1074				part_count * sizeof(PARTITION_INFORMATION);
1075		drive_layout = (DRIVE_LAYOUT_INFORMATION*)ntfs_malloc(buf_size);
1076		if (!drive_layout) {
1077			errno = ENOMEM;
1078			return FALSE;
1079		}
1080		if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL,
1081				0, (BYTE*)drive_layout, buf_size, &i, NULL))
1082			break;
1083		err = GetLastError();
1084		free(drive_layout);
1085		if (err != ERROR_INSUFFICIENT_BUFFER) {
1086			ntfs_log_trace("GetDriveLayout failed.\n");
1087			errno = ntfs_w32error_to_errno(err);
1088			return FALSE;
1089		}
1090		ntfs_log_debug("More than %u partitions.\n", part_count);
1091		part_count <<= 1;
1092		if (part_count > 512) {
1093			ntfs_log_trace("GetDriveLayout failed: More than 512 "
1094					"partitions?\n");
1095			errno = ENOBUFS;
1096			return FALSE;
1097		}
1098	} while (1);
1099	for (i = 0; i < drive_layout->PartitionCount; i++) {
1100		if (drive_layout->PartitionEntry[i].PartitionNumber ==
1101				partition_id) {
1102			*part_offset = drive_layout->PartitionEntry[i].
1103					StartingOffset.QuadPart;
1104			*part_length = drive_layout->PartitionEntry[i].
1105					PartitionLength.QuadPart;
1106			*hidden_sectors = drive_layout->PartitionEntry[i].
1107					HiddenSectors;
1108			free(drive_layout);
1109			return TRUE;
1110		}
1111	}
1112	free(drive_layout);
1113	errno = ENOENT;
1114	return FALSE;
1115}
1116
1117/**
1118 * ntfs_device_win32_open_partition - open a partition via win32 API
1119 * @drive_id:		drive to open
1120 * @partition_id:	partition to open
1121 * @fd:			win32 file device to return
1122 * @flags:		unix open status flags
1123 *
1124 * Return  0 if o.k.
1125 *        -1 if not, and errno set.
1126 *
1127 * When fails, fd contents may have not been preserved.
1128 */
1129static int ntfs_device_win32_open_partition(int drive_id,
1130		unsigned int partition_id, win32_fd *fd, int flags)
1131{
1132	s64 part_start, part_length;
1133	HANDLE handle;
1134	int err, hidden_sectors;
1135	char drive_name[MAX_PATH];
1136
1137	sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id);
1138	/* Open the entire device without locking, ask questions later */
1139	if ((err = ntfs_device_win32_simple_open_file(drive_name, &handle,
1140			flags, FALSE))) {
1141		/* error */
1142		return err;
1143	}
1144	if (ntfs_device_win32_find_partition(handle, partition_id, &part_start,
1145			&part_length, &hidden_sectors)) {
1146		s64 tmp;
1147		HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition(
1148			drive_id, part_start, part_length, flags);
1149		/* Store the drive geometry. */
1150		ntfs_device_win32_getgeo(handle, fd);
1151		fd->handle = handle;
1152		fd->pos = 0;
1153		fd->part_start = part_start;
1154		fd->part_length = part_length;
1155		fd->part_hidden_sectors = hidden_sectors;
1156		fd->geo_sector_size = 512;
1157		fd->ntdll = FALSE;
1158		tmp = ntfs_device_win32_getntfssize(vol_handle);
1159		if (tmp > 0)
1160			fd->geo_size = tmp;
1161		else
1162			fd->geo_size = fd->part_length;
1163		if (vol_handle != INVALID_HANDLE_VALUE) {
1164			if (((flags & O_RDWR) == O_RDWR) &&
1165					ntfs_device_win32_lock(vol_handle)) {
1166				CloseHandle(vol_handle);
1167				CloseHandle(handle);
1168				return -1;
1169			}
1170			fd->vol_handle = vol_handle;
1171		} else {
1172			if ((flags & O_RDWR) == O_RDWR) {
1173				/* Access if read-write, no volume found. */
1174				ntfs_log_trace("Partitions containing Spanned/"
1175						"Mirrored volumes are not "
1176						"supported in R/W status "
1177						"yet.\n");
1178				CloseHandle(handle);
1179				errno = EOPNOTSUPP;
1180				return -1;
1181			}
1182			fd->vol_handle = INVALID_HANDLE_VALUE;
1183		}
1184		return 0;
1185	} else {
1186		ntfs_log_debug("Partition %u not found on drive %d.\n",
1187				partition_id, drive_id);
1188		CloseHandle(handle);
1189		errno = ENODEV;
1190		return -1;
1191	}
1192}
1193
1194/**
1195 * ntfs_device_win32_open - open a device
1196 * @dev:	a pointer to the NTFS_DEVICE to open
1197 * @flags:	unix open status flags
1198 *
1199 * @dev->d_name must hold the device name, the rest is ignored.
1200 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
1201 *
1202 * If name is in format "(hd[0-9],[0-9])" then open a partition.
1203 * If name is in format "(hd[0-9])" then open a volume.
1204 * Otherwise open a file.
1205 */
1206static int ntfs_device_win32_open(struct ntfs_device *dev, int flags)
1207{
1208	int drive_id = 0, numparams;
1209	unsigned int part = 0;
1210	char drive_char;
1211	win32_fd fd;
1212	int err;
1213
1214	if (NDevOpen(dev)) {
1215		errno = EBUSY;
1216		return -1;
1217	}
1218	ntfs_device_win32_init_imports();
1219	numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part);
1220	if (!numparams
1221	    && (dev->d_name[1] == ':')
1222	    && (dev->d_name[2] == '\0')) {
1223		drive_char = dev->d_name[0];
1224		numparams = 3;
1225		drive_id = toupper(drive_char) - 'A';
1226	}
1227	switch (numparams) {
1228	case 0:
1229		ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name);
1230		err = ntfs_device_win32_open_file(dev->d_name, &fd, flags);
1231		break;
1232	case 1:
1233		ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev->d_name,
1234				drive_id);
1235		err = ntfs_device_win32_open_drive(drive_id, &fd, flags);
1236		break;
1237	case 2:
1238		ntfs_log_debug("win32_open(%s) -> drive %d, part %u.\n",
1239				dev->d_name, drive_id, part);
1240		err = ntfs_device_win32_open_partition(drive_id, part, &fd,
1241				flags);
1242		break;
1243	case 3:
1244		ntfs_log_debug("win32_open(%s) -> drive %c:\n",
1245				dev->d_name, drive_char);
1246		err = ntfs_device_win32_open_lowlevel(drive_id, &fd,
1247				flags);
1248		break;
1249	default:
1250		ntfs_log_debug("win32_open(%s) -> unknwon file format.\n",
1251				dev->d_name);
1252		err = -1;
1253	}
1254	if (err)
1255		return err;
1256	ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev->d_name,
1257			dev, fd.part_start);
1258	/* Setup our read-only flag. */
1259	if ((flags & O_RDWR) != O_RDWR)
1260		NDevSetReadOnly(dev);
1261	dev->d_private = (win32_fd*)ntfs_malloc(sizeof(win32_fd));
1262	memcpy(dev->d_private, &fd, sizeof(win32_fd));
1263	NDevSetOpen(dev);
1264	NDevClearDirty(dev);
1265	return 0;
1266}
1267
1268/**
1269 * ntfs_device_win32_seek - change current logical file position
1270 * @dev:	ntfs device obtained via ->open
1271 * @offset:	required offset from the whence anchor
1272 * @whence:	whence anchor specifying what @offset is relative to
1273 *
1274 * Return the new position on the volume on success and -1 on error with errno
1275 * set to the error code.
1276 *
1277 * @whence may be one of the following:
1278 *	SEEK_SET - Offset is relative to file start.
1279 *	SEEK_CUR - Offset is relative to current position.
1280 *	SEEK_END - Offset is relative to end of file.
1281 */
1282static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset,
1283		int whence)
1284{
1285	s64 abs_ofs;
1286	win32_fd *fd = (win32_fd *)dev->d_private;
1287
1288	ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence);
1289	switch (whence) {
1290	case SEEK_SET:
1291		abs_ofs = offset;
1292		break;
1293	case SEEK_CUR:
1294		abs_ofs = fd->pos + offset;
1295		break;
1296	case SEEK_END:
1297		/* End of partition != end of disk. */
1298		if (fd->part_length == -1) {
1299			ntfs_log_trace("Position relative to end of disk not "
1300					"implemented.\n");
1301			errno = EOPNOTSUPP;
1302			return -1;
1303		}
1304		abs_ofs = fd->part_length + offset;
1305		break;
1306	default:
1307		ntfs_log_trace("Wrong mode %d.\n", whence);
1308		errno = EINVAL;
1309		return -1;
1310	}
1311	if ((abs_ofs < 0)
1312	    || (fd->ntdll && (abs_ofs > fd->part_length))) {
1313		ntfs_log_trace("Seeking outsize seekable area.\n");
1314		errno = EINVAL;
1315		return -1;
1316	}
1317	fd->pos = abs_ofs;
1318	return abs_ofs;
1319}
1320
1321/**
1322 * ntfs_device_win32_pio - positioned low level i/o
1323 * @fd:		win32 device descriptor obtained via ->open
1324 * @pos:	at which position to do i/o from/to
1325 * @count:	how many bytes should be transfered
1326 * @b:		source/destination buffer
1327 * @write:	TRUE if write transfer and FALSE if read transfer
1328 *
1329 * On success returns the number of bytes transfered (can be < @count) and on
1330 * error returns -1 and errno set.  Transfer starts from position @pos on @fd.
1331 *
1332 * Notes:
1333 *	- @pos, @buf, and @count must be aligned to geo_sector_size
1334 *	- When dealing with volumes, a single call must not span both volume
1335 *	  and disk extents.
1336 *	- Does not use/set @fd->pos.
1337 */
1338static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos,
1339		const s64 count, void *rbuf, const void *wbuf)
1340{
1341	LARGE_INTEGER li;
1342	HANDLE handle;
1343	DWORD bt;
1344	BOOL res;
1345	s64 bytes;
1346
1347	ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n",
1348			(long long)pos, (long long)count, write ? "write" :
1349			"read");
1350	li.QuadPart = pos;
1351	if (fd->vol_handle != INVALID_HANDLE_VALUE && pos < fd->geo_size) {
1352		ntfs_log_debug("Transfering via vol_handle.\n");
1353		handle = fd->vol_handle;
1354	} else {
1355		ntfs_log_debug("Transfering via handle.\n");
1356		handle = fd->handle;
1357		li.QuadPart += fd->part_start;
1358	}
1359
1360	if (fd->ntdll) {
1361		IO_STATUS_BLOCK io_status;
1362		NTSTATUS res;
1363		LARGE_INTEGER offset;
1364
1365		io_status.Status = STATUS_SUCCESS;
1366		io_status.Information = 0;
1367		offset.QuadPart = pos;
1368		if (wbuf) {
1369			res = NtWriteFile(fd->handle,(HANDLE)NULL,
1370					(PIO_APC_ROUTINE)NULL,(void*)NULL,
1371					&io_status, wbuf, count,
1372					&offset, (PULONG)NULL);
1373		} else {
1374			res = NtReadFile(fd->handle,(HANDLE)NULL,
1375					(PIO_APC_ROUTINE)NULL,(void*)NULL,
1376					&io_status, rbuf, count,
1377					&offset, (PULONG)NULL);
1378		}
1379		if (res == STATUS_SUCCESS) {
1380			bytes = io_status.Information;
1381		} else {
1382			bytes = -1;
1383			errno = ntfs_ntstatus_to_errno(res);
1384		}
1385	} else {
1386		if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) {
1387			errno = ntfs_w32error_to_errno(GetLastError());
1388			ntfs_log_trace("SetFilePointer failed.\n");
1389			return -1;
1390		}
1391		if (wbuf)
1392			res = WriteFile(handle, wbuf, count, &bt, NULL);
1393		else
1394			res = ReadFile(handle, rbuf, count, &bt, NULL);
1395		bytes = bt;
1396		if (!res) {
1397			errno = ntfs_w32error_to_errno(GetLastError());
1398			ntfs_log_trace("%sFile() failed.\n", write ?
1399							"Write" : "Read");
1400			return -1;
1401		}
1402		if (rbuf && !pos) {
1403			/* get the sector size from the boot sector */
1404			char *boot = (char*)rbuf;
1405			fd->geo_sector_size = (boot[11] & 255)
1406						+ ((boot[12] & 255) << 8);
1407		}
1408	}
1409	return bytes;
1410}
1411
1412/**
1413 * ntfs_device_win32_pread_simple - positioned simple read
1414 * @fd:		win32 device descriptor obtained via ->open
1415 * @pos:	at which position to read from
1416 * @count:	how many bytes should be read
1417 * @b:		a pointer to where to put the contents
1418 *
1419 * On success returns the number of bytes read (can be < @count) and on error
1420 * returns -1 and errno set.  Read starts from position @pos.
1421 *
1422 * Notes:
1423 *	- @pos, @buf, and @count must be aligned to geo_sector_size.
1424 *	- When dealing with volumes, a single call must not span both volume
1425 *	  and disk extents.
1426 *	- Does not use/set @fd->pos.
1427 */
1428static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos,
1429		const s64 count, void *b)
1430{
1431	return ntfs_device_win32_pio(fd, pos, count, b, (void*)NULL);
1432}
1433
1434/**
1435 * ntfs_device_win32_read - read bytes from an ntfs device
1436 * @dev:	ntfs device obtained via ->open
1437 * @b:		pointer to where to put the contents
1438 * @count:	how many bytes should be read
1439 *
1440 * On success returns the number of bytes actually read (can be < @count).
1441 * On error returns -1 with errno set.
1442 */
1443static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count)
1444{
1445	s64 old_pos, to_read, i, br = 0;
1446	win32_fd *fd = (win32_fd *)dev->d_private;
1447	BYTE *alignedbuffer;
1448	int old_ofs, ofs;
1449
1450	old_pos = fd->pos;
1451	old_ofs = ofs = old_pos & (fd->geo_sector_size - 1);
1452	to_read = (ofs + count + fd->geo_sector_size - 1) &
1453			~(s64)(fd->geo_sector_size - 1);
1454	/* Impose maximum of 2GB to be on the safe side. */
1455	if (to_read > 0x80000000) {
1456		int delta = to_read - count;
1457		to_read = 0x80000000;
1458		count = to_read - delta;
1459	}
1460	ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, "
1461			"ofs = %i, to_read = 0x%llx.\n", fd, b,
1462			(long long)count, (long long)old_pos, ofs,
1463			(long long)to_read);
1464	if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs &&
1465			!(count & (fd->geo_sector_size - 1)))
1466		alignedbuffer = b;
1467	else {
1468		alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT,
1469				PAGE_READWRITE);
1470		if (!alignedbuffer) {
1471			errno = ntfs_w32error_to_errno(GetLastError());
1472			ntfs_log_trace("VirtualAlloc failed for read.\n");
1473			return -1;
1474		}
1475	}
1476	if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) {
1477		s64 vol_to_read = fd->geo_size - old_pos;
1478		if (count > vol_to_read) {
1479			br = ntfs_device_win32_pread_simple(fd,
1480					old_pos & ~(s64)(fd->geo_sector_size - 1),
1481					ofs + vol_to_read, alignedbuffer);
1482			if (br == -1)
1483				goto read_error;
1484			to_read -= br;
1485			if (br < ofs) {
1486				br = 0;
1487				goto read_partial;
1488			}
1489			br -= ofs;
1490			fd->pos += br;
1491			ofs = fd->pos & (fd->geo_sector_size - 1);
1492			if (br != vol_to_read)
1493				goto read_partial;
1494		}
1495	}
1496	i = ntfs_device_win32_pread_simple(fd,
1497			fd->pos & ~(s64)(fd->geo_sector_size - 1), to_read,
1498			alignedbuffer + br);
1499	if (i == -1) {
1500		if (br)
1501			goto read_partial;
1502		goto read_error;
1503	}
1504	if (i < ofs)
1505		goto read_partial;
1506	i -= ofs;
1507	br += i;
1508	if (br > count)
1509		br = count;
1510	fd->pos = old_pos + br;
1511read_partial:
1512	if (alignedbuffer != b) {
1513		memcpy((void*)b, alignedbuffer + old_ofs, br);
1514		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1515	}
1516	return br;
1517read_error:
1518	if (alignedbuffer != b)
1519		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1520	return -1;
1521}
1522
1523/**
1524 * ntfs_device_win32_close - close an open ntfs deivce
1525 * @dev:	ntfs device obtained via ->open
1526 *
1527 * Return 0 if o.k.
1528 *	 -1 if not, and errno set.  Note if error fd->vol_handle is trashed.
1529 */
1530static int ntfs_device_win32_close(struct ntfs_device *dev)
1531{
1532	win32_fd *fd = (win32_fd *)dev->d_private;
1533	BOOL rvl;
1534
1535	ntfs_log_trace("Closing device %p.\n", dev);
1536	if (!NDevOpen(dev)) {
1537		errno = EBADF;
1538		return -1;
1539	}
1540	if (fd->vol_handle != INVALID_HANDLE_VALUE) {
1541		if (!NDevReadOnly(dev)) {
1542			ntfs_device_win32_dismount(fd->vol_handle);
1543			ntfs_device_win32_unlock(fd->vol_handle);
1544		}
1545		if (!CloseHandle(fd->vol_handle))
1546			ntfs_log_trace("CloseHandle() failed for volume.\n");
1547	}
1548	if (fd->ntdll) {
1549		ntfs_device_win32_setlock(fd->handle,FSCTL_UNLOCK_VOLUME);
1550		rvl = NtClose(fd->handle) == STATUS_SUCCESS;
1551	} else
1552		rvl = CloseHandle(fd->handle);
1553	NDevClearOpen(dev);
1554	free(fd);
1555	if (!rvl) {
1556		errno = ntfs_w32error_to_errno(GetLastError());
1557		if (fd->ntdll)
1558			ntfs_log_trace("NtClose() failed.\n");
1559		else
1560			ntfs_log_trace("CloseHandle() failed.\n");
1561		return -1;
1562	}
1563	return 0;
1564}
1565
1566/**
1567 * ntfs_device_win32_sync - flush write buffers to disk
1568 * @dev:	ntfs device obtained via ->open
1569 *
1570 * Return 0 if o.k.
1571 *	 -1 if not, and errno set.
1572 *
1573 * Note: Volume syncing works differently in windows.
1574 *	 Disk cannot be synced in windows.
1575 */
1576static int ntfs_device_win32_sync(struct ntfs_device *dev)
1577{
1578	int err = 0;
1579	BOOL to_clear = TRUE;
1580
1581	if (!NDevReadOnly(dev) && NDevDirty(dev)) {
1582		win32_fd *fd = (win32_fd *)dev->d_private;
1583
1584		if ((fd->vol_handle != INVALID_HANDLE_VALUE) &&
1585				!FlushFileBuffers(fd->vol_handle)) {
1586			to_clear = FALSE;
1587			err = ntfs_w32error_to_errno(GetLastError());
1588		}
1589		if (!FlushFileBuffers(fd->handle)) {
1590			to_clear = FALSE;
1591			if (!err)
1592				err = ntfs_w32error_to_errno(GetLastError());
1593		}
1594		if (!to_clear) {
1595			ntfs_log_trace("Could not sync.\n");
1596			errno = err;
1597			return -1;
1598		}
1599		NDevClearDirty(dev);
1600	}
1601	return 0;
1602}
1603
1604/**
1605 * ntfs_device_win32_pwrite_simple - positioned simple write
1606 * @fd:		win32 device descriptor obtained via ->open
1607 * @pos:	at which position to write to
1608 * @count:	how many bytes should be written
1609 * @b:		a pointer to the data to write
1610 *
1611 * On success returns the number of bytes written and on error returns -1 and
1612 * errno set.  Write starts from position @pos.
1613 *
1614 * Notes:
1615 *	- @pos, @buf, and @count must be aligned to geo_sector_size.
1616 *	- When dealing with volumes, a single call must not span both volume
1617 *	  and disk extents.
1618 *	- Does not use/set @fd->pos.
1619 */
1620static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos,
1621		const s64 count, const void *b)
1622{
1623	return ntfs_device_win32_pio(fd, pos, count, (void*)NULL, b);
1624}
1625
1626/**
1627 * ntfs_device_win32_write - write bytes to an ntfs device
1628 * @dev:	ntfs device obtained via ->open
1629 * @b:		pointer to the data to write
1630 * @count:	how many bytes should be written
1631 *
1632 * On success returns the number of bytes actually written.
1633 * On error returns -1 with errno set.
1634 */
1635static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b,
1636		s64 count)
1637{
1638	s64 old_pos, to_write, i, bw = 0;
1639	win32_fd *fd = (win32_fd *)dev->d_private;
1640	const BYTE *alignedbuffer;
1641	BYTE *readbuffer;
1642	int old_ofs, ofs;
1643
1644	old_pos = fd->pos;
1645	old_ofs = ofs = old_pos & (fd->geo_sector_size - 1);
1646	to_write = (ofs + count + fd->geo_sector_size - 1) &
1647			~(s64)(fd->geo_sector_size - 1);
1648	/* Impose maximum of 2GB to be on the safe side. */
1649	if (to_write > 0x80000000) {
1650		int delta = to_write - count;
1651		to_write = 0x80000000;
1652		count = to_write - delta;
1653	}
1654	ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, "
1655			"ofs = %i, to_write = 0x%llx.\n", fd, b,
1656			(long long)count, (long long)old_pos, ofs,
1657			(long long)to_write);
1658	if (NDevReadOnly(dev)) {
1659		ntfs_log_trace("Can't write on a R/O device.\n");
1660		errno = EROFS;
1661		return -1;
1662	}
1663	if (!count)
1664		return 0;
1665	NDevSetDirty(dev);
1666	readbuffer = (BYTE*)NULL;
1667	if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs &&
1668			!(count & (fd->geo_sector_size - 1)))
1669		alignedbuffer = (const BYTE *)b;
1670	else {
1671		s64 end;
1672
1673		readbuffer = (BYTE *)VirtualAlloc(NULL, to_write,
1674				MEM_COMMIT, PAGE_READWRITE);
1675		if (!readbuffer) {
1676			errno = ntfs_w32error_to_errno(GetLastError());
1677			ntfs_log_trace("VirtualAlloc failed for write.\n");
1678			return -1;
1679		}
1680		/* Read first sector if start of write not sector aligned. */
1681		if (ofs) {
1682			i = ntfs_device_win32_pread_simple(fd,
1683					old_pos & ~(s64)(fd->geo_sector_size - 1),
1684					fd->geo_sector_size, readbuffer);
1685			if (i != fd->geo_sector_size) {
1686				if (i >= 0)
1687					errno = EIO;
1688				goto write_error;
1689			}
1690		}
1691		/*
1692		 * Read last sector if end of write not sector aligned and last
1693		 * sector is either not the same as the first sector or it is
1694		 * the same as the first sector but this has not been read in
1695		 * yet, i.e. the start of the write is sector aligned.
1696		 */
1697		end = old_pos + count;
1698		if ((end & (fd->geo_sector_size - 1)) &&
1699				((to_write > fd->geo_sector_size) || !ofs)) {
1700			i = ntfs_device_win32_pread_simple(fd,
1701					end & ~(s64)(fd->geo_sector_size - 1),
1702					fd->geo_sector_size, readbuffer +
1703					to_write - fd->geo_sector_size);
1704			if (i != fd->geo_sector_size) {
1705				if (i >= 0)
1706					errno = EIO;
1707				goto write_error;
1708			}
1709		}
1710		/* Copy the data to be written into @readbuffer. */
1711		memcpy(readbuffer + ofs, b, count);
1712		alignedbuffer = readbuffer;
1713	}
1714	if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) {
1715		s64 vol_to_write = fd->geo_size - old_pos;
1716		if (count > vol_to_write) {
1717			bw = ntfs_device_win32_pwrite_simple(fd,
1718					old_pos & ~(s64)(fd->geo_sector_size - 1),
1719					ofs + vol_to_write, alignedbuffer);
1720			if (bw == -1)
1721				goto write_error;
1722			to_write -= bw;
1723			if (bw < ofs) {
1724				bw = 0;
1725				goto write_partial;
1726			}
1727			bw -= ofs;
1728			fd->pos += bw;
1729			ofs = fd->pos & (fd->geo_sector_size - 1);
1730			if (bw != vol_to_write)
1731				goto write_partial;
1732		}
1733	}
1734	i = ntfs_device_win32_pwrite_simple(fd,
1735			fd->pos & ~(s64)(fd->geo_sector_size - 1), to_write,
1736			alignedbuffer + bw);
1737	if (i == -1) {
1738		if (bw)
1739			goto write_partial;
1740		goto write_error;
1741	}
1742	if (i < ofs)
1743		goto write_partial;
1744	i -= ofs;
1745	bw += i;
1746	if (bw > count)
1747		bw = count;
1748	fd->pos = old_pos + bw;
1749write_partial:
1750	if (readbuffer)
1751		VirtualFree(readbuffer, 0, MEM_RELEASE);
1752	return bw;
1753write_error:
1754	bw = -1;
1755	goto write_partial;
1756}
1757
1758/**
1759 * ntfs_device_win32_stat - get a unix-like stat structure for an ntfs device
1760 * @dev:	ntfs device obtained via ->open
1761 * @buf:	pointer to the stat structure to fill
1762 *
1763 * Note: Only st_mode, st_size, and st_blocks are filled.
1764 *
1765 * Return 0 if o.k.
1766 *	 -1 if not and errno set. in this case handle is trashed.
1767 */
1768static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf)
1769{
1770	win32_fd *fd = (win32_fd *)dev->d_private;
1771	mode_t st_mode;
1772
1773	if ((dev->d_name[1] == ':') && (dev->d_name[2] == '\0'))
1774		st_mode = S_IFBLK;
1775	else
1776		switch (GetFileType(fd->handle)) {
1777		case FILE_TYPE_CHAR:
1778			st_mode = S_IFCHR;
1779			break;
1780		case FILE_TYPE_DISK:
1781			st_mode = S_IFREG;
1782			break;
1783		case FILE_TYPE_PIPE:
1784			st_mode = S_IFIFO;
1785			break;
1786		default:
1787			st_mode = 0;
1788		}
1789	memset(buf, 0, sizeof(struct stat));
1790	buf->st_mode = st_mode;
1791	buf->st_size = fd->part_length;
1792	if (buf->st_size != -1)
1793		buf->st_blocks = buf->st_size >> 9;
1794	else
1795		buf->st_size = 0;
1796	return 0;
1797}
1798
1799#ifdef HDIO_GETGEO
1800/**
1801 * ntfs_win32_hdio_getgeo - get drive geometry
1802 * @dev:	ntfs device obtained via ->open
1803 * @argp:	pointer to where to put the output
1804 *
1805 * Note: Works on windows NT/2k/XP only.
1806 *
1807 * Return 0 if o.k.
1808 *	 -1 if not, and errno set.  Note if error fd->handle is trashed.
1809 */
1810static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev,
1811		struct hd_geometry *argp)
1812{
1813	win32_fd *fd = (win32_fd *)dev->d_private;
1814
1815	argp->heads = fd->geo_heads;
1816	argp->sectors = fd->geo_sectors;
1817	argp->cylinders = fd->geo_cylinders;
1818	argp->start = fd->part_hidden_sectors;
1819	return 0;
1820}
1821#endif
1822
1823/**
1824 * ntfs_win32_blksszget - get block device sector size
1825 * @dev:	ntfs device obtained via ->open
1826 * @argp:	pointer to where to put the output
1827 *
1828 * Note: Works on windows NT/2k/XP only.
1829 *
1830 * Return 0 if o.k.
1831 *	 -1 if not, and errno set.  Note if error fd->handle is trashed.
1832 */
1833static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp)
1834{
1835	win32_fd *fd = (win32_fd *)dev->d_private;
1836	DWORD bytesReturned;
1837	DISK_GEOMETRY dg;
1838
1839	if (DeviceIoControl(fd->handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
1840			&dg, sizeof(DISK_GEOMETRY), &bytesReturned, NULL)) {
1841		/* success */
1842		*argp = dg.BytesPerSector;
1843		return 0;
1844	}
1845	errno = ntfs_w32error_to_errno(GetLastError());
1846	ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n");
1847	return -1;
1848}
1849
1850static int ntfs_device_win32_ioctl(struct ntfs_device *dev,
1851		unsigned long request, void *argp)
1852{
1853#if defined(BLKGETSIZE) | defined(BLKGETSIZE64)
1854	win32_fd *fd = (win32_fd *)dev->d_private;
1855#endif
1856
1857	ntfs_log_trace("win32_ioctl(0x%lx) called.\n", request);
1858	switch (request) {
1859#if defined(BLKGETSIZE)
1860	case BLKGETSIZE:
1861		ntfs_log_debug("BLKGETSIZE detected.\n");
1862		if (fd->part_length >= 0) {
1863			*(int *)argp = (int)(fd->part_length / 512);
1864			return 0;
1865		}
1866		errno = EOPNOTSUPP;
1867		return -1;
1868#endif
1869#if defined(BLKGETSIZE64)
1870	case BLKGETSIZE64:
1871		ntfs_log_debug("BLKGETSIZE64 detected.\n");
1872		if (fd->part_length >= 0) {
1873			*(s64 *)argp = fd->part_length;
1874			return 0;
1875		}
1876		errno = EOPNOTSUPP;
1877		return -1;
1878#endif
1879#ifdef HDIO_GETGEO
1880	case HDIO_GETGEO:
1881		ntfs_log_debug("HDIO_GETGEO detected.\n");
1882		return ntfs_win32_hdio_getgeo(dev, (struct hd_geometry *)argp);
1883#endif
1884#ifdef BLKSSZGET
1885	case BLKSSZGET:
1886		ntfs_log_debug("BLKSSZGET detected.\n");
1887		if (fd && !fd->ntdll) {
1888			*(int*)argp = fd->geo_sector_size;
1889			return (0);
1890		} else
1891			return ntfs_win32_blksszget(dev, (int *)argp);
1892#endif
1893#ifdef BLKBSZSET
1894	case BLKBSZSET:
1895		ntfs_log_debug("BLKBSZSET detected.\n");
1896		/* Nothing to do on Windows. */
1897		return 0;
1898#endif
1899	default:
1900		ntfs_log_debug("unimplemented ioctl 0x%lx.\n", request);
1901		errno = EOPNOTSUPP;
1902		return -1;
1903	}
1904}
1905
1906static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b,
1907		s64 count, s64 offset)
1908{
1909	s64 got;
1910	win32_fd *fd;
1911
1912		/* read the fast way if sector aligned */
1913	fd = (win32_fd*)dev->d_private;
1914	if (!((count | offset) & (fd->geo_sector_size - 1))) {
1915		got = ntfs_device_win32_pio(fd, offset, count, b, (void*)NULL);
1916	} else {
1917		if (ntfs_device_win32_seek(dev, offset, 0) == -1)
1918			got = 0;
1919		else
1920			got = ntfs_device_win32_read(dev, b, count);
1921	}
1922
1923	return (got);
1924}
1925
1926static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b,
1927		s64 count, s64 offset)
1928{
1929	s64 put;
1930	win32_fd *fd;
1931
1932		/* write the fast way if sector aligned */
1933	fd = (win32_fd*)dev->d_private;
1934	if (!((count | offset) & (fd->geo_sector_size - 1))) {
1935		put = ntfs_device_win32_pio(fd, offset, count, (void*)NULL, b);
1936	} else {
1937		if (ntfs_device_win32_seek(dev, offset, 0) == -1)
1938			put = 0;
1939		else
1940			put = ntfs_device_win32_write(dev, b, count);
1941	}
1942	return (put);
1943}
1944
1945struct ntfs_device_operations ntfs_device_win32_io_ops = {
1946	.open		= ntfs_device_win32_open,
1947	.close		= ntfs_device_win32_close,
1948	.seek		= ntfs_device_win32_seek,
1949	.read		= ntfs_device_win32_read,
1950	.write		= ntfs_device_win32_write,
1951	.pread		= ntfs_device_win32_pread,
1952	.pwrite		= ntfs_device_win32_pwrite,
1953	.sync		= ntfs_device_win32_sync,
1954	.stat		= ntfs_device_win32_stat,
1955	.ioctl		= ntfs_device_win32_ioctl
1956};
1957
1958/*
1959 *			Mark an open file as sparse
1960 *
1961 *	This is only called by ntfsclone when cloning a volume to a file.
1962 *	The argument is the target file, not a volume.
1963 *
1964 *	Returns 0 if successful.
1965 */
1966
1967int ntfs_win32_set_sparse(int fd)
1968{
1969	BOOL ok;
1970	HANDLE handle;
1971	DWORD bytes;
1972
1973	handle = get_osfhandle(fd);
1974	if (handle == INVALID_HANDLE_VALUE)
1975		ok = FALSE;
1976	else
1977		ok = DeviceIoControl(handle, FSCTL_SET_SPARSE,
1978				(void*)NULL, 0, (void*)NULL, 0,
1979				&bytes, (LPOVERLAPPED)NULL);
1980	return (!ok);
1981}
1982
1983/*
1984 *			Resize an open file
1985 *
1986 *	This is only called by ntfsclone when cloning a volume to a file.
1987 *	The argument must designate a file, not a volume.
1988 *
1989 *	Returns 0 if successful.
1990 */
1991
1992static int win32_ftruncate(HANDLE handle, s64 size)
1993{
1994	BOOL ok;
1995	LONG hsize, lsize;
1996	LONG ohsize, olsize;
1997
1998	if (handle == INVALID_HANDLE_VALUE)
1999		ok = FALSE;
2000	else {
2001		SetLastError(NO_ERROR);
2002			/* save original position */
2003		ohsize = 0;
2004		olsize = SetFilePointer(handle, 0, &ohsize, 1);
2005		hsize = size >> 32;
2006		lsize = size & 0xffffffff;
2007		ok = (SetFilePointer(handle, lsize, &hsize, 0) == (DWORD)lsize)
2008		    && (GetLastError() == NO_ERROR)
2009		    && SetEndOfFile(handle);
2010			/* restore original position, even if above failed */
2011		SetFilePointer(handle, olsize, &ohsize, 0);
2012		if (GetLastError() != NO_ERROR)
2013			ok = FALSE;
2014	}
2015	if (!ok)
2016		errno = EINVAL;
2017	return (ok ? 0 : -1);
2018}
2019
2020int ntfs_device_win32_ftruncate(struct ntfs_device *dev, s64 size)
2021{
2022	win32_fd *fd;
2023	int ret;
2024
2025	ret = -1;
2026	fd = (win32_fd*)dev->d_private;
2027	if (fd && !fd->ntdll)
2028		ret = win32_ftruncate(fd->handle, size);
2029	return (ret);
2030}
2031
2032int ntfs_win32_ftruncate(int fd, s64 size)
2033{
2034	int ret;
2035	HANDLE handle;
2036
2037	handle = get_osfhandle(fd);
2038	ret = win32_ftruncate(handle, size);
2039	return (ret);
2040}
2041