Re: [AVFS] ~/.avfs and symlinks

Ralf Hoffmann <[email protected]> Tue, 07 Mar 2006 23:01:08 +0100
Newsgroups gmane.comp.file-systems.avfs.user
Message-ID <[email protected]>
Hi,

On 2006-03-06 12:08, Miklos Szeredi wrote:
>> I'm not sure what will happen if someone does a chroot to the avfs
>> mountpoint. In this case the symlink rewriting could be wrong again.
> 
> Yes, that is also a problematic case.
> 
> Attached the symlink transformation in sshfs.  sshfs.base_path is the
> path within the remote filesystem which is mounted.  For example if
> you mount 'user@host:/', then base path is '/', if you mount
> 'user@host:/foo' then the base path is '/foo'.  If you mount
> 'user@host:', then the base path is '.' (which means the home
> directory), in this case the transformation doesn't work.

Is this code GPL'd?

I used the code to modify the readlink function, see the attached patch.
I still have some questions:

Is it possible to add a mount option or something to switch the symlink
rewriting on or off?

I think we don't need to take care of the mount point since it looks
like fuse passes the path without the mount point. But you are the
export in fuse, are my changes in transform_symlink okay?

>     newlink = malloc(dotdots * 3 + l ? strlen(l) : 1 + 10);

There is a bug in the malloc argument: the expression is equal to

(dotdots * 3 + l) ? strlen(l) : (l + 10)

which is not correct, it should be

dotdots * 3 + (*l ? strlen(l) : 1) + 10

Best Regards,

Ralf Hoffmann

-- 
Homepage: http://www.boomerangsworld.de
E-Mail: Ralf Hoffmann <[email protected]>
  english or german
avfsd.diff (text/x-patch, 2.5 KB)
diff --git a/fuse/avfsd.c b/fuse/avfsd.c
index bbadd6f..3a644ea 100644
--- a/fuse/avfsd.c
+++ b/fuse/avfsd.c
@@ -1,6 +1,7 @@
 /*
     FUSE: Filesystem in Userspace
     Copyright (C) 2001  Miklos Szeredi ([email protected])
+    Copyright (C) 2006  Ralf Hoffmann ([email protected])
 
     This program can be distributed under the terms of the GNU GPL.
     See the file COPYING.
@@ -16,6 +17,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <avfs.h>
 
 struct fuse *fuse;
 
@@ -34,6 +36,72 @@ static int avfsd_getattr(const char *pat
     return 0;
 }
 
+static int count_components(const char *p)
+{
+    int ctr;
+
+    for (; *p == '/'; p++);
+    for (ctr = 0; *p; ctr++) {
+        for (; *p && *p != '/'; p++);
+        for (; *p == '/'; p++);
+    }
+    return ctr;
+}
+
+static void strip_common(const char **sp, const char **tp)
+{
+    const char *s = *sp;
+    const char *t = *tp;
+    do {
+        for (; *s == '/'; s++);
+        for (; *t == '/'; t++);
+        *tp = t;
+        *sp = s;
+        for (; *s == *t && *s && *s != '/'; s++, t++);
+    } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
+}
+
+static void transform_symlink(const char *path, const char *linkdest, char **linkp)
+{
+    const char *l = linkdest;
+    char *newlink;
+    char *s;
+    int dotdots;
+    int i;
+
+    if (linkp == NULL)
+        return;
+    
+    *linkp = NULL;
+
+    if (l[0] != '/' || path[0] != '/')
+        return;
+
+    strip_common(&l, &path);
+
+    dotdots = count_components(path);
+    if (!dotdots)
+        return;
+    dotdots--;
+
+    newlink = malloc(dotdots * 3 + (*l ? strlen(l) : 1) + 10);
+    if (!newlink) {
+        av_log(AVLOG_ERROR, "readlink: memory allocation failed");
+        exit(1);
+    }
+    for (s = newlink, i = 0; i < dotdots; i++, s += 3)
+        strcpy(s, "../");
+
+    if (l[0])
+        strcpy(s, l);
+    else if (!dotdots)
+        strcpy(s, ".");
+    else
+        s[0] = '\0';
+
+    *linkp = newlink;
+}
+
 static int avfsd_readlink(const char *path, char *buf, size_t size)
 {
     int res;
@@ -43,6 +111,18 @@ static int avfsd_readlink(const char *pa
         return -errno;
 
     buf[res] = '\0';
+
+    if (buf[0] == '/') {
+        char *rel_link = NULL;
+        
+        transform_symlink(path, buf, &rel_link);
+        if (rel_link != NULL) {
+            strncpy(buf, rel_link, size - 1);
+            buf[size - 1] = '\0';
+            free(rel_link);
+        }
+    }
+
     return 0;
 }