Re: [AVFS] ~/.avfs and symlinks

Miklos Szeredi <[email protected]> Mon, 06 Mar 2006 12:08:11 +0100
Newsgroups gmane.comp.file-systems.avfs.user
Message-ID <[email protected]>
> On 2006-01-31 16:41, Peter wrote:
> > Don't know if this is even a bug, but avfs has problems when a symlinked
> > directory is used. For example,
> 
> I have looked into this problem and I think it's not a bug in avfs. The
> kernel asks the avfsd for the attributes for each part of the path. In
> case of a symlink the kernel asks avfsd to "readlink" and avfs returns
> the destination of the symlinks. If this is an absolute link, the
> destination is outside the avfs mount point and the kernel don't ask
> avfsd again.
> 
> The only solution I can think of is to concatenate the avfs mountpoint
> and the actual link destination for absolute links. On the one hand I
> don't like the idea of returning modified data but on the other hand the
> "file system in user space" can return whatever it want so it can also
> return a modified symlink destination.
> 
> Miklos, what do think about this. And is there an easy way to get the
> mount point (except parsing /proc/mounts)?

There are problems with that.  For example a filesystem may be mounted
in multiple places (mount --bind) or moved (mount --move).  A better
solution is to turn  absolute symlinks into relative ones.

For example: the link '/foo/link' pointing to '/bar/baz' could be
turned into a link pointing to '../bar/baz' etc.

> 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.

Miklos

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, char **linkp)
{
    const char *l = *linkp;
    const char *b = sshfs.base_path;
    char *newlink;
    char *s;
    int dotdots;
    int i;

    if (l[0] != '/' || b[0] != '/')
        return;

    strip_common(&l, &b);
    if (*b)
        return;

    strip_common(&l, &path);
    dotdots = count_components(path);
    if (!dotdots)
        return;
    dotdots--;

    newlink = malloc(dotdots * 3 + l ? strlen(l) : 1 + 10);
    if (!newlink) {
        fprintf(stderr, "sshfs: memory allocation failed\n");
        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';

    free(*linkp);
    *linkp = newlink;
}




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642