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