summaryrefslogtreecommitdiff
path: root/libntfs-3g/win32_io.c (plain)
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
45typedef unsigned long long DWORD64;
46#endif
47
48typedef 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
79struct ntfs_volume;
80typedef 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. */
120typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD);
121typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD);
122typedef BOOL (WINAPI *LPFN_FINDVOLUMECLOSE)(HANDLE);
123typedef BOOL (WINAPI *LPFN_SETFILEPOINTEREX)(HANDLE, LARGE_INTEGER,
124 PLARGE_INTEGER, DWORD);
125
126static LPFN_FINDFIRSTVOLUME fnFindFirstVolume = NULL;
127static LPFN_FINDNEXTVOLUME fnFindNextVolume = NULL;
128static LPFN_FINDVOLUMECLOSE fnFindVolumeClose = NULL;
129static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL;
130
131#ifdef UNICODE
132#define FNPOSTFIX "W"
133#else
134#define FNPOSTFIX "A"
135#endif
136
137enum { /* 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
158typedef u32 NTSTATUS; /* do not let the compiler choose the size */
159#ifdef __x86_64__
160typedef unsigned long long ULONG_PTR; /* an integer the same size as a pointer */
161#else
162typedef unsigned long ULONG_PTR; /* an integer the same size as a pointer */
163#endif
164
165HANDLE get_osfhandle(int); /* from msvcrt.dll */
166
167/*
168 * A few needed definitions not included in <windows.h>
169 */
170
171typedef 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
179typedef 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
188typedef 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
212typedef void (WINAPI *PIO_APC_ROUTINE)(void*, PIO_STATUS_BLOCK, ULONG);
213
214extern 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
223extern 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
235extern 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
247extern NTSTATUS WINAPI NtClose(
248 HANDLE Handle
249);
250
251extern 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
264extern 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 */
280typedef 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 */
300static 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
343static 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 */
369static 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 */
400static 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 */
444static __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 */
479static 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 */
505static 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 */
526static 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
540static 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 */
569static 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 */
592static 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 */
618static 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 */
648static 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 */
686static 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
744static 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 */
794static __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 */
836static __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 */
874static __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 */
961static 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 */
1057static 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 */
1126static 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 */
1203static 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 */
1279static 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 */
1335static 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 */
1425static 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 */
1440static 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;
1508read_partial:
1509 if (alignedbuffer != b) {
1510 memcpy((void*)b, alignedbuffer + old_ofs, br);
1511 VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1512 }
1513 return br;
1514read_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 */
1527static 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 */
1572static 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 */
1616static 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 */
1631static 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;
1742write_partial:
1743 if (alignedbuffer != b)
1744 VirtualFree(alignedbuffer, 0, MEM_RELEASE);
1745 return bw;
1746write_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 */
1761static 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 */
1803static __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 */
1826static __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
1843static 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
1895static 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
1915static 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
1934struct 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
1956int 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
1981static 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
2009int 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
2021int 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