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