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