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