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