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