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