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