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