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