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