1 /**** 2 ** Platform-dependent file 3 ****/ 4 5 /* io.c - Virtual disk input/output 6 7 Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> 8 Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> 9 Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> 10 Copyright (C) 2015 Andreas Bombe <aeb@debian.org> 11 12 This program is free software: you can redistribute it and/or modify 13 it under the terms of the GNU General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 25 The complete text of the GNU General Public License 26 can be found in /usr/share/common-licenses/GPL-3 file. 27 */ 28 29 /* 30 * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de> 31 * Fixed nasty bug that caused every file with a name like 32 * xxxxxxxx.xxx to be treated as bad name that needed to be fixed. 33 */ 34 35 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 36 * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ 37 38 #include "vfatlib.h" 39 40 #define NDEBUG 41 #include <debug.h> 42 43 44 #define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS) 45 46 typedef struct _change { 47 void *data; 48 off_t pos; 49 int size; 50 struct _change *next; 51 } CHANGE; 52 53 static CHANGE *changes, *last; 54 static int did_change = 0; 55 static HANDLE fd; 56 static LARGE_INTEGER CurrentOffset; 57 58 59 /**** Win32 / NT support ******************************************************/ 60 61 static int WIN32close(HANDLE FileHandle) 62 { 63 if (!NT_SUCCESS(NtClose(FileHandle))) 64 return -1; 65 return 0; 66 } 67 #define close WIN32close 68 69 static int WIN32read(HANDLE FileHandle, void *buf, unsigned int len) 70 { 71 NTSTATUS Status; 72 IO_STATUS_BLOCK IoStatusBlock; 73 74 Status = NtReadFile(FileHandle, 75 NULL, 76 NULL, 77 NULL, 78 &IoStatusBlock, 79 buf, 80 len, 81 &CurrentOffset, 82 NULL); 83 if (!NT_SUCCESS(Status)) 84 { 85 DPRINT1("NtReadFile() failed (Status %lx)\n", Status); 86 return -1; 87 } 88 89 CurrentOffset.QuadPart += len; 90 return (int)len; 91 } 92 #define read WIN32read 93 94 static int WIN32write(HANDLE FileHandle, void *buf, unsigned int len) 95 { 96 NTSTATUS Status; 97 IO_STATUS_BLOCK IoStatusBlock; 98 99 Status = NtWriteFile(FileHandle, 100 NULL, 101 NULL, 102 NULL, 103 &IoStatusBlock, 104 buf, 105 len, 106 &CurrentOffset, 107 NULL); 108 if (!NT_SUCCESS(Status)) 109 { 110 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status); 111 return -1; 112 } 113 114 CurrentOffset.QuadPart += len; 115 return (int)len; 116 } 117 #define write WIN32write 118 119 static off_t WIN32lseek(HANDLE fd, off_t offset, int whence) 120 { 121 LARGE_INTEGER Offset; 122 Offset.QuadPart = (LONGLONG)offset; 123 124 switch (whence) 125 { 126 case SEEK_SET: 127 break; 128 129 case SEEK_CUR: 130 Offset.QuadPart += CurrentOffset.QuadPart; 131 break; 132 133 // case SEEK_END: 134 // Offset.QuadPart += FileSize.QuadPart; 135 // break; 136 137 default: 138 // errno = EINVAL; 139 return (off_t)-1; 140 } 141 142 if (Offset.QuadPart < 0LL) 143 { 144 // errno = EINVAL; 145 return (off_t)-1; 146 } 147 // if (Offset.QuadPart > FileSize.QuadPart) 148 // { 149 // // errno = EINVAL; 150 // return (off_t)-1; 151 // } 152 153 CurrentOffset = Offset; 154 155 return CurrentOffset.QuadPart; 156 } 157 #define lseek WIN32lseek 158 159 /******************************************************************************/ 160 161 162 NTSTATUS fs_open(PUNICODE_STRING DriveRoot, int read_write) 163 { 164 NTSTATUS Status; 165 OBJECT_ATTRIBUTES ObjectAttributes; 166 IO_STATUS_BLOCK Iosb; 167 168 InitializeObjectAttributes(&ObjectAttributes, 169 DriveRoot, 170 0, 171 NULL, 172 NULL); 173 174 Status = NtOpenFile(&fd, 175 FILE_GENERIC_READ | (read_write ? FILE_GENERIC_WRITE : 0), 176 &ObjectAttributes, 177 &Iosb, 178 read_write ? 0 : FILE_SHARE_READ, 179 FILE_SYNCHRONOUS_IO_ALERT); 180 if (!NT_SUCCESS(Status)) 181 { 182 DPRINT1("NtOpenFile() failed with status 0x%.08x\n", Status); 183 return Status; 184 } 185 186 // If read_write is specified, then the volume should be exclusively locked 187 if (read_write) 188 { 189 Status = fs_lock(TRUE); 190 } 191 192 // Query geometry and partition info, to have bytes per sector, etc 193 194 CurrentOffset.QuadPart = 0LL; 195 196 changes = last = NULL; 197 did_change = 0; 198 199 return Status; 200 } 201 202 BOOLEAN fs_isdirty(void) 203 { 204 NTSTATUS Status; 205 ULONG DirtyMask = 0; 206 IO_STATUS_BLOCK IoSb; 207 208 /* Check if volume is dirty */ 209 Status = NtFsControlFile(fd, 210 NULL, NULL, NULL, &IoSb, 211 FSCTL_IS_VOLUME_DIRTY, 212 NULL, 0, &DirtyMask, sizeof(DirtyMask)); 213 214 if (!NT_SUCCESS(Status)) 215 { 216 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status); 217 return FALSE; 218 } 219 220 /* Convert Dirty mask to a boolean value */ 221 return (DirtyMask & 1); 222 } 223 224 NTSTATUS fs_lock(BOOLEAN LockVolume) 225 { 226 NTSTATUS Status; 227 IO_STATUS_BLOCK IoSb; 228 229 /* Check if volume is dirty */ 230 Status = NtFsControlFile(fd, 231 NULL, NULL, NULL, &IoSb, 232 LockVolume ? FSCTL_LOCK_VOLUME 233 : FSCTL_UNLOCK_VOLUME, 234 NULL, 0, NULL, 0); 235 236 if (!NT_SUCCESS(Status)) 237 { 238 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status); 239 #if 1 240 /* FIXME: ReactOS HACK for 1stage due to IopParseDevice() hack */ 241 if (Status == STATUS_INVALID_DEVICE_REQUEST) 242 { 243 Status = STATUS_ACCESS_DENIED; 244 } 245 #endif 246 } 247 248 return Status; 249 } 250 251 void fs_dismount(void) 252 { 253 NTSTATUS Status; 254 IO_STATUS_BLOCK IoSb; 255 256 /* Check if volume is dirty */ 257 Status = NtFsControlFile(fd, 258 NULL, NULL, NULL, &IoSb, 259 FSCTL_DISMOUNT_VOLUME, 260 NULL, 0, NULL, 0); 261 262 if (!NT_SUCCESS(Status)) 263 { 264 DPRINT1("NtFsControlFile() failed with Status 0x%08x\n", Status); 265 } 266 } 267 268 /** 269 * Read data from the partition, accounting for any pending updates that are 270 * queued for writing. 271 * 272 * @param[in] pos Byte offset, relative to the beginning of the partition, 273 * at which to read 274 * @param[in] size Number of bytes to read 275 * @param[out] data Where to put the data read 276 */ 277 void fs_read(off_t pos, int size, void *data) 278 { 279 CHANGE *walk; 280 int got; 281 282 #if 1 // TMN 283 284 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; 285 const off_t seekpos_aligned = pos - (pos % 512); 286 const size_t seek_delta = (size_t)(pos - seekpos_aligned); 287 #if DBG 288 const size_t readsize = (size_t)(pos - seekpos_aligned) + readsize_aligned; 289 #endif 290 char* tmpBuf = alloc(readsize_aligned); 291 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos); 292 if ((got = read(fd, tmpBuf, readsize_aligned)) < 0) pdie("Read %d bytes at %lld",size,pos); 293 assert(got >= size); 294 got = size; 295 assert(seek_delta + size <= readsize); 296 memcpy(data, tmpBuf+seek_delta, size); 297 free(tmpBuf); 298 299 #else // TMN: 300 301 if (lseek(fd, pos, 0) != pos) 302 pdie("Seek to %lld", pos); 303 if ((got = read(fd, data, size)) < 0) 304 pdie("Read %d bytes at %lld", size, pos); 305 306 #endif // TMN: 307 308 if (got != size) 309 die("Got %d bytes instead of %d at %lld", got, size, pos); 310 for (walk = changes; walk; walk = walk->next) { 311 if (walk->pos < pos + size && walk->pos + walk->size > pos) { 312 if (walk->pos < pos) 313 memcpy(data, (char *)walk->data + pos - walk->pos, 314 min(size, walk->size - pos + walk->pos)); 315 else 316 memcpy((char *)data + walk->pos - pos, walk->data, 317 min(walk->size, size + pos - walk->pos)); 318 } 319 } 320 } 321 322 323 int fs_test(off_t pos, int size) 324 { 325 void *scratch; 326 int okay; 327 328 #if 1 // TMN 329 330 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; // TMN: 331 const off_t seekpos_aligned = pos - (pos % 512); // TMN: 332 scratch = alloc(readsize_aligned); 333 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",pos); 334 okay = read(fd, scratch, readsize_aligned) == (int)readsize_aligned; 335 free(scratch); 336 337 #else // TMN: 338 339 if (lseek(fd, pos, 0) != pos) 340 pdie("Seek to %lld", pos); 341 scratch = alloc(size); 342 okay = read(fd, scratch, size) == size; 343 free(scratch); 344 345 #endif // TMN: 346 return okay; 347 } 348 349 350 void fs_write(off_t pos, int size, void *data) 351 { 352 CHANGE *new; 353 int did; 354 355 assert(interactive || rw); 356 357 #if 1 //SAE 358 359 if (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE) { 360 void *scratch; 361 const size_t readsize_aligned = (size % 512) ? (size + (512 - (size % 512))) : size; 362 const off_t seekpos_aligned = pos - (pos % 512); 363 const size_t seek_delta = (size_t)(pos - seekpos_aligned); 364 BOOLEAN use_read = (seek_delta != 0) || ((readsize_aligned-size) != 0); 365 366 /* Aloc temp buffer if write is not aligned */ 367 if (use_read) 368 scratch = alloc(readsize_aligned); 369 else 370 scratch = data; 371 372 did_change = 1; 373 if (lseek(fd, seekpos_aligned, 0) != seekpos_aligned) pdie("Seek to %lld",seekpos_aligned); 374 375 if (use_read) 376 { 377 /* Read aligned data */ 378 if (read(fd, scratch, readsize_aligned) < 0) pdie("Read %d bytes at %lld",size,pos); 379 380 /* Patch data in memory */ 381 memcpy((char *)scratch + seek_delta, data, size); 382 } 383 384 /* Write it back */ 385 if ((did = write(fd, scratch, readsize_aligned)) == (int)readsize_aligned) 386 { 387 if (use_read) free(scratch); 388 return; 389 } 390 if (did < 0) pdie("Write %d bytes at %lld", size, pos); 391 die("Wrote %d bytes instead of %d at %lld", did, size, pos); 392 } 393 394 #else //SAE 395 396 if (write_immed) { 397 did_change = 1; 398 if (lseek(fd, pos, 0) != pos) 399 pdie("Seek to %lld", pos); 400 if ((did = write(fd, data, size)) == size) 401 return; 402 if (did < 0) 403 pdie("Write %d bytes at %lld", size, pos); 404 die("Wrote %d bytes instead of %d at %lld", did, size, pos); 405 } 406 407 #endif //SAE 408 409 new = alloc(sizeof(CHANGE)); 410 new->pos = pos; 411 memcpy(new->data = alloc(new->size = size), data, size); 412 new->next = NULL; 413 if (last) 414 last->next = new; 415 else 416 changes = new; 417 last = new; 418 } 419 420 421 static void fs_flush(void) 422 { 423 #if 1 424 425 CHANGE *this; 426 int old_write_immed = (FsCheckFlags & FSCHECK_IMMEDIATE_WRITE); 427 428 /* Disable writes to the list now */ 429 FsCheckFlags |= FSCHECK_IMMEDIATE_WRITE; 430 431 while (changes) { 432 this = changes; 433 changes = changes->next; 434 435 fs_write(this->pos, this->size, this->data); 436 437 free(this->data); 438 free(this); 439 } 440 441 /* Restore values */ 442 if (!old_write_immed) FsCheckFlags ^= FSCHECK_IMMEDIATE_WRITE; 443 444 #else 445 446 CHANGE *this; 447 int size; 448 449 while (changes) { 450 this = changes; 451 changes = changes->next; 452 if (lseek(fd, this->pos, 0) != this->pos) 453 { 454 // printf("Seek to %lld failed: %s\n Did not write %d bytes.\n", 455 // (long long)this->pos, strerror(errno), this->size); 456 printf("Seek to %lld failed\n Did not write %d bytes.\n", 457 (long long)this->pos, this->size); 458 } 459 else if ((size = write(fd, this->data, this->size)) < 0) 460 { 461 // printf("Writing %d bytes at %lld failed: %s\n", this->size, 462 // (long long)this->pos, strerror(errno)); 463 printf("Writing %d bytes at %lld failed\n", 464 this->size, (long long)this->pos); 465 } 466 else if (size != this->size) 467 printf("Wrote %d bytes instead of %d bytes at %lld.\n", 468 size, this->size, (long long)this->pos); 469 free(this->data); 470 free(this); 471 } 472 473 #endif 474 } 475 476 int fs_close(int write) 477 { 478 CHANGE *next; 479 int changed; 480 481 changed = !!changes; 482 if (write) 483 fs_flush(); 484 else 485 while (changes) { 486 next = changes->next; 487 free(changes->data); 488 free(changes); 489 changes = next; 490 } 491 if (close(fd) < 0) 492 pdie("closing filesystem"); 493 return changed || did_change; 494 } 495 496 int fs_changed(void) 497 { 498 return !!changes || did_change; 499 } 500