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