Skip to contentSkip to conversation

symlink_prefix

DISCUSSION [email protected] 5 participants 7 messages Jun 3, 2001 – Jun 7, 2001

lore
Share & tools
lore
#0 lore raw

From [email protected] Sun Jun 3 02:49:08 2001

On Sun, 3 Jun 2001 [email protected] wrote:

> This evening I needed to work on a filesystem of a non-Linux OS, > full of absolute symlinks. After mounting the fs on /mnt, each > symlink pointing to /foo/bar in that filesystem should be > regarded as pointing to /mnt/foo/bar.
>
> Since doing ls -ld on every component of every pathname was > far too slow, I made a small kernel wart, where a mount option > -o symlink_prefix=/pathname would cause /pathname to be prepended > in front of every absolute symlink in the given filesystem > (when the symlink is followed). That works satisfactorily.

Absolute symlinks... Dunno. _If_ we want that at all, we probably want it on per-mountpoint basis. However, that opens a door to _really_ ugly feature requests. E.g. "if symlink starts with /foo - replace it with /mnt/bar, but if it starts with /foo/baz - replace with /mnt/splat instead".

Suppose I have devices /dev/a, /dev/b, /dev/c that contain the /, /usr and /usr/spool filesystems for FOO OS. Now
mount /dev/a /mnt -o symlink_prefix=/mnt mount /dev/b /mnt/usr -o symlink_prefix=/mnt mount /dev/c /mnt/usr/spool -o symlink_prefix=/mnt
suffice.
I do not immediately see a realistic use for changing symlink contents halfway.

I can see how to implement per-mountpoint variant. However, I'm less than enthusiastic about the API side of that and about the ugliness it will lead to. It smells like a wrong approach. And no, I don't see a good one right now.

As for the API... How would you pass that option? Yet another mount(2) argument?

What I did was: add a field `char *mnt_symlink_prefix;' to the struct vfsmount, fill it in super.c:add_vfsmnt(), use it in namei.c:vfs_follow_link(). Pick the value up by recognizing in super.c:do_mount() the option "symlink_prefix=" before giving the options to the separate filesystems.

[One could start a subdiscussion about that part. The mount(2) system call needs to transport vfs information and per-fs information. So far, the vfs information used flag bits only, but sooner or later we'll want to have strings, and need a vfs_parse_mount_options(). Indeed, many filesystems today have uid= and gid= and umask= options that might be removed from the individual filesystems and put into vfs. After all, such options are also useful for (foreign) ext2 filesystems.]

Andries

#1 lore raw
Quoted 6 lines · On Sun, 3 Jun 2001 [email protected] wrote:

What I did was: add a field `char *mnt_symlink_prefix;' to the struct vfsmount, fill it in super.c:add_vfsmnt(), use it in namei.c:vfs_follow_link(). Pick the value up by recognizing in super.c:do_mount() the option "symlink_prefix=" before giving the options to the separate filesystems.

Quoted 7 lines

[One could start a subdiscussion about that part. The mount(2) system call needs to transport vfs information and per-fs information. So far, the vfs information used flag bits only, but sooner or later we'll want to have strings, and need a vfs_parse_mount_options(). Indeed, many filesystems today have uid= and gid= and umask= options that might be removed from the individual filesystems and put into vfs. After all, such options are also useful for (foreign) ext2 filesystems.]

_Please_, if we do anything of that kind - let's use a new syscall. Ideally, I'd say

	fs_fd = open("/fs/ext2", O_RDWR);
	/* error -> no such filesystem */
	write(fs_fd. "/dev/sda1", strlen("/dev/sda1"));
	/* error handling */
	write(fs_fd, "reserve=5", strlen(....));
	...
	dir = open("/usr/local", O_DIRECTORY);
	/* error handling */
	new_mount(dir, MNT_SET, fs_fd);	/* closes dir and fs_fd */
	/* error handling */

First open gives you a new channel. Preferably - wit datagram semantics (i.e. write() boundaries are preserved). Then you convince fs driver to give you fs. Then you mount it.

Notice that all cruft with "mount ncpfs and then use ioctls to authenticate" goes away - authentication happens before you mount. Parsers are also easier that way. Moreover, seeing what filesystem types are available is also trivial, etc. We need only one special case - mounting that fstypefs. Fine, let's make new_mount(dir, MNT_TYPES) do that.

BTW, bind and friends are also easy - it's

	what = open(old, 0);
	where = open(mountpoint, 0);
	new_mount(where, MNT_BIND, what);

Comments?

#2 lore raw
Quoted 4 lines · Alexander Viro
        ...
        dir = open("/usr/local", O_DIRECTORY);
        /* error handling */
        new_mount(dir, MNT_SET, fs_fd); /* closes dir and fs_fd */

Do you really want to start using fds instead of strings for tree modifying commands (link, unlink, symlink, rename, mount and umount)? Even if it were possible in the new_mount case it wouldn't have the atomic lookup+act nature of the old mount. And then, _I_ would prefer a uniform interface for tree management commands - strings.

Ciao, ET.

#3 lore raw
Quoted 11 lines · Edgar Toernig
Quoted 4 lines · Alexander Viro
        ...
        dir = open("/usr/local", O_DIRECTORY);
        /* error handling */
        new_mount(dir, MNT_SET, fs_fd); /* closes dir and fs_fd */

Do you really want to start using fds instead of strings for tree modifying commands (link, unlink, symlink, rename, mount and umount)? Even if it were possible in the new_mount case it wouldn't have the atomic lookup+act nature of the old mount. And then, _I_ would prefer a uniform interface for tree management commands - strings.

You have exactly the same atomicity warranties. That is to say, none. Mountpoint can be renamed between the lookup and mounting.

Moreover, even after mount(2) you can rename() parent of mountpoint. On all Unices I've seen (well, aside of v7 which didn't have rename(2)). So if you rely on anything of that kind - you are screwed. Portably screwed, at that.

I would argue that mount(2) is seriously different from rename(2) and friends, but even if your argument makes sense, it only makes sense for "dir" argument. "device" is nothing but a filesystem-specific option.

#4 lore raw
Quoted 9 lines · Alexander Viro
Quoted 6 lines · Edgar Toernig
Quoted 4 lines · Alexander Viro
        ...
        dir = open("/usr/local", O_DIRECTORY);
        /* error handling */
        new_mount(dir, MNT_SET, fs_fd); /* closes dir and fs_fd */
Quoted 8 lines
Quoted 5 lines

Do you really want to start using fds instead of strings for tree modifying commands (link, unlink, symlink, rename, mount and umount)? Even if it were possible in the new_mount case it wouldn't have the atomic lookup+act nature of the old mount. And then, _I_ would prefer a uniform interface for tree management commands - strings.

You have exactly the same atomicity warranties. That is to say, none. Mountpoint can be renamed between the lookup and mounting.

Ok. I thought, mounting is an atomic operation (though normally not required). Hmm... but looking at your last batch of VFS patches sent to lkml you consider mount a more used call in the future ;-) Maybe it would be better to have some more strict rules for mount if ie each login performs a dozen of them...

Quoted 4 lines

Moreover, even after mount(2) you can rename() parent of mountpoint. On all Unices I've seen (well, aside of v7 which didn't have rename(2)). So if you rely on anything of that kind - you are screwed. Portably screwed, at that.

I thought more about a rename of ie "/usr/local" between the open and the new_mount call. I guess, an unlink("/usr/local") after the open will let the new_mount fail. Btw, what happens in this case of two concurrent mounts?

	fd1=open("/foo")
	fd2=open("/foo")
	new_mount(fd1...)
	new_mount(fd2...)	// or vice versa, first fd2 then fd1
Quoted 2 lines

[...] but even if your argument makes sense, it only makes sense for "dir" argument. "device" is nothing but a filesystem-specific option.

Sure. I only meant the "dir" argument.

Maybe I've just an uneasy feeling about such a change because it exposes and depends on internal implementation details of the kernel (the dcache). On other systems it's normally not possible to associate a unique name with a file descriptor. Newer Linux versions may support this for directories due to the dcache (not sure if this is really always the case). And this calling convention for new_mount would be the first one that makes this visible in userspace. And it would depend on this feature. This may limit future changes of the kernel VFS implementation (maybe someone really adds some kind of hardlinked directories or something else that makes it impossible to get a unique name for a dir fd).

Ciao, ET.

#5 lore raw
In reply to [email protected]
Quoted 6 lines · Alexander Viro

BTW, bind and friends are also easy - it's

	what = open(old, 0);
	where = open(mountpoint, 0);
	new_mount(where, MNT_BIND, what);

Comments?

What if what' and or where' aren't directories but e.g. sockets? (IOW, would this allow binding sockets into the filesystem, or am I being too perverse here?)

Happy Hacking

Signature · 1 line
Linux 2.4.5-ac6 #2 Sun Jun 3 15:45:27 CEST 2001
#6 lore raw
In reply to [email protected]

Hi!

Quoted 27 lines
Quoted 6 lines

What I did was: add a field `char *mnt_symlink_prefix;' to the struct vfsmount, fill it in super.c:add_vfsmnt(), use it in namei.c:vfs_follow_link(). Pick the value up by recognizing in super.c:do_mount() the option "symlink_prefix=" before giving the options to the separate filesystems.

Quoted 7 lines

[One could start a subdiscussion about that part. The mount(2) system call needs to transport vfs information and per-fs information. So far, the vfs information used flag bits only, but sooner or later we'll want to have strings, and need a vfs_parse_mount_options(). Indeed, many filesystems today have uid= and gid= and umask= options that might be removed from the individual filesystems and put into vfs. After all, such options are also useful for (foreign) ext2 filesystems.]

_Please_, if we do anything of that kind - let's use a new syscall. Ideally, I'd say

	fs_fd = open("/fs/ext2", O_RDWR);
	/* error -> no such filesystem */
	write(fs_fd. "/dev/sda1", strlen("/dev/sda1"));
	/* error handling */
	write(fs_fd, "reserve=5", strlen(....));
	...
	dir = open("/usr/local", O_DIRECTORY);
	/* error handling */
	new_mount(dir, MNT_SET, fs_fd);	/* closes dir and fs_fd */
	/* error handling */
Quoted 3 lines

First open gives you a new channel. Preferably - wit datagram semantics (i.e. write() boundaries are preserved). Then you convince fs driver to give you fs. Then you mount it.

Looks good. Will it be possible to emulate old mount in libc in (distant) future.
Pavel

Signature · 2 lines
I'm [email protected]. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents at [email protected]