1/* 2 Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4*/ 5 6#include "system_dependencies.h" 7 8#include "iter.h" 9#include "dosfs.h" 10#include "dlist.h" 11#include "fat.h" 12#include "dir.h" 13#include "file.h" 14#include "attr.h" 15#include "vcache.h" 16#include "util.h" 17 18#define DPRINTF(a,b) if (debug_file > (a)) dprintf b 19 20#define MAX_FILE_SIZE 0xffffffffLL 21 22 23typedef struct filecookie { 24 uint32 mode; // open mode 25} filecookie; 26 27 28mode_t 29make_mode(nspace *volume, vnode *node) 30{ 31 mode_t result = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; 32 if (node->mode & FAT_SUBDIR) { 33 result &= ~S_IFREG; 34 result |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; 35 } 36 if ((node->mode & FAT_READ_ONLY) != 0) 37 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH); 38 39 return result; 40} 41 42 43// Sets node.st_time to the current time. 44// If willWrite is true, also writes the updated time to the corresponding direntry. 45static status_t 46_update_last_modified(nspace* vol, vnode* node, bool willWrite) 47{ 48 ASSERT_LOCKED_RECURSIVE(&(vol->vlock)); 49 50 status_t result = B_OK; 51 52 time(&(node->st_time)); 53 54 if (willWrite && node->vnid != vol->root_vnode.vnid) 55 result = write_vnode_entry(vol, node); 56 57 return result; 58} 59 60 61// Sets st_time of _node's parent directory to the current time, and writes the updated 62// time to the corresponding direntry. 63static status_t 64_update_parent_last_modified(fs_volume *_vol, fs_vnode *_node) 65{ 66 nspace *vol = (nspace *)_vol->private_volume; 67 vnode *node = (vnode *)_node->private_node; 68 69 ASSERT_LOCKED_RECURSIVE(&(vol->vlock)); 70 71 vnode* parent_node; 72 status_t result = get_vnode(_vol, node->dir_vnid, (void**)&parent_node); 73 if (result != B_OK) 74 return result; 75 if (_update_last_modified(vol, parent_node, true) != B_OK) 76 dprintf("_update_parent_last_modified: update failed for directory %s\n", 77 parent_node->filename); 78 return put_vnode(_vol, node->dir_vnid); 79} 80 81 82status_t 83dosfs_get_vnode_name(fs_volume *_ns, fs_vnode *_node, char *buffer, 84 size_t bufferSize) 85{ 86 vnode *node = (vnode*)_node->private_node; 87 strlcpy(buffer, node->filename, bufferSize); 88 return B_OK; 89} 90 91 92status_t write_vnode_entry(nspace *vol, vnode *node) 93{ 94 uint32 i; 95 struct diri diri; 96 uint8 *buffer; 97 98 // TODO : is it needed ? vfs job ? 99 // don't update entries of deleted files 100 //if (is_vnode_removed(vol->id, node->vnid) > 0) return 0; 101 102 // XXX: should check if directory position is still valid even 103 // though we do the is_vnode_removed check above 104 105 if ((node->cluster != 0) && !IS_DATA_CLUSTER(node->cluster)) { 106 dprintf("write_vnode_entry called on invalid cluster (%" B_PRIu32 ")\n", 107 node->cluster); 108 return EINVAL; 109 } 110 111 buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->eindex, &diri); 112 if (buffer == NULL) 113 return ENOENT; 114 115 diri_make_writable(&diri); 116 buffer[0x0b] = node->mode; // file attributes 117 118 memset(buffer+0xc, 0, 0x16-0xc); 119 i = time_t2dos(node->st_crtim); 120 buffer[0x0e] = i & 0xff; 121 buffer[0x0f] = (i >> 8) & 0xff; 122 buffer[0x10] = (i >> 16) & 0xff; 123 buffer[0x11] = (i >> 24) & 0xff; 124 i = time_t2dos(node->st_time); 125 buffer[0x16] = i & 0xff; 126 buffer[0x17] = (i >> 8) & 0xff; 127 buffer[0x18] = (i >> 16) & 0xff; 128 buffer[0x19] = (i >> 24) & 0xff; 129 buffer[0x1a] = node->cluster & 0xff; // starting cluster 130 buffer[0x1b] = (node->cluster >> 8) & 0xff; 131 if (vol->fat_bits == 32) { 132 buffer[0x14] = (node->cluster >> 16) & 0xff; 133 buffer[0x15] = (node->cluster >> 24) & 0xff; 134 } 135 if (node->mode & FAT_SUBDIR) { 136 buffer[0x1c] = buffer[0x1d] = buffer[0x1e] = buffer[0x1f] = 0; 137 } else { 138 buffer[0x1c] = node->st_size & 0xff; // file size 139 buffer[0x1d] = (node->st_size >> 8) & 0xff; 140 buffer[0x1e] = (node->st_size >> 16) & 0xff; 141 buffer[0x1f] = (node->st_size >> 24) & 0xff; 142 } 143 144 // TODO: figure out which stats have actually changed 145 notify_stat_changed(vol->id, -1, node->vnid, B_STAT_MODE | B_STAT_UID 146 | B_STAT_GID | B_STAT_SIZE | B_STAT_ACCESS_TIME 147 | B_STAT_MODIFICATION_TIME | B_STAT_CREATION_TIME 148 | B_STAT_CHANGE_TIME); 149 150 return B_OK; 151} 152 153 154// called when fs is done with vnode 155// after close, etc. free vnode resources here 156status_t 157dosfs_release_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter) 158{ 159 nspace *vol = (nspace *)_vol->private_volume; 160 vnode *node = (vnode *)_node->private_node; 161 162 if (node != NULL) { 163 DPRINTF(0, ("dosfs_release_vnode (ino_t %" B_PRIdINO ")\n", node->vnid)); 164 165 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) { 166 RecursiveLocker lock(vol->vlock); 167 _dosfs_sync(vol); 168 } 169 170 if (node->filename) free(node->filename); 171 172 if (node->vnid != vol->root_vnode.vnid) { 173 file_cache_delete(node->cache); 174 file_map_delete(node->file_map); 175 free(node); 176 } 177 } 178 179 return 0; 180} 181 182 183status_t 184dosfs_rstat(fs_volume *_vol, fs_vnode *_node, struct stat *st) 185{ 186 nspace *vol = (nspace*)_vol->private_volume; 187 vnode *node = (vnode*)_node->private_node; 188 189 RecursiveLocker lock(vol->vlock); 190 191 DPRINTF(1, ("dosfs_rstat (vnode id %" B_PRIdINO ")\n", node->vnid)); 192 193 st->st_dev = vol->id; 194 st->st_ino = node->vnid; 195 st->st_mode = make_mode(vol, node); 196 197 st->st_nlink = 1; 198 st->st_uid = 0; 199 st->st_gid = 0; 200 st->st_size = node->st_size; 201 st->st_blocks = (node->st_size + 511) / 512; 202 st->st_blksize = 0x10000; /* this value was chosen arbitrarily */ 203 st->st_atim.tv_sec = st->st_mtim.tv_sec = st->st_ctim.tv_sec 204 = node->st_time; 205 st->st_crtim.tv_sec = node->st_crtim; 206 st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec 207 = st->st_crtim.tv_nsec = 0; 208 209 return B_NO_ERROR; 210} 211 212 213status_t 214dosfs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st, 215 uint32 mask) 216{ 217 int err = B_OK; 218 nspace *vol = (nspace*)_vol->private_volume; 219 vnode *node = (vnode*)_node->private_node; 220 bool dirty = false; 221 222 RecursiveLocker lock(vol->vlock); 223 224 DPRINTF(0, ("dosfs_wstat (vnode id %" B_PRIdINO ")\n", node->vnid)); 225 226 if (vol->flags & B_FS_IS_READONLY) { 227 dprintf("can't wstat on read-only volume\n"); 228 return EROFS; 229 } 230 231 if (node->disk_image == 2) { 232 dprintf("can't wstat disk image\n"); 233 return EPERM; 234 } 235 236 if ((mask & B_STAT_MODE) != 0) { 237 DPRINTF(0, ("setting file mode to %o\n", st->st_mode)); 238 if (st->st_mode & S_IWUSR) 239 node->mode &= ~FAT_READ_ONLY; 240 else 241 node->mode |= FAT_READ_ONLY; 242 dirty = true; 243 } 244 245 if ((mask & B_STAT_SIZE) != 0) { 246 DPRINTF(0, ("setting file size to %" B_PRIdOFF "\n", st->st_size)); 247 if (node->mode & FAT_SUBDIR) { 248 dprintf("dosfs_wstat: can't set file size of directory!\n"); 249 err = EISDIR; 250 } else if (st->st_size > MAX_FILE_SIZE) { 251 dprintf("dosfs_wstat: desired file size exceeds fat limit\n"); 252 err = E2BIG; 253 } else { 254 uint32 clusters = (st->st_size + vol->bytes_per_sector 255 * vol->sectors_per_cluster - 1) / vol->bytes_per_sector 256 / vol->sectors_per_cluster; 257 DPRINTF(0, ("setting fat chain length to %" B_PRIu32 " clusters\n", 258 clusters)); 259 if ((err = set_fat_chain_length(vol, node, clusters, false)) 260 == B_OK) { 261 node->st_size = st->st_size; 262 node->iteration++; 263 dirty = true; 264 file_cache_set_size(node->cache, node->st_size); 265 file_map_set_size(node->file_map, node->st_size); 266 } 267 } 268 } 269 270 if ((mask & B_STAT_MODIFICATION_TIME) != 0) { 271 DPRINTF(0, ("setting modification time\n")); 272 if ((node->mode & FAT_SUBDIR) == 0) 273 node->mode |= FAT_ARCHIVE; 274 node->st_time = st->st_mtime; 275 dirty = true; 276 } 277 278 if ((mask & B_STAT_CREATION_TIME) != 0) { 279 DPRINTF(0, ("setting creation time\n")); 280 // As a file's modification time is also set when it is created, 281 // the archive bit should be set automatically. 282 node->st_crtim = st->st_crtime; 283 dirty = true; 284 } 285 286 if (dirty) { 287 write_vnode_entry(vol, node); 288 289 if (vol->fs_flags & FS_FLAGS_OP_SYNC) { 290 // sync the filesystem 291 _dosfs_sync(vol); 292 node->dirty = false; 293 } 294 } 295 296 if (err != B_OK) 297 DPRINTF(0, ("dosfs_wstat (%s)\n", strerror(err))); 298 299 return err; 300} 301 302 303status_t 304dosfs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **_cookie) 305{ 306 status_t result = EINVAL; 307 nspace *vol = (nspace *)_vol->private_volume; 308 vnode* node = (vnode*)_node->private_node; 309 310 *_cookie = NULL; 311 312 RecursiveLocker lock(vol->vlock); 313 314 DPRINTF(0, ("dosfs_open: vnode id %" B_PRIdINO ", omode %o\n", node->vnid, 315 omode)); 316 317 if (omode & O_CREAT) { 318 dprintf("dosfs_open called with O_CREAT. call dosfs_create instead!\n"); 319 return EINVAL; 320 } 321 322 if ((vol->flags & B_FS_IS_READONLY) 323 || (node->mode & FAT_READ_ONLY) 324 || (node->disk_image != 0) 325 // allow opening directories for ioctl() calls 326 // and to let BVolume to work 327 || (node->mode & FAT_SUBDIR)) { 328 omode = (omode & ~O_RWMASK) | O_RDONLY; 329 } 330 331 if ((omode & O_TRUNC) && ((omode & O_RWMASK) == O_RDONLY)) { 332 DPRINTF(0, ("can't open file for reading with O_TRUNC\n")); 333 return EPERM; 334 } 335 336 if (omode & O_TRUNC) { 337 DPRINTF(0, ("dosfs_open called with O_TRUNC set\n")); 338 if ((result = set_fat_chain_length(vol, node, 0, false)) != B_OK) { 339 dprintf("dosfs_open: error truncating file\n"); 340 return result; 341 } 342 node->mode = 0; 343 node->st_size = 0; 344 node->iteration++; 345 } 346 347 filecookie *cookie = (filecookie*)calloc(sizeof(filecookie), 1); 348 if (cookie == NULL) 349 return ENOMEM; 350 351 cookie->mode = omode; 352 *_cookie = cookie; 353 result = B_OK; 354 355 return result; 356} 357 358 359status_t 360dosfs_read(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 361 void *buf, size_t *len) 362{ 363 nspace *vol = (nspace *)_vol->private_volume; 364 vnode *node = (vnode *)_node->private_node; 365 filecookie *cookie = (filecookie *)_cookie; 366 int result = B_OK; 367 368 RecursiveLocker lock(vol->vlock); 369 370 if (node->mode & FAT_SUBDIR) { 371 DPRINTF(0, ("dosfs_read called on subdirectory %" B_PRIdINO "\n", 372 node->vnid)); 373 *len = 0; 374 return EISDIR; 375 } 376 377 DPRINTF(0, ("dosfs_read called %" B_PRIuSIZE " bytes at %" B_PRIdOFF 378 " (vnode id %" B_PRIdINO ")\n", *len, pos, node->vnid)); 379 380 lock.Unlock(); 381 382 result = file_cache_read(node->cache, cookie, pos, buf, len); 383 384 if (result != B_OK) { 385 DPRINTF(0, ("dosfs_read (%s)\n", strerror(result))); 386 } else { 387 DPRINTF(0, ("dosfs_read: read %" B_PRIuSIZE " bytes\n", *len)); 388 } 389 390 return result; 391} 392 393 394status_t 395dosfs_write(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 396 const void *buf, size_t *len) 397{ 398 nspace *vol = (nspace *)_vol->private_volume; 399 vnode *node = (vnode *)_node->private_node; 400 filecookie *cookie = (filecookie *)_cookie; 401 int result = B_OK; 402 403 RecursiveLocker lock(vol->vlock); 404 405 if ((vol->flags & B_FS_IS_READONLY) != 0) 406 return EROFS; 407 408 if (node->mode & FAT_SUBDIR) { 409 DPRINTF(0, ("dosfs_write called on subdirectory %" B_PRIdINO "\n", 410 node->vnid)); 411 *len = 0; 412 return EISDIR; 413 } 414 415 DPRINTF(0, ("dosfs_write called %" B_PRIuSIZE " bytes at %" B_PRIdOFF 416 " from buffer at %p (vnode id %" B_PRIdINO ")\n", *len, pos, buf, 417 node->vnid)); 418 419 if ((cookie->mode & O_RWMASK) == O_RDONLY) { 420 dprintf("dosfs_write: called on file opened as read-only\n"); 421 *len = 0; 422 return EPERM; 423 } 424 425 if (pos < 0) pos = 0; 426 427 if (cookie->mode & O_APPEND) { 428 pos = node->st_size; 429 } 430 431 if (pos >= MAX_FILE_SIZE) { 432 dprintf("dosfs_write: write position exceeds fat limits\n"); 433 *len = 0; 434 return E2BIG; 435 } 436 437 if (pos + *len >= MAX_FILE_SIZE) { 438 *len = (size_t)(MAX_FILE_SIZE - pos); 439 } 440 441 // extend file size if needed 442 if (pos + (off_t)*len > (off_t)node->st_size) { 443 uint32 clusters = (pos + *len + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / vol->bytes_per_sector / vol->sectors_per_cluster; 444 if (node->st_size <= (clusters - 1) * vol->sectors_per_cluster * vol->bytes_per_sector) { 445 if ((result = set_fat_chain_length(vol, node, clusters, false)) 446 != B_OK) { 447 return result; 448 } 449 node->iteration++; 450 } 451 node->st_size = pos + *len; 452 /* needs to be written to disk asap so that later vnid calculations 453 * by get_next_dirent are correct 454 */ 455 write_vnode_entry(vol, node); 456 457 DPRINTF(0, ("setting file size to %" B_PRIdOFF " (%" B_PRIu32 458 " clusters)\n", node->st_size, clusters)); 459 node->dirty = true; 460 file_cache_set_size(node->cache, node->st_size); 461 file_map_set_size(node->file_map, node->st_size); 462 } 463 464 result = _update_last_modified(vol, node, false); 465 if (result != B_OK) { 466 dprintf("dosfs_write: failed to update last-modified time for %s (%s)\n", 467 node->filename, strerror(result)); 468 } 469 470 lock.Unlock(); 471 result = file_cache_write(node->cache, cookie, pos, buf, len); 472 return result; 473} 474 475 476status_t 477dosfs_close(fs_volume *_vol, fs_vnode *_node, void *_cookie) 478{ 479 nspace *vol = (nspace *)_vol->private_volume; 480 vnode *node = (vnode *)_node->private_node; 481 482 RecursiveLocker lock(vol->vlock); 483 484 DPRINTF(0, ("dosfs_close (vnode id %" B_PRIdINO ")\n", node->vnid)); 485 486 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) { 487 _dosfs_sync(vol); 488 node->dirty = false; 489 } 490 491 return 0; 492} 493 494 495status_t 496dosfs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie) 497{ 498 nspace *vol = (nspace *)_vol->private_volume; 499 vnode *node = (vnode *)_node->private_node; 500 filecookie *cookie = (filecookie *)_cookie; 501 502 RecursiveLocker lock(vol->vlock); 503 504 DPRINTF(0, ("dosfs_free_cookie (vnode id %" B_PRIdINO ")\n", node->vnid)); 505 506 free(cookie); 507 508 return B_OK; 509} 510 511 512status_t 513dosfs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode, 514 int perms, void **_cookie, ino_t *vnid) 515{ 516 nspace *vol = (nspace *)_vol->private_volume; 517 vnode *dir = (vnode *)_dir->private_node, *file; 518 filecookie *cookie; 519 status_t result = EINVAL; 520 bool dups_exist; 521 522 RecursiveLocker lock(vol->vlock); 523 524 ASSERT(name != NULL); 525 if (name == NULL) { 526 dprintf("dosfs_create called with null name\n"); 527 return EINVAL; 528 } 529 530 DPRINTF(0, ("dosfs_create called: %" B_PRIdINO "/%s perms=%o omode=%o\n", 531 dir->vnid, name, perms, omode)); 532 533 if (vol->flags & B_FS_IS_READONLY) { 534 dprintf("dosfs_create called on read-only volume\n"); 535 return EROFS; 536 } 537 538 // TODO : is it needed ? vfs job ? 539 /*if (is_vnode_removed(vol->id, dir->vnid) > 0) { 540 dprintf("dosfs_create() called in removed directory. disallowed.\n"); 541 return EPERM; 542 }*/ 543 544 if ((omode & O_RWMASK) == O_RDONLY) { 545 dprintf("invalid permissions used in creating file\n"); 546 return EPERM; 547 } 548 549 // create file cookie; do it here to make cleaning up easier 550 cookie = (filecookie *)calloc(sizeof(filecookie), 1); 551 MemoryDeleter cookieDeleter(cookie); 552 if (cookie == NULL) 553 return ENOMEM; 554 555 result = findfile_case_duplicates(vol, dir, name, vnid, &file, &dups_exist); 556 if (result == B_OK) { 557 if (omode & O_EXCL) { 558 dprintf("exclusive dosfs_create called on existing file %s\n", name); 559 put_vnode(_vol, file->vnid); 560 return EEXIST; 561 } 562 563 if (file->mode & FAT_SUBDIR) { 564 dprintf("can't dosfs_create over an existing subdirectory\n"); 565 put_vnode(_vol, file->vnid); 566 return EPERM; 567 } 568 569 if (file->disk_image) { 570 dprintf("can't dosfs_create over a disk image\n"); 571 put_vnode(_vol, file->vnid); 572 return EPERM; 573 } 574 575 if (omode & O_TRUNC) { 576 set_fat_chain_length(vol, file, 0, false); 577 file->st_size = 0; 578 file->iteration++; 579 } 580 } else if (result == ENOENT && dups_exist) { 581 // the file doesn't exist in the exact case, but another does in the 582 // non-exact case. We wont create the new file. 583 return EEXIST; 584 } else if (result == ENOENT && !dups_exist) { 585 // the file doesn't already exist in any case 586 vnode dummy; /* used only to create directory entry */ 587 588 dummy.dir_vnid = dir->vnid; 589 dummy.cluster = 0; 590 dummy.end_cluster = 0; 591 dummy.mode = 0; 592 dummy.st_size = 0; 593 time(&(dummy.st_time)); 594 dummy.st_crtim = dummy.st_time; 595 596 if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) { 597 dprintf("dosfs_create: error creating directory entry for %s (%s)\n", name, strerror(result)); 598 return result; 599 } 600 601 result = _update_last_modified(vol, dir, true); 602 if (result != B_OK) { 603 dprintf("dosfs_create: failed to update last-modified time of " 604 " parent directory %s after creating directory entry (%s)\n", 605 dir->filename, strerror(result)); 606 } 607 608 dummy.vnid = GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex); 609 // XXX: dangerous construct 610 if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) { 611 dummy.vnid = generate_unique_vnid(vol); 612 if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex))) < 0) { 613 // XXX: should remove entry on failure 614 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 615 _dosfs_sync(vol); 616 return result; 617 } 618 } 619 *vnid = dummy.vnid; 620 621 result = get_vnode(_vol, *vnid, (void **)&file); 622 if (result < B_OK) { 623 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 624 _dosfs_sync(vol); 625 return result; 626 } 627 } else { 628 return result; 629 } 630 631 cookie->mode = omode; 632 *_cookie = cookie; 633 cookieDeleter.Detach(); 634 635 notify_entry_created(vol->id, dir->vnid, name, *vnid); 636 637 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 638 _dosfs_sync(vol); 639 640 return B_OK; 641} 642 643 644status_t 645dosfs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms) 646{ 647 nspace *vol = (nspace *)_vol->private_volume; 648 vnode *dir = (vnode *)_dir->private_node, dummy; 649 status_t result = EINVAL; 650 struct csi csi; 651 uchar *buffer; 652 uint32 i; 653 654 recursive_lock_lock(&vol->vlock); 655 656 // TODO : is it needed ? vfs job ? 657 /*if (is_vnode_removed(vol->id, dir->vnid) > 0) { 658 dprintf("dosfs_mkdir() called in removed directory. disallowed.\n"); 659 recursive_lock_unlock(&vol->vlock); 660 return EPERM; 661 }*/ 662 663 DPRINTF(0, ("dosfs_mkdir called: %" B_PRIdINO "/%s (perm %o)\n", dir->vnid, 664 name, perms)); 665 666 if ((dir->mode & FAT_SUBDIR) == 0) { 667 dprintf("dosfs_mkdir: vnode id %" B_PRIdINO " is not a directory\n", 668 dir->vnid); 669 recursive_lock_unlock(&vol->vlock); 670 return EINVAL; 671 } 672 673 // S_IFDIR is never set in perms, so we patch it 674 perms &= ~S_IFMT; perms |= S_IFDIR; 675 676 if (vol->flags & B_FS_IS_READONLY) { 677 dprintf("mkdir called on read-only volume\n"); 678 recursive_lock_unlock(&vol->vlock); 679 return EROFS; 680 } 681 682 /* only used to create directory entry */ 683 dummy.dir_vnid = dir->vnid; 684 if ((result = allocate_n_fat_entries(vol, 1, (int32 *)&(dummy.cluster))) < 0) { 685 dprintf("dosfs_mkdir: error allocating space for %s (%s))\n", name, strerror(result)); 686 goto bi; 687 } 688 dummy.end_cluster = dummy.cluster; 689 dummy.mode = FAT_SUBDIR; 690 if (!(perms & (S_IWUSR | S_IWGRP | S_IWOTH))) { 691 dummy.mode |= FAT_READ_ONLY; 692 } 693 dummy.st_size = vol->bytes_per_sector*vol->sectors_per_cluster; 694 time(&(dummy.st_time)); 695 dummy.st_crtim = dummy.st_time; 696 697 dummy.vnid = GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster); 698 // XXX: dangerous construct 699 if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) { 700 dummy.vnid = generate_unique_vnid(vol); 701 if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster))) < 0) 702 goto bi2; 703 } 704 705 if ((result = dlist_add(vol, dummy.vnid)) < 0) { 706 dprintf("dosfs_mkdir: error adding directory %s to dlist (%s)\n", name, strerror(result)); 707 goto bi3; 708 } 709 710 buffer = (uchar *)malloc(vol->bytes_per_sector); 711 if (!buffer) { 712 result = ENOMEM; 713 goto bi4; 714 } 715 716 if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) { 717 dprintf("dosfs_mkdir: error creating directory entry for %s (%s))\n", name, strerror(result)); 718 goto bi5; 719 } 720 721 // create '.' and '..' entries and then end of directories 722 memset(buffer, 0, vol->bytes_per_sector); 723 memset(buffer, ' ', 11); 724 memset(buffer+0x20, ' ', 11); 725 buffer[0] = buffer[0x20] = buffer[0x21] = '.'; 726 buffer[0x0b] = buffer[0x2b] = FAT_SUBDIR; 727 i = time_t2dos(dummy.st_time); 728 buffer[0x0e] = i & 0xff; 729 buffer[0x0f] = (i >> 8) & 0xff; 730 buffer[0x10] = (i >> 16) & 0xff; 731 buffer[0x11] = (i >> 24) & 0xff; 732 buffer[0x16] = i & 0xff; 733 buffer[0x17] = (i >> 8) & 0xff; 734 buffer[0x18] = (i >> 16) & 0xff; 735 buffer[0x19] = (i >> 24) & 0xff; 736 i = time_t2dos(dir->st_crtim); 737 buffer[0x2e] = i & 0xff; 738 buffer[0x2f] = (i >> 8) & 0xff; 739 buffer[0x30] = (i >> 16) & 0xff; 740 buffer[0x31] = (i >> 24) & 0xff; 741 i = time_t2dos(dir->st_time); 742 buffer[0x36] = i & 0xff; 743 buffer[0x37] = (i >> 8) & 0xff; 744 buffer[0x38] = (i >> 16) & 0xff; 745 buffer[0x39] = (i >> 24) & 0xff; 746 buffer[0x1a] = dummy.cluster & 0xff; 747 buffer[0x1b] = (dummy.cluster >> 8) & 0xff; 748 if (vol->fat_bits == 32) { 749 buffer[0x14] = (dummy.cluster >> 16) & 0xff; 750 buffer[0x15] = (dummy.cluster >> 24) & 0xff; 751 } 752 // root directory is always denoted by cluster 0, even for fat32 (!) 753 if (dir->vnid != vol->root_vnode.vnid) { 754 buffer[0x3a] = dir->cluster & 0xff; 755 buffer[0x3b] = (dir->cluster >> 8) & 0xff; 756 if (vol->fat_bits == 32) { 757 buffer[0x34] = (dir->cluster >> 16) & 0xff; 758 buffer[0x35] = (dir->cluster >> 24) & 0xff; 759 } 760 } 761 762 init_csi(vol, dummy.cluster, 0, &csi); 763 csi_write_block(&csi, buffer); 764 765 // clear out rest of cluster to keep scandisk happy 766 memset(buffer, 0, vol->bytes_per_sector); 767 for (i=1;i<vol->sectors_per_cluster;i++) { 768 if (iter_csi(&csi, 1) != B_OK) { 769 dprintf("dosfs_mkdir: error writing directory cluster\n"); 770 break; 771 } 772 csi_write_block(&csi, buffer); 773 } 774 775 free(buffer); 776 777 notify_entry_created(vol->id, dir->vnid, name, dummy.vnid); 778 779 result = B_OK; 780 781 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 782 _dosfs_sync(vol); 783 784 recursive_lock_unlock(&vol->vlock); 785 return result; 786 787bi5: 788 free(buffer); 789bi4: 790 dlist_remove(vol, dummy.vnid); 791bi3: 792 if (IS_ARTIFICIAL_VNID(dummy.vnid)) 793 remove_from_vcache(vol, dummy.vnid); 794bi2: 795 clear_fat_chain(vol, dummy.cluster, false); 796 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 797 _dosfs_sync(vol); 798bi: 799 recursive_lock_unlock(&vol->vlock); 800 if (result != B_OK) DPRINTF(0, ("dosfs_mkdir (%s)\n", strerror(result))); 801 return result; 802} 803 804 805status_t 806dosfs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname, 807 fs_vnode *_ndir, const char *newname) 808{ 809 status_t result = EINVAL; 810 nspace *vol = (nspace *)_vol->private_volume; 811 vnode *odir = (vnode *)_odir->private_node; 812 vnode *ndir = (vnode *)_ndir->private_node; 813 vnode *file, *file2; 814 uint32 ns, ne; 815 bool dups_exist; 816 bool dirty = false; 817 818 RecursiveLocker lock(vol->vlock); 819 820 DPRINTF(0, ("dosfs_rename called: %" B_PRIdINO "/%s->%" B_PRIdINO "/%s\n", 821 odir->vnid, oldname, ndir->vnid, newname)); 822 823 if (!oldname || !(*oldname) || !newname || !(*newname)) 824 return EINVAL; 825 826 if(!is_filename_legal(newname)) { 827 dprintf("dosfs_rename called with invalid name '%s'\n", newname); 828 return EINVAL; 829 } 830 831 if (vol->flags & B_FS_IS_READONLY) { 832 dprintf("rename called on read-only volume\n"); 833 return EROFS; 834 } 835 836 if ((odir->vnid == ndir->vnid) && !strcmp(oldname, newname)) { 837 return EPERM; 838 } 839 840 // locate the file 841 if ((result = findfile_case(vol,odir,oldname,NULL,&file)) != B_OK) { 842 DPRINTF(0, ("dosfs_rename: can't find file %s in directory %" B_PRIdINO 843 "\n", oldname, odir->vnid)); 844 return result; 845 } 846 847 if (file->disk_image) { 848 dprintf("rename called on disk image or disk image directory\n"); 849 return EPERM; 850 } 851 852 // don't move a directory into one of its children 853 if (file->mode & FAT_SUBDIR) { 854 ino_t vnid = ndir->vnid; 855 while (1) { 856 vnode *dir; 857 ino_t parent; 858 859 if (vnid == file->vnid) { 860 result = EINVAL; 861 goto bi1; 862 } 863 864 if (vnid == vol->root_vnode.vnid) 865 break; 866 867 result = get_vnode(_vol, vnid, (void **)&dir); 868 if (result < B_OK) 869 goto bi1; 870 parent = dir->dir_vnid; 871 put_vnode(_vol, vnid); 872 vnid = parent; 873 } 874 } 875 876 // see if file already exists and erase it if it does 877 result = findfile_case_duplicates(vol, ndir, newname, NULL, &file2, &dups_exist); 878 if (result == B_OK) { 879 if (file2->mode & FAT_SUBDIR) { 880 dprintf("destination already occupied by a directory\n"); 881 result = EPERM; 882 goto bi2; 883 } 884 885 if (file2->disk_image) { 886 DPRINTF(0, ("dosfs_rename: can't replace disk image or disk image directory\n")); 887 result = EPERM; 888 goto bi2; 889 } 890 ns = file2->sindex; ne = file2->eindex; 891 892 // let others know the old file is gone 893 notify_entry_removed(vol->id, ndir->vnid, oldname, file2->vnid); 894 895 // Make sure this vnode 1) is in the vcache and 2) no longer has a 896 // location associated with it. See discussion in dosfs_unlink() 897 vcache_set_entry(vol, file2->vnid, generate_unique_vnid(vol)); 898 899 // mark vnode for removal (dosfs_remove_vnode will clear the fat chain) 900 // note we don't have to lock the file because the fat chain doesn't 901 // get wiped from the disk until dosfs_remove_vnode() is called; we'll 902 // have a phantom chain in effect until the last file is closed. 903 remove_vnode(_vol, file2->vnid); // must be done in this order 904 put_vnode(_vol, file2->vnid); 905 906 dirty = true; 907 908 // erase old directory entry 909 if ((result = erase_dir_entry(vol, file)) != B_OK) { 910 dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result)); 911 goto bi1; 912 } 913 } else if (result == ENOENT && (!dups_exist || (odir->vnid == ndir->vnid && !strcasecmp(oldname, newname)))) { 914 // there isn't an entry and there are no duplicates in the target dir or 915 // there isn't an entry and the target dir is the same as the source dir and 916 // the source and target name are the same, case-insensitively 917 918 // erase old directory entry 919 if ((result = erase_dir_entry(vol, file)) != B_OK) { 920 dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result)); 921 goto bi1; 922 } 923 924 dirty = true; 925 926 // create the new directory entry 927 if ((result = create_dir_entry(vol, ndir, file, newname, &ns, &ne)) != B_OK) { 928 dprintf("dosfs_rename: error creating directory entry for %s\n", newname); 929 goto bi1; 930 } 931 } else if (result == ENOENT && dups_exist) { 932 // the entry doesn't exist but a non-case entry does, so we can't do it 933 result = EEXIST; 934 goto bi1; 935 } else { 936 goto bi1; 937 } 938 939 // shrink the directory (an error here is not disastrous) 940 compact_directory(vol, odir); 941 942 dirty = true; 943 944 // update vnode information 945 file->dir_vnid = ndir->vnid; 946 file->sindex = ns; 947 file->eindex = ne; 948 if (odir->vnid != ndir->vnid) { 949 result = _update_last_modified(vol, odir, true); 950 if (result != B_OK) { 951 dprintf("dosfs_rename: failed to update last-modified time of original directory " 952 "%s (%s)\n", odir->filename, strerror(result)); 953 return result; 954 } 955 result = _update_last_modified(vol, ndir, true); 956 if (result != B_OK) { 957 dprintf("dosfs_rename: failed to update last-modified time of new directory " 958 "%s (%s)\n", ndir->filename, strerror(result)); 959 return result; 960 } 961 } 962 963 // update vcache 964 vcache_set_entry(vol, file->vnid, 965 (file->st_size) ? 966 GENERATE_DIR_CLUSTER_VNID(file->dir_vnid, file->cluster) : 967 GENERATE_DIR_INDEX_VNID(file->dir_vnid, file->sindex)); 968 969 // XXX: only write changes in the directory entry if needed 970 // (i.e. old entry, not new) 971 write_vnode_entry(vol, file); 972 973 if (file->mode & FAT_SUBDIR) { 974 // update '..' directory entry if needed 975 // this should most properly be in write_vnode, but it is safe 976 // to keep it here since this is the only way the cluster of 977 // the parent can change. 978 struct diri diri; 979 uint8 *buffer; 980 if ((buffer = diri_init(vol, file->cluster, 1, &diri)) == NULL) { 981 dprintf("error opening directory :(\n"); 982 result = EIO; 983 goto bi2; 984 } 985 986 diri_make_writable(&diri); 987 988 if (memcmp(buffer, ".. ", 11)) { 989 dprintf("invalid directory :(\n"); 990 result = EIO; 991 goto bi2; 992 } 993 if (ndir->vnid == vol->root_vnode.vnid) { 994 // root directory always has cluster = 0 995 buffer[0x1a] = buffer[0x1b] = 0; 996 } else { 997 buffer[0x1a] = ndir->cluster & 0xff; 998 buffer[0x1b] = (ndir->cluster >> 8) & 0xff; 999 if (vol->fat_bits == 32) { 1000 buffer[0x14] = (ndir->cluster >> 16) & 0xff; 1001 buffer[0x15] = (ndir->cluster >> 24) & 0xff; 1002 } 1003 } 1004 } 1005 1006 if (file->filename) free(file->filename); 1007 file->filename = (char*)malloc(strlen(newname) + 1); 1008 if (file->filename) strcpy(file->filename, newname); 1009 1010 notify_entry_moved(vol->id, odir->vnid, oldname, ndir->vnid, newname, 1011 file->vnid); 1012 1013 // update MIME information 1014 if(!(file->mode & FAT_SUBDIR)) { 1015 set_mime_type(file, newname); 1016 notify_attribute_changed(vol->id, -1, file->vnid, "BEOS:TYPE", 1017 B_ATTR_CHANGED); 1018 } 1019 1020 result = 0; 1021 1022bi2: 1023 if (result != B_OK) 1024 put_vnode(_vol, file2->vnid); 1025bi1: 1026 put_vnode(_vol, file->vnid); 1027 if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && dirty) 1028 _dosfs_sync(vol); 1029 if (result != B_OK) DPRINTF(0, ("dosfs_rename (%s)\n", strerror(result))); 1030 return result; 1031} 1032 1033 1034status_t 1035dosfs_remove_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter) 1036{ 1037 nspace *vol = (nspace *)_vol->private_volume; 1038 vnode *node = (vnode *)_node->private_node; 1039 1040 RecursiveLocker lock(vol->vlock); 1041 1042 DPRINTF(0, ("dosfs_remove_vnode (%" B_PRIdINO ")\n", node->vnid)); 1043 1044 if (vol->flags & B_FS_IS_READONLY) { 1045 dprintf("dosfs_remove_vnode: read-only volume\n"); 1046 return EROFS; 1047 } 1048 1049 status_t result = _update_parent_last_modified(_vol, _node); 1050 if (result != B_OK) { 1051 dprintf("dosfs_remove_vnode: failed to update directory last-modified time when " 1052 "deleting %s (%s)\n", node->filename, strerror(result)); 1053 return result; 1054 } 1055 1056 // clear the fat chain 1057 ASSERT((node->cluster == 0) || IS_DATA_CLUSTER(node->cluster)); 1058 /* XXX: the following assertion was tripped */ 1059 ASSERT((node->cluster != 0) || (node->st_size == 0)); 1060 if (node->cluster != 0) 1061 clear_fat_chain(vol, node->cluster, (node->mode & FAT_SUBDIR) != 0); 1062 1063 /* remove vnode id from the cache */ 1064 if (find_vnid_in_vcache(vol, node->vnid) == B_OK) 1065 remove_from_vcache(vol, node->vnid); 1066 1067 /* at this point, the node shouldn't be in the dlist anymore */ 1068 if ((node->mode & FAT_SUBDIR) != 0) { 1069 ASSERT(dlist_find(vol, CLUSTER_OF_DIR_CLUSTER_VNID(node->vnid)) == -1); 1070 } 1071 1072 free(node); 1073 1074 if (!reenter && vol->fs_flags & FS_FLAGS_OP_SYNC) { 1075 // sync the entire filesystem, 1076 // but only if we're not reentrant. Presumably the 1077 // function that called this will sync. 1078 _dosfs_sync(vol); 1079 } 1080 1081 return B_OK; 1082} 1083 1084 1085// get rid of node or directory 1086static status_t 1087do_unlink(fs_volume *_vol, fs_vnode *_dir, const char *name, bool is_file) 1088{ 1089 status_t result = EINVAL; 1090 nspace *vol = (nspace *)_vol->private_volume; 1091 vnode *dir = (vnode *)_dir->private_node, *file; 1092 ino_t vnid; 1093 1094 if (!strcmp(name, ".")) 1095 return EPERM; 1096 1097 if (!strcmp(name, "..")) 1098 return EPERM; 1099 1100 RecursiveLocker lock(vol->vlock); 1101 1102 DPRINTF(0, ("do_unlink %" B_PRIdINO "/%s\n", dir->vnid, name)); 1103 1104 if (vol->flags & B_FS_IS_READONLY) { 1105 dprintf("do_unlink: read-only volume\n"); 1106 return EROFS; 1107 } 1108 1109 // locate the file 1110 if ((result = findfile_case(vol,dir,name,&vnid,&file)) != B_OK) { 1111 DPRINTF(0, ("do_unlink: can't find file %s in directory %" B_PRIdINO 1112 "\n", name, dir->vnid)); 1113 return ENOENT; 1114 } 1115 1116 if (file->disk_image) { 1117 DPRINTF(0, ("do_unlink: can't unlink disk image or disk image directory\n")); 1118 result = EPERM; 1119 goto bi1; 1120 } 1121 1122 // don't need to check file permissions because it will be done for us 1123 // also don't need to lock the file (see dosfs_rename for reasons why) 1124 if (is_file) { 1125 if (file->mode & FAT_SUBDIR) { 1126 result = EISDIR; 1127 goto bi1; 1128 } 1129 } else { 1130 if ((file->mode & FAT_SUBDIR) == 0) { 1131 result = ENOTDIR; 1132 goto bi1; 1133 } 1134 1135 if (file->vnid == vol->root_vnode.vnid) { 1136 // this actually isn't a problem since the root vnode 1137 // will always be busy while the volume mounted 1138 dprintf("dosfs_rmdir: don't call this on the root directory\n"); 1139 result = EPERM; 1140 goto bi1; 1141 } 1142 1143 if ((result = check_dir_empty(vol, file)) < 0) { 1144 if (result == ENOTEMPTY) DPRINTF(0, ("dosfs_rmdir called on non-empty directory\n")); 1145 goto bi1; 1146 } 1147 } 1148 1149 // erase the entry in the parent directory 1150 if ((result = erase_dir_entry(vol, file)) != B_OK) 1151 goto bi1; 1152 1153 // shrink the parent directory (errors here are not disastrous) 1154 compact_directory(vol, dir); 1155 1156 notify_entry_removed(vol->id, dir->vnid, name, file->vnid); 1157 1158 /* Set the loc to a unique value. This effectively removes it from the 1159 * vcache without releasing its vnid for reuse. It also nicely reserves 1160 * the vnid from use by other nodes. This is okay because the vnode is 1161 * locked in memory after this point and loc will not be referenced from 1162 * here on. 1163 */ 1164 vcache_set_entry(vol, file->vnid, generate_unique_vnid(vol)); 1165 1166 if (!is_file) 1167 dlist_remove(vol, file->vnid); 1168 1169 // fsil doesn't call dosfs_write_vnode for us, so we have to free the 1170 // vnode manually here. 1171 remove_vnode(_vol, file->vnid); 1172 1173 result = 0; 1174 1175 if (vol->fs_flags & FS_FLAGS_OP_SYNC) 1176 _dosfs_sync(vol); 1177 1178bi1: 1179 put_vnode(_vol, vnid); // get 1 free 1180 if (result != B_OK) DPRINTF(0, ("do_unlink (%s)\n", strerror(result))); 1181 1182 return result; 1183} 1184 1185 1186status_t 1187dosfs_unlink(fs_volume *vol, fs_vnode *dir, const char *name) 1188{ 1189 DPRINTF(1, ("dosfs_unlink called\n")); 1190 1191 return do_unlink(vol, dir, name, true); 1192} 1193 1194 1195status_t 1196dosfs_rmdir(fs_volume *vol, fs_vnode *dir, const char *name) 1197{ 1198 DPRINTF(1, ("dosfs_rmdir called\n")); 1199 1200 return do_unlink(vol, dir, name, false); 1201} 1202 1203 1204bool 1205dosfs_can_page(fs_volume *_vol, fs_vnode *_node, void *_cookie) 1206{ 1207 // ToDo: we're obviously not even asked... 1208 return false; 1209} 1210 1211 1212status_t 1213dosfs_read_pages(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 1214 const iovec *vecs, size_t count, size_t *_numBytes) 1215{ 1216 nspace *vol = (nspace *)_vol->private_volume; 1217 vnode *node = (vnode *)_node->private_node; 1218 uint32 vecIndex = 0; 1219 size_t vecOffset = 0; 1220 size_t bytesLeft = *_numBytes; 1221 status_t status; 1222 1223 if (node->cache == NULL) 1224 return B_BAD_VALUE; 1225 1226 RecursiveLocker lock(vol->vlock); 1227 1228 while (true) { 1229 struct file_io_vec fileVecs[8]; 1230 size_t fileVecCount = 8; 1231 bool bufferOverflow; 1232 size_t bytes = bytesLeft; 1233 1234 status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs, 1235 &fileVecCount, 0); 1236 if (status != B_OK && status != B_BUFFER_OVERFLOW) 1237 break; 1238 1239 bufferOverflow = status == B_BUFFER_OVERFLOW; 1240 1241 status = read_file_io_vec_pages(vol->fd, fileVecs, 1242 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 1243 if (status != B_OK || !bufferOverflow) 1244 break; 1245 1246 pos += bytes; 1247 bytesLeft -= bytes; 1248 } 1249 1250 return status; 1251} 1252 1253 1254status_t 1255dosfs_write_pages(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, 1256 const iovec *vecs, size_t count, size_t *_numBytes) 1257{ 1258 nspace *vol = (nspace *)_vol->private_volume; 1259 vnode *node = (vnode *)_node->private_node; 1260 uint32 vecIndex = 0; 1261 size_t vecOffset = 0; 1262 size_t bytesLeft = *_numBytes; 1263 status_t status; 1264 1265 if (node->cache == NULL) 1266 return B_BAD_VALUE; 1267 1268 RecursiveLocker lock(vol->vlock); 1269 1270 if ((vol->flags & B_FS_IS_READONLY) != 0) 1271 return EROFS; 1272 1273 while (true) { 1274 struct file_io_vec fileVecs[8]; 1275 size_t fileVecCount = 8; 1276 bool bufferOverflow; 1277 size_t bytes = bytesLeft; 1278 1279 status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs, 1280 &fileVecCount, 0); 1281 if (status != B_OK && status != B_BUFFER_OVERFLOW) 1282 break; 1283 1284 bufferOverflow = status == B_BUFFER_OVERFLOW; 1285 1286 status = write_file_io_vec_pages(vol->fd, fileVecs, 1287 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 1288 if (status != B_OK || !bufferOverflow) 1289 break; 1290 1291 pos += bytes; 1292 bytesLeft -= bytes; 1293 } 1294 1295 return status; 1296} 1297 1298 1299status_t 1300dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t position, 1301 size_t length, struct file_io_vec *vecs, size_t *_count) 1302{ 1303 nspace *vol = (nspace *)_vol->private_volume; 1304 vnode *node = (vnode *)_node->private_node; 1305 struct csi iter; 1306 status_t result; 1307 uint32 skipSectors; 1308 off_t offset; 1309 size_t index = 0; 1310 size_t max = *_count; 1311 1312 RecursiveLocker lock(vol->vlock); 1313 *_count = 0; 1314 1315 if ((node->mode & FAT_SUBDIR) != 0) { 1316 DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO 1317 "\n", node->vnid)); 1318 return EISDIR; 1319 } 1320 1321 DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE " bytes at %" B_PRIdOFF 1322 " (vnode id %" B_PRIdINO ")\n", length, position, node->vnid)); 1323 1324 if (position < 0) 1325 position = 0; 1326 1327 if (node->st_size == 0 || length == 0 || position >= node->st_size) { 1328 return B_OK; 1329 } 1330 1331 // Truncate to file size, taking overflow into account. 1332 if (position + (off_t)length >= node->st_size || position + (off_t)length < position) 1333 length = node->st_size - position; 1334 1335 result = init_csi(vol, node->cluster, 0, &iter); 1336 if (result != B_OK) { 1337 dprintf("dosfs_get_file_map: invalid starting cluster (%" B_PRIu32 1338 ")\n", node->cluster); 1339 return EIO; 1340 } 1341 1342 skipSectors = position / vol->bytes_per_sector; 1343 if (skipSectors > 0) { 1344 result = iter_csi(&iter, skipSectors); 1345 if (result != B_OK) { 1346 dprintf("dosfs_get_file_map: end of file reached (init)\n"); 1347 return EIO; 1348 } 1349 } 1350 1351 ASSERT(iter.cluster == (uint32)get_nth_fat_entry(vol, node->cluster, 1352 position / vol->bytes_per_sector / vol->sectors_per_cluster)); 1353 1354 offset = position % vol->bytes_per_sector; 1355 while (length > 0) { 1356 off_t block = csi_to_block(&iter); 1357 uint32 sectors = 1; 1358 1359 length -= min_c((off_t)length, vol->bytes_per_sector - offset); 1360 1361 while (length > 0) { 1362 result = iter_csi(&iter, 1); 1363 if (result != B_OK) { 1364 dprintf("dosfs_get_file_map: end of file reached\n"); 1365 return EIO; 1366 } 1367 1368 if (block + sectors != csi_to_block(&iter)) { 1369 // Disjoint sectors, need to flush and begin a new vector. 1370 break; 1371 } 1372 1373 length -= min_c(length, vol->bytes_per_sector); 1374 sectors++; 1375 } 1376 1377 vecs[index].offset = block * vol->bytes_per_sector + offset; 1378 vecs[index].length = sectors * vol->bytes_per_sector - offset; 1379 index++; 1380 1381 if (length == 0) 1382 break; 1383 1384 if (index >= max) { 1385 // we're out of file_io_vecs; let's bail out 1386 *_count = index; 1387 return B_BUFFER_OVERFLOW; 1388 } 1389 1390 offset = 0; 1391 } 1392 1393 *_count = index; 1394 1395 return B_OK; 1396} 1397