xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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  *
10  * This program/include file is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as published
12  * by the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program/include file is distributed in the hope that it will be
16  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program (in the main directory of the NTFS-3G
22  * distribution in the file COPYING); if not, write to the Free Software
23  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25 
26 #include "config.h"
27 
28 #ifdef HAVE_WINDOWS_H
29 #include <windows.h>
30 #endif
31 #include <winioctl.h>
32 
33 #ifdef HAVE_STDIO_H
34 #include <stdio.h>
35 #endif
36 #ifdef HAVE_CTYPE_H
37 #include <ctype.h>
38 #endif
39 #ifdef HAVE_ERRNO_H
40 #include <errno.h>
41 #endif
42 #ifdef HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
45 
46 /* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */
47 #define _NTFS_VOLUME_H
48 struct ntfs_volume;
49 typedef struct ntfs_volume ntfs_volume;
50 
51 #include "debug.h"
52 #include "types.h"
53 #include "device.h"
54 
55 #ifndef NTFS_BLOCK_SIZE
56 #define NTFS_BLOCK_SIZE		512
57 #define NTFS_BLOCK_SIZE_BITS	9
58 #endif
59 
60 #ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
61 #define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096
62 #endif
63 
64 /* Windows 2k+ imports. */
65 typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD);
66 typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD);
67 typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE);
68 typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER,
69 		PLARGE_INTEGER, DWORD);
70 
71 static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL;
72 static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL;
73 static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL;
74 static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL;
75 
76 #ifdef UNICODE
77 #define FNPOSTFIX "W"
78 #else
79 #define FNPOSTFIX "A"
80 #endif
81 
82 /**
83  * struct win32_fd -
84  */
85 typedef struct {
86 	HANDLE handle;
87 	s64 pos;		/* Logical current position on the volume. */
88 	s64 part_start;
89 	s64 part_length;
90 	int part_hidden_sectors;
91 	s64 geo_size, geo_cylinders;
92 	DWORD geo_sectors, geo_heads;
93 	HANDLE vol_handle;
94 } win32_fd;
95 
96 /**
97  * ntfs_w32error_to_errno - convert a win32 error code to the unix one
98  * @w32error:	the win32 error code
99  *
100  * Limited to a relatively small but useful number of codes.
101  */
102 static int ntfs_w32error_to_errno(unsigned int w32error)
103 {
104 	ntfs_log_trace("Converting w32error 0x%x.\n",w32error);
105 	switch (w32error) {
106 		case ERROR_INVALID_FUNCTION:
107 			return EBADRQC;
108 		case ERROR_FILE_NOT_FOUND:
109 		case ERROR_PATH_NOT_FOUND:
110 		case ERROR_INVALID_NAME:
111 			return ENOENT;
112 		case ERROR_TOO_MANY_OPEN_FILES:
113 			return EMFILE;
114 		case ERROR_ACCESS_DENIED:
115 			return EACCES;
116 		case ERROR_INVALID_HANDLE:
117 			return EBADF;
118 		case ERROR_NOT_ENOUGH_MEMORY:
119 			return ENOMEM;
120 		case ERROR_OUTOFMEMORY:
121 			return ENOSPC;
122 		case ERROR_INVALID_DRIVE:
123 		case ERROR_BAD_UNIT:
124 			return ENODEV;
125 		case ERROR_WRITE_PROTECT:
126 			return EROFS;
127 		case ERROR_NOT_READY:
128 		case ERROR_SHARING_VIOLATION:
129 			return EBUSY;
130 		case ERROR_BAD_COMMAND:
131 			return EINVAL;
132 		case ERROR_SEEK:
133 		case ERROR_NEGATIVE_SEEK:
134 			return ESPIPE;
135 		case ERROR_NOT_SUPPORTED:
136 			return EOPNOTSUPP;
137 		case ERROR_BAD_NETPATH:
138 			return ENOSHARE;
139 		default:
140 			/* generic message */
141 			return ENOMSG;
142 	}
143 }
144 
145 /**
146  * libntfs_SetFilePointerEx - emulation for SetFilePointerEx()
147  *
148  * We use this to emulate SetFilePointerEx() when it is not present.  This can
149  * happen since SetFilePointerEx() only exists in Win2k+.
150  */
151 static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile,
152 		LARGE_INTEGER liDistanceToMove,
153 		PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
154 {
155 	liDistanceToMove.LowPart = SetFilePointer(hFile,
156 			liDistanceToMove.LowPart, &liDistanceToMove.HighPart,
157 			dwMoveMethod);
158 	if (liDistanceToMove.LowPart == INVALID_SET_FILE_POINTER &&
159 			GetLastError() != NO_ERROR) {
160 		if (lpNewFilePointer)
161 			lpNewFilePointer->QuadPart = -1;
162 		return FALSE;
163 	}
164 	if (lpNewFilePointer)
165 		lpNewFilePointer->QuadPart = liDistanceToMove.QuadPart;
166 	return TRUE;
167 }
168 
169 /**
170  * ntfs_device_win32_init_imports - initialize the function pointers
171  *
172  * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such
173  * we cannot just staticly import them.
174  *
175  * This function initializes the imports if the functions do exist and in the
176  * SetFilePointerEx case, we emulate the function ourselves if it is not
177  * present.
178  *
179  * Note: The values are cached, do be afraid to run it more than once.
180  */
181 static void ntfs_device_win32_init_imports(void)
182 {
183 	HMODULE kernel32 = GetModuleHandle("kernel32");
184 	if (!kernel32) {
185 		errno = ntfs_w32error_to_errno(GetLastError());
186 		ntfs_log_trace("kernel32.dll could not be imported.\n");
187 	}
188 	if (!fnSetFilePointerEx) {
189 		if (kernel32)
190 			fnSetFilePointerEx = (LPFN_SETFILEPOINTEREX)
191 					GetProcAddress(kernel32,
192 					"SetFilePointerEx");
193 		/*
194 		 * If we did not get kernel32.dll or it is not Win2k+, emulate
195 		 * SetFilePointerEx().
196 		 */
197 		if (!fnSetFilePointerEx) {
198 			ntfs_log_debug("SetFilePonterEx() not found in "
199 					"kernel32.dll: Enabling emulation.\n");
200 			fnSetFilePointerEx = libntfs_SetFilePointerEx;
201 		}
202 	}
203 	/* Cannot do lookups if we could not get kernel32.dll... */
204 	if (!kernel32)
205 		return;
206 	if (!fnFindFirstVolume)
207 		fnFindFirstVolume = (LPFN_FINDFIRSTVOLUME)
208 				GetProcAddress(kernel32, "FindFirstVolume"
209 				FNPOSTFIX);
210 	if (!fnFindNextVolume)
211 		fnFindNextVolume = (LPFN_FINDNEXTVOLUME)
212 				GetProcAddress(kernel32, "FindNextVolume"
213 				FNPOSTFIX);
214 	if (!fnFindVolumeClose)
215 		fnFindVolumeClose = (LPFN_FINDVOLUMECLOSE)
216 				GetProcAddress(kernel32, "FindVolumeClose");
217 }
218 
219 /**
220  * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags
221  * @flags:	unix open status flags
222  *
223  * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
224  */
225 static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags)
226 {
227 	int win_mode;
228 
229 	switch (flags & O_ACCMODE) {
230 	case O_RDONLY:
231 		win_mode = FILE_READ_DATA;
232 		break;
233 	case O_WRONLY:
234 		win_mode = FILE_WRITE_DATA;
235 		break;
236 	case O_RDWR:
237 		win_mode = FILE_READ_DATA | FILE_WRITE_DATA;
238 		break;
239 	default:
240 		/* error */
241 		ntfs_log_trace("Unknown status flags.\n");
242 		win_mode = 0;
243 	}
244 	return win_mode;
245 }
246 
247 
248 /**
249  * ntfs_device_win32_simple_open_file - just open a file via win32 API
250  * @filename:	name of the file to open
251  * @handle:	pointer the a HANDLE in which to put the result
252  * @flags:	unix open status flags
253  * @locking:	will the function gain an exclusive lock on the file?
254  *
255  * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
256  *
257  * Return 0 if o.k.
258  *	 -1 if not, and errno set.  In this case handle is trashed.
259  */
260 static int ntfs_device_win32_simple_open_file(const char *filename,
261 		HANDLE *handle, int flags, BOOL locking)
262 {
263 	*handle = CreateFile(filename,
264 			ntfs_device_unix_status_flags_to_win32(flags),
265 			locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ),
266 			NULL, OPEN_EXISTING, 0, NULL);
267 	if (*handle == INVALID_HANDLE_VALUE) {
268 		errno = ntfs_w32error_to_errno(GetLastError());
269 		ntfs_log_trace("CreateFile(%s) failed.\n", filename);
270 		return -1;
271 	}
272 	return 0;
273 }
274 
275 /**
276  * ntfs_device_win32_lock - lock the volume
277  * @handle:	a win32 HANDLE for a volume to lock
278  *
279  * Locking a volume means no one can access its contents.
280  * Exiting the process automatically unlocks the volume, except in old NT4s.
281  *
282  * Return 0 if o.k.
283  *	 -1 if not, and errno set.
284  */
285 static int ntfs_device_win32_lock(HANDLE handle)
286 {
287 	DWORD i;
288 
289 	if (!DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &i,
290 			NULL)) {
291 		errno = ntfs_w32error_to_errno(GetLastError());
292 		ntfs_log_trace("Couldn't lock volume.\n");
293 		return -1;
294 	}
295 	ntfs_log_debug("Volume locked.\n");
296 	return 0;
297 }
298 
299 /**
300  * ntfs_device_win32_unlock - unlock the volume
301  * @handle:	the win32 HANDLE which the volume was locked with
302  *
303  * Return 0 if o.k.
304  *	 -1 if not, and errno set.
305  */
306 static int ntfs_device_win32_unlock(HANDLE handle)
307 {
308 	DWORD i;
309 
310 	if (!DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &i,
311 			NULL)) {
312 		errno = ntfs_w32error_to_errno(GetLastError());
313 		ntfs_log_trace("Couldn't unlock volume.\n");
314 		return -1;
315 	}
316 	ntfs_log_debug("Volume unlocked.\n");
317 	return 0;
318 }
319 
320 /**
321  * ntfs_device_win32_dismount - dismount a volume
322  * @handle:	a win32 HANDLE for a volume to dismount
323  *
324  * Dismounting means the system will refresh the volume in the first change it
325  * gets.  Usefull after altering the file structures.
326  * The volume must be locked by the current process while dismounting.
327  * A side effect is that the volume is also unlocked, but you must not rely om
328  * this.
329  *
330  * Return 0 if o.k.
331  *	 -1 if not, and errno set.
332  */
333 static int ntfs_device_win32_dismount(HANDLE handle)
334 {
335 	DWORD i;
336 
337 	if (!DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0,
338 			&i, NULL)) {
339 		errno = ntfs_w32error_to_errno(GetLastError());
340 		ntfs_log_trace("Couldn't dismount volume.\n");
341 		return -1;
342 	}
343 	ntfs_log_debug("Volume dismounted.\n");
344 	return 0;
345 }
346 
347 /**
348  * ntfs_device_win32_getsize - get file size via win32 API
349  * @handle:	pointer the file HANDLE obtained via open
350  *
351  * Only works on ordinary files.
352  *
353  * Return The file size if o.k.
354  *	 -1 if not, and errno set.
355  */
356 static s64 ntfs_device_win32_getsize(HANDLE handle)
357 {
358 	DWORD loword, hiword;
359 
360 	loword = GetFileSize(handle, &hiword);
361 	if (loword == INVALID_FILE_SIZE) {
362 		errno = ntfs_w32error_to_errno(GetLastError());
363 		ntfs_log_trace("Couldn't get file size.\n");
364 		return -1;
365 	}
366 	return ((s64)hiword << 32) + loword;
367 }
368 
369 /**
370  * ntfs_device_win32_getdisklength - get disk size via win32 API
371  * @handle:	pointer the file HANDLE obtained via open
372  * @argp:	pointer to result buffer
373  *
374  * Only works on PhysicalDriveX type handles.
375  *
376  * Return The disk size if o.k.
377  *	 -1 if not, and errno set.
378  */
379 static s64 ntfs_device_win32_getdisklength(HANDLE handle)
380 {
381 	GET_LENGTH_INFORMATION buf;
382 	DWORD i;
383 
384 	if (!DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf,
385 			sizeof(buf), &i, NULL)) {
386 		errno = ntfs_w32error_to_errno(GetLastError());
387 		ntfs_log_trace("Couldn't get disk length.\n");
388 		return -1;
389 	}
390 	ntfs_log_debug("Disk length: %lld.\n", buf.Length.QuadPart);
391 	return buf.Length.QuadPart;
392 }
393 
394 /**
395  * ntfs_device_win32_getntfssize - get NTFS volume size via win32 API
396  * @handle:	pointer the file HANDLE obtained via open
397  * @argp:	pointer to result buffer
398  *
399  * Only works on NTFS volume handles.
400  * An annoying bug in windows is that an NTFS volume does not occupy the entire
401  * partition, namely not the last sector (which holds the backup boot sector,
402  * and normally not interesting).
403  * Use this function to get the length of the accessible space through a given
404  * volume handle.
405  *
406  * Return The volume size if o.k.
407  *	 -1 if not, and errno set.
408  */
409 static s64 ntfs_device_win32_getntfssize(HANDLE handle)
410 {
411 	s64 rvl;
412 #ifdef FSCTL_GET_NTFS_VOLUME_DATA
413 	DWORD i;
414 	NTFS_VOLUME_DATA_BUFFER buf;
415 
416 	if (!DeviceIoControl(handle, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &buf,
417 			sizeof(buf), &i, NULL)) {
418 		errno = ntfs_w32error_to_errno(GetLastError());
419 		ntfs_log_trace("Couldn't get NTFS volume length.\n");
420 		return -1;
421 	}
422 	rvl = buf.NumberSectors.QuadPart * buf.BytesPerSector;
423 	ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl);
424 #else
425 	errno = EINVAL;
426 	rvl = -1;
427 #endif
428 	return rvl;
429 }
430 
431 /**
432  * ntfs_device_win32_getgeo - get CHS information of a drive
433  * @handle:	an open handle to the PhysicalDevice
434  * @fd:		a win_fd structure that will be filled
435  *
436  * Return 0 if o.k.
437  *	 -1 if not, and errno  set.
438  *
439  * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1.
440  * In Windows XP+: fills size, sectors, cylinders, and heads.
441  *
442  * Note: In pre XP, this requires write permission, even though nothing is
443  * actually written.
444  *
445  * If fails, sets sectors, cylinders, heads, and size to -1.
446  */
447 static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd)
448 {
449 	DWORD i;
450 	BOOL rvl;
451 	BYTE b[sizeof(DISK_GEOMETRY) + sizeof(DISK_PARTITION_INFO) +
452 			sizeof(DISK_DETECTION_INFO) + 512];
453 
454 	rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL,
455 			0, &b, sizeof(b), &i, NULL);
456 	if (rvl) {
457 		ntfs_log_debug("GET_DRIVE_GEOMETRY_EX detected.\n");
458 		DISK_DETECTION_INFO *ddi = (PDISK_DETECTION_INFO)
459 				(((PBYTE)(&((PDISK_GEOMETRY_EX)b)->Data)) +
460 				(((PDISK_PARTITION_INFO)
461 				(&((PDISK_GEOMETRY_EX)b)->Data))->
462 				SizeOfPartitionInfo));
463 		fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart;
464 		fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack;
465 		fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart;
466 		switch (ddi->DetectionType) {
467 		case DetectInt13:
468 			fd->geo_cylinders = ddi->Int13.MaxCylinders;
469 			fd->geo_sectors = ddi->Int13.SectorsPerTrack;
470 			fd->geo_heads = ddi->Int13.MaxHeads;
471 			return 0;
472 		case DetectExInt13:
473 			fd->geo_cylinders = ddi->ExInt13.ExCylinders;
474 			fd->geo_sectors = ddi->ExInt13.ExSectorsPerTrack;
475 			fd->geo_heads = ddi->ExInt13.ExHeads;
476 			return 0;
477 		case DetectNone:
478 		default:
479 			break;
480 		}
481 	} else
482 		fd->geo_heads = -1;
483 	rvl = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
484 			&b, sizeof(b), &i, NULL);
485 	if (rvl) {
486 		ntfs_log_debug("GET_DRIVE_GEOMETRY detected.\n");
487 		fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart;
488 		fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack;
489 		fd->geo_size = fd->geo_cylinders * fd->geo_sectors *
490 				((DISK_GEOMETRY*)&b)->TracksPerCylinder *
491 				((DISK_GEOMETRY*)&b)->BytesPerSector;
492 		return 0;
493 	}
494 	errno = ntfs_w32error_to_errno(GetLastError());
495 	ntfs_log_trace("Couldn't retrieve disk geometry.\n");
496 	fd->geo_cylinders = -1;
497 	fd->geo_sectors = -1;
498 	fd->geo_size = -1;
499 	return -1;
500 }
501 
502 /**
503  * ntfs_device_win32_open_file - open a file via win32 API
504  * @filename:	name of the file to open
505  * @fd:		pointer to win32 file device in which to put the result
506  * @flags:	unix open status flags
507  *
508  * Return 0 if o.k.
509  *	 -1 if not, and errno set.
510  */
511 static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd,
512 		int flags)
513 {
514 	HANDLE handle;
515 
516 	if (ntfs_device_win32_simple_open_file(filename, &handle, flags,
517 			FALSE)) {
518 		/* open error */
519 		return -1;
520 	}
521 	/* fill fd */
522 	fd->handle = handle;
523 	fd->part_start = 0;
524 	fd->part_length = ntfs_device_win32_getsize(handle);
525 	fd->pos = 0;
526 	fd->part_hidden_sectors = -1;
527 	fd->geo_size = -1;	/* used as a marker that this is a file */
528 	fd->vol_handle = INVALID_HANDLE_VALUE;
529 	return 0;
530 }
531 
532 /**
533  * ntfs_device_win32_open_drive - open a drive via win32 API
534  * @drive_id:	drive to open
535  * @fd:		pointer to win32 file device in which to put the result
536  * @flags:	unix open status flags
537  *
538  * return 0 if o.k.
539  *        -1 if not, and errno set.
540  */
541 static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd,
542 		int flags)
543 {
544 	HANDLE handle;
545 	int err;
546 	char filename[MAX_PATH];
547 
548 	sprintf(filename, "\\\\.\\PhysicalDrive%d", drive_id);
549 	if ((err = ntfs_device_win32_simple_open_file(filename, &handle, flags,
550 			TRUE))) {
551 		/* open error */
552 		return err;
553 	}
554 	/* store the drive geometry */
555 	ntfs_device_win32_getgeo(handle, fd);
556 	/* Just to be sure */
557 	if (fd->geo_size == -1)
558 		fd->geo_size = ntfs_device_win32_getdisklength(handle);
559 	/* fill fd */
560 	fd->handle = handle;
561 	fd->part_start = 0;
562 	fd->part_length = fd->geo_size;
563 	fd->pos = 0;
564 	fd->part_hidden_sectors = -1;
565 	fd->vol_handle = INVALID_HANDLE_VALUE;
566 	return 0;
567 }
568 
569 /**
570  * ntfs_device_win32_open_volume_for_partition - find and open a volume
571  *
572  * Windows NT/2k/XP handles volumes instead of partitions.
573  * This function gets the partition details and return an open volume handle.
574  * That volume is the one whose only physical location on disk is the described
575  * partition.
576  *
577  * The function required Windows 2k/XP, otherwise it fails (gracefully).
578  *
579  * Return success: a valid open volume handle.
580  *        fail   : INVALID_HANDLE_VALUE
581  */
582 static HANDLE ntfs_device_win32_open_volume_for_partition(unsigned int drive_id,
583 		s64 part_offset, s64 part_length, int flags)
584 {
585 	HANDLE vol_find_handle;
586 	TCHAR vol_name[MAX_PATH];
587 
588 	/* Make sure all the required imports exist. */
589 	if (!fnFindFirstVolume || !fnFindNextVolume || !fnFindVolumeClose) {
590 		ntfs_log_trace("Required dll imports not found.\n");
591 		return INVALID_HANDLE_VALUE;
592 	}
593 	/* Start iterating through volumes. */
594 	ntfs_log_trace("Entering with drive_id=%d, part_offset=%lld, "
595 			"path_length=%lld, flags=%d.\n", drive_id,
596 			(unsigned long long)part_offset,
597 			(unsigned long long)part_length, flags);
598 	vol_find_handle = fnFindFirstVolume(vol_name, MAX_PATH);
599 	/* If a valid handle could not be aquired, reply with "don't know". */
600 	if (vol_find_handle == INVALID_HANDLE_VALUE) {
601 		ntfs_log_trace("FindFirstVolume failed.\n");
602 		return INVALID_HANDLE_VALUE;
603 	}
604 	do {
605 		int vol_name_length;
606 		HANDLE handle;
607 
608 		/* remove trailing '/' from vol_name */
609 #ifdef UNICODE
610 		vol_name_length = wcslen(vol_name);
611 #else
612 		vol_name_length = strlen(vol_name);
613 #endif
614 		if (vol_name_length>0)
615 			vol_name[vol_name_length-1]=0;
616 
617 		ntfs_log_debug("Processing %s.\n", vol_name);
618 		/* open the file */
619 		handle = CreateFile(vol_name,
620 				ntfs_device_unix_status_flags_to_win32(flags),
621 				FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
622 				OPEN_EXISTING, 0, NULL);
623 		if (handle != INVALID_HANDLE_VALUE) {
624 			DWORD bytesReturned;
625 #define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT)
626 			char extents[EXTENTS_SIZE];
627 
628 			/* Check physical locations. */
629 			if (DeviceIoControl(handle,
630 					IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
631 					NULL, 0, extents, EXTENTS_SIZE,
632 					&bytesReturned, NULL)) {
633 				if (((VOLUME_DISK_EXTENTS *)extents)->
634 						NumberOfDiskExtents == 1) {
635 					DISK_EXTENT *extent = &((
636 							VOLUME_DISK_EXTENTS *)
637 							extents)->Extents[0];
638 					if ((extent->DiskNumber==drive_id) &&
639 							(extent->StartingOffset.
640 							QuadPart==part_offset)
641 							&& (extent->
642 							ExtentLength.QuadPart
643 							== part_length)) {
644 						/*
645 						 * Eureka! (Archimedes, 287 BC,
646 						 * "I have found it!")
647 						 */
648 						fnFindVolumeClose(
649 							vol_find_handle);
650 						return handle;
651 					}
652 				}
653 			}
654 		} else
655 			ntfs_log_trace("getExtents() Failed.\n");
656 	} while (fnFindNextVolume(vol_find_handle, vol_name, MAX_PATH));
657 	/* End of iteration through volumes. */
658 	ntfs_log_trace("Closing, volume was not found.\n");
659 	fnFindVolumeClose(vol_find_handle);
660 	return INVALID_HANDLE_VALUE;
661 }
662 
663 /**
664  * ntfs_device_win32_find_partition - locates partition details by id.
665  * @handle:		HANDLE to the PhysicalDrive
666  * @partition_id:	the partition number to locate
667  * @part_offset:	pointer to where to put the offset to the partition
668  * @part_length:	pointer to where to put the length of the partition
669  * @hidden_sectors:	pointer to where to put the hidden sectors
670  *
671  * This function requires an open PhysicalDrive handle and a partition_id.
672  * If a partition with the required id is found on the supplied device,
673  * the partition attributes are returned back.
674  *
675  * Returns: TRUE  if found, and sets the output parameters.
676  *          FALSE if not and errno is set to the error code.
677  */
678 static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id,
679 		s64 *part_offset, s64 *part_length, int *hidden_sectors)
680 {
681 	DRIVE_LAYOUT_INFORMATION *drive_layout;
682 	unsigned int err, buf_size, part_count;
683 	DWORD i;
684 
685 	/*
686 	 * There is no way to know the required buffer, so if the ioctl fails,
687 	 * try doubling the buffer size each time until the ioctl succeeds.
688 	 */
689 	part_count = 8;
690 	do {
691 		buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) +
692 				part_count * sizeof(PARTITION_INFORMATION);
693 		drive_layout = malloc(buf_size);
694 		if (!drive_layout) {
695 			errno = ENOMEM;
696 			return FALSE;
697 		}
698 		if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL,
699 				0, (BYTE*)drive_layout, buf_size, &i, NULL))
700 			break;
701 		err = GetLastError();
702 		free(drive_layout);
703 		if (err != ERROR_INSUFFICIENT_BUFFER) {
704 			ntfs_log_trace("GetDriveLayout failed.\n");
705 			errno = ntfs_w32error_to_errno(err);
706 			return FALSE;
707 		}
708 		ntfs_log_debug("More than %u partitions.\n", part_count);
709 		part_count <<= 1;
710 		if (part_count > 512) {
711 			ntfs_log_trace("GetDriveLayout failed: More than 512 "
712 					"partitions?\n");
713 			errno = ENOBUFS;
714 			return FALSE;
715 		}
716 	} while (1);
717 	for (i = 0; i < drive_layout->PartitionCount; i++) {
718 		if (drive_layout->PartitionEntry[i].PartitionNumber ==
719 				partition_id) {
720 			*part_offset = drive_layout->PartitionEntry[i].
721 					StartingOffset.QuadPart;
722 			*part_length = drive_layout->PartitionEntry[i].
723 					PartitionLength.QuadPart;
724 			*hidden_sectors = drive_layout->PartitionEntry[i].
725 					HiddenSectors;
726 			free(drive_layout);
727 			return TRUE;
728 		}
729 	}
730 	free(drive_layout);
731 	errno = ENOENT;
732 	return FALSE;
733 }
734 
735 /**
736  * ntfs_device_win32_open_partition - open a partition via win32 API
737  * @drive_id:		drive to open
738  * @partition_id:	partition to open
739  * @fd:			win32 file device to return
740  * @flags:		unix open status flags
741  *
742  * Return  0 if o.k.
743  *        -1 if not, and errno set.
744  *
745  * When fails, fd contents may have not been preserved.
746  */
747 static int ntfs_device_win32_open_partition(int drive_id,
748 		unsigned int partition_id, win32_fd *fd, int flags)
749 {
750 	s64 part_start, part_length;
751 	HANDLE handle;
752 	int err, hidden_sectors;
753 	char drive_name[MAX_PATH];
754 
755 	sprintf(drive_name, "\\\\.\\PhysicalDrive%d", drive_id);
756 	/* Open the entire device without locking, ask questions later */
757 	if ((err = ntfs_device_win32_simple_open_file(drive_name, &handle,
758 			flags, FALSE))) {
759 		/* error */
760 		return err;
761 	}
762 	if (ntfs_device_win32_find_partition(handle, partition_id, &part_start,
763 			&part_length, &hidden_sectors)) {
764 		s64 tmp;
765 		HANDLE vol_handle = ntfs_device_win32_open_volume_for_partition(
766 			drive_id, part_start, part_length, flags);
767 		/* Store the drive geometry. */
768 		ntfs_device_win32_getgeo(handle, fd);
769 		fd->handle = handle;
770 		fd->pos = 0;
771 		fd->part_start = part_start;
772 		fd->part_length = part_length;
773 		fd->part_hidden_sectors = hidden_sectors;
774 		tmp = ntfs_device_win32_getntfssize(vol_handle);
775 		if (tmp > 0)
776 			fd->geo_size = tmp;
777 		else
778 			fd->geo_size = fd->part_length;
779 		if (vol_handle != INVALID_HANDLE_VALUE) {
780 			if (((flags & O_RDWR) == O_RDWR) &&
781 					ntfs_device_win32_lock(vol_handle)) {
782 				CloseHandle(vol_handle);
783 				CloseHandle(handle);
784 				return -1;
785 			}
786 			fd->vol_handle = vol_handle;
787 		} else {
788 			if ((flags & O_RDWR) == O_RDWR) {
789 				/* Access if read-write, no volume found. */
790 				ntfs_log_trace("Partitions containing Spanned/"
791 						"Mirrored volumes are not "
792 						"supported in R/W status "
793 						"yet.\n");
794 				CloseHandle(handle);
795 				errno = EOPNOTSUPP;
796 				return -1;
797 			}
798 			fd->vol_handle = INVALID_HANDLE_VALUE;
799 		}
800 		return 0;
801 	} else {
802 		ntfs_log_debug("Partition %u not found on drive %d.\n",
803 				partition_id, drive_id);
804 		CloseHandle(handle);
805 		errno = ENODEV;
806 		return -1;
807 	}
808 }
809 
810 /**
811  * ntfs_device_win32_open - open a device
812  * @dev:	a pointer to the NTFS_DEVICE to open
813  * @flags:	unix open status flags
814  *
815  * @dev->d_name must hold the device name, the rest is ignored.
816  * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
817  *
818  * If name is in format "(hd[0-9],[0-9])" then open a partition.
819  * If name is in format "(hd[0-9])" then open a volume.
820  * Otherwise open a file.
821  */
822 static int ntfs_device_win32_open(struct ntfs_device *dev, int flags)
823 {
824 	int drive_id = 0, numparams;
825 	unsigned int part = 0;
826 	char drive_char;
827 	win32_fd fd;
828 	int err;
829 
830 	if (NDevOpen(dev)) {
831 		errno = EBUSY;
832 		return -1;
833 	}
834 	ntfs_device_win32_init_imports();
835 	numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part);
836 	drive_id = toupper(drive_char) - 'A';
837 	switch (numparams) {
838 	case 0:
839 		ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name);
840 		err = ntfs_device_win32_open_file(dev->d_name, &fd, flags);
841 		break;
842 	case 1:
843 		ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev->d_name,
844 				drive_id);
845 		err = ntfs_device_win32_open_drive(drive_id, &fd, flags);
846 		break;
847 	case 2:
848 		ntfs_log_debug("win32_open(%s) -> drive %d, part %u.\n",
849 				dev->d_name, drive_id, part);
850 		err = ntfs_device_win32_open_partition(drive_id, part, &fd,
851 				flags);
852 		break;
853 	default:
854 		ntfs_log_debug("win32_open(%s) -> unknwon file format.\n",
855 				dev->d_name);
856 		err = -1;
857 	}
858 	if (err)
859 		return err;
860 	ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev->d_name,
861 			dev, fd.part_start);
862 	/* Setup our read-only flag. */
863 	if ((flags & O_RDWR) != O_RDWR)
864 		NDevSetReadOnly(dev);
865 	dev->d_private = malloc(sizeof(win32_fd));
866 	memcpy(dev->d_private, &fd, sizeof(win32_fd));
867 	NDevSetOpen(dev);
868 	NDevClearDirty(dev);
869 	return 0;
870 }
871 
872 /**
873  * ntfs_device_win32_seek - change current logical file position
874  * @dev:	ntfs device obtained via ->open
875  * @offset:	required offset from the whence anchor
876  * @whence:	whence anchor specifying what @offset is relative to
877  *
878  * Return the new position on the volume on success and -1 on error with errno
879  * set to the error code.
880  *
881  * @whence may be one of the following:
882  *	SEEK_SET - Offset is relative to file start.
883  *	SEEK_CUR - Offset is relative to current position.
884  *	SEEK_END - Offset is relative to end of file.
885  */
886 static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset,
887 		int whence)
888 {
889 	s64 abs_ofs;
890 	win32_fd *fd = (win32_fd *)dev->d_private;
891 
892 	ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence);
893 	switch (whence) {
894 	case SEEK_SET:
895 		abs_ofs = offset;
896 		break;
897 	case SEEK_CUR:
898 		abs_ofs = fd->pos + offset;
899 		break;
900 	case SEEK_END:
901 		/* End of partition != end of disk. */
902 		if (fd->part_length == -1) {
903 			ntfs_log_trace("Position relative to end of disk not "
904 					"implemented.\n");
905 			errno = EOPNOTSUPP;
906 			return -1;
907 		}
908 		abs_ofs = fd->part_length + offset;
909 		break;
910 	default:
911 		ntfs_log_trace("Wrong mode %d.\n", whence);
912 		errno = EINVAL;
913 		return -1;
914 	}
915 	if (abs_ofs < 0 || abs_ofs > fd->part_length) {
916 		ntfs_log_trace("Seeking outsize seekable area.\n");
917 		errno = EINVAL;
918 		return -1;
919 	}
920 	fd->pos = abs_ofs;
921 	return abs_ofs;
922 }
923 
924 /**
925  * ntfs_device_win32_pio - positioned low level i/o
926  * @fd:		win32 device descriptor obtained via ->open
927  * @pos:	at which position to do i/o from/to
928  * @count:	how many bytes should be transfered
929  * @b:		source/destination buffer
930  * @write:	TRUE if write transfer and FALSE if read transfer
931  *
932  * On success returns the number of bytes transfered (can be < @count) and on
933  * error returns -1 and errno set.  Transfer starts from position @pos on @fd.
934  *
935  * Notes:
936  *	- @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE.
937  *	- When dealing with volumes, a single call must not span both volume
938  *	  and disk extents.
939  *	- Does not use/set @fd->pos.
940  */
941 static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos,
942 		const s64 count, void *b, const BOOL write)
943 {
944 	LARGE_INTEGER li;
945 	HANDLE handle;
946 	DWORD bt;
947 	BOOL res;
948 
949 	ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n",
950 			(long long)pos, (long long)count, write ? "write" :
951 			"read");
952 	li.QuadPart = pos;
953 	if (fd->vol_handle != INVALID_HANDLE_VALUE && pos < fd->geo_size) {
954 		ntfs_log_debug("Transfering via vol_handle.\n");
955 		handle = fd->vol_handle;
956 	} else {
957 		ntfs_log_debug("Transfering via handle.\n");
958 		handle = fd->handle;
959 		li.QuadPart += fd->part_start;
960 	}
961 	if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) {
962 		errno = ntfs_w32error_to_errno(GetLastError());
963 		ntfs_log_trace("SetFilePointer failed.\n");
964 		return -1;
965 	}
966 	if (write)
967 		res = WriteFile(handle, b, count, &bt, NULL);
968 	else
969 		res = ReadFile(handle, b, count, &bt, NULL);
970 	if (!res) {
971 		errno = ntfs_w32error_to_errno(GetLastError());
972 		ntfs_log_trace("%sFile() failed.\n", write ? "Write" : "Read");
973 		return -1;
974 	}
975 	return bt;
976 }
977 
978 /**
979  * ntfs_device_win32_pread_simple - positioned simple read
980  * @fd:		win32 device descriptor obtained via ->open
981  * @pos:	at which position to read from
982  * @count:	how many bytes should be read
983  * @b:		a pointer to where to put the contents
984  *
985  * On success returns the number of bytes read (can be < @count) and on error
986  * returns -1 and errno set.  Read starts from position @pos.
987  *
988  * Notes:
989  *	- @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE.
990  *	- When dealing with volumes, a single call must not span both volume
991  *	  and disk extents.
992  *	- Does not use/set @fd->pos.
993  */
994 static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos,
995 		const s64 count, void *b)
996 {
997 	return ntfs_device_win32_pio(fd, pos, count, b, FALSE);
998 }
999 
1000 /**
1001  * ntfs_device_win32_read - read bytes from an ntfs device
1002  * @dev:	ntfs device obtained via ->open
1003  * @b:		pointer to where to put the contents
1004  * @count:	how many bytes should be read
1005  *
1006  * On success returns the number of bytes actually read (can be < @count).
1007  * On error returns -1 with errno set.
1008  */
1009 static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count)
1010 {
1011 	s64 old_pos, to_read, i, br = 0;
1012 	win32_fd *fd = (win32_fd *)dev->d_private;
1013 	BYTE *alignedbuffer;
1014 	int old_ofs, ofs;
1015 
1016 	old_pos = fd->pos;
1017 	old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1);
1018 	to_read = (ofs + count + NTFS_BLOCK_SIZE - 1) &
1019 			~(s64)(NTFS_BLOCK_SIZE - 1);
1020 	/* Impose maximum of 2GB to be on the safe side. */
1021 	if (to_read > 0x80000000) {
1022 		int delta = to_read - count;
1023 		to_read = 0x80000000;
1024 		count = to_read - delta;
1025 	}
1026 	ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, "
1027 			"ofs = %i, to_read = 0x%llx.\n", fd, b,
1028 			(long long)count, (long long)old_pos, ofs,
1029 			(long long)to_read);
1030 	if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs &&
1031 			!(count & (NTFS_BLOCK_SIZE - 1)))
1032 		alignedbuffer = b;
1033 	else {
1034 		alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT,
1035 				PAGE_READWRITE);
1036 		if (!alignedbuffer) {
1037 			errno = ntfs_w32error_to_errno(GetLastError());
1038 			ntfs_log_trace("VirtualAlloc failed for read.\n");
1039 			return -1;
1040 		}
1041 	}
1042 	if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) {
1043 		s64 vol_to_read = fd->geo_size - old_pos;
1044 		if (count > vol_to_read) {
1045 			br = ntfs_device_win32_pread_simple(fd,
1046 					old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1),
1047 					ofs + vol_to_read, alignedbuffer);
1048 			if (br == -1)
1049 				goto read_error;
1050 			to_read -= br;
1051 			if (br < ofs) {
1052 				br = 0;
1053 				goto read_partial;
1054 			}
1055 			br -= ofs;
1056 			fd->pos += br;
1057 			ofs = fd->pos & (NTFS_BLOCK_SIZE - 1);
1058 			if (br != vol_to_read)
1059 				goto read_partial;
1060 		}
1061 	}
1062 	i = ntfs_device_win32_pread_simple(fd,
1063 			fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_read,
1064 			alignedbuffer + br);
1065 	if (i == -1) {
1066 		if (br)
1067 			goto read_partial;
1068 		goto read_error;
1069 	}
1070 	if (i < ofs)
1071 		goto read_partial;
1072 	i -= ofs;
1073 	br += i;
1074 	if (br > count)
1075 		br = count;
1076 	fd->pos = old_pos + br;
1077 read_partial:
1078 	if (alignedbuffer != b) {
1079 		memcpy((void*)b, alignedbuffer + old_ofs, br);
1080 		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1081 	}
1082 	return br;
1083 read_error:
1084 	if (alignedbuffer != b)
1085 		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1086 	return -1;
1087 }
1088 
1089 /**
1090  * ntfs_device_win32_close - close an open ntfs deivce
1091  * @dev:	ntfs device obtained via ->open
1092  *
1093  * Return 0 if o.k.
1094  *	 -1 if not, and errno set.  Note if error fd->vol_handle is trashed.
1095  */
1096 static int ntfs_device_win32_close(struct ntfs_device *dev)
1097 {
1098 	win32_fd *fd = (win32_fd *)dev->d_private;
1099 	BOOL rvl;
1100 
1101 	ntfs_log_trace("Closing device %p.\n", dev);
1102 	if (!NDevOpen(dev)) {
1103 		errno = EBADF;
1104 		return -1;
1105 	}
1106 	if (fd->vol_handle != INVALID_HANDLE_VALUE) {
1107 		if (!NDevReadOnly(dev)) {
1108 			ntfs_device_win32_dismount(fd->vol_handle);
1109 			ntfs_device_win32_unlock(fd->vol_handle);
1110 		}
1111 		if (!CloseHandle(fd->vol_handle))
1112 			ntfs_log_trace("CloseHandle() failed for volume.\n");
1113 	}
1114 	rvl = CloseHandle(fd->handle);
1115 	free(fd);
1116 	if (!rvl) {
1117 		errno = ntfs_w32error_to_errno(GetLastError());
1118 		ntfs_log_trace("CloseHandle() failed.\n");
1119 		return -1;
1120 	}
1121 	return 0;
1122 }
1123 
1124 /**
1125  * ntfs_device_win32_sync - flush write buffers to disk
1126  * @dev:	ntfs device obtained via ->open
1127  *
1128  * Return 0 if o.k.
1129  *	 -1 if not, and errno set.
1130  *
1131  * Note: Volume syncing works differently in windows.
1132  *	 Disk cannot be synced in windows.
1133  */
1134 static int ntfs_device_win32_sync(struct ntfs_device *dev)
1135 {
1136 	int err = 0;
1137 	BOOL to_clear = TRUE;
1138 
1139 	if (!NDevReadOnly(dev) && NDevDirty(dev)) {
1140 		win32_fd *fd = (win32_fd *)dev->d_private;
1141 
1142 		if ((fd->vol_handle != INVALID_HANDLE_VALUE) &&
1143 				!FlushFileBuffers(fd->vol_handle)) {
1144 			to_clear = FALSE;
1145 			err = ntfs_w32error_to_errno(GetLastError());
1146 		}
1147 		if (!FlushFileBuffers(fd->handle)) {
1148 			to_clear = FALSE;
1149 			if (!err)
1150 				err = ntfs_w32error_to_errno(GetLastError());
1151 		}
1152 		if (!to_clear) {
1153 			ntfs_log_trace("Could not sync.\n");
1154 			errno = err;
1155 			return -1;
1156 		}
1157 		NDevClearDirty(dev);
1158 	}
1159 	return 0;
1160 }
1161 
1162 /**
1163  * ntfs_device_win32_pwrite_simple - positioned simple write
1164  * @fd:		win32 device descriptor obtained via ->open
1165  * @pos:	at which position to write to
1166  * @count:	how many bytes should be written
1167  * @b:		a pointer to the data to write
1168  *
1169  * On success returns the number of bytes written and on error returns -1 and
1170  * errno set.  Write starts from position @pos.
1171  *
1172  * Notes:
1173  *	- @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE.
1174  *	- When dealing with volumes, a single call must not span both volume
1175  *	  and disk extents.
1176  *	- Does not use/set @fd->pos.
1177  */
1178 static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos,
1179 		const s64 count, const void *b)
1180 {
1181 	return ntfs_device_win32_pio(fd, pos, count, (void *)b, TRUE);
1182 }
1183 
1184 /**
1185  * ntfs_device_win32_write - write bytes to an ntfs device
1186  * @dev:	ntfs device obtained via ->open
1187  * @b:		pointer to the data to write
1188  * @count:	how many bytes should be written
1189  *
1190  * On success returns the number of bytes actually written.
1191  * On error returns -1 with errno set.
1192  */
1193 static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b,
1194 		s64 count)
1195 {
1196 	s64 old_pos, to_write, i, bw = 0;
1197 	win32_fd *fd = (win32_fd *)dev->d_private;
1198 	BYTE *alignedbuffer;
1199 	int old_ofs, ofs;
1200 
1201 	old_pos = fd->pos;
1202 	old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1);
1203 	to_write = (ofs + count + NTFS_BLOCK_SIZE - 1) &
1204 			~(s64)(NTFS_BLOCK_SIZE - 1);
1205 	/* Impose maximum of 2GB to be on the safe side. */
1206 	if (to_write > 0x80000000) {
1207 		int delta = to_write - count;
1208 		to_write = 0x80000000;
1209 		count = to_write - delta;
1210 	}
1211 	ntfs_log_trace("fd = %p, b = %p, count = 0x%llx, pos = 0x%llx, "
1212 			"ofs = %i, to_write = 0x%llx.\n", fd, b,
1213 			(long long)count, (long long)old_pos, ofs,
1214 			(long long)to_write);
1215 	if (NDevReadOnly(dev)) {
1216 		ntfs_log_trace("Can't write on a R/O device.\n");
1217 		errno = EROFS;
1218 		return -1;
1219 	}
1220 	if (!count)
1221 		return 0;
1222 	NDevSetDirty(dev);
1223 	if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs &&
1224 			!(count & (NTFS_BLOCK_SIZE - 1)))
1225 		alignedbuffer = (BYTE *)b;
1226 	else {
1227 		s64 end;
1228 
1229 		alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_write,
1230 				MEM_COMMIT, PAGE_READWRITE);
1231 		if (!alignedbuffer) {
1232 			errno = ntfs_w32error_to_errno(GetLastError());
1233 			ntfs_log_trace("VirtualAlloc failed for write.\n");
1234 			return -1;
1235 		}
1236 		/* Read first sector if start of write not sector aligned. */
1237 		if (ofs) {
1238 			i = ntfs_device_win32_pread_simple(fd,
1239 					old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1),
1240 					NTFS_BLOCK_SIZE, alignedbuffer);
1241 			if (i != NTFS_BLOCK_SIZE) {
1242 				if (i >= 0)
1243 					errno = EIO;
1244 				goto write_error;
1245 			}
1246 		}
1247 		/*
1248 		 * Read last sector if end of write not sector aligned and last
1249 		 * sector is either not the same as the first sector or it is
1250 		 * the same as the first sector but this has not been read in
1251 		 * yet, i.e. the start of the write is sector aligned.
1252 		 */
1253 		end = old_pos + count;
1254 		if ((end & (NTFS_BLOCK_SIZE - 1)) &&
1255 				((to_write > NTFS_BLOCK_SIZE) || !ofs)) {
1256 			i = ntfs_device_win32_pread_simple(fd,
1257 					end & ~(s64)(NTFS_BLOCK_SIZE - 1),
1258 					NTFS_BLOCK_SIZE, alignedbuffer +
1259 					to_write - NTFS_BLOCK_SIZE);
1260 			if (i != NTFS_BLOCK_SIZE) {
1261 				if (i >= 0)
1262 					errno = EIO;
1263 				goto write_error;
1264 			}
1265 		}
1266 		/* Copy the data to be written into @alignedbuffer. */
1267 		memcpy(alignedbuffer + ofs, b, count);
1268 	}
1269 	if (fd->vol_handle != INVALID_HANDLE_VALUE && old_pos < fd->geo_size) {
1270 		s64 vol_to_write = fd->geo_size - old_pos;
1271 		if (count > vol_to_write) {
1272 			bw = ntfs_device_win32_pwrite_simple(fd,
1273 					old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1),
1274 					ofs + vol_to_write, alignedbuffer);
1275 			if (bw == -1)
1276 				goto write_error;
1277 			to_write -= bw;
1278 			if (bw < ofs) {
1279 				bw = 0;
1280 				goto write_partial;
1281 			}
1282 			bw -= ofs;
1283 			fd->pos += bw;
1284 			ofs = fd->pos & (NTFS_BLOCK_SIZE - 1);
1285 			if (bw != vol_to_write)
1286 				goto write_partial;
1287 		}
1288 	}
1289 	i = ntfs_device_win32_pwrite_simple(fd,
1290 			fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_write,
1291 			alignedbuffer + bw);
1292 	if (i == -1) {
1293 		if (bw)
1294 			goto write_partial;
1295 		goto write_error;
1296 	}
1297 	if (i < ofs)
1298 		goto write_partial;
1299 	i -= ofs;
1300 	bw += i;
1301 	if (bw > count)
1302 		bw = count;
1303 	fd->pos = old_pos + bw;
1304 write_partial:
1305 	if (alignedbuffer != b)
1306 		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1307 	return bw;
1308 write_error:
1309 	bw = -1;
1310 	goto write_partial;
1311 }
1312 
1313 /**
1314  * ntfs_device_win32_stat - get a unix-like stat structure for an ntfs device
1315  * @dev:	ntfs device obtained via ->open
1316  * @buf:	pointer to the stat structure to fill
1317  *
1318  * Note: Only st_mode, st_size, and st_blocks are filled.
1319  *
1320  * Return 0 if o.k.
1321  *	 -1 if not and errno set. in this case handle is trashed.
1322  */
1323 static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf)
1324 {
1325 	win32_fd *fd = (win32_fd *)dev->d_private;
1326 	mode_t st_mode;
1327 
1328 	switch (GetFileType(fd->handle)) {
1329 	case FILE_TYPE_CHAR:
1330 		st_mode = S_IFCHR;
1331 		break;
1332 	case FILE_TYPE_DISK:
1333 		st_mode = S_IFBLK;
1334 		break;
1335 	case FILE_TYPE_PIPE:
1336 		st_mode = S_IFIFO;
1337 		break;
1338 	default:
1339 		st_mode = 0;
1340 	}
1341 	memset(buf, 0, sizeof(struct stat));
1342 	buf->st_mode = st_mode;
1343 	buf->st_size = fd->part_length;
1344 	if (buf->st_size != -1)
1345 		buf->st_blocks = buf->st_size >> 9;
1346 	else
1347 		buf->st_size = 0;
1348 	return 0;
1349 }
1350 
1351 /**
1352  * ntfs_win32_hdio_getgeo - get drive geometry
1353  * @dev:	ntfs device obtained via ->open
1354  * @argp:	pointer to where to put the output
1355  *
1356  * Note: Works on windows NT/2k/XP only.
1357  *
1358  * Return 0 if o.k.
1359  *	 -1 if not, and errno set.  Note if error fd->handle is trashed.
1360  */
1361 static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev,
1362 		struct hd_geometry *argp)
1363 {
1364 	win32_fd *fd = (win32_fd *)dev->d_private;
1365 
1366 	argp->heads = fd->geo_heads;
1367 	argp->sectors = fd->geo_sectors;
1368 	argp->cylinders = fd->geo_cylinders;
1369 	argp->start = fd->part_hidden_sectors;
1370 	return 0;
1371 }
1372 
1373 /**
1374  * ntfs_win32_blksszget - get block device sector size
1375  * @dev:	ntfs device obtained via ->open
1376  * @argp:	pointer to where to put the output
1377  *
1378  * Note: Works on windows NT/2k/XP only.
1379  *
1380  * Return 0 if o.k.
1381  *	 -1 if not, and errno set.  Note if error fd->handle is trashed.
1382  */
1383 static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp)
1384 {
1385 	win32_fd *fd = (win32_fd *)dev->d_private;
1386 	DWORD bytesReturned;
1387 	DISK_GEOMETRY dg;
1388 
1389 	if (DeviceIoControl(fd->handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
1390 			&dg, sizeof(DISK_GEOMETRY), &bytesReturned, NULL)) {
1391 		/* success */
1392 		*argp = dg.BytesPerSector;
1393 		return 0;
1394 	}
1395 	errno = ntfs_w32error_to_errno(GetLastError());
1396 	ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n");
1397 	return -1;
1398 }
1399 
1400 static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request,
1401 		void *argp)
1402 {
1403 	win32_fd *fd = (win32_fd *)dev->d_private;
1404 
1405 	ntfs_log_trace("win32_ioctl(%d) called.\n", request);
1406 	switch (request) {
1407 #if defined(BLKGETSIZE)
1408 	case BLKGETSIZE:
1409 		ntfs_log_debug("BLKGETSIZE detected.\n");
1410 		if (fd->part_length >= 0) {
1411 			*(int *)argp = (int)(fd->part_length / 512);
1412 			return 0;
1413 		}
1414 		errno = EOPNOTSUPP;
1415 		return -1;
1416 #endif
1417 #if defined(BLKGETSIZE64)
1418 	case BLKGETSIZE64:
1419 		ntfs_log_debug("BLKGETSIZE64 detected.\n");
1420 		if (fd->part_length >= 0) {
1421 			*(s64 *)argp = fd->part_length;
1422 			return 0;
1423 		}
1424 		errno = EOPNOTSUPP;
1425 		return -1;
1426 #endif
1427 #ifdef HDIO_GETGEO
1428 	case HDIO_GETGEO:
1429 		ntfs_log_debug("HDIO_GETGEO detected.\n");
1430 		return ntfs_win32_hdio_getgeo(dev, (struct hd_geometry *)argp);
1431 #endif
1432 #ifdef BLKSSZGET
1433 	case BLKSSZGET:
1434 		ntfs_log_debug("BLKSSZGET detected.\n");
1435 		return ntfs_win32_blksszget(dev, (int *)argp);
1436 #endif
1437 #ifdef BLKBSZSET
1438 	case BLKBSZSET:
1439 		ntfs_log_debug("BLKBSZSET detected.\n");
1440 		/* Nothing to do on Windows. */
1441 		return 0;
1442 #endif
1443 	default:
1444 		ntfs_log_debug("unimplemented ioctl %d.\n", request);
1445 		errno = EOPNOTSUPP;
1446 		return -1;
1447 	}
1448 }
1449 
1450 static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b,
1451 		s64 count, s64 offset)
1452 {
1453 	return ntfs_pread(dev, offset, count, b);
1454 }
1455 
1456 static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b,
1457 		s64 count, s64 offset)
1458 {
1459 	return ntfs_pwrite(dev, offset, count, b);
1460 }
1461 
1462 struct ntfs_device_operations ntfs_device_win32_io_ops = {
1463 	.open		= ntfs_device_win32_open,
1464 	.close		= ntfs_device_win32_close,
1465 	.seek		= ntfs_device_win32_seek,
1466 	.read		= ntfs_device_win32_read,
1467 	.write		= ntfs_device_win32_write,
1468 	.pread		= ntfs_device_win32_pread,
1469 	.pwrite		= ntfs_device_win32_pwrite,
1470 	.sync		= ntfs_device_win32_sync,
1471 	.stat		= ntfs_device_win32_stat,
1472 	.ioctl		= ntfs_device_win32_ioctl
1473 };
1474