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