1/* 2 This file contains a simple memory based file system that is used 3 as the top-level name space by the vnode layer. It is a complete 4 file system in and of itself but it only supports creating directories 5 and symlinks. It is also entirely memory based so it is re-created 6 each time the vnode layer is initialized. It is only used to mount 7 other file systems (i.e. to create a directory that can be used as 8 a mount point for another file system). 9 10 11 THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED 12 OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR 13 NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. 14 15 FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). 16 17 Dominic Giampaolo 18 dbg@be.com 19*/ 20#include "compat.h" 21 22#include <stdlib.h> 23#include <string.h> 24#include <fcntl.h> 25#include <time.h> 26 27#include "skiplist.h" 28#include "lock.h" 29 30#include "fsproto.h" 31 32typedef struct vnode vnode; 33typedef struct nspace nspace; 34typedef struct dirpos dirpos; 35 36 37struct nspace { 38 nspace_id nsid; 39 long vnnum; 40 vnode * root; 41 vnode_id nxvnid; 42 lock lock; 43 SkipList skiplist; 44}; 45 46struct vnode { 47 char * name; 48 char removed; 49 nspace *ns; 50 vnode_id vnid; 51 vnode *parent; 52 time_t crtime; 53 time_t mtime; 54 uid_t uid; 55 gid_t gid; 56 mode_t mode; 57 vnode * next; 58 vnode * prev; 59 vnode * head; /* for directories */ 60 char * symlink; /* for symbolic links */ 61}; 62 63struct dirpos { 64 lock lock; 65 int pos; 66 char name[FILE_NAME_LENGTH]; 67}; 68 69 70static int rootfs_read_vnode(void *ns, vnode_id vnid, char r, 71 void **node); 72static int rootfs_write_vnode(void *ns, void *node, char r); 73static int rootfs_remove_vnode(void *ns, void *node, char r); 74static int rootfs_walk(void *ns, void *base, const char *file, 75 char **newpath, vnode_id *vnid); 76static int rootfs_access(void *ns, void *node, int mode); 77static int rootfs_symlink(void *ns, void *dir, const char *name, 78 const char *path); 79static int rootfs_mkdir(void *ns, void *dir, const char *name, 80 int perms); 81static int rootfs_rename(void *ns, void *olddir, const char *oldname, 82 void *newdir, const char *newname); 83static int rootfs_unlink(void *ns, void *dir, const char *name); 84static int rootfs_rmdir(void *ns, void *dir, const char *name); 85static int rootfs_readlink(void *ns, void *node, char *buf, 86 size_t *bufsize); 87static int rootfs_opendir(void *ns, void *node, void **cookie); 88static int rootfs_closedir(void *ns, void *node, void *cookie); 89static int rootfs_free_dircookie(void *ns, void *node, void *cookie); 90static int rootfs_rewinddir(void *ns, void *node, void *cookie); 91static int rootfs_readdir(void *ns, void *node, void *cookie, 92 long *num, struct my_dirent *buf, size_t bufsize); 93static int rootfs_rstat(void *ns, void *node, struct my_stat *st); 94static int rootfs_wstat(void *ns, void *node, struct my_stat *st, long mask); 95static int rootfs_mount(nspace_id nsid, const char *device, ulong flags, 96 void *parms, size_t len, void **data, vnode_id *vnid); 97static int rootfs_unmount(void *ns); 98 99static int compare_vnode(vnode *vna, vnode *vnb); 100static int do_create(nspace *ns, vnode *dir, const char *name, 101 mode_t mode, vnode **vnp); 102static int do_unlink(nspace *ns, vnode *dir, const char *name, bool isdir); 103 104vnode_ops rootfs = { 105 &rootfs_read_vnode, 106 &rootfs_write_vnode, 107 &rootfs_remove_vnode, 108 NULL, 109 &rootfs_walk, 110 &rootfs_access, 111 NULL, 112 &rootfs_mkdir, 113 &rootfs_symlink, 114 NULL, 115 &rootfs_rename, 116 &rootfs_unlink, 117 &rootfs_rmdir, 118 &rootfs_readlink, 119 &rootfs_opendir, 120 &rootfs_closedir, 121 &rootfs_free_dircookie, 122 &rootfs_rewinddir, 123 &rootfs_readdir, 124 NULL, 125 NULL, 126 NULL, 127 NULL, 128 NULL, 129 NULL, 130 NULL, 131 NULL, 132 NULL, 133 &rootfs_rstat, 134 &rootfs_wstat, 135 NULL, 136 NULL, 137 &rootfs_mount, 138 &rootfs_unmount, 139 NULL 140}; 141 142 143#define LCK_MULTIPLE 1 144#define LCK_EXCLUSIVE 1000 145 146#define OMODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) 147 148 149/* ----------------------------------------------------------------- */ 150 151 152static int 153rootfs_walk(void *_ns, void *_base, const char *file, char **newpath, 154 vnode_id *vnid) 155{ 156 nspace *ns; 157 vnode *base; 158 int err; 159 vnode *vn; 160 char *np; 161 162 ns = (nspace *) _ns; 163 base = (vnode *) _base; 164 165 LOCK(ns->lock); 166 167 /* 168 make sure base is a directory and that it has not been removed. 169 */ 170 171 if (!MY_S_ISDIR(base->mode)) { 172 err = FS_ENOTDIR; 173 goto exit; 174 } 175 if (base->removed) { 176 err = FS_ENOENT; 177 goto exit; 178 } 179 180 /* 181 lookup the special directory '.' 182 */ 183 184 if (!strcmp(file, ".")) { 185 err = get_vnode(ns->nsid, base->vnid, (void *)&vn); 186 if (!err) 187 *vnid = base->vnid; 188 goto exit; 189 } 190 191 /* 192 lookup the special directory '..' 193 */ 194 195 if (!strcmp(file, "..")) { 196 err = get_vnode(ns->nsid, base->parent->vnid, (void *)&vn); 197 if (!err) 198 *vnid = vn->vnid; 199 goto exit; 200 } 201 202 /* 203 lookup the name in the directory 204 */ 205 206 vn = base->head; 207 while (vn) { 208 if (!strcmp(vn->name, file)) 209 break; 210 vn = vn->next; 211 } 212 213 /* 214 the name has not been found. advance the path pointer, update the vnid 215 and report an error. 216 */ 217 218 if (!vn) { 219 err = FS_ENOENT; 220 goto exit; 221 } 222 223 /* 224 we have found the item. 225 */ 226 227 /* 228 it is a symbolic link that the kernel wants us to eat. 229 */ 230 231 if (MY_S_ISLNK(vn->mode) && newpath) { 232 err = new_path(vn->symlink, &np); 233 if (err) 234 goto exit; 235 236 *newpath = np; 237 238 } else { 239 240 /* 241 it is a directory or it is a symbolic link that the kernel does 242 not want to 'eat'. 243 */ 244 245 err = get_vnode(ns->nsid, vn->vnid, (void *)&vn); 246 if (!err) 247 *vnid = vn->vnid; 248 } 249 250exit: 251 UNLOCK(ns->lock); 252 return err; 253} 254 255 256static int 257rootfs_mkdir(void *_ns, void *_dir, const char *name, int perms) 258{ 259 nspace *ns; 260 vnode *dir; 261 int err; 262 vnode *vn; 263 264 ns = (nspace *) _ns; 265 dir = (vnode *) _dir; 266 267 LOCK(ns->lock); 268 err = do_create(ns, dir, name, (perms & ~MY_S_IFMT) | MY_S_IFDIR, &vn); 269 UNLOCK(ns->lock); 270 return err; 271} 272 273static int 274rootfs_symlink(void *_ns, void *_dir, const char *name, const char *path) 275{ 276 nspace *ns; 277 vnode *dir; 278 int err; 279 char *buf; 280 vnode *vn; 281 282 ns = (nspace *) _ns; 283 dir = (vnode *) _dir; 284 285 buf = (char *) malloc(strlen(path)+1); 286 if (!buf) { 287 err = FS_ENOMEM; 288 goto error1; 289 } 290 strcpy(buf, path); 291 LOCK(ns->lock); 292 err = do_create(ns, dir, name, MY_S_IFLNK, &vn); 293 if (err) 294 goto error2; 295 vn->symlink = buf; 296 UNLOCK(ns->lock); 297 return 0; 298 299error2: 300 UNLOCK(ns->lock); 301error1: 302 return err; 303} 304 305 306static int 307rootfs_rename(void *_ns, void *_olddir, const char *oldname, void *_newdir, 308 const char *newname) 309{ 310 nspace *ns; 311 vnode *olddir, *newdir; 312 int err; 313 vnode *vn, *nvn, *pvn, *avn; 314 char *p; 315 316 ns = (nspace *) _ns; 317 olddir = (vnode *) _olddir; 318 newdir = (vnode *) _newdir; 319 320 LOCK(ns->lock); 321 if (!MY_S_ISDIR(olddir->mode) || !MY_S_ISDIR(newdir->mode)) { 322 err = FS_ENOTDIR; 323 goto error1; 324 } 325 326 /* 327 find (olddir, oldname) 328 */ 329 330 if (!strcmp(oldname, ".") || !strcmp(oldname, "..")) { 331 err = FS_EPERM; 332 goto error1; 333 } 334 335 vn = olddir->head; 336 while (vn) { 337 if (!strcmp(vn->name, oldname)) 338 break; 339 vn = vn->next; 340 } 341 if (!vn) { 342 err = FS_ENOENT; 343 goto error1; 344 } 345 346 /* 347 look for (newdir, newname) 348 */ 349 350 if (!strcmp(newname, ".") || !strcmp(newname, "..")) { 351 err = FS_EPERM; 352 goto error1; 353 } 354 355 nvn = newdir->head; 356 while (nvn) { 357 if (!strcmp(nvn->name, newname)) 358 break; 359 nvn = nvn->next; 360 } 361 362 /* 363 don't do anything if old and new are the same 364 */ 365 366 if (vn == nvn) 367 goto exit; 368 369 /* 370 make sure new is not a subdirectory of old 371 */ 372 373 avn = newdir; 374 while (avn != ns->root) { 375 avn = avn->parent; 376 if (avn == olddir) { 377 err = FS_EINVAL; 378 goto error1; 379 } 380 } 381 382 if (strlen(newname) > strlen(vn->name)) { 383 p = (char *) realloc(vn->name, strlen(newname)+1); 384 if (!p) { 385 err = FS_ENOMEM; 386 goto error1; 387 } 388 } else 389 p = vn->name; 390 391 /* 392 if (newdir, newname) exists, remove it from the name space 393 */ 394 395 if (nvn) { 396 397 /* 398 make sure it is not the root and it is not empty 399 */ 400 401 if (nvn == nvn->ns->root) { 402 err = FS_EBUSY; 403 goto error1; 404 } 405 if (MY_S_ISDIR(nvn->mode) && nvn->head) { 406 err = FS_ENOTEMPTY; 407 goto error1; 408 } 409 410 err = get_vnode(ns->nsid, nvn->vnid, (void *)&nvn); 411 if (err) 412 goto error1; 413 414 err = remove_vnode(ns->nsid, nvn->vnid); 415 if (err) 416 goto error1; 417 418 if (nvn->prev) 419 nvn->prev->next = nvn->next; 420 else 421 nvn->parent->head = nvn->next; 422 if (nvn->next) 423 nvn->next->prev = nvn->prev; 424 nvn->prev = nvn->next = NULL; 425 426 put_vnode(ns->nsid, nvn->vnid); 427 } 428 429 if (vn->prev) 430 vn->prev->next = vn->next; 431 else 432 vn->parent->head = vn->next; 433 if (vn->next) 434 vn->next->prev = vn->prev; 435 436 pvn = NULL; 437 nvn = newdir->head; 438 while (nvn && (strcmp(newname, nvn->name) > 0)) { 439 pvn = nvn; 440 nvn = nvn->next; 441 } 442 vn->next = nvn; 443 if (nvn) 444 nvn->prev = vn; 445 vn->prev = pvn; 446 if (pvn) 447 pvn->next = vn; 448 else 449 newdir->head = vn; 450 451 vn->parent = newdir; 452 newdir->mtime = olddir->mtime = time(NULL); 453 strcpy(p, newname); 454 vn->name = p; 455 456exit: 457 UNLOCK(ns->lock); 458 return 0; 459 460error1: 461 UNLOCK(ns->lock); 462 return err; 463} 464 465static int 466rootfs_unlink(void *_ns, void *_dir, const char *name) 467{ 468 nspace *ns; 469 vnode *dir; 470 471 ns = (nspace *) _ns; 472 dir = (vnode *) _dir; 473 474 return do_unlink(ns, dir, name, FALSE); 475} 476 477static int 478rootfs_rmdir(void *_ns, void *_dir, const char *name) 479{ 480 nspace *ns; 481 vnode *dir; 482 483 ns = (nspace *) _ns; 484 dir = (vnode *) _dir; 485 486 return do_unlink(ns, dir, name, TRUE); 487} 488 489 490 491static int 492rootfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node) 493{ 494 nspace *ns; 495 vnode *vn; 496 vnode fakevn; 497 498 ns = (nspace *) _ns; 499 500 if (!r) 501 LOCK(ns->lock); 502 fakevn.vnid = vnid; 503 fakevn.ns = ns; 504 vn = SearchSL(ns->skiplist, &fakevn); 505 if (vn) 506 *node = vn; 507 if (!r) 508 UNLOCK(ns->lock); 509 return (vn ? 0 : ENOENT); 510} 511 512static int 513rootfs_write_vnode(void *_ns, void *_node, char r) 514{ 515 return 0; 516} 517 518static int 519rootfs_remove_vnode(void *_ns, void *_node, char r) 520{ 521 nspace *ns; 522 vnode *node; 523 524 ns = (nspace *) _ns; 525 node = (vnode *) _node; 526 527 if (!r) 528 LOCK(ns->lock); 529 DeleteSL(ns->skiplist, node); 530 if (!r) 531 UNLOCK(ns->lock); 532 533 atomic_add(&ns->vnnum, -1); 534 if (node->symlink) 535 free(node->symlink); 536 free(node->name); 537 free(node); 538 return 0; 539} 540 541 542static int 543rootfs_readlink(void *_ns, void *_node, char *buf, size_t *bufsize) 544{ 545 nspace *ns; 546 vnode *node; 547 int err; 548 size_t l; 549 550 ns = (nspace *) _ns; 551 node = (vnode *) _node; 552 553 if (!MY_S_ISLNK(node->mode)) { 554 err = FS_EINVAL; 555 goto error1; 556 } 557 l = strlen(node->symlink); 558 if (l > *bufsize) 559 memcpy(buf, node->symlink, *bufsize); 560 else 561 memcpy(buf, node->symlink, l); 562 *bufsize = l; 563 return 0; 564 565error1: 566 return err; 567} 568 569static int 570rootfs_opendir(void *_ns, void *_node, void **cookie) 571{ 572 nspace *ns; 573 vnode *node; 574 int err; 575 dirpos *pos; 576 577 ns = (nspace *) _ns; 578 node = (vnode *) _node; 579 580 if (!MY_S_ISDIR(node->mode)) { 581 err = FS_ENOTDIR; 582 goto error1; 583 } 584 pos = (dirpos *) malloc(sizeof(dirpos)); 585 if (!pos) { 586 err = FS_ENOMEM; 587 goto error1; 588 } 589 if (new_lock(&pos->lock, "rootdirlock") < 0) { 590 err = FS_EINVAL; 591 goto error2; 592 } 593 pos->pos = 0; 594 pos->name[0] = '\0'; 595 *cookie = pos; 596 return 0; 597 598error2: 599 free(pos); 600error1: 601 return err; 602} 603 604static int 605rootfs_closedir(void *_ns, void *_node, void *_cookie) 606{ 607 return 0; 608} 609 610static int 611rootfs_free_dircookie(void *_ns, void *_node, void *_cookie) 612{ 613 nspace *ns; 614 vnode *node; 615 dirpos *cookie; 616 617 ns = (nspace *) _ns; 618 node = (vnode *) _node; 619 cookie = (dirpos *) _cookie; 620 621 free_lock(&cookie->lock); 622 free(cookie); 623 return 0; 624} 625 626static int 627rootfs_rewinddir(void *_ns, void *_node, void *_cookie) 628{ 629 nspace *ns; 630 vnode *node; 631 dirpos *cookie; 632 633 ns = (nspace *) _ns; 634 node = (vnode *) _node; 635 cookie = (dirpos *) _cookie; 636 637 LOCK(cookie->lock); 638 cookie->pos = 0; 639 cookie->name[0] = '\0'; 640 UNLOCK(cookie->lock); 641 return 0; 642} 643 644static int 645rootfs_readdir(void *_ns, void *_node, void *_cookie, long *num, 646 struct my_dirent *buf, size_t bufsize) 647{ 648 nspace *ns; 649 vnode *node; 650 dirpos *cookie; 651 char *e; 652 struct my_dirent *p; 653 long i; 654 vnode *vn; 655 vnode_id vnid; 656 char *name, *last = ""; 657 int sl, rl; 658 659 ns = (nspace *) _ns; 660 node = (vnode *) _node; 661 cookie = (dirpos *) _cookie; 662 663 LOCK(ns->lock); 664 LOCK(cookie->lock); 665 vn = node->head; 666 p = (struct my_dirent *) buf; 667 e = (char *) buf + bufsize; 668 if (cookie->pos > 2) 669 while (vn && (strcmp(cookie->name, vn->name) >= 0)) 670 vn = vn->next; 671 for(i=0; (i < *num) && ((cookie->pos < 2) || vn); i++, cookie->pos++) { 672 switch(cookie->pos) { 673 case 0: 674 name = "."; 675 vnid = node->vnid; 676 break; 677 case 1: 678 name = ".."; 679 vnid = node->parent->vnid; 680 break; 681 default: 682 name = vn->name; 683 vnid = vn->vnid; 684 vn = vn->next; 685 break; 686 } 687 sl = strlen(name) + 1; 688 rl = sizeof(struct my_dirent) + sl - 1; 689 if ((char *)p + rl > e) 690 break; 691 last = name; 692 p->d_reclen = (rl + 7) & ~7; 693 p->d_ino = vnid; 694 memcpy(p->d_name, name, sl); 695 p = (struct my_dirent *)((char *)p + p->d_reclen); 696 } 697 if ((cookie->pos > 2) && (i > 0)) 698 strcpy(cookie->name, last); 699 700 *num = i; 701 702 UNLOCK(cookie->lock); 703 UNLOCK(ns->lock); 704 return 0; 705} 706 707static int 708rootfs_rstat(void *_ns, void *_node, struct my_stat *st) 709{ 710 nspace *ns; 711 vnode *node; 712 713 ns = (nspace *) _ns; 714 node = (vnode *) _node; 715 716 LOCK(ns->lock); 717 st->dev = ns->nsid; 718 st->ino = node->vnid; 719 st->mode = node->mode; 720 st->nlink = 1; 721 st->uid = node->uid; 722 st->gid = node->gid; 723 st->size = 0; 724 st->blksize = 0; 725 st->atime = st->ctime = st->mtime = node->mtime; 726 UNLOCK(ns->lock); 727 return 0; 728} 729 730static int 731rootfs_wstat(void *_ns, void *_node, struct my_stat *st, long mask) 732{ 733 nspace *ns; 734 vnode *node; 735 736 ns = (nspace *) _ns; 737 node = (vnode *) _node; 738 739 if (mask & WSTAT_SIZE) 740 return FS_EINVAL; 741 742 LOCK(ns->lock); 743 744 if (mask & WSTAT_MODE) 745 node->mode = (node->mode & MY_S_IFMT) | (st->mode & ~MY_S_IFMT); 746 if (mask & WSTAT_UID) 747 node->uid = st->uid; 748 if (mask & WSTAT_GID) 749 node->gid = st->gid; 750 if (mask & WSTAT_MTIME) 751 node->mtime = st->mtime; 752 if (mask & WSTAT_ATIME) 753 node->mtime = st->atime; 754 755 UNLOCK(ns->lock); 756 return 0; 757} 758 759static int 760rootfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, 761 size_t len, void **data, vnode_id *vnid) 762{ 763 int err; 764 nspace *ns; 765 vnode *root; 766 vnode_id rvnid; 767 768 if (device || parms || (len != 0)) { 769 err = FS_EINVAL; 770 goto error1; 771 } 772 773 ns = (nspace *) malloc(sizeof(nspace)); 774 if (!ns) { 775 err = FS_ENOMEM; 776 goto error1; 777 } 778 779 root = (vnode *) malloc(sizeof(vnode)); 780 if (!root) { 781 err = FS_ENOMEM; 782 goto error2; 783 } 784 785 rvnid = 1; 786 787 ns->nsid = nsid; 788 ns->vnnum = 0; 789 ns->nxvnid = rvnid; 790 ns->root = root; 791 if (new_lock(&ns->lock, "rootfs") < 0) { 792 err = -1; 793 goto error3; 794 } 795 ns->skiplist = NewSL(&compare_vnode, NULL, NO_DUPLICATES); 796 if (!ns->skiplist) { 797 err = -1; 798 goto error4; 799 } 800 801 root->vnid = rvnid; 802 root->parent = root; 803 root->ns = ns; 804 root->removed = FALSE; 805 root->name = NULL; 806 root->next = root->prev = NULL; 807 root->head = NULL; 808 root->symlink = NULL; 809 810 /* ### do it for real */ 811 root->uid = 0; 812 root->gid = 0; 813 root->mode = MY_S_IFDIR | 0777; 814 root->mtime = time(NULL); 815 816 err = new_vnode(nsid, rvnid, root); 817 if (err) 818 goto error5; 819 820 *data = ns; 821 *vnid = rvnid; 822 823 return 0; 824 825error5: 826 FreeSL(ns->skiplist); 827error4: 828 free_lock(&ns->lock); 829error3: 830 free(root); 831error2: 832 free(ns); 833error1: 834 return err; 835} 836 837static int 838rootfs_unmount(void *_ns) 839{ 840 nspace *ns; 841 vnode *vn, *avn; 842 843 ns = (nspace *) _ns; 844 845 vn = ns->root; 846 while (TRUE) { 847 while(vn->head) 848 vn = vn->head; 849 vn = vn->parent; 850 if (vn == ns->root) 851 break; 852 avn = vn->head; 853 if (avn->prev) 854 avn->prev->next = avn->next; 855 else 856 vn->head = avn->next; 857 if (avn->next) 858 avn->next->prev = avn->prev; 859 rootfs_remove_vnode(ns, avn, TRUE); 860 } 861 free(ns->root); 862 free_lock(&ns->lock); 863 FreeSL(ns->skiplist); 864 free(ns); 865 return 0; 866} 867 868/* ### should do real permission check */ 869static int 870rootfs_access(void *_ns, void *_node, int mode) 871{ 872 return 0; 873} 874 875static int 876compare_vnode(vnode *vna, vnode *vnb) 877{ 878 if (vna->vnid > vnb->vnid) 879 return 1; 880 else 881 if (vna->vnid < vnb->vnid) 882 return -1; 883 else 884 return 0; 885} 886 887static int 888do_create(nspace *ns, vnode *dir, const char *name, mode_t mode, vnode **vnp) 889{ 890 int err; 891 int c; 892 vnode *vn, *pvn, *nvn; 893 char *buf; 894 vnode_id vnid; 895 896 if (!MY_S_ISDIR(dir->mode)) { 897 err = FS_ENOTDIR; 898 goto error1; 899 } 900 901 /* 902 make sure we are not trying to create something in a directory 903 that has been removed. 904 */ 905 906 if (dir->removed) { 907 err = FS_ENOENT; 908 goto error1; 909 } 910 911 /* 912 filter the name: 913 can't be '.', or '..' 914 */ 915 916 if (!strcmp(name, ".") || !strcmp(name, "..")) { 917 err = FS_EEXIST; 918 goto error1; 919 } 920 921 /* 922 lookup the name in the directory 923 */ 924 925 vn = dir->head; 926 while (vn) { 927 c = strcmp(name, vn->name); 928 if (c < 0) 929 vn = NULL; 930 if (c <= 0) 931 break; 932 vn = vn->next; 933 } 934 935 936 /* 937 if it was found, report an error. 938 */ 939 940 if (vn) { 941 err = FS_EEXIST; 942 goto error1; 943 } 944 945 /* 946 allocate a vnode and fill it 947 */ 948 949 vn = NULL; 950 buf = NULL; 951 vn = (vnode *) malloc(sizeof(vnode)); 952 buf = (char *) malloc(strlen(name)+1); 953 if (!vn || !buf) { 954 err = FS_ENOMEM; 955 goto error2; 956 } 957 strcpy(buf, name); 958 959 vnid = ++ns->nxvnid; 960 961 vn->vnid = vnid; 962 vn->parent = dir; 963 vn->ns = ns; 964 vn->removed = FALSE; 965 vn->name = buf; 966 967 vn->mode = mode; 968 vn->uid = 0; 969 vn->gid = 0; 970 dir->mtime = vn->mtime = time(NULL); 971 972 pvn = NULL; 973 nvn = dir->head; 974 while (nvn && (strcmp(name, nvn->name) > 0)) { 975 pvn = nvn; 976 nvn = nvn->next; 977 } 978 vn->next = nvn; 979 if (nvn) 980 nvn->prev = vn; 981 vn->prev = pvn; 982 if (pvn) 983 pvn->next = vn; 984 else 985 dir->head = vn; 986 987 vn->head = NULL; 988 vn->symlink = NULL; 989 990 atomic_add(&ns->vnnum, 1); 991 992 InsertSL(ns->skiplist, vn); 993 994 *vnp = vn; 995 996 return 0; 997 998error2: 999 if (vn) 1000 free(vn); 1001 if (buf) 1002 free(buf); 1003error1: 1004 return err; 1005} 1006 1007static int 1008do_unlink(nspace *ns, vnode *dir, const char *name, bool isdir) 1009{ 1010 int err; 1011 vnode *vn; 1012 1013 LOCK(ns->lock); 1014 if (!MY_S_ISDIR(dir->mode)) { 1015 err = FS_ENOTDIR; 1016 goto error1; 1017 } 1018 1019 /* 1020 can't delete '..' and '.' 1021 */ 1022 1023 if (!strcmp(name, "..") || !strcmp(name, ".")) { 1024 err = FS_EINVAL; 1025 goto error1; 1026 } 1027 1028 /* 1029 lookup the name in the directory 1030 */ 1031 1032 vn = dir->head; 1033 while (vn) { 1034 if (!strcmp(vn->name, name)) 1035 break; 1036 vn = vn->next; 1037 } 1038 1039 /* 1040 if it was not found, report an error. 1041 */ 1042 1043 if (!vn) { 1044 err = FS_ENOENT; 1045 goto error1; 1046 } 1047 1048 /* 1049 ensure it is of the appropriate type. 1050 */ 1051 1052 if (isdir && !(vn->mode & MY_S_IFDIR)) { 1053 err = FS_ENOTDIR; 1054 goto error1; 1055 } 1056 1057 if (!isdir && (vn->mode & MY_S_IFDIR)) { 1058 err = FS_EISDIR; 1059 goto error1; 1060 } 1061 1062 /* 1063 make sure it is not the root 1064 */ 1065 1066 if (vn == vn->ns->root) { 1067 err = FS_EBUSY; 1068 goto error1; 1069 } 1070 1071 /* 1072 if it is a directory, make sure it is empty 1073 */ 1074 1075 if (MY_S_ISDIR(vn->mode) && vn->head) { 1076 err = FS_ENOTEMPTY; 1077 goto error1; 1078 } 1079 1080 err = get_vnode(ns->nsid, vn->vnid, (void *)&vn); 1081 if (err) 1082 goto error1; 1083 1084 err = remove_vnode(ns->nsid, vn->vnid); 1085 if (err) 1086 goto error1; 1087 1088 if (vn->prev) 1089 vn->prev->next = vn->next; 1090 else 1091 vn->parent->head = vn->next; 1092 if (vn->next) 1093 vn->next->prev = vn->prev; 1094 vn->prev = vn->next = NULL; 1095 vn->removed = TRUE; 1096 dir->mtime = time(NULL); 1097 1098 put_vnode(ns->nsid, vn->vnid); 1099 1100 UNLOCK(ns->lock); 1101 1102 return 0; 1103 1104error1: 1105 UNLOCK(ns->lock); 1106 return err; 1107} 1108