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