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