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