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 # include "lock.h" 16 #else 17 # include <KernelExport.h> 18 # include <vfs.h> 19 # include <debug.h> 20 # include <khash.h> 21 # include <lock.h> 22 # include <util/AutoLock.h> 23 # include <vm.h> 24 25 # include <NodeMonitor.h> 26 27 # include <sys/stat.h> 28 # include <malloc.h> 29 # include <string.h> 30 # include <stdio.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 } 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 time_t modification_time; 65 time_t 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 mutex 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 struct rootfs_vnode *current; 86 int32 state; // iteration state 87 }; 88 89 // directory iteration states 90 enum { 91 ITERATION_STATE_DOT = 0, 92 ITERATION_STATE_DOT_DOT = 1, 93 ITERATION_STATE_OTHERS = 2, 94 ITERATION_STATE_BEGIN = ITERATION_STATE_DOT, 95 }; 96 97 // extern and in a private namespace only to make forward declaration possible 98 namespace { 99 extern fs_volume_ops sVolumeOps; 100 extern fs_vnode_ops sVnodeOps; 101 } 102 103 #define ROOTFS_HASH_SIZE 16 104 105 106 static uint32 107 rootfs_vnode_hash_func(void *_v, const void *_key, uint32 range) 108 { 109 struct rootfs_vnode *vnode = (rootfs_vnode*)_v; 110 const ino_t *key = (const ino_t*)_key; 111 112 if (vnode != NULL) 113 return vnode->id % range; 114 115 return (uint64)*key % range; 116 } 117 118 119 static int 120 rootfs_vnode_compare_func(void *_v, const void *_key) 121 { 122 struct rootfs_vnode *v = (rootfs_vnode*)_v; 123 const ino_t *key = (const ino_t*)_key; 124 125 if (v->id == *key) 126 return 0; 127 128 return -1; 129 } 130 131 132 static struct rootfs_vnode * 133 rootfs_create_vnode(struct rootfs *fs, struct rootfs_vnode *parent, 134 const char *name, int type) 135 { 136 struct rootfs_vnode *vnode; 137 138 vnode = (rootfs_vnode*)malloc(sizeof(struct rootfs_vnode)); 139 if (vnode == NULL) 140 return NULL; 141 142 memset(vnode, 0, sizeof(struct rootfs_vnode)); 143 144 if (name != NULL) { 145 vnode->name = strdup(name); 146 if (vnode->name == NULL) { 147 free(vnode); 148 return NULL; 149 } 150 } 151 152 vnode->id = fs->next_vnode_id++; 153 vnode->stream.type = type; 154 vnode->creation_time = vnode->modification_time = time(NULL); 155 vnode->uid = geteuid(); 156 vnode->gid = parent ? parent->gid : getegid(); 157 // inherit group from parent if possible 158 159 if (S_ISDIR(type)) 160 list_init(&vnode->stream.dir.cookies); 161 162 return vnode; 163 } 164 165 166 static status_t 167 rootfs_delete_vnode(struct rootfs *fs, struct rootfs_vnode *v, bool force_delete) 168 { 169 // cant delete it if it's in a directory or is a directory 170 // and has children 171 if (!force_delete && (v->stream.dir.dir_head != NULL || v->dir_next != NULL)) 172 return EPERM; 173 174 // remove it from the global hash table 175 hash_remove(fs->vnode_list_hash, v); 176 177 free(v->name); 178 free(v); 179 180 return 0; 181 } 182 183 184 /* makes sure none of the dircookies point to the vnode passed in */ 185 186 static void 187 update_dir_cookies(struct rootfs_vnode *dir, struct rootfs_vnode *vnode) 188 { 189 struct rootfs_dir_cookie *cookie = NULL; 190 191 while ((cookie = (rootfs_dir_cookie*)list_get_next_item( 192 &dir->stream.dir.cookies, cookie)) != NULL) { 193 if (cookie->current == vnode) 194 cookie->current = vnode->dir_next; 195 } 196 } 197 198 199 static struct rootfs_vnode * 200 rootfs_find_in_dir(struct rootfs_vnode *dir, const char *path) 201 { 202 struct rootfs_vnode *vnode; 203 204 if (!strcmp(path, ".")) 205 return dir; 206 if (!strcmp(path, "..")) 207 return dir->parent; 208 209 for (vnode = dir->stream.dir.dir_head; vnode; vnode = vnode->dir_next) { 210 if (strcmp(vnode->name, path) == 0) 211 return vnode; 212 } 213 return NULL; 214 } 215 216 217 static status_t 218 rootfs_insert_in_dir(struct rootfs *fs, struct rootfs_vnode *dir, 219 struct rootfs_vnode *vnode) 220 { 221 // make sure the directory stays sorted alphabetically 222 223 struct rootfs_vnode *node = dir->stream.dir.dir_head, *last = NULL; 224 while (node && strcmp(node->name, vnode->name) < 0) { 225 last = node; 226 node = node->dir_next; 227 } 228 if (last == NULL) { 229 // the new vnode is the first entry in the list 230 vnode->dir_next = dir->stream.dir.dir_head; 231 dir->stream.dir.dir_head = vnode; 232 } else { 233 // insert after that node 234 vnode->dir_next = last->dir_next; 235 last->dir_next = vnode; 236 } 237 238 vnode->parent = dir; 239 dir->modification_time = time(NULL); 240 241 notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME); 242 return B_OK; 243 } 244 245 246 static status_t 247 rootfs_remove_from_dir(struct rootfs *fs, struct rootfs_vnode *dir, 248 struct rootfs_vnode *findit) 249 { 250 struct rootfs_vnode *v; 251 struct rootfs_vnode *last_v; 252 253 for (v = dir->stream.dir.dir_head, last_v = NULL; v; last_v = v, v = v->dir_next) { 254 if (v == findit) { 255 /* make sure all dircookies dont point to this vnode */ 256 update_dir_cookies(dir, v); 257 258 if (last_v) 259 last_v->dir_next = v->dir_next; 260 else 261 dir->stream.dir.dir_head = v->dir_next; 262 v->dir_next = NULL; 263 264 dir->modification_time = time(NULL); 265 notify_stat_changed(fs->id, dir->id, B_STAT_MODIFICATION_TIME); 266 return B_OK; 267 } 268 } 269 return B_ENTRY_NOT_FOUND; 270 } 271 272 273 static bool 274 rootfs_is_dir_empty(struct rootfs_vnode *dir) 275 { 276 return !dir->stream.dir.dir_head; 277 } 278 279 280 /** You must hold the FS lock when calling this function */ 281 282 static status_t 283 remove_node(struct rootfs *fs, struct rootfs_vnode *directory, 284 struct rootfs_vnode *vnode) 285 { 286 // schedule this vnode to be removed when it's ref goes to zero 287 288 void* dummy; 289 bool gotNode = (get_vnode(fs->volume, vnode->id, &dummy) == B_OK); 290 291 status_t status = B_OK; 292 if (gotNode) 293 status = remove_vnode(fs->volume, vnode->id); 294 295 if (status == B_OK) { 296 rootfs_remove_from_dir(fs, directory, vnode); 297 notify_entry_removed(fs->id, directory->id, vnode->name, vnode->id); 298 } 299 300 if (gotNode) 301 put_vnode(fs->volume, vnode->id); 302 303 return status; 304 } 305 306 307 static status_t 308 rootfs_remove(struct rootfs *fs, struct rootfs_vnode *dir, const char *name, bool isDirectory) 309 { 310 struct rootfs_vnode *vnode; 311 status_t status = B_OK; 312 313 mutex_lock(&fs->lock); 314 315 vnode = rootfs_find_in_dir(dir, name); 316 if (!vnode) 317 status = B_ENTRY_NOT_FOUND; 318 else if (isDirectory && !S_ISDIR(vnode->stream.type)) 319 status = B_NOT_A_DIRECTORY; 320 else if (!isDirectory && S_ISDIR(vnode->stream.type)) 321 status = B_IS_A_DIRECTORY; 322 else if (isDirectory && !rootfs_is_dir_empty(vnode)) 323 status = B_DIRECTORY_NOT_EMPTY; 324 325 if (status < B_OK) 326 goto err; 327 328 status = remove_node(fs, dir, vnode); 329 330 err: 331 mutex_unlock(&fs->lock); 332 333 return status; 334 } 335 336 337 // #pragma mark - 338 339 340 static status_t 341 rootfs_mount(fs_volume *volume, const char *device, uint32 flags, 342 const char *args, ino_t *root_vnid) 343 { 344 struct rootfs *fs; 345 struct rootfs_vnode *vnode; 346 status_t err; 347 348 TRACE(("rootfs_mount: entry\n")); 349 350 fs = (rootfs*)malloc(sizeof(struct rootfs)); 351 if (fs == NULL) 352 return B_NO_MEMORY; 353 354 volume->private_volume = fs; 355 volume->ops = &sVolumeOps; 356 fs->volume = volume; 357 fs->id = volume->id; 358 fs->next_vnode_id = 1; 359 360 mutex_init(&fs->lock, "rootfs_mutex"); 361 362 fs->vnode_list_hash = hash_init(ROOTFS_HASH_SIZE, (addr_t)&vnode->all_next - (addr_t)vnode, 363 &rootfs_vnode_compare_func, &rootfs_vnode_hash_func); 364 if (fs->vnode_list_hash == NULL) { 365 err = B_NO_MEMORY; 366 goto err2; 367 } 368 369 // create the root vnode 370 vnode = rootfs_create_vnode(fs, NULL, ".", S_IFDIR | 0777); 371 if (vnode == NULL) { 372 err = B_NO_MEMORY; 373 goto err3; 374 } 375 vnode->parent = vnode; 376 377 fs->root_vnode = vnode; 378 hash_insert(fs->vnode_list_hash, vnode); 379 publish_vnode(volume, vnode->id, vnode, &sVnodeOps, vnode->stream.type, 0); 380 381 *root_vnid = vnode->id; 382 383 return B_OK; 384 385 err3: 386 hash_uninit(fs->vnode_list_hash); 387 err2: 388 mutex_destroy(&fs->lock); 389 free(fs); 390 391 return err; 392 } 393 394 395 static status_t 396 rootfs_unmount(fs_volume *_volume) 397 { 398 struct rootfs *fs = (struct rootfs *)_volume->private_volume; 399 struct rootfs_vnode *v; 400 struct hash_iterator i; 401 402 TRACE(("rootfs_unmount: entry fs = %p\n", fs)); 403 404 // release the reference to the root 405 put_vnode(fs->volume, fs->root_vnode->id); 406 407 // delete all of the vnodes 408 hash_open(fs->vnode_list_hash, &i); 409 while ((v = (struct rootfs_vnode *)hash_next(fs->vnode_list_hash, &i)) != NULL) { 410 rootfs_delete_vnode(fs, v, true); 411 } 412 hash_close(fs->vnode_list_hash, &i, false); 413 414 hash_uninit(fs->vnode_list_hash); 415 mutex_destroy(&fs->lock); 416 free(fs); 417 418 return B_OK; 419 } 420 421 422 static status_t 423 rootfs_sync(fs_volume *_volume) 424 { 425 TRACE(("rootfs_sync: entry\n")); 426 427 return B_OK; 428 } 429 430 431 static status_t 432 rootfs_lookup(fs_volume *_volume, fs_vnode *_dir, const char *name, ino_t *_id) 433 { 434 struct rootfs *fs = (struct rootfs *)_volume->private_volume; 435 struct rootfs_vnode *dir = (struct rootfs_vnode *)_dir->private_node; 436 struct rootfs_vnode *vnode,*vdummy; 437 status_t status; 438 439 TRACE(("rootfs_lookup: entry dir %p, name '%s'\n", dir, name)); 440 if (!S_ISDIR(dir->stream.type)) 441 return B_NOT_A_DIRECTORY; 442 443 mutex_lock(&fs->lock); 444 445 // look it up 446 vnode = rootfs_find_in_dir(dir, name); 447 if (!vnode) { 448 status = B_ENTRY_NOT_FOUND; 449 goto err; 450 } 451 452 status = get_vnode(fs->volume, vnode->id, (void**)&vdummy); 453 if (status < B_OK) 454 goto err; 455 456 *_id = vnode->id; 457 458 err: 459 mutex_unlock(&fs->lock); 460 461 return status; 462 } 463 464 465 static status_t 466 rootfs_get_vnode_name(fs_volume *_volume, fs_vnode *_vnode, char *buffer, 467 size_t bufferSize) 468 { 469 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode->private_node; 470 471 TRACE(("rootfs_get_vnode_name: vnode = %p (name = %s)\n", vnode, vnode->name)); 472 473 strlcpy(buffer, vnode->name, bufferSize); 474 return B_OK; 475 } 476 477 478 static status_t 479 rootfs_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_vnode, int *_type, 480 uint32 *_flags, bool reenter) 481 { 482 struct rootfs *fs = (struct rootfs *)_volume->private_volume; 483 struct rootfs_vnode *vnode; 484 485 TRACE(("rootfs_getvnode: asking for vnode %Ld, r %d\n", id, reenter)); 486 487 if (!reenter) 488 mutex_lock(&fs->lock); 489 490 vnode = (rootfs_vnode*)hash_lookup(fs->vnode_list_hash, &id); 491 492 if (!reenter) 493 mutex_unlock(&fs->lock); 494 495 TRACE(("rootfs_getnvnode: looked it up at %p\n", *_vnode)); 496 497 if (vnode == NULL) 498 return B_ENTRY_NOT_FOUND; 499 500 _vnode->private_node = vnode; 501 _vnode->ops = &sVnodeOps; 502 *_type = vnode->stream.type; 503 *_flags = 0; 504 505 return B_OK; 506 } 507 508 509 static status_t 510 rootfs_put_vnode(fs_volume *_volume, fs_vnode *_vnode, bool reenter) 511 { 512 #ifdef TRACE_ROOTFS 513 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode->private_node; 514 515 TRACE(("rootfs_putvnode: entry on vnode 0x%Lx, r %d\n", vnode->id, reenter)); 516 #endif 517 return B_OK; // whatever 518 } 519 520 521 static status_t 522 rootfs_remove_vnode(fs_volume *_volume, fs_vnode *_vnode, bool reenter) 523 { 524 struct rootfs *fs = (struct rootfs *)_volume->private_volume; 525 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode->private_node; 526 527 TRACE(("rootfs_remove_vnode: remove %p (0x%Lx), r %d\n", vnode, vnode->id, reenter)); 528 529 if (!reenter) 530 mutex_lock(&fs->lock); 531 532 if (vnode->dir_next) { 533 // can't remove node if it's linked to the dir 534 panic("rootfs_remove_vnode: vnode %p asked to be removed is present in dir\n", vnode); 535 } 536 537 rootfs_delete_vnode(fs, vnode, false); 538 539 if (!reenter) 540 mutex_unlock(&fs->lock); 541 542 return B_OK; 543 } 544 545 546 static status_t 547 rootfs_create(fs_volume *_volume, fs_vnode *_dir, const char *name, int omode, 548 int perms, void **_cookie, ino_t *_newID) 549 { 550 return B_BAD_VALUE; 551 } 552 553 554 static status_t 555 rootfs_open(fs_volume *_volume, fs_vnode *_v, int oflags, void **_cookie) 556 { 557 // allow to open the file, but it can't be done anything with it 558 559 *_cookie = NULL; 560 return B_OK; 561 } 562 563 564 static status_t 565 rootfs_close(fs_volume *_volume, fs_vnode *_v, void *_cookie) 566 { 567 #ifdef TRACE_ROOTFS 568 struct rootfs_vnode *v = _v->private_node; 569 struct rootvoid **cookie = _cookie; 570 571 TRACE(("rootfs_close: entry vnode %p, cookie %p\n", v, cookie)); 572 #endif 573 return B_OK; 574 } 575 576 577 static status_t 578 rootfs_free_cookie(fs_volume *_volume, fs_vnode *_v, void *_cookie) 579 { 580 return B_OK; 581 } 582 583 584 static status_t 585 rootfs_fsync(fs_volume *_volume, fs_vnode *_v) 586 { 587 return B_OK; 588 } 589 590 591 static status_t 592 rootfs_read(fs_volume *_volume, fs_vnode *_vnode, void *_cookie, 593 off_t pos, void *buffer, size_t *_length) 594 { 595 return EINVAL; 596 } 597 598 599 static status_t 600 rootfs_write(fs_volume *_volume, fs_vnode *vnode, void *cookie, 601 off_t pos, const void *buffer, size_t *_length) 602 { 603 TRACE(("rootfs_write: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", 604 vnode, cookie, pos, *_length)); 605 606 return EPERM; 607 } 608 609 610 static status_t 611 rootfs_create_dir(fs_volume *_volume, fs_vnode *_dir, const char *name, 612 int mode, ino_t *_newID) 613 { 614 struct rootfs *fs = (rootfs*)_volume->private_volume; 615 struct rootfs_vnode *dir = (rootfs_vnode*)_dir->private_node; 616 struct rootfs_vnode *vnode; 617 status_t status = 0; 618 619 TRACE(("rootfs_create_dir: dir %p, name = '%s', perms = %d, id = 0x%Lx pointer id = %p\n", 620 dir, name, mode,*_newID, _newID)); 621 622 mutex_lock(&fs->lock); 623 624 vnode = rootfs_find_in_dir(dir, name); 625 if (vnode != NULL) { 626 status = B_FILE_EXISTS; 627 goto err; 628 } 629 630 TRACE(("rootfs_create: creating new vnode\n")); 631 vnode = rootfs_create_vnode(fs, dir, name, S_IFDIR | (mode & S_IUMSK)); 632 if (vnode == NULL) { 633 status = B_NO_MEMORY; 634 goto err; 635 } 636 637 rootfs_insert_in_dir(fs, dir, vnode); 638 hash_insert(fs->vnode_list_hash, vnode); 639 640 notify_entry_created(fs->id, dir->id, name, vnode->id); 641 642 mutex_unlock(&fs->lock); 643 return B_OK; 644 645 err: 646 mutex_unlock(&fs->lock); 647 648 return status; 649 } 650 651 652 static status_t 653 rootfs_remove_dir(fs_volume *_volume, fs_vnode *_dir, const char *name) 654 { 655 struct rootfs *fs = (rootfs*)_volume->private_volume; 656 struct rootfs_vnode *dir = (rootfs_vnode*)_dir->private_node; 657 658 TRACE(("rootfs_remove_dir: dir %p (0x%Lx), name '%s'\n", dir, dir->id, name)); 659 660 return rootfs_remove(fs, dir, name, true); 661 } 662 663 664 static status_t 665 rootfs_open_dir(fs_volume *_volume, fs_vnode *_v, void **_cookie) 666 { 667 struct rootfs *fs = (struct rootfs *)_volume->private_volume; 668 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_v->private_node; 669 struct rootfs_dir_cookie *cookie; 670 671 TRACE(("rootfs_open: vnode %p\n", vnode)); 672 673 if (!S_ISDIR(vnode->stream.type)) 674 return B_BAD_VALUE; 675 676 cookie = (rootfs_dir_cookie*)malloc(sizeof(struct rootfs_dir_cookie)); 677 if (cookie == NULL) 678 return B_NO_MEMORY; 679 680 mutex_lock(&fs->lock); 681 682 cookie->current = vnode->stream.dir.dir_head; 683 cookie->state = ITERATION_STATE_BEGIN; 684 685 list_add_item(&vnode->stream.dir.cookies, cookie); 686 *_cookie = cookie; 687 688 mutex_unlock(&fs->lock); 689 690 return B_OK; 691 } 692 693 694 static status_t 695 rootfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_vnode, void *_cookie) 696 { 697 struct rootfs_dir_cookie *cookie = (rootfs_dir_cookie*)_cookie; 698 struct rootfs_vnode *vnode = (rootfs_vnode*)_vnode->private_node; 699 struct rootfs *fs = (rootfs*)_volume->private_volume; 700 701 mutex_lock(&fs->lock); 702 list_remove_item(&vnode->stream.dir.cookies, cookie); 703 mutex_unlock(&fs->lock); 704 705 free(cookie); 706 return B_OK; 707 } 708 709 710 static status_t 711 rootfs_read_dir(fs_volume *_volume, fs_vnode *_vnode, void *_cookie, 712 struct dirent *dirent, size_t bufferSize, uint32 *_num) 713 { 714 struct rootfs_vnode *vnode = (struct rootfs_vnode *)_vnode->private_node; 715 struct rootfs_dir_cookie *cookie = (rootfs_dir_cookie*)_cookie; 716 struct rootfs *fs = (rootfs*)_volume->private_volume; 717 status_t status = B_OK; 718 struct rootfs_vnode *childNode = NULL; 719 const char *name = NULL; 720 struct rootfs_vnode *nextChildNode = NULL; 721 int nextState = cookie->state; 722 723 TRACE(("rootfs_read_dir: vnode %p, cookie %p, buffer = %p, bufferSize = %ld, num = %p\n", _vnode, cookie, dirent, bufferSize,_num)); 724 725 mutex_lock(&fs->lock); 726 727 switch (cookie->state) { 728 case ITERATION_STATE_DOT: 729 childNode = vnode; 730 name = "."; 731 nextChildNode = vnode->stream.dir.dir_head; 732 nextState = cookie->state + 1; 733 break; 734 case ITERATION_STATE_DOT_DOT: 735 childNode = vnode->parent; 736 name = ".."; 737 nextChildNode = vnode->stream.dir.dir_head; 738 nextState = cookie->state + 1; 739 break; 740 default: 741 childNode = cookie->current; 742 if (childNode) { 743 name = childNode->name; 744 nextChildNode = childNode->dir_next; 745 } 746 break; 747 } 748 749 if (!childNode) { 750 // we're at the end of the directory 751 *_num = 0; 752 goto err; 753 } 754 755 dirent->d_dev = fs->id; 756 dirent->d_ino = childNode->id; 757 dirent->d_reclen = strlen(name) + sizeof(struct dirent); 758 759 if (dirent->d_reclen > bufferSize) { 760 status = ENOBUFS; 761 goto err; 762 } 763 764 status = user_strlcpy(dirent->d_name, name, 765 bufferSize - sizeof(struct dirent)); 766 if (status < B_OK) 767 goto err; 768 769 cookie->current = nextChildNode; 770 cookie->state = nextState; 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, bool reenter) 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, bool reenter) 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, // get_file_map() 1150 1151 /* common */ 1152 &rootfs_ioctl, 1153 NULL, // fs_set_flags() 1154 NULL, // select 1155 NULL, // deselect 1156 &rootfs_fsync, 1157 1158 &rootfs_read_link, 1159 &rootfs_symlink, 1160 NULL, // fs_link() 1161 &rootfs_unlink, 1162 &rootfs_rename, 1163 1164 NULL, // fs_access() 1165 &rootfs_read_stat, 1166 &rootfs_write_stat, 1167 1168 /* file */ 1169 &rootfs_create, 1170 &rootfs_open, 1171 &rootfs_close, 1172 &rootfs_free_cookie, 1173 &rootfs_read, 1174 &rootfs_write, 1175 1176 /* directory */ 1177 &rootfs_create_dir, 1178 &rootfs_remove_dir, 1179 &rootfs_open_dir, 1180 &rootfs_close, // same as for files - it does nothing, anyway 1181 &rootfs_free_dir_cookie, 1182 &rootfs_read_dir, 1183 &rootfs_rewind_dir, 1184 1185 /* attribute directory operations */ 1186 NULL, // open_attr_dir 1187 NULL, // close_attr_dir 1188 NULL, // free_attr_dir_cookie 1189 NULL, // read_attr_dir 1190 NULL, // rewind_attr_dir 1191 1192 /* attribute operations */ 1193 NULL, // create_attr 1194 NULL, // open_attr 1195 NULL, // close_attr 1196 NULL, // free_attr_cookie 1197 NULL, // read_attr 1198 NULL, // write_attr 1199 1200 NULL, // read_attr_stat 1201 NULL, // write_attr_stat 1202 NULL, // rename_attr 1203 NULL, // remove_attr 1204 1205 /* support for node and FS layers */ 1206 &rootfs_create_special_node, 1207 NULL, // get_super_vnode, 1208 }; 1209 1210 } // namespace 1211 1212 file_system_module_info gRootFileSystem = { 1213 { 1214 "file_systems/rootfs" B_CURRENT_FS_API_VERSION, 1215 0, 1216 rootfs_std_ops, 1217 }, 1218 1219 "rootfs", // short_name 1220 "Root File System", // pretty_name 1221 0, // DDM flags 1222 1223 NULL, // identify_partition() 1224 NULL, // scan_partition() 1225 NULL, // free_identify_partition_cookie() 1226 NULL, // free_partition_content_cookie() 1227 1228 &rootfs_mount, 1229 }; 1230 1231