1 /* 2 * Copyright 2002-2017, 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 "OpenHashTable.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 openMode, void** _cookie) 587 { 588 struct rootfs_vnode* vnode = (rootfs_vnode*)_v->private_node; 589 590 if (S_ISDIR(vnode->stream.type) && (openMode & O_RWMASK) != O_RDONLY) 591 return B_IS_A_DIRECTORY; 592 if ((openMode & O_DIRECTORY) != 0 && !S_ISDIR(vnode->stream.type)) 593 return B_NOT_A_DIRECTORY; 594 595 // allow to open the file, but it can't be done anything with it 596 597 *_cookie = NULL; 598 return B_OK; 599 } 600 601 602 static status_t 603 rootfs_close(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 604 { 605 TRACE(("rootfs_close: entry vnode %p, cookie %p\n", _vnode->private_node, 606 _cookie)); 607 return B_OK; 608 } 609 610 611 static status_t 612 rootfs_free_cookie(fs_volume* _volume, fs_vnode* _v, void* _cookie) 613 { 614 return B_OK; 615 } 616 617 618 static status_t 619 rootfs_fsync(fs_volume* _volume, fs_vnode* _v) 620 { 621 return B_OK; 622 } 623 624 625 static status_t 626 rootfs_read(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 627 off_t pos, void* buffer, size_t* _length) 628 { 629 return EINVAL; 630 } 631 632 633 static status_t 634 rootfs_write(fs_volume* _volume, fs_vnode* vnode, void* cookie, 635 off_t pos, const void* buffer, size_t* _length) 636 { 637 TRACE(("rootfs_write: vnode %p, cookie %p, pos 0x%Lx , len %#x\n", 638 vnode, cookie, pos, (int)*_length)); 639 640 return EPERM; 641 } 642 643 644 static status_t 645 rootfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char* name, 646 int mode) 647 { 648 struct rootfs* fs = (rootfs*)_volume->private_volume; 649 struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node; 650 struct rootfs_vnode* vnode; 651 652 TRACE(("rootfs_create_dir: dir %p, name = '%s', perms = %d\n", dir, name, 653 mode)); 654 655 WriteLocker locker(fs->lock); 656 657 vnode = rootfs_find_in_dir(dir, name); 658 if (vnode != NULL) 659 return B_FILE_EXISTS; 660 661 TRACE(("rootfs_create: creating new vnode\n")); 662 vnode = rootfs_create_vnode(fs, dir, name, S_IFDIR | (mode & S_IUMSK)); 663 if (vnode == NULL) 664 return B_NO_MEMORY; 665 666 rootfs_insert_in_dir(fs, dir, vnode); 667 fs->vnode_list_hash->Insert(vnode); 668 669 entry_cache_add(fs->volume->id, dir->id, name, vnode->id); 670 notify_entry_created(fs->id, dir->id, name, vnode->id); 671 672 return B_OK; 673 } 674 675 676 static status_t 677 rootfs_remove_dir(fs_volume* _volume, fs_vnode* _dir, const char* name) 678 { 679 struct rootfs* fs = (rootfs*)_volume->private_volume; 680 struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node; 681 682 TRACE(("rootfs_remove_dir: dir %p (0x%Lx), name '%s'\n", dir, dir->id, 683 name)); 684 685 return rootfs_remove(fs, dir, name, true); 686 } 687 688 689 static status_t 690 rootfs_open_dir(fs_volume* _volume, fs_vnode* _v, void** _cookie) 691 { 692 struct rootfs* fs = (struct rootfs*)_volume->private_volume; 693 struct rootfs_vnode* vnode = (struct rootfs_vnode*)_v->private_node; 694 struct rootfs_dir_cookie* cookie; 695 696 TRACE(("rootfs_open: vnode %p\n", vnode)); 697 698 if (!S_ISDIR(vnode->stream.type)) 699 return B_BAD_VALUE; 700 701 cookie = (rootfs_dir_cookie*)malloc(sizeof(struct rootfs_dir_cookie)); 702 if (cookie == NULL) 703 return B_NO_MEMORY; 704 705 mutex_init(&cookie->lock, "rootfs dir cookie"); 706 707 ReadLocker locker(fs->lock); 708 709 cookie->current = vnode->stream.dir.dir_head; 710 cookie->iteration_state = ITERATION_STATE_BEGIN; 711 712 mutex_lock(&vnode->stream.dir.cookie_lock); 713 list_add_item(&vnode->stream.dir.cookies, cookie); 714 mutex_unlock(&vnode->stream.dir.cookie_lock); 715 716 *_cookie = cookie; 717 718 return B_OK; 719 } 720 721 722 static status_t 723 rootfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 724 { 725 struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie; 726 struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node; 727 struct rootfs* fs = (rootfs*)_volume->private_volume; 728 729 ReadLocker locker(fs->lock); 730 731 mutex_lock(&vnode->stream.dir.cookie_lock); 732 list_remove_item(&vnode->stream.dir.cookies, cookie); 733 mutex_unlock(&vnode->stream.dir.cookie_lock); 734 735 locker.Unlock(); 736 737 mutex_destroy(&cookie->lock); 738 739 free(cookie); 740 return B_OK; 741 } 742 743 744 static status_t 745 rootfs_read_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie, 746 struct dirent* dirent, size_t bufferSize, uint32* _num) 747 { 748 struct rootfs_vnode* vnode = (struct rootfs_vnode*)_vnode->private_node; 749 struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie; 750 struct rootfs* fs = (rootfs*)_volume->private_volume; 751 struct rootfs_vnode* childNode = NULL; 752 const char* name = NULL; 753 struct rootfs_vnode* nextChildNode = NULL; 754 755 TRACE(("rootfs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %d, " 756 "num = %p\n", _vnode, cookie, dirent, (int)bufferSize, _num)); 757 758 ReadLocker locker(fs->lock); 759 760 MutexLocker cookieLocker(cookie->lock); 761 int nextState = cookie->iteration_state; 762 763 switch (cookie->iteration_state) { 764 case ITERATION_STATE_DOT: 765 childNode = vnode; 766 name = "."; 767 nextChildNode = vnode->stream.dir.dir_head; 768 nextState = cookie->iteration_state + 1; 769 break; 770 case ITERATION_STATE_DOT_DOT: 771 childNode = vnode->parent; 772 name = ".."; 773 nextChildNode = vnode->stream.dir.dir_head; 774 nextState = cookie->iteration_state + 1; 775 break; 776 default: 777 childNode = cookie->current; 778 if (childNode) { 779 name = childNode->name; 780 nextChildNode = childNode->dir_next; 781 } 782 break; 783 } 784 785 if (!childNode) { 786 // we're at the end of the directory 787 *_num = 0; 788 return B_OK; 789 } 790 791 dirent->d_dev = fs->id; 792 dirent->d_ino = childNode->id; 793 dirent->d_reclen = offsetof(struct dirent, d_name) + strlen(name) + 1; 794 795 if (dirent->d_reclen > bufferSize) 796 return ENOBUFS; 797 798 int nameLength = user_strlcpy(dirent->d_name, name, 799 bufferSize - offsetof(struct dirent, d_name)); 800 if (nameLength < B_OK) 801 return nameLength; 802 803 cookie->current = nextChildNode; 804 cookie->iteration_state = nextState; 805 *_num = 1; 806 return B_OK; 807 } 808 809 810 static status_t 811 rootfs_rewind_dir(fs_volume* _volume, fs_vnode* _vnode, void* _cookie) 812 { 813 struct rootfs_dir_cookie* cookie = (rootfs_dir_cookie*)_cookie; 814 struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node; 815 struct rootfs* fs = (rootfs*)_volume->private_volume; 816 817 ReadLocker locker(fs->lock); 818 MutexLocker cookieLocker(cookie->lock); 819 820 cookie->current = vnode->stream.dir.dir_head; 821 cookie->iteration_state = ITERATION_STATE_BEGIN; 822 823 return B_OK; 824 } 825 826 827 static status_t 828 rootfs_ioctl(fs_volume* _volume, fs_vnode* _v, void* _cookie, uint32 op, 829 void* buffer, size_t length) 830 { 831 TRACE(("rootfs_ioctl: vnode %p, cookie %p, op %d, buf %p, length %d\n", 832 _volume, _cookie, (int)op, buffer, (int)length)); 833 834 return B_BAD_VALUE; 835 } 836 837 838 static bool 839 rootfs_can_page(fs_volume* _volume, fs_vnode* _v, void* cookie) 840 { 841 return false; 842 } 843 844 845 static status_t 846 rootfs_read_pages(fs_volume* _volume, fs_vnode* _v, void* cookie, off_t pos, 847 const iovec* vecs, size_t count, size_t* _numBytes) 848 { 849 return B_NOT_ALLOWED; 850 } 851 852 853 static status_t 854 rootfs_write_pages(fs_volume* _volume, fs_vnode* _v, void* cookie, off_t pos, 855 const iovec* vecs, size_t count, size_t* _numBytes) 856 { 857 return B_NOT_ALLOWED; 858 } 859 860 861 static status_t 862 rootfs_read_link(fs_volume* _volume, fs_vnode* _link, char* buffer, 863 size_t* _bufferSize) 864 { 865 struct rootfs_vnode* link = (rootfs_vnode*)_link->private_node; 866 867 if (!S_ISLNK(link->stream.type)) 868 return B_BAD_VALUE; 869 870 memcpy(buffer, link->stream.symlink.path, min_c(*_bufferSize, 871 link->stream.symlink.length)); 872 873 *_bufferSize = link->stream.symlink.length; 874 875 return B_OK; 876 } 877 878 879 static status_t 880 rootfs_symlink(fs_volume* _volume, fs_vnode* _dir, const char* name, 881 const char* path, int mode) 882 { 883 struct rootfs* fs = (rootfs*)_volume->private_volume; 884 struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node; 885 struct rootfs_vnode* vnode; 886 887 TRACE(("rootfs_symlink: dir %p, name = '%s', path = %s\n", dir, name, path)); 888 889 WriteLocker locker(fs->lock); 890 891 vnode = rootfs_find_in_dir(dir, name); 892 if (vnode != NULL) 893 return B_FILE_EXISTS; 894 895 TRACE(("rootfs_create: creating new symlink\n")); 896 vnode = rootfs_create_vnode(fs, dir, name, S_IFLNK | (mode & S_IUMSK)); 897 if (vnode == NULL) 898 return B_NO_MEMORY; 899 900 rootfs_insert_in_dir(fs, dir, vnode); 901 fs->vnode_list_hash->Insert(vnode); 902 903 vnode->stream.symlink.path = strdup(path); 904 if (vnode->stream.symlink.path == NULL) { 905 rootfs_delete_vnode(fs, vnode, false); 906 return B_NO_MEMORY; 907 } 908 vnode->stream.symlink.length = strlen(path); 909 910 entry_cache_add(fs->volume->id, dir->id, name, vnode->id); 911 912 notify_entry_created(fs->id, dir->id, name, vnode->id); 913 914 return B_OK; 915 } 916 917 918 static status_t 919 rootfs_unlink(fs_volume* _volume, fs_vnode* _dir, const char* name) 920 { 921 struct rootfs* fs = (rootfs*)_volume->private_volume; 922 struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node; 923 924 TRACE(("rootfs_unlink: dir %p (0x%Lx), name '%s'\n", dir, dir->id, name)); 925 926 return rootfs_remove(fs, dir, name, false); 927 } 928 929 930 static status_t 931 rootfs_rename(fs_volume* _volume, fs_vnode* _fromDir, const char* fromName, 932 fs_vnode* _toDir, const char* toName) 933 { 934 struct rootfs* fs = (rootfs*)_volume->private_volume; 935 struct rootfs_vnode* fromDirectory = (rootfs_vnode*)_fromDir->private_node; 936 struct rootfs_vnode* toDirectory = (rootfs_vnode*)_toDir->private_node; 937 938 TRACE(("rootfs_rename: from %p (0x%Lx, %s), fromName '%s', to %p " 939 "(0x%Lx, %s), toName '%s'\n", fromDirectory, fromDirectory->id, 940 fromDirectory->name != NULL ? fromDirectory->name : "NULL", 941 fromName, toDirectory, toDirectory->id, 942 toDirectory->name != NULL ? toDirectory->name : "NULL", 943 toName)); 944 945 // Prevent renaming /boot, since that will stop everything from working. 946 // TODO: This should be solved differently. Either root should still be 947 // able to do this or a mechanism should be introduced that does this 948 // at the VFS level, for example by checking nodes for a specific 949 // attribute. 950 if (fromDirectory->id == 1 && strcmp(fromName, "boot") == 0) 951 return EPERM; 952 953 WriteLocker locker(fs->lock); 954 955 struct rootfs_vnode* vnode = rootfs_find_in_dir(fromDirectory, fromName); 956 if (vnode == NULL) 957 return B_ENTRY_NOT_FOUND; 958 959 // make sure the target is not a subdirectory of us 960 struct rootfs_vnode* parent = toDirectory->parent; 961 while (parent != NULL && parent != parent->parent) { 962 if (parent == vnode) 963 return B_BAD_VALUE; 964 965 parent = parent->parent; 966 } 967 968 struct rootfs_vnode* targetVnode = rootfs_find_in_dir(toDirectory, toName); 969 if (targetVnode != NULL) { 970 // target node exists, let's see if it is an empty directory 971 if (S_ISDIR(targetVnode->stream.type) 972 && !rootfs_is_dir_empty(targetVnode)) 973 return B_NAME_IN_USE; 974 975 // so we can cleanly remove it 976 entry_cache_remove(fs->volume->id, toDirectory->id, toName); 977 remove_node(fs, toDirectory, targetVnode); 978 } 979 980 // we try to reuse the existing name buffer if possible 981 if (strlen(fromName) < strlen(toName)) { 982 char* nameBuffer = strdup(toName); 983 if (nameBuffer == NULL) 984 return B_NO_MEMORY; 985 986 free(vnode->name); 987 vnode->name = nameBuffer; 988 } else { 989 // we can just copy it 990 strcpy(vnode->name, toName); 991 } 992 993 // remove it from the dir 994 entry_cache_remove(fs->volume->id, fromDirectory->id, fromName); 995 rootfs_remove_from_dir(fs, fromDirectory, vnode); 996 997 // Add it back to the dir with the new name. 998 // We need to do this even in the same directory, 999 // so that it keeps sorted correctly. 1000 rootfs_insert_in_dir(fs, toDirectory, vnode); 1001 1002 entry_cache_add(fs->volume->id, toDirectory->id, toName, vnode->id); 1003 1004 notify_entry_moved(fs->id, fromDirectory->id, fromName, toDirectory->id, 1005 toName, vnode->id); 1006 1007 return B_OK; 1008 } 1009 1010 1011 static status_t 1012 rootfs_read_stat(fs_volume* _volume, fs_vnode* _v, struct stat* stat) 1013 { 1014 struct rootfs* fs = (rootfs*)_volume->private_volume; 1015 struct rootfs_vnode* vnode = (rootfs_vnode*)_v->private_node; 1016 1017 TRACE(("rootfs_read_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, 1018 stat)); 1019 1020 // stream exists, but we know to return size 0, since we can only hold 1021 // directories 1022 stat->st_dev = fs->id; 1023 stat->st_ino = vnode->id; 1024 if (S_ISLNK(vnode->stream.type)) 1025 stat->st_size = vnode->stream.symlink.length; 1026 else 1027 stat->st_size = 0; 1028 stat->st_mode = vnode->stream.type; 1029 1030 stat->st_nlink = 1; 1031 stat->st_blksize = 65536; 1032 stat->st_blocks = 0; 1033 1034 stat->st_uid = vnode->uid; 1035 stat->st_gid = vnode->gid; 1036 1037 stat->st_atim.tv_sec = real_time_clock(); 1038 stat->st_atim.tv_nsec = 0; 1039 stat->st_mtim = stat->st_ctim = vnode->modification_time; 1040 stat->st_crtim = vnode->creation_time; 1041 1042 return B_OK; 1043 } 1044 1045 1046 static status_t 1047 rootfs_write_stat(fs_volume* _volume, fs_vnode* _vnode, const struct stat* stat, 1048 uint32 statMask) 1049 { 1050 struct rootfs* fs = (rootfs*)_volume->private_volume; 1051 struct rootfs_vnode* vnode = (rootfs_vnode*)_vnode->private_node; 1052 1053 TRACE(("rootfs_write_stat: vnode %p (0x%Lx), stat %p\n", vnode, vnode->id, 1054 stat)); 1055 1056 // we cannot change the size of anything 1057 if (statMask & B_STAT_SIZE) 1058 return B_BAD_VALUE; 1059 1060 WriteLocker locker(fs->lock); 1061 1062 if ((statMask & B_STAT_MODE) != 0) { 1063 vnode->stream.type = (vnode->stream.type & ~S_IUMSK) 1064 | (stat->st_mode & S_IUMSK); 1065 } 1066 1067 if ((statMask & B_STAT_UID) != 0) 1068 vnode->uid = stat->st_uid; 1069 if ((statMask & B_STAT_GID) != 0) 1070 vnode->gid = stat->st_gid; 1071 1072 if ((statMask & B_STAT_MODIFICATION_TIME) != 0) 1073 vnode->modification_time = stat->st_mtim; 1074 if ((statMask & B_STAT_CREATION_TIME) != 0) 1075 vnode->creation_time = stat->st_crtim; 1076 1077 locker.Unlock(); 1078 1079 notify_stat_changed(fs->id, get_parent_id(vnode), vnode->id, statMask); 1080 return B_OK; 1081 } 1082 1083 1084 static status_t 1085 rootfs_create_special_node(fs_volume* _volume, fs_vnode* _dir, const char* name, 1086 fs_vnode* subVnode, mode_t mode, uint32 flags, fs_vnode* _superVnode, 1087 ino_t* _nodeID) 1088 { 1089 struct rootfs* fs = (rootfs*)_volume->private_volume; 1090 struct rootfs_vnode* dir = (rootfs_vnode*)_dir->private_node; 1091 struct rootfs_vnode* vnode; 1092 1093 WriteLocker locker(fs->lock); 1094 1095 if (name != NULL) { 1096 vnode = rootfs_find_in_dir(dir, name); 1097 if (vnode != NULL) 1098 return B_FILE_EXISTS; 1099 } 1100 1101 vnode = rootfs_create_vnode(fs, dir, name, mode); 1102 if (vnode == NULL) 1103 return B_NO_MEMORY; 1104 1105 if (name != NULL) 1106 rootfs_insert_in_dir(fs, dir, vnode); 1107 else 1108 flags |= B_VNODE_PUBLISH_REMOVED; 1109 1110 fs->vnode_list_hash->Insert(vnode); 1111 1112 _superVnode->private_node = vnode; 1113 _superVnode->ops = &sVnodeOps; 1114 *_nodeID = vnode->id; 1115 1116 if (subVnode == NULL) 1117 subVnode = _superVnode; 1118 1119 status_t status = publish_vnode(fs->volume, vnode->id, 1120 subVnode->private_node, subVnode->ops, mode, flags); 1121 if (status != B_OK) { 1122 if (name != NULL) 1123 rootfs_remove_from_dir(fs, dir, vnode); 1124 rootfs_delete_vnode(fs, vnode, false); 1125 return status; 1126 } 1127 1128 if (name != NULL) { 1129 entry_cache_add(fs->volume->id, dir->id, name, vnode->id); 1130 notify_entry_created(fs->id, dir->id, name, vnode->id); 1131 } 1132 1133 return B_OK; 1134 } 1135 1136 1137 static status_t 1138 rootfs_std_ops(int32 op, ...) 1139 { 1140 switch (op) { 1141 case B_MODULE_INIT: 1142 return B_OK; 1143 1144 case B_MODULE_UNINIT: 1145 return B_OK; 1146 1147 default: 1148 return B_ERROR; 1149 } 1150 } 1151 1152 1153 namespace { 1154 1155 fs_volume_ops sVolumeOps = { 1156 &rootfs_unmount, 1157 NULL, 1158 NULL, 1159 &rootfs_sync, 1160 &rootfs_get_vnode, 1161 1162 // the other operations are not supported (indices, queries) 1163 NULL, 1164 }; 1165 1166 fs_vnode_ops sVnodeOps = { 1167 &rootfs_lookup, 1168 &rootfs_get_vnode_name, 1169 1170 &rootfs_put_vnode, 1171 &rootfs_remove_vnode, 1172 1173 &rootfs_can_page, 1174 &rootfs_read_pages, 1175 &rootfs_write_pages, 1176 1177 NULL, // io() 1178 NULL, // cancel_io() 1179 1180 NULL, // get_file_map() 1181 1182 /* common */ 1183 &rootfs_ioctl, 1184 NULL, // fs_set_flags() 1185 NULL, // select 1186 NULL, // deselect 1187 &rootfs_fsync, 1188 1189 &rootfs_read_link, 1190 &rootfs_symlink, 1191 NULL, // fs_link() 1192 &rootfs_unlink, 1193 &rootfs_rename, 1194 1195 NULL, // fs_access() 1196 &rootfs_read_stat, 1197 &rootfs_write_stat, 1198 NULL, 1199 1200 /* file */ 1201 &rootfs_create, 1202 &rootfs_open, 1203 &rootfs_close, 1204 &rootfs_free_cookie, 1205 &rootfs_read, 1206 &rootfs_write, 1207 1208 /* directory */ 1209 &rootfs_create_dir, 1210 &rootfs_remove_dir, 1211 &rootfs_open_dir, 1212 &rootfs_close, // same as for files - it does nothing, anyway 1213 &rootfs_free_dir_cookie, 1214 &rootfs_read_dir, 1215 &rootfs_rewind_dir, 1216 1217 /* attribute directory operations */ 1218 NULL, // open_attr_dir 1219 NULL, // close_attr_dir 1220 NULL, // free_attr_dir_cookie 1221 NULL, // read_attr_dir 1222 NULL, // rewind_attr_dir 1223 1224 /* attribute operations */ 1225 NULL, // create_attr 1226 NULL, // open_attr 1227 NULL, // close_attr 1228 NULL, // free_attr_cookie 1229 NULL, // read_attr 1230 NULL, // write_attr 1231 1232 NULL, // read_attr_stat 1233 NULL, // write_attr_stat 1234 NULL, // rename_attr 1235 NULL, // remove_attr 1236 1237 /* support for node and FS layers */ 1238 &rootfs_create_special_node, 1239 NULL, // get_super_vnode, 1240 }; 1241 1242 } // namespace 1243 1244 file_system_module_info gRootFileSystem = { 1245 { 1246 "file_systems/rootfs" B_CURRENT_FS_API_VERSION, 1247 0, 1248 rootfs_std_ops, 1249 }, 1250 1251 "rootfs", // short_name 1252 "Root File System", // pretty_name 1253 0, // DDM flags 1254 1255 NULL, // identify_partition() 1256 NULL, // scan_partition() 1257 NULL, // free_identify_partition_cookie() 1258 NULL, // free_partition_content_cookie() 1259 1260 &rootfs_mount, 1261 }; 1262