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
48struct ntfs_volume;
49typedef 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. */
69typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD);
70typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD);
71typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE);
72typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER,
73		PLARGE_INTEGER, DWORD);
74
75static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL;
76static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL;
77static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL;
78static 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 */
89typedef 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 */
106static 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 */
155static 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 */
185static 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 */
229static __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 */
264static 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 */
289static 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 */
310static 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 */
337static 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 */
360static 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 */
383static 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 */
413static 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 */
451static 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 */
515static __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 */
545static __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 */
586static 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 */
682static 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 */
751static 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 */
826static 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 */
890static 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 */
945static 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 */
998static 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 */
1013static 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;
1081read_partial:
1082	if (alignedbuffer != b) {
1083		memcpy((void*)b, alignedbuffer + old_ofs, br);
1084		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1085	}
1086	return br;
1087read_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 */
1100static 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 */
1138static 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 */
1182static 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 */
1197static 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;
1308write_partial:
1309	if (alignedbuffer != b)
1310		VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1311	return bw;
1312write_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 */
1327static 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 */
1365static __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 */
1387static __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
1404static 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
1454static 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
1460static 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
1466struct 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