sys_rename: allow arbitrary rename
authorRonald G. Minnich <rminnich@google.com>
Sat, 26 Jul 2014 18:00:18 +0000 (18:00 +0000)
committerRonald G. Minnich <rminnich@google.com>
Sun, 27 Jul 2014 20:25:51 +0000 (20:25 +0000)
This got a little tricky as we have to strip the mountpoint
base of the target file, and it was essentially impossible
to find the mountpoint after the fact. Don't know if this is the prettiest
way to do it but it works.

Signed-off-by: Ronald G. Minnich <rminnich@google.com>
kern/include/ns.h
kern/src/net/eipconv.c
kern/src/ns/chan.c
kern/src/syscall.c

index 0755e3b..2e08181 100644 (file)
@@ -385,6 +385,10 @@ struct chan {
                void *aux;
                char tag[4];                    /* for iproute */
        };
+       /* mountpoint, as discovered during walk.
+        * Used for rename at present.
+        */
+       struct chan *mountpoint;
        struct chan *mchan;                     /* channel to mounted server */
        struct qid mqid;                        /* qid of root of mount point */
        struct cname *name;
index 8be05bc..f6ddb8a 100644 (file)
@@ -115,13 +115,16 @@ void printqid(void (*putch) (int, void **), void **putdat, struct qid *q)
 
 void printcname(void (*putch) (int, void **), void **putdat, struct cname *c)
 {
-       printfmt(putch, putdat, "{ref %d, alen %d, len %d, s %s}",
-                kref_refcnt(&c->ref), c->alen, c->len, c->s);
+       if (c)
+               printfmt(putch, putdat, "{ref %d, alen %d, len %d, s %s}",
+                       kref_refcnt(&c->ref), c->alen, c->len, c->s);
 }
 
 void printchan(void (*putch) (int, void **), void **putdat, struct chan *c)
 {
-
+       if (! c)
+               return;
+       printfmt(putch, putdat, "(%p): ", c);
        printfmt(putch, putdat, "%slocked ", spin_locked(&c->lock) ? "":"un");
        printfmt(putch, putdat, "refs %p ", kref_refcnt(&c->ref));
 //     printfmt(putch, putdat, "%p ", struct chan *next,
@@ -142,7 +145,8 @@ void printchan(void (*putch) (int, void **), void **putdat, struct chan *c)
        printfmt(putch, putdat, "dri %p ", c->dri);
        printfmt(putch, putdat, "mountid %p ", c->mountid);
        printfmt(putch, putdat, "mntcache %p ", c->mcp);
-       printfmt(putch, putdat, "mnt %p ", c->mux);
+       printfmt(putch, putdat, "mux %p ", c->mux);
+       if (c->mux && c->mux->c)        printfmt(putch, putdat, "mux->c %p ", c->mux->c);
        printfmt(putch, putdat, "aux %p ", c->aux);
        printfmt(putch, putdat, "mchan %p ", c->mchan);
        printfmt(putch, putdat, "mqid %p ");
index 9aa7975..d2872a0 100644 (file)
@@ -658,7 +658,7 @@ static char Edoesnotexist[] = "does not exist";
 int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
 {
        int dev, dotdot, i, n, nhave, ntry, type;
-       struct chan *c, *nc;
+       struct chan *c, *nc, *lastmountpoint = NULL;
        struct cname *cname;
        struct mount *f;
        struct mhead *mh, *nmh;
@@ -672,13 +672,13 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
 
        /*
         * While we haven't gotten all the way down the path:
-        *    1. step through a mount po int unused_int, if any
+        *    1. step through a mount point, if any
         *    2. send a walk request for initial dotdot or initial prefix without dotdot
         *    3. move to the first mountpoint along the way.
         *    4. repeat.
         *
         * An invariant is that each time through the loop, c is on the undomount
-        * side of the mount po int unused_int, and c's name is cname.
+        * side of the mount point, and c's name is cname.
         */
        for (nhave = 0; nhave < nnames; nhave += n) {
                if ((c->qid.type & QTDIR) == 0) {
@@ -780,6 +780,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
                                        cclose(wq->clone);
                                        wq->clone = NULL;
                                }
+                               lastmountpoint = nc;
                                n = i + 1;
                        }
                        for (i = 0; i < n; i++)
@@ -804,6 +805,7 @@ int walk(struct chan **cp, char **names, int nnames, int nomount, int *nerror)
 
        cnameclose(c->name);
        c->name = cname;
+       c->mountpoint = lastmountpoint;
 
        cclose(*cp);
        *cp = c;
@@ -972,7 +974,6 @@ struct chan *namec(char *aname, int amode, int omode, uint32_t perm)
        if (name[0] == '\0')
                error("empty file name");
        validname(name, 1);
-
        /*
         * Find the starting off point (the current slash, the root of
         * a device tree, or the current dot) as well as the name to
@@ -1178,7 +1179,7 @@ Open:
 
                case Atodir:
                        /*
-                        * Directories (e.g. for cd) are left before the mount po int unused_int,
+                        * Directories (e.g. for cd) are left before the mount point,
                         * so one may mount on / or . and see the effect.
                         */
                        if (!(c->qid.type & QTDIR))
@@ -1198,7 +1199,6 @@ Open:
                         * We've walked to the place where it *could* be created.
                         * Return that chan.
                         */
-                       printk("Acreatechan: all but last? c is %p\n", c);
                        break;
 
                case Acreate:
index 29ef551..e3976d0 100644 (file)
@@ -1859,6 +1859,7 @@ intreg_t sys_rename(struct proc *p, char *old_path, size_t old_path_l,
                     char *new_path, size_t new_path_l)
 {
        ERRSTACK(1);
+       int mountpointlen = 0;
        char *from_path = user_strdup_errno(p, old_path, old_path_l);
        char *to_path = user_strdup_errno(p, new_path, new_path_l);
        struct chan *oldchan, *newchan = NULL;
@@ -1866,48 +1867,80 @@ intreg_t sys_rename(struct proc *p, char *old_path, size_t old_path_l,
 
        if ((!from_path) || (!to_path))
                return -1;
-       printk("sys_rename :%s: to :%s: : ", from_path, to_path);
+       printd("sys_rename :%s: to :%s: : ", from_path, to_path);
 
        /* we need a fid for the wstat. */
        oldchan = namec(from_path, Aaccess, 0, 0);
-       printk("Oldchan: %C\n", oldchan);
        if (! oldchan) {
-               printk("Could not get a chan for %s\n", from_path);
+               printd("Could not get a chan for %s\n", from_path);
                set_errno(ENOENT);
                goto done;
        }
-       /* the omode and perm are of no importance, we think. */
+
+       printd("Oldchan: %C\n", oldchan);
+       printd("Oldchan: mchan %C\n", oldchan->mchan);
+
+       /* If walked through a mountpoint, we need to take that
+        * into account for the Twstat.
+        */
+       if (oldchan->mountpoint) {
+               printd("mountpoint: %C\n", oldchan->mountpoint);
+               if (oldchan->mountpoint->name)
+                       mountpointlen = oldchan->mountpoint->name->len;
+       }
+
+       /* This test makes sense even when mountpointlen is 0 */
+       if (strlen(to_path) < mountpointlen) {
+               set_errno(EINVAL);
+               goto done;
+       }
+
+       /* the omode and perm are of no importance. */
        newchan = namec(to_path, Acreatechan, 0, 0);
        if (newchan == NULL) {
                printd("sys_rename %s to %s found no chan\n", from_path, to_path);
                set_errno(EPERM);
                goto done;
        }
-       printk("Newchan: %C\n", newchan);
+       printd("Newchan: %C\n", newchan);
+       printd("Newchan: mchan %C\n", newchan->mchan);
+
        if ((newchan->dev != oldchan->dev) || 
                (newchan->type != oldchan->type)) {
-               printk("Old chan and new chan do not match\n");
+               printd("Old chan and new chan do not match\n");
                set_errno(ENODEV);
                goto done;
        }
 
-       printk("let's do it. ");
        struct dir dir;
        size_t mlen;
        uint8_t mbuf[STATFIXLEN + MAX_PATH_LEN + 1];
 
        init_empty_dir(&dir);
        dir.name = to_path;
+       /* absolute paths need the mountpoint name stripped from them.
+        * Once stripped, it still has to be an absolute path.
+        */
+       if (dir.name[0] == '/') {
+               dir.name = to_path + mountpointlen;
+               if (dir.name[0] != '/') {
+                       set_errno(EINVAL);
+                       goto done;
+               }
+       }
+
        mlen = convD2M(&dir, mbuf, sizeof(mbuf));
        if (! mlen) {
                printk("convD2M failed\n");
                set_errno(EINVAL);
                goto done;
        }
+
        if (waserror()) {
                printk("validstat failed: %s\n", current_errstr());
                goto done;
        }
+
        validstat(mbuf, mlen, 1);
        poperror();
 
@@ -1918,6 +1951,7 @@ intreg_t sys_rename(struct proc *p, char *old_path, size_t old_path_l,
 
        retval = devtab[oldchan->type].wstat(oldchan, mbuf, mlen);
 
+       poperror();
        if (retval == mlen) {
                retval = mlen;
        } else {