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