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.
6 * Copyright (c) 2003-2004 Lode Leroy
7 * Copyright (c) 2003-2006 Anton Altaparmakov
8 * Copyright (c) 2004-2005 Yuval Fledel
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.
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.
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
46 /* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */
47 #define _NTFS_VOLUME_H
49 typedef struct ntfs_volume ntfs_volume
;
59 #ifndef NTFS_BLOCK_SIZE
60 #define NTFS_BLOCK_SIZE 512
61 #define NTFS_BLOCK_SIZE_BITS 9
64 #ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
65 #define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096
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
);
75 static LPFN_FINDFIRSTVOLUME fnFindFirstVolume
= NULL
;
76 static LPFN_FINDNEXTVOLUME fnFindNextVolume
= NULL
;
77 static LPFN_FINDVOLUMECLOSE fnFindVolumeClose
= NULL
;
78 static LPFN_SETFILEPOINTEREX fnSetFilePointerEx
= NULL
;
91 s64 pos
; /* Logical current position on the volume. */
94 int part_hidden_sectors
;
95 s64 geo_size
, geo_cylinders
;
96 DWORD geo_sectors
, geo_heads
;
101 * ntfs_w32error_to_errno - convert a win32 error code to the unix one
102 * @w32error: the win32 error code
104 * Limited to a relatively small but useful number of codes.
106 static int ntfs_w32error_to_errno(unsigned int w32error
)
108 ntfs_log_trace("Converting w32error 0x%x.\n",w32error
);
110 case ERROR_INVALID_FUNCTION
:
112 case ERROR_FILE_NOT_FOUND
:
113 case ERROR_PATH_NOT_FOUND
:
114 case ERROR_INVALID_NAME
:
116 case ERROR_TOO_MANY_OPEN_FILES
:
118 case ERROR_ACCESS_DENIED
:
120 case ERROR_INVALID_HANDLE
:
122 case ERROR_NOT_ENOUGH_MEMORY
:
124 case ERROR_OUTOFMEMORY
:
126 case ERROR_INVALID_DRIVE
:
129 case ERROR_WRITE_PROTECT
:
131 case ERROR_NOT_READY
:
132 case ERROR_SHARING_VIOLATION
:
134 case ERROR_BAD_COMMAND
:
137 case ERROR_NEGATIVE_SEEK
:
139 case ERROR_NOT_SUPPORTED
:
141 case ERROR_BAD_NETPATH
:
144 /* generic message */
150 * libntfs_SetFilePointerEx - emulation for SetFilePointerEx()
152 * We use this to emulate SetFilePointerEx() when it is not present. This can
153 * happen since SetFilePointerEx() only exists in Win2k+.
155 static BOOL WINAPI
libntfs_SetFilePointerEx(HANDLE hFile
,
156 LARGE_INTEGER liDistanceToMove
,
157 PLARGE_INTEGER lpNewFilePointer
, DWORD dwMoveMethod
)
159 liDistanceToMove
.LowPart
= SetFilePointer(hFile
,
160 liDistanceToMove
.LowPart
, &liDistanceToMove
.HighPart
,
162 if (liDistanceToMove
.LowPart
== INVALID_SET_FILE_POINTER
&&
163 GetLastError() != NO_ERROR
) {
164 if (lpNewFilePointer
)
165 lpNewFilePointer
->QuadPart
= -1;
168 if (lpNewFilePointer
)
169 lpNewFilePointer
->QuadPart
= liDistanceToMove
.QuadPart
;
174 * ntfs_device_win32_init_imports - initialize the function pointers
176 * The Find*Volume and SetFilePointerEx functions exist only on win2k+, as such
177 * we cannot just staticly import them.
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
183 * Note: The values are cached, do be afraid to run it more than once.
185 static void ntfs_device_win32_init_imports(void)
187 HMODULE kernel32
= GetModuleHandle("kernel32");
189 errno
= ntfs_w32error_to_errno(GetLastError());
190 ntfs_log_trace("kernel32.dll could not be imported.\n");
192 if (!fnSetFilePointerEx
) {
194 fnSetFilePointerEx
= (LPFN_SETFILEPOINTEREX
)
195 GetProcAddress(kernel32
,
198 * If we did not get kernel32.dll or it is not Win2k+, emulate
199 * SetFilePointerEx().
201 if (!fnSetFilePointerEx
) {
202 ntfs_log_debug("SetFilePonterEx() not found in "
203 "kernel32.dll: Enabling emulation.\n");
204 fnSetFilePointerEx
= libntfs_SetFilePointerEx
;
207 /* Cannot do lookups if we could not get kernel32.dll... */
210 if (!fnFindFirstVolume
)
211 fnFindFirstVolume
= (LPFN_FINDFIRSTVOLUME
)
212 GetProcAddress(kernel32
, "FindFirstVolume"
214 if (!fnFindNextVolume
)
215 fnFindNextVolume
= (LPFN_FINDNEXTVOLUME
)
216 GetProcAddress(kernel32
, "FindNextVolume"
218 if (!fnFindVolumeClose
)
219 fnFindVolumeClose
= (LPFN_FINDVOLUMECLOSE
)
220 GetProcAddress(kernel32
, "FindVolumeClose");
224 * ntfs_device_unix_status_flags_to_win32 - convert unix->win32 open flags
225 * @flags: unix open status flags
227 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
229 static __inline__
int ntfs_device_unix_status_flags_to_win32(int flags
)
233 switch (flags
& O_ACCMODE
) {
235 win_mode
= FILE_READ_DATA
;
238 win_mode
= FILE_WRITE_DATA
;
241 win_mode
= FILE_READ_DATA
| FILE_WRITE_DATA
;
245 ntfs_log_trace("Unknown status flags.\n");
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?
259 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
262 * -1 if not, and errno set. In this case handle is trashed.
264 static int ntfs_device_win32_simple_open_file(const char *filename
,
265 HANDLE
*handle
, int flags
, BOOL locking
)
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
);
280 * ntfs_device_win32_lock - lock the volume
281 * @handle: a win32 HANDLE for a volume to lock
283 * Locking a volume means no one can access its contents.
284 * Exiting the process automatically unlocks the volume, except in old NT4s.
287 * -1 if not, and errno set.
289 static int ntfs_device_win32_lock(HANDLE handle
)
293 if (!DeviceIoControl(handle
, FSCTL_LOCK_VOLUME
, NULL
, 0, NULL
, 0, &i
,
295 errno
= ntfs_w32error_to_errno(GetLastError());
296 ntfs_log_trace("Couldn't lock volume.\n");
299 ntfs_log_debug("Volume locked.\n");
304 * ntfs_device_win32_unlock - unlock the volume
305 * @handle: the win32 HANDLE which the volume was locked with
308 * -1 if not, and errno set.
310 static int ntfs_device_win32_unlock(HANDLE handle
)
314 if (!DeviceIoControl(handle
, FSCTL_UNLOCK_VOLUME
, NULL
, 0, NULL
, 0, &i
,
316 errno
= ntfs_w32error_to_errno(GetLastError());
317 ntfs_log_trace("Couldn't unlock volume.\n");
320 ntfs_log_debug("Volume unlocked.\n");
325 * ntfs_device_win32_dismount - dismount a volume
326 * @handle: a win32 HANDLE for a volume to dismount
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
335 * -1 if not, and errno set.
337 static int ntfs_device_win32_dismount(HANDLE handle
)
341 if (!DeviceIoControl(handle
, FSCTL_DISMOUNT_VOLUME
, NULL
, 0, NULL
, 0,
343 errno
= ntfs_w32error_to_errno(GetLastError());
344 ntfs_log_trace("Couldn't dismount volume.\n");
347 ntfs_log_debug("Volume dismounted.\n");
352 * ntfs_device_win32_getsize - get file size via win32 API
353 * @handle: pointer the file HANDLE obtained via open
355 * Only works on ordinary files.
357 * Return The file size if o.k.
358 * -1 if not, and errno set.
360 static s64
ntfs_device_win32_getsize(HANDLE handle
)
362 DWORD loword
, hiword
;
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");
370 return ((s64
)hiword
<< 32) + loword
;
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
378 * Only works on PhysicalDriveX type handles.
380 * Return The disk size if o.k.
381 * -1 if not, and errno set.
383 static s64
ntfs_device_win32_getdisklength(HANDLE handle
)
385 GET_LENGTH_INFORMATION buf
;
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");
394 ntfs_log_debug("Disk length: %lld.\n", buf
.Length
.QuadPart
);
395 return buf
.Length
.QuadPart
;
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
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
410 * Return The volume size if o.k.
411 * -1 if not, and errno set.
413 static s64
ntfs_device_win32_getntfssize(HANDLE handle
)
416 #ifdef FSCTL_GET_NTFS_VOLUME_DATA
418 NTFS_VOLUME_DATA_BUFFER buf
;
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");
426 rvl
= buf
.NumberSectors
.QuadPart
* buf
.BytesPerSector
;
427 ntfs_log_debug("NTFS volume length: 0x%llx.\n", (long long)rvl
);
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
441 * -1 if not, and errno set.
443 * In Windows NT+: fills size, sectors, and cylinders and sets heads to -1.
444 * In Windows XP+: fills size, sectors, cylinders, and heads.
446 * Note: In pre XP, this requires write permission, even though nothing is
449 * If fails, sets sectors, cylinders, heads, and size to -1.
451 static int ntfs_device_win32_getgeo(HANDLE handle
, win32_fd
*fd
)
455 BYTE b
[sizeof(DISK_GEOMETRY
) + sizeof(DISK_PARTITION_INFO
) +
456 sizeof(DISK_DETECTION_INFO
) + 512];
458 rvl
= DeviceIoControl(handle
, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
, NULL
,
459 0, &b
, sizeof(b
), &i
, NULL
);
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
) {
472 fd
->geo_cylinders
= ddi
->Int13
.MaxCylinders
;
473 fd
->geo_sectors
= ddi
->Int13
.SectorsPerTrack
;
474 fd
->geo_heads
= ddi
->Int13
.MaxHeads
;
477 fd
->geo_cylinders
= ddi
->ExInt13
.ExCylinders
;
478 fd
->geo_sectors
= ddi
->ExInt13
.ExSectorsPerTrack
;
479 fd
->geo_heads
= ddi
->ExInt13
.ExHeads
;
487 rvl
= DeviceIoControl(handle
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0,
488 &b
, sizeof(b
), &i
, NULL
);
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
;
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;
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
513 * -1 if not, and errno set.
515 static __inline__
int ntfs_device_win32_open_file(char *filename
, win32_fd
*fd
,
520 if (ntfs_device_win32_simple_open_file(filename
, &handle
, flags
,
528 fd
->part_length
= ntfs_device_win32_getsize(handle
);
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
;
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
543 * -1 if not, and errno set.
545 static __inline__
int ntfs_device_win32_open_drive(int drive_id
, win32_fd
*fd
,
550 char filename
[MAX_PATH
];
552 sprintf(filename
, "\\\\.\\PhysicalDrive%d", drive_id
);
553 if ((err
= ntfs_device_win32_simple_open_file(filename
, &handle
, flags
,
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
);
566 fd
->part_length
= fd
->geo_size
;
568 fd
->part_hidden_sectors
= -1;
569 fd
->vol_handle
= INVALID_HANDLE_VALUE
;
574 * ntfs_device_win32_open_volume_for_partition - find and open a volume
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
581 * The function required Windows 2k/XP, otherwise it fails (gracefully).
583 * Return success: a valid open volume handle.
584 * fail : INVALID_HANDLE_VALUE
586 static HANDLE
ntfs_device_win32_open_volume_for_partition(unsigned int drive_id
,
587 s64 part_offset
, s64 part_length
, int flags
)
589 HANDLE vol_find_handle
;
590 TCHAR vol_name
[MAX_PATH
];
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
;
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
;
612 /* remove trailing '/' from vol_name */
614 vol_name_length
= wcslen(vol_name
);
616 vol_name_length
= strlen(vol_name
);
618 if (vol_name_length
>0)
619 vol_name
[vol_name_length
-1]=0;
621 ntfs_log_debug("Processing %s.\n", vol_name
);
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
) {
629 #define EXTENTS_SIZE sizeof(VOLUME_DISK_EXTENTS) + 9 * sizeof(DISK_EXTENT)
630 char extents
[EXTENTS_SIZE
];
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
)
646 ExtentLength
.QuadPart
649 * Eureka! (Archimedes, 287 BC,
650 * "I have found it!")
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
;
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
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.
679 * Returns: TRUE if found, and sets the output parameters.
680 * FALSE if not and errno is set to the error code.
682 static BOOL
ntfs_device_win32_find_partition(HANDLE handle
, DWORD partition_id
,
683 s64
*part_offset
, s64
*part_length
, int *hidden_sectors
)
685 DRIVE_LAYOUT_INFORMATION
*drive_layout
;
686 unsigned int err
, buf_size
, part_count
;
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.
695 buf_size
= sizeof(DRIVE_LAYOUT_INFORMATION
) +
696 part_count
* sizeof(PARTITION_INFORMATION
);
697 drive_layout
= malloc(buf_size
);
702 if (DeviceIoControl(handle
, IOCTL_DISK_GET_DRIVE_LAYOUT
, NULL
,
703 0, (BYTE
*)drive_layout
, buf_size
, &i
, NULL
))
705 err
= GetLastError();
707 if (err
!= ERROR_INSUFFICIENT_BUFFER
) {
708 ntfs_log_trace("GetDriveLayout failed.\n");
709 errno
= ntfs_w32error_to_errno(err
);
712 ntfs_log_debug("More than %u partitions.\n", part_count
);
714 if (part_count
> 512) {
715 ntfs_log_trace("GetDriveLayout failed: More than 512 "
721 for (i
= 0; i
< drive_layout
->PartitionCount
; i
++) {
722 if (drive_layout
->PartitionEntry
[i
].PartitionNumber
==
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
].
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
747 * -1 if not, and errno set.
749 * When fails, fd contents may have not been preserved.
751 static int ntfs_device_win32_open_partition(int drive_id
,
752 unsigned int partition_id
, win32_fd
*fd
, int flags
)
754 s64 part_start
, part_length
;
756 int err
, hidden_sectors
;
757 char drive_name
[MAX_PATH
];
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
,
766 if (ntfs_device_win32_find_partition(handle
, partition_id
, &part_start
,
767 &part_length
, &hidden_sectors
)) {
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
);
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
);
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
);
790 fd
->vol_handle
= vol_handle
;
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 "
802 fd
->vol_handle
= INVALID_HANDLE_VALUE
;
806 ntfs_log_debug("Partition %u not found on drive %d.\n",
807 partition_id
, drive_id
);
815 * ntfs_device_win32_open - open a device
816 * @dev: a pointer to the NTFS_DEVICE to open
817 * @flags: unix open status flags
819 * @dev->d_name must hold the device name, the rest is ignored.
820 * Supported flags are O_RDONLY, O_WRONLY and O_RDWR.
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.
826 static int ntfs_device_win32_open(struct ntfs_device
*dev
, int flags
)
828 int drive_id
= 0, numparams
;
829 unsigned int part
= 0;
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';
843 ntfs_log_debug("win32_open(%s) -> file.\n", dev
->d_name
);
844 err
= ntfs_device_win32_open_file(dev
->d_name
, &fd
, flags
);
847 ntfs_log_debug("win32_open(%s) -> drive %d.\n", dev
->d_name
,
849 err
= ntfs_device_win32_open_drive(drive_id
, &fd
, flags
);
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
,
858 ntfs_log_debug("win32_open(%s) -> unknwon file format.\n",
864 ntfs_log_debug("win32_open(%s) -> %p, offset 0x%llx.\n", dev
->d_name
,
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
));
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
882 * Return the new position on the volume on success and -1 on error with errno
883 * set to the error code.
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.
890 static s64
ntfs_device_win32_seek(struct ntfs_device
*dev
, s64 offset
,
894 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
896 ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset
, whence
);
902 abs_ofs
= fd
->pos
+ offset
;
905 /* End of partition != end of disk. */
906 if (fd
->part_length
== -1) {
907 ntfs_log_trace("Position relative to end of disk not "
912 abs_ofs
= fd
->part_length
+ offset
;
915 ntfs_log_trace("Wrong mode %d.\n", whence
);
919 if (abs_ofs
< 0 || abs_ofs
> fd
->part_length
) {
920 ntfs_log_trace("Seeking outsize seekable area.\n");
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
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.
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
943 * - Does not use/set @fd->pos.
945 static s64
ntfs_device_win32_pio(win32_fd
*fd
, const s64 pos
,
946 const s64 count
, void *b
, const BOOL write
)
953 ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n",
954 (long long)pos
, (long long)count
, write
? "write" :
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
;
961 ntfs_log_debug("Transfering via handle.\n");
963 li
.QuadPart
+= fd
->part_start
;
965 if (!fnSetFilePointerEx(handle
, li
, NULL
, FILE_BEGIN
)) {
966 errno
= ntfs_w32error_to_errno(GetLastError());
967 ntfs_log_trace("SetFilePointer failed.\n");
971 res
= WriteFile(handle
, b
, count
, &bt
, NULL
);
973 res
= ReadFile(handle
, b
, count
, &bt
, NULL
);
975 errno
= ntfs_w32error_to_errno(GetLastError());
976 ntfs_log_trace("%sFile() failed.\n", write
? "Write" : "Read");
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
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.
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
996 * - Does not use/set @fd->pos.
998 static inline s64
ntfs_device_win32_pread_simple(win32_fd
*fd
, const s64 pos
,
999 const s64 count
, void *b
)
1001 return ntfs_device_win32_pio(fd
, pos
, count
, b
, FALSE
);
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
1010 * On success returns the number of bytes actually read (can be < @count).
1011 * On error returns -1 with errno set.
1013 static s64
ntfs_device_win32_read(struct ntfs_device
*dev
, void *b
, s64 count
)
1015 s64 old_pos
, to_read
, i
, br
= 0;
1016 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1017 BYTE
*alignedbuffer
;
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
;
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)))
1038 alignedbuffer
= (BYTE
*)VirtualAlloc(NULL
, to_read
, MEM_COMMIT
,
1040 if (!alignedbuffer
) {
1041 errno
= ntfs_w32error_to_errno(GetLastError());
1042 ntfs_log_trace("VirtualAlloc failed for read.\n");
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
);
1061 ofs
= fd
->pos
& (NTFS_BLOCK_SIZE
- 1);
1062 if (br
!= vol_to_read
)
1066 i
= ntfs_device_win32_pread_simple(fd
,
1067 fd
->pos
& ~(s64
)(NTFS_BLOCK_SIZE
- 1), to_read
,
1068 alignedbuffer
+ br
);
1080 fd
->pos
= old_pos
+ br
;
1082 if (alignedbuffer
!= b
) {
1083 memcpy((void*)b
, alignedbuffer
+ old_ofs
, br
);
1084 VirtualFree(alignedbuffer
, 0, MEM_RELEASE
);
1088 if (alignedbuffer
!= b
)
1089 VirtualFree(alignedbuffer
, 0, MEM_RELEASE
);
1094 * ntfs_device_win32_close - close an open ntfs deivce
1095 * @dev: ntfs device obtained via ->open
1098 * -1 if not, and errno set. Note if error fd->vol_handle is trashed.
1100 static int ntfs_device_win32_close(struct ntfs_device
*dev
)
1102 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1105 ntfs_log_trace("Closing device %p.\n", dev
);
1106 if (!NDevOpen(dev
)) {
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
);
1115 if (!CloseHandle(fd
->vol_handle
))
1116 ntfs_log_trace("CloseHandle() failed for volume.\n");
1118 rvl
= CloseHandle(fd
->handle
);
1121 errno
= ntfs_w32error_to_errno(GetLastError());
1122 ntfs_log_trace("CloseHandle() failed.\n");
1129 * ntfs_device_win32_sync - flush write buffers to disk
1130 * @dev: ntfs device obtained via ->open
1133 * -1 if not, and errno set.
1135 * Note: Volume syncing works differently in windows.
1136 * Disk cannot be synced in windows.
1138 static int ntfs_device_win32_sync(struct ntfs_device
*dev
)
1141 BOOL to_clear
= TRUE
;
1143 if (!NDevReadOnly(dev
) && NDevDirty(dev
)) {
1144 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1146 if ((fd
->vol_handle
!= INVALID_HANDLE_VALUE
) &&
1147 !FlushFileBuffers(fd
->vol_handle
)) {
1149 err
= ntfs_w32error_to_errno(GetLastError());
1151 if (!FlushFileBuffers(fd
->handle
)) {
1154 err
= ntfs_w32error_to_errno(GetLastError());
1157 ntfs_log_trace("Could not sync.\n");
1161 NDevClearDirty(dev
);
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
1173 * On success returns the number of bytes written and on error returns -1 and
1174 * errno set. Write starts from position @pos.
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
1180 * - Does not use/set @fd->pos.
1182 static inline s64
ntfs_device_win32_pwrite_simple(win32_fd
*fd
, const s64 pos
,
1183 const s64 count
, const void *b
)
1185 return ntfs_device_win32_pio(fd
, pos
, count
, (void *)b
, TRUE
);
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
1194 * On success returns the number of bytes actually written.
1195 * On error returns -1 with errno set.
1197 static s64
ntfs_device_win32_write(struct ntfs_device
*dev
, const void *b
,
1200 s64 old_pos
, to_write
, i
, bw
= 0;
1201 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1202 BYTE
*alignedbuffer
;
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
;
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");
1227 if (!((unsigned long)b
& (NTFS_BLOCK_SIZE
- 1)) && !old_ofs
&&
1228 !(count
& (NTFS_BLOCK_SIZE
- 1)))
1229 alignedbuffer
= (BYTE
*)b
;
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");
1240 /* Read first sector if start of write not sector aligned. */
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
) {
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.
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
) {
1270 /* Copy the data to be written into @alignedbuffer. */
1271 memcpy(alignedbuffer
+ ofs
, b
, count
);
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
);
1288 ofs
= fd
->pos
& (NTFS_BLOCK_SIZE
- 1);
1289 if (bw
!= vol_to_write
)
1293 i
= ntfs_device_win32_pwrite_simple(fd
,
1294 fd
->pos
& ~(s64
)(NTFS_BLOCK_SIZE
- 1), to_write
,
1295 alignedbuffer
+ bw
);
1307 fd
->pos
= old_pos
+ bw
;
1309 if (alignedbuffer
!= b
)
1310 VirtualFree(alignedbuffer
, 0, MEM_RELEASE
);
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
1322 * Note: Only st_mode, st_size, and st_blocks are filled.
1325 * -1 if not and errno set. in this case handle is trashed.
1327 static int ntfs_device_win32_stat(struct ntfs_device
*dev
, struct stat
*buf
)
1329 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1332 switch (GetFileType(fd
->handle
)) {
1333 case FILE_TYPE_CHAR
:
1336 case FILE_TYPE_DISK
:
1339 case FILE_TYPE_PIPE
:
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;
1356 * ntfs_win32_hdio_getgeo - get drive geometry
1357 * @dev: ntfs device obtained via ->open
1358 * @argp: pointer to where to put the output
1360 * Note: Works on windows NT/2k/XP only.
1363 * -1 if not, and errno set. Note if error fd->handle is trashed.
1365 static __inline__
int ntfs_win32_hdio_getgeo(struct ntfs_device
*dev
,
1366 struct hd_geometry
*argp
)
1368 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
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
;
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
1382 * Note: Works on windows NT/2k/XP only.
1385 * -1 if not, and errno set. Note if error fd->handle is trashed.
1387 static __inline__
int ntfs_win32_blksszget(struct ntfs_device
*dev
,int *argp
)
1389 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1390 DWORD bytesReturned
;
1393 if (DeviceIoControl(fd
->handle
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0,
1394 &dg
, sizeof(DISK_GEOMETRY
), &bytesReturned
, NULL
)) {
1396 *argp
= dg
.BytesPerSector
;
1399 errno
= ntfs_w32error_to_errno(GetLastError());
1400 ntfs_log_trace("GET_DRIVE_GEOMETRY failed.\n");
1404 static int ntfs_device_win32_ioctl(struct ntfs_device
*dev
, int request
,
1407 win32_fd
*fd
= (win32_fd
*)dev
->d_private
;
1409 ntfs_log_trace("win32_ioctl(%d) called.\n", request
);
1411 #if defined(BLKGETSIZE)
1413 ntfs_log_debug("BLKGETSIZE detected.\n");
1414 if (fd
->part_length
>= 0) {
1415 *(int *)argp
= (int)(fd
->part_length
/ 512);
1421 #if defined(BLKGETSIZE64)
1423 ntfs_log_debug("BLKGETSIZE64 detected.\n");
1424 if (fd
->part_length
>= 0) {
1425 *(s64
*)argp
= fd
->part_length
;
1433 ntfs_log_debug("HDIO_GETGEO detected.\n");
1434 return ntfs_win32_hdio_getgeo(dev
, (struct hd_geometry
*)argp
);
1438 ntfs_log_debug("BLKSSZGET detected.\n");
1439 return ntfs_win32_blksszget(dev
, (int *)argp
);
1443 ntfs_log_debug("BLKBSZSET detected.\n");
1444 /* Nothing to do on Windows. */
1448 ntfs_log_debug("unimplemented ioctl %d.\n", request
);
1454 static s64
ntfs_device_win32_pread(struct ntfs_device
*dev
, void *b
,
1455 s64 count
, s64 offset
)
1457 return ntfs_pread(dev
, offset
, count
, b
);
1460 static s64
ntfs_device_win32_pwrite(struct ntfs_device
*dev
, const void *b
,
1461 s64 count
, s64 offset
)
1463 return ntfs_pwrite(dev
, offset
, count
, b
);
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