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