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