1 /* 2 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #if FS_SHELL 11 # include "fssh_api_wrapper.h" 12 13 # include "hash.h" 14 # include "list.h" 15 # include "lock.h" 16 #else 17 # include <KernelExport.h> 18 # include <vfs.h> 19 # include <debug.h> 20 # include <khash.h> 21 # include <lock.h> 22 # include <vm.h> 23 24 # include <NodeMonitor.h> 25 26 # include <sys/stat.h> 27 # include <malloc.h> 28 # include <string.h> 29 # include <stdio.h> 30 #endif 31 32 33 #if FS_SHELL 34 using namespace FSShell; 35 # define user_strlcpy(to, from, len) (strlcpy(to, from, len), FSSH_B_OK) 36 #endif 37 38 39 //#define TRACE_ROOTFS 40 #ifdef TRACE_ROOTFS 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) 44 #endif 45 46 47 struct rootfs_stream { 48 mode_t type; 49 struct stream_dir { 50 struct rootfs_vnode *dir_head; 51 struct list cookies; 52 } dir; 53 struct stream_symlink { 54 char *path; 55 size_t length; 56 } symlink; 57 }; 58 59 struct rootfs_vnode { 60 struct rootfs_vnode *all_next; 61 ino_t id; 62 char *name; 63 time_t modification_time; 64 time_t creation_time; 65 uid_t uid; 66 gid_t gid; 67 struct rootfs_vnode *parent; 68 struct rootfs_vnode *dir_next; 69 struct rootfs_stream stream; 70 }; 71 72 struct rootfs { 73 dev_t id; 74 mutex lock; 75 ino_t next_vnode_id; 76 hash_table *vnode_list_hash; 77 struct rootfs_vnode *root_vnode; 78 }; 79 80 // dircookie, dirs are only types of streams supported by rootfs 81 struct rootfs_dir_cookie { 82 struct list_link link; 83 struct rootfs_vnode *current; 84 int32 state; // iteration state 85 }; 86 87 // directory iteration states 88 enum { 89 ITERATION_STATE_DOT = 0, 90 ITERATION_STATE_DOT_DOT = 1, 91 ITERATION_STATE_OTHERS = 2, 92 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT, 93 }; 94 95 #define ROOTFS_HASH_SIZE 16 96 97 98 static uint32 99 rootfs_vnode_hash_func(void *_v, const void *_key, uint32 range) 100 { 101 struct rootfs_vnode *vnode = (rootfs_vnode*)_v; 102 const ino_t *key = (const ino_t*)_key; 103 104 if (vnode != NULL) 105 return vnode->id % range; 106 107 return (uint64)*key % range; 108 } 109 110 111 static int 112 rootfs_vnode_compare_func(void *_v, const void *_key) 113 { 114 struct rootfs_vnode *v = (rootfs_vnode*)_v; 115 const ino_t *key = (const ino_t*)_key; 116 117 if (v->id == *key) 118 return 0; 119 120 return -1; 121 } 122 123 124 static struct rootfs_vnode * 125 rootfs_create_vnode(struct rootfs *fs, struct rootfs_vnode *parent, 126 const char *name, int type) 127 { 128 struct rootfs_vnode *vnode; 129 130 vnode = (rootfs_vnode*)malloc(sizeof(struct rootfs_vnode)); 131 if (vnode == NULL) 132 return NULL; 133 134 memset(vnode, 0, sizeof(struct rootfs_vnode)); 135 136 vnode->name = strdup(name); 137 if (vnode->name == NULL) { 138 free(vnode); 139 return NULL; 140 } 141 142 vnode->id = fs->next_vnode_id++; 143 vnode->stream.type = type; 144 vnode->creation_time = vnode->modification_time = time(NULL); 145 vnode->uid = geteuid(); 146 vnode->gid = parent ? parent->gid : getegid(); 147 // inherit group from parent if possible 148 149 if (S_ISDIR(type)) 150 list_init(&vnode->stream.dir.cookies); 151 152 return vnode; 153 } 154 155 156 static status_t 157 rootfs_delete_vnode(struct rootfs *fs, struct rootfs_vnode *v, bool force_delete) 158 { 159 // cant delete it if it's in a directory or is a directory 160 // and has children 161 if (!force_delete && (v->stream.dir.dir_head != NULL || v->dir_next != NULL)) 162 return EPERM; 163 164 // remove it from the global hash table 165 hash_remove(fs->vnode_list_hash, v); 166 167 free(v->name); 168 free(v); 169 170 return 0; 171 } 172 173 174 /* makes sure none of the dircookies point to the vnode passed in */ 175 176 static void 177 update_dir_cookies(struct rootfs_vnode *dir, struct rootfs_vnode *vnode) 178 { 179 struct rootfs_dir_cookie *cookie = NULL; 180 181 while ((cookie = (rootfs_dir_cookie*)list_get_next_item( 182 &dir->stream.dir.cookies, cookie)) != NULL) { 183 if (cookie->current == vnode) 184 cookie->current = vnode->dir_next; 185 } 186 } 187 188 189 static struct rootfs_vnode * 190 rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path) 191 { 192 struct rootfs_vnode *vnode; 193 194 if (!strcmp(path, ".")) 195 return dir; 196 if (!strcmp(path, "..")) 197 return dir->parent; 198 199 for (vnode = dir->stream.dir.dir_head; vnode; vnode = vnode->dir_next) { 200 if (strcmp(vnode->name, path) == 0) 201 return vnode; 202 } 203 return NULL; 204 } 205 206 207 static status_t 208 rootfs_insert_in_dir(struct rootfs *fs, struct rootfs_vnode *dir, 209 struct rootfs_vnode *vnode) 210 { 211 // make sure the directory stays sorted alphabetically 212 213 struct rootfs_vnode *node = dir->stream.dir.dir_head, *last = NULL; 214 while (node && strcmp(node->name, vnode->name) < 0) { 215 last = node; 216 node = node->dir_next; 217 } 218 if (last == NULL) { 219 // the new vnode is the first entry in the list 220 vnode->dir_next = dir->stream.dir.dir_head; 221 dir->stream.dir.dir_head = vnode; 222 } else { 223 // insert after that node 224 vnode->dir_next = last->dir_next; 225 last->dir_next = vnode; 226 } 227 228 vnode->parent = dir; 229 dir->modification_time = time(NULL); 230 231 notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME); 232 return B_OK; 233 } 234 235 236 static status_t 237 rootfs_remove_from_dir(struct rootfs *fs, struct rootfs_vnode *dir, 238 struct rootfs_vnode *findit) 239 { 240 struct rootfs_vnode *v; 241 struct rootfs_vnode *last_v; 242 243 for (v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { 244 if (v == findit) { 245 /* make sure all dircookies dont point to this vnode */ 246 update_dir_cookies(dir, v); 247 248 if (last_v) 249 last_v->dir_next = v->dir_next; 250 else 251 dir->stream.dir.dir_head = v->dir_next; 252 v->dir_next = NULL; 253 254 dir->modification_time = time(NULL); 255 notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME); 256 return B_OK; 257 } 258 } 259 return B_ENTRY_NOT_FOUND; 260 } 261 262 263 static bool 264 rootfs_is_dir_empty(struct rootfs_vnode *dir) 265 { 266 return !dir->stream.dir.dir_head; 267 } 268 269 270 /** You must hold the FS lock when calling this function */ 271 272 static status_t 273 remove_node(struct rootfs *fs, struct rootfs_vnode *directory, struct rootfs_vnode *vnode) 274 { 275 // schedule this vnode to be removed when it's ref goes to zero 276 status_t status = remove_vnode(fs->id, vnode->id); 277 if (status < B_OK) 278 return status; 279 280 rootfs_remove_from_dir(fs, directory, vnode); 281 notify_entry_removed(fs->id, directory->id, vnode->name, vnode->id); 282 283 return B_OK; 284 } 285 286 287 static status_t 288 rootfs_remove(struct rootfs *fs, struct rootfs_vnode *dir, const char *name, bool isDirectory) 289 { 290 struct rootfs_vnode *vnode; 291 status_t status = B_OK; 292 293 mutex_lock(&fs->lock); 294 295 vnode = rootfs_find_in_dir(dir, name); 296 if (!vnode) 297 status = B_ENTRY_NOT_FOUND; 298 else if (isDirectory && !S_ISDIR(vnode->stream.type)) 299 status = B_NOT_A_DIRECTORY; 300 else if (!isDirectory && S_ISDIR(vnode->stream.type)) 301 status = B_IS_A_DIRECTORY; 302 else if (isDirectory && !rootfs_is_dir_empty(vnode)) 303 status = B_DIRECTORY_NOT_EMPTY; 304 305 if (status < B_OK) 306 goto err; 307 308 status = remove_node(fs, dir, vnode); 309 310 err: 311 mutex_unlock(&fs->lock); 312 313 return status; 314 } 315 316 317 // #pragma mark - 318 319 320 static status_t 321 rootfs_mount(dev_t id, const char *device, uint32 flags, const char *args, 322 fs_volume *_fs, ino_t *root_vnid) 323 { 324 struct rootfs *fs; 325 struct rootfs_vnode *vnode; 326 status_t err; 327 328 TRACE(("rootfs_mount: entry\n")); 329 330 fs = (rootfs*)malloc(sizeof(struct rootfs)); 331 if (fs == NULL) 332 return B_NO_MEMORY; 333 334 fs->id = id; 335 fs->next_vnode_id = 1; 336 337 err = mutex_init(&fs->lock, "rootfs_mutex"); 338 if (err < B_OK) 339 goto err1; 340 341 fs->vnode_list_hash = hash_init(ROOTFS_HASH_SIZE, (addr_t)&vnode->all_next - (addr_t)vnode, 342 &rootfs_vnode_compare_func, &rootfs_vnode_hash_func); 343 if (fs->vnode_list_hash == NULL) { 344 err = B_NO_MEMORY; 345 goto err2; 346 } 347 348 // create the root vnode 349 vnode = rootfs_create_vnode(fs, NULL, ".", S_IFDIR | 0777); 350 if (vnode == NULL) { 351 err = B_NO_MEMORY; 352 goto err3; 353 } 354 vnode->parent = vnode; 355 356 fs->root_vnode = vnode; 357 hash_insert(fs->vnode_list_hash, vnode); 358 publish_vnode(id, vnode->id, vnode); 359 360 *root_vnid = vnode->id; 361 *_fs = fs; 362 363 return B_OK; 364 365 err3: 366 hash_uninit(fs->vnode_list_hash); 367 err2: 368 mutex_destroy(&fs->lock); 369 err1: 370 free(fs); 371 372 return err; 373 } 374 375 376 static status_t 377 rootfs_unmount(fs_volume _fs) 378 { 379 struct rootfs *fs = (struct rootfs *)_fs; 380 struct rootfs_vnode *v; 381 struct hash_iterator i; 382 383 TRACE(("rootfs_unmount: entry fs = %p\n", fs)); 384 385 // release the reference to the root 386 put_vnode(fs->id, fs->root_vnode->id); 387 388 // delete all of the vnodes 389 hash_open(fs->vnode_list_hash, &i); 390 while ((v = (struct rootfs_vnode *)hash_next(fs->vnode_list_hash, &i)) != NULL) { 391 rootfs_delete_vnode(fs, v, true); 392 } 393 hash_close(fs->vnode_list_hash, &i, false); 394 395 hash_uninit(fs->vnode_list_hash); 396 mutex_destroy(&fs->lock); 397 free(fs); 398 399 return B_OK; 400 } 401 402 403 static status_t 404 rootfs_sync(fs_volume fs) 405 { 406 TRACE(("rootfs_sync: entry\n")); 407 408 return B_OK; 409 } 410 411 412 static status_t 413 rootfs_lookup(fs_volume _fs, fs_vnode _dir, const char *name, ino_t *_id, int *_type) 414 { 415 struct rootfs *fs = (struct rootfs *)_fs; 416 struct rootfs_vnode *dir = (struct rootfs_vnode *)_dir; 417 struct rootfs_vnode *vnode,*vdummy; 418 status_t status; 419 420 TRACE(("rootfs_lookup: entry dir %p, name '%s'\n", dir, name)); 421 if (!S_ISDIR(dir->stream.type)) 422 return B_NOT_A_DIRECTORY; 423 424 mutex_lock(&fs->lock); 425 426 // look it up 427 vnode = rootfs_find_in_dir(dir, name); 428 if (!vnode) { 429 status = B_ENTRY_NOT_FOUND; 430 goto err; 431 } 432 433 status = get_vnode(fs->id, vnode->id, (fs_vnode *)&vdummy); 434 if (status < B_OK) 435 goto err; 436 437 *_id = vnode->id; 438 *_type = vnode->stream.type; 439 440 err: 441 mutex_unlock(&fs->lock); 442 443 return status; 444 } 445 446 447 static status_t 448 rootfs_get_vnode_name(fs_volume _fs, fs_vnode _vnode, char *buffer, size_t bufferSize) 449 { 450 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; 451 452 TRACE(("rootfs_get_vnode_name: vnode = %p (name = %s)\n", vnode, vnode->name)); 453 454 strlcpy(buffer, vnode->name, bufferSize); 455 return B_OK; 456 } 457 458 459 static status_t 460 rootfs_get_vnode(fs_volume _fs, ino_t id, fs_vnode *_vnode, bool reenter) 461 { 462 struct rootfs *fs = (struct rootfs *)_fs; 463 struct rootfs_vnode *vnode; 464 465 TRACE(("rootfs_getvnode: asking for vnode %Ld, r %d\n", id, reenter)); 466 467 if (!reenter) 468 mutex_lock(&fs->lock); 469 470 vnode = (rootfs_vnode*)hash_lookup(fs->vnode_list_hash, &id); 471 472 if (!reenter) 473 mutex_unlock(&fs->lock); 474 475 TRACE(("rootfs_getnvnode: looked it up at %p\n", *_vnode)); 476 477 if (vnode == NULL) 478 return B_ENTRY_NOT_FOUND; 479 480 *_vnode = vnode; 481 return B_OK; 482 } 483 484 485 static status_t 486 rootfs_put_vnode(fs_volume _fs, fs_vnode _vnode, bool reenter) 487 { 488 #ifdef TRACE_ROOTFS 489 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; 490 491 TRACE(("rootfs_putvnode: entry on vnode 0x%Lx, r %d\n", vnode->id, reenter)); 492 #endif 493 return B_OK; // whatever 494 } 495 496 497 static status_t 498 rootfs_remove_vnode(fs_volume _fs, fs_vnode _vnode, bool reenter) 499 { 500 struct rootfs *fs = (struct rootfs *)_fs; 501 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; 502 503 TRACE(("rootfs_remove_vnode: remove %p (0x%Lx), r %d\n", vnode, vnode->id, reenter)); 504 505 if (!reenter) 506 mutex_lock(&fs->lock); 507 508 if (vnode->dir_next) { 509 // can't remove node if it's linked to the dir 510 panic("rootfs_remove_vnode: vnode %p asked to be removed is present in dir\n", vnode); 511 } 512 513 rootfs_delete_vnode(fs, vnode, false); 514 515 if (!reenter) 516 mutex_unlock(&fs->lock); 517 518 return B_OK; 519 } 520 521 522 static status_t 523 rootfs_create(fs_volume _fs, fs_vnode _dir, const char *name, int omode, 524 int perms, fs_cookie *_cookie, ino_t *new_vnid) 525 { 526 return B_BAD_VALUE; 527 } 528 529 530 static status_t 531 rootfs_open(fs_volume _fs, fs_vnode _v, int oflags, fs_cookie *_cookie) 532 { 533 // allow to open the file, but it can't be done anything with it 534 535 *_cookie = NULL; 536 return B_OK; 537 } 538 539 540 static status_t 541 rootfs_close(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) 542 { 543 #ifdef TRACE_ROOTFS 544 struct rootfs_vnode *v = _v; 545 struct rootfs_cookie *cookie = _cookie; 546 547 TRACE(("rootfs_close: entry vnode %p, cookie %p\n", v, cookie)); 548 #endif 549 return B_OK; 550 } 551 552 553 static status_t 554 rootfs_free_cookie(fs_volume _fs, fs_vnode _v, fs_cookie _cookie) 555 { 556 return B_OK; 557 } 558 559 560 static status_t 561 rootfs_fsync(fs_volume _fs, fs_vnode _v) 562 { 563 return B_OK; 564 } 565 566 567 static status_t 568 rootfs_read(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, 569 off_t pos, void *buffer, size_t *_length) 570 { 571 return EINVAL; 572 } 573 574 575 static status_t 576 rootfs_write(fs_volume fs, fs_vnode vnode, fs_cookie cookie, 577 off_t pos, const void *buffer, size_t *_length) 578 { 579 TRACE(("rootfs_write: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", 580 vnode, cookie, pos, *_length)); 581 582 return EPERM; 583 } 584 585 586 static status_t 587 rootfs_create_dir(fs_volume _fs, fs_vnode _dir, const char *name, int mode, 588 ino_t *_newID) 589 { 590 struct rootfs *fs = (rootfs*)_fs; 591 struct rootfs_vnode *dir = (rootfs_vnode*)_dir; 592 struct rootfs_vnode *vnode; 593 status_t status = 0; 594 595 TRACE(("rootfs_create_dir: dir %p, name = '%s', perms = %d, id = 0x%Lx pointer id = %p\n", 596 dir, name, mode,*_newID, _newID)); 597 598 mutex_lock(&fs->lock); 599 600 vnode = rootfs_find_in_dir(dir, name); 601 if (vnode != NULL) { 602 status = B_FILE_EXISTS; 603 goto err; 604 } 605 606 TRACE(("rootfs_create: creating new vnode\n")); 607 vnode = rootfs_create_vnode(fs, dir, name, S_IFDIR | (mode & S_IUMSK)); 608 if (vnode == NULL) { 609 status = B_NO_MEMORY; 610 goto err; 611 } 612 613 rootfs_insert_in_dir(fs, dir, vnode); 614 hash_insert(fs->vnode_list_hash, vnode); 615 616 notify_entry_created(fs->id, dir->id, name, vnode->id); 617 618 mutex_unlock(&fs->lock); 619 return B_OK; 620 621 err: 622 mutex_unlock(&fs->lock); 623 624 return status; 625 } 626 627 628 static status_t 629 rootfs_remove_dir(fs_volume _fs, fs_vnode _dir, const char *name) 630 { 631 struct rootfs *fs = (rootfs*)_fs; 632 struct rootfs_vnode *dir = (rootfs_vnode*)_dir; 633 634 TRACE(("rootfs_remove_dir: dir %p (0x%Lx), name '%s'\n", dir, dir->id, name)); 635 636 return rootfs_remove(fs, dir, name, true); 637 } 638 639 640 static status_t 641 rootfs_open_dir(fs_volume _fs, fs_vnode _v, fs_cookie *_cookie) 642 { 643 struct rootfs *fs = (struct rootfs *)_fs; 644 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_v; 645 struct rootfs_dir_cookie *cookie; 646 647 TRACE(("rootfs_open: vnode %p\n", vnode)); 648 649 if (!S_ISDIR(vnode->stream.type)) 650 return B_BAD_VALUE; 651 652 cookie = (rootfs_dir_cookie*)malloc(sizeof(struct rootfs_dir_cookie)); 653 if (cookie == NULL) 654 return B_NO_MEMORY; 655 656 mutex_lock(&fs->lock); 657 658 cookie->current = vnode->stream.dir.dir_head; 659 cookie->state = ITERATION_STATE_BEGIN; 660 661 list_add_item(&vnode->stream.dir.cookies, cookie); 662 *_cookie = cookie; 663 664 mutex_unlock(&fs->lock); 665 666 return B_OK; 667 } 668 669 670 static status_t 671 rootfs_free_dir_cookie(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) 672 { 673 struct rootfs_dir_cookie *cookie = (rootfs_dir_cookie*)_cookie; 674 struct rootfs_vnode *vnode = (rootfs_vnode*)_vnode; 675 struct rootfs *fs = (rootfs*)_fs; 676 677 mutex_lock(&fs->lock); 678 list_remove_item(&vnode->stream.dir.cookies, cookie); 679 mutex_unlock(&fs->lock); 680 681 free(cookie); 682 return B_OK; 683 } 684 685 686 static status_t 687 rootfs_read_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num) 688 { 689 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode; 690 struct rootfs_dir_cookie *cookie = (rootfs_dir_cookie*)_cookie; 691 struct rootfs *fs = (rootfs*)_fs; 692 status_t status = B_OK; 693 struct rootfs_vnode *childNode = NULL; 694 const char *name = NULL; 695 struct rootfs_vnode *nextChildNode = NULL; 696 int nextState = cookie->state; 697 698 TRACE(("rootfs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _vnode, cookie, dirent, bufferSize,_num)); 699 700 mutex_lock(&fs->lock); 701 702 switch (cookie->state) { 703 case ITERATION_STATE_DOT: 704 childNode = vnode; 705 name = "."; 706 nextChildNode = vnode->stream.dir.dir_head; 707 nextState = cookie->state + 1; 708 break; 709 case ITERATION_STATE_DOT_DOT: 710 childNode = vnode->parent; 711 name = ".."; 712 nextChildNode = vnode->stream.dir.dir_head; 713 nextState = cookie->state + 1; 714 break; 715 default: 716 childNode = cookie->current; 717 if (childNode) { 718 name = childNode->name; 719 nextChildNode = childNode->dir_next; 720 } 721 break; 722 } 723 724 if (!childNode) { 725 // we're at the end of the directory 726 *_num = 0; 727 goto err; 728 } 729 730 dirent->d_dev = fs->id; 731 dirent->d_ino = childNode->id; 732 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 733 734 if (dirent->d_reclen > bufferSize) { 735 status = ENOBUFS; 736 goto err; 737 } 738 739 status = user_strlcpy(dirent->d_name, name, 740 bufferSize - sizeof(struct dirent)); 741 if (status < B_OK) 742 goto err; 743 744 cookie->current = nextChildNode; 745 cookie->state = nextState; 746 status = B_OK; 747 748 err: 749 mutex_unlock(&fs->lock); 750 751 return status; 752 } 753 754 755 static status_t 756 rootfs_rewind_dir(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie) 757 { 758 struct rootfs_dir_cookie *cookie = (rootfs_dir_cookie*)_cookie; 759 struct rootfs_vnode *vnode = (rootfs_vnode*)_vnode; 760 struct rootfs *fs = (rootfs*)_fs; 761 762 mutex_lock(&fs->lock); 763 764 cookie->current = vnode->stream.dir.dir_head; 765 cookie->state = ITERATION_STATE_BEGIN; 766 767 mutex_unlock(&fs->lock); 768 return B_OK; 769 } 770 771 772 static status_t 773 rootfs_ioctl(fs_volume _fs, fs_vnode _v, fs_cookie _cookie, ulong op, void *buf, size_t len) 774 { 775 TRACE(("rootfs_ioctl: vnode %p, cookie %p, op %ld, buf %p, len %ld\n", _v, _cookie, op, buf, len)); 776 777 return EINVAL; 778 } 779 780 781 static bool 782 rootfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie cookie) 783 { 784 return false; 785 } 786 787 788 static status_t 789 rootfs_read_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, 790 const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, 791 bool reenter) 792 { 793 return B_NOT_ALLOWED; 794 } 795 796 797 static status_t 798 rootfs_write_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, 799 const iovec *vecs, size_t count, size_t *_numBytes, bool mayBlock, 800 bool reenter) 801 { 802 return B_NOT_ALLOWED; 803 } 804 805 806 static status_t 807 rootfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t *_bufferSize) 808 { 809 struct rootfs_vnode *link = (rootfs_vnode*)_link; 810 size_t bufferSize = *_bufferSize; 811 812 if (!S_ISLNK(link->stream.type)) 813 return B_BAD_VALUE; 814 815 *_bufferSize = link->stream.symlink.length + 1; 816 // we always need to return the number of bytes we intend to write! 817 818 if (bufferSize <= link->stream.symlink.length) 819 return B_BUFFER_OVERFLOW; 820 821 memcpy(buffer, link->stream.symlink.path, link->stream.symlink.length + 1); 822 return B_OK; 823 } 824 825 826 static status_t 827 rootfs_symlink(fs_volume _fs, fs_vnode _dir, const char *name, const char *path, int mode) 828 { 829 struct rootfs *fs = (rootfs*)_fs; 830 struct rootfs_vnode *dir = (rootfs_vnode*)_dir; 831 struct rootfs_vnode *vnode; 832 status_t status = B_OK; 833 834 TRACE(("rootfs_symlink: dir %p, name = '%s', path = %s\n", dir, name, path)); 835 836 mutex_lock(&fs->lock); 837 838 vnode = rootfs_find_in_dir(dir, name); 839 if (vnode != NULL) { 840 status = B_FILE_EXISTS; 841 goto err; 842 } 843 844 TRACE(("rootfs_create: creating new symlink\n")); 845 vnode = rootfs_create_vnode(fs, dir, name, S_IFLNK | (mode & S_IUMSK)); 846 if (vnode == NULL) { 847 status = B_NO_MEMORY; 848 goto err; 849 } 850 851 rootfs_insert_in_dir(fs, dir, vnode); 852 hash_insert(fs->vnode_list_hash, vnode); 853 854 vnode->stream.symlink.path = strdup(path); 855 if (vnode->stream.symlink.path == NULL) { 856 status = B_NO_MEMORY; 857 goto err1; 858 } 859 vnode->stream.symlink.length = strlen(path); 860 861 notify_entry_created(fs->id, dir->id, name, vnode->id); 862 863 mutex_unlock(&fs->lock); 864 return B_OK; 865 866 err1: 867 rootfs_delete_vnode(fs, vnode, false); 868 err: 869 mutex_unlock(&fs->lock); 870 return status; 871 } 872 873 874 static status_t 875 rootfs_unlink(fs_volume _fs, fs_vnode _dir, const char *name) 876 { 877 struct rootfs *fs = (rootfs*)_fs; 878 struct rootfs_vnode *dir = (rootfs_vnode*)_dir; 879 880 TRACE(("rootfs_unlink: dir %p (0x%Lx), name '%s'\n", dir, dir->id, name)); 881 882 return rootfs_remove(fs, dir, name, false); 883 } 884 885 886 static status_t 887 rootfs_rename(fs_volume _fs, fs_vnode _fromDir, const char *fromName, fs_vnode _toDir, const char *toName) 888 { 889 struct rootfs *fs = (rootfs*)_fs; 890 struct rootfs_vnode *fromDirectory = (rootfs_vnode*)_fromDir; 891 struct rootfs_vnode *toDirectory = (rootfs_vnode*)_toDir; 892 struct rootfs_vnode *vnode, *targetVnode, *parent; 893 status_t status; 894 char *nameBuffer = NULL; 895 896 TRACE(("rootfs_rename: from %p (0x%Lx), fromName '%s', to %p (0x%Lx), toName '%s'\n", 897 fromDirectory, fromDirectory->id, fromName, toDirectory, toDirectory->id, toName)); 898 899 mutex_lock(&fs->lock); 900 901 vnode = rootfs_find_in_dir(fromDirectory, fromName); 902 if (vnode != NULL) { 903 status = B_ENTRY_NOT_FOUND; 904 goto err; 905 } 906 907 // make sure the target not a subdirectory of us 908 parent = toDirectory->parent; 909 while (parent != NULL) { 910 if (parent == vnode) { 911 status = B_BAD_VALUE; 912 goto err; 913 } 914 915 parent = parent->parent; 916 } 917 918 // we'll reuse the name buffer if possible 919 if (strlen(fromName) >= strlen(toName)) { 920 nameBuffer = strdup(toName); 921 if (nameBuffer == NULL) { 922 status = B_NO_MEMORY; 923 goto err; 924 } 925 } 926 927 targetVnode = rootfs_find_in_dir(toDirectory, toName); 928 if (targetVnode != NULL) { 929 // target node exists, let's see if it is an empty directory 930 if (S_ISDIR(targetVnode->stream.type) && !rootfs_is_dir_empty(targetVnode)) { 931 status = B_NAME_IN_USE; 932 goto err; 933 } 934 935 // so we can cleanly remove it 936 remove_node(fs, toDirectory, targetVnode); 937 } 938 939 // change the name on this node 940 if (nameBuffer == NULL) { 941 // we can just copy it 942 strcpy(vnode->name, toName); 943 } else { 944 free(vnode->name); 945 vnode->name = nameBuffer; 946 } 947 948 // remove it from the dir 949 rootfs_remove_from_dir(fs, fromDirectory, vnode); 950 951 // Add it back to the dir with the new name. 952 // We need to do this even in the same directory, 953 // so that it keeps sorted correctly. 954 rootfs_insert_in_dir(fs, toDirectory, vnode); 955 956 notify_entry_moved(fs->id, fromDirectory->id, fromName, toDirectory->id, toName, vnode->id); 957 status = B_OK; 958 959 err: 960 if (status != B_OK) 961 free(nameBuffer); 962 963 mutex_unlock(&fs->lock); 964 965 return status; 966 } 967 968 969 static status_t 970 rootfs_read_stat(fs_volume _fs, fs_vnode _v, struct stat *stat) 971 { 972 struct rootfs *fs = (rootfs*)_fs; 973 struct rootfs_vnode *vnode = (rootfs_vnode*)_v; 974 975 TRACE(("rootfs_read_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, stat)); 976 977 // stream exists, but we know to return size 0, since we can only hold directories 978 stat->st_dev = fs->id; 979 stat->st_ino = vnode->id; 980 stat->st_size = 0; 981 stat->st_mode = vnode->stream.type; 982 983 stat->st_nlink = 1; 984 stat->st_blksize = 65536; 985 986 stat->st_uid = vnode->uid; 987 stat->st_gid = vnode->gid; 988 989 stat->st_atime = time(NULL); 990 stat->st_mtime = stat->st_ctime = vnode->modification_time; 991 stat->st_crtime = vnode->creation_time; 992 993 return B_OK; 994 } 995 996 997 static status_t 998 rootfs_write_stat(fs_volume _fs, fs_vnode _vnode, const struct stat *stat, uint32 statMask) 999 { 1000 struct rootfs *fs = (rootfs*)_fs; 1001 struct rootfs_vnode *vnode = (rootfs_vnode*)_vnode; 1002 1003 TRACE(("rootfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, stat)); 1004 1005 // we cannot change the size of anything 1006 if (statMask & B_STAT_SIZE) 1007 return B_BAD_VALUE; 1008 1009 mutex_lock(&fs->lock); 1010 1011 if (statMask & B_STAT_MODE) 1012 vnode->stream.type = (vnode->stream.type & ~S_IUMSK) | (stat->st_mode & S_IUMSK); 1013 1014 if (statMask & B_STAT_UID) 1015 vnode->uid = stat->st_uid; 1016 if (statMask & B_STAT_GID) 1017 vnode->gid = stat->st_gid; 1018 1019 if (statMask & B_STAT_MODIFICATION_TIME) 1020 vnode->modification_time = stat->st_mtime; 1021 if (statMask & B_STAT_CREATION_TIME) 1022 vnode->creation_time = stat->st_crtime; 1023 1024 mutex_unlock(&fs->lock); 1025 1026 notify_stat_changed(fs->id, vnode->id, statMask); 1027 return B_OK; 1028 } 1029 1030 1031 static status_t 1032 rootfs_std_ops(int32 op, ...) 1033 { 1034 switch (op) { 1035 case B_MODULE_INIT: 1036 return B_OK; 1037 1038 case B_MODULE_UNINIT: 1039 return B_OK; 1040 1041 default: 1042 return B_ERROR; 1043 } 1044 } 1045 1046 1047 file_system_module_info gRootFileSystem = { 1048 { 1049 "file_systems/rootfs" B_CURRENT_FS_API_VERSION, 1050 0, 1051 rootfs_std_ops, 1052 }, 1053 1054 "Root File System", 1055 0, // DDM flags 1056 1057 NULL, // identify_partition() 1058 NULL, // scan_partition() 1059 NULL, // free_identify_partition_cookie() 1060 NULL, // free_partition_content_cookie() 1061 1062 &rootfs_mount, 1063 &rootfs_unmount, 1064 NULL, 1065 NULL, 1066 &rootfs_sync, 1067 1068 &rootfs_lookup, 1069 &rootfs_get_vnode_name, 1070 1071 &rootfs_get_vnode, 1072 &rootfs_put_vnode, 1073 &rootfs_remove_vnode, 1074 1075 &rootfs_can_page, 1076 &rootfs_read_pages, 1077 &rootfs_write_pages, 1078 1079 NULL, // get_file_map() 1080 1081 /* common */ 1082 &rootfs_ioctl, 1083 NULL, // fs_set_flags() 1084 NULL, // select 1085 NULL, // deselect 1086 &rootfs_fsync, 1087 1088 &rootfs_read_link, 1089 &rootfs_symlink, 1090 NULL, // fs_link() 1091 &rootfs_unlink, 1092 &rootfs_rename, 1093 1094 NULL, // fs_access() 1095 &rootfs_read_stat, 1096 &rootfs_write_stat, 1097 1098 /* file */ 1099 &rootfs_create, 1100 &rootfs_open, 1101 &rootfs_close, 1102 &rootfs_free_cookie, 1103 &rootfs_read, 1104 &rootfs_write, 1105 1106 /* directory */ 1107 &rootfs_create_dir, 1108 &rootfs_remove_dir, 1109 &rootfs_open_dir, 1110 &rootfs_close, // same as for files - it does nothing, anyway 1111 &rootfs_free_dir_cookie, 1112 &rootfs_read_dir, 1113 &rootfs_rewind_dir, 1114 1115 // the other operations are not supported (attributes, indices, queries) 1116 NULL, 1117 }; 1118 1119