Subject: kern/1781: 'magic' symbolic link expansion
To: None <gnats-bugs@gnats.netbsd.org>
From: Chris G. Demetriou <cgd@NetBSD.ORG>
List: netbsd-bugs
Date: 11/22/1995 20:30:00
>Number: 1781
>Category: kern
>Synopsis: system-specific special expansions in symlinks.
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: kern-bug-people (Kernel Bug People)
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Wed Nov 22 20:50:08 1995
>Last-Modified:
>Originator: Chris G. Demetriou
>Organization:
Kernel Hackers 'r' Us
>Release: NetBSD-current (trunk), 11/22/95
>Environment:
System: NetBSD sun-lamp.pc.cs.cmu.edu 1.0A NetBSD 1.0A (SUN_LAMP) #9: Wed Nov 22 16:05:06 EST 1995 cgd@sun-lamp.pc.cs.cmu.edu:/usr/src/sys/arch/i386/compile/SUN_LAMP i386
>Description:
Expansion of 'special' strings in symlinks into system-specific
values is a very useful feature in some situations, like:
(1) when booting diskless clients (to help find various
configuration files, etc., when sharing the same
root partition), and
(2) when trying to mount root from a CD-ROM that
contains binaries for several architectures.
NetBSD currently has no such facility, and, because of the
problems with doing multiple-architecture CD-ROMs, it arguably
should. I've implemented solution that enables "magic"
symlink expansion on a per-file system basis. They are enabled
by mounting a file system with the 'magiclinks' mount option,
which sets the MNT_MAGICLINKS mount flag.
On file systems with the MNT_MAGICLINKS flag set, the name
lookup code replaces the "magic" strings in symlinks with
the appropriate replacement text. Magic strings are
of the form "@name", and must end at the end of a pathname
component. That is, assuming "@foo" is a valid magic
string, symlinks containing:
"@foo" would be matched
"@foo/bar" would be matched
"bar@foo" would be matched
"@foobar" would not be matched
etc. The entire "@name" string is replaced by the
replacement text. If, for some reason the replacement
text is empty, the string will be replaced by an
empty string. This can lead to strange results
if the magic string is the only thing in the symlink,
but they're the same as you get when using a sylink
that points to "" (i.e. which is empty).
To get this behaviour for the root file system at
'mountroot' time, the ROOTFS_MAGICLINKS kernel option
is provided. If used, the root file system will be
mounted with MNT_MAGICLINKS set.
The "magic strings" that are supported by my implementation
(the diff for which is included below) are:
@machine_arch value of MACHINE_ARCH for the system.
@machine value of MACHINE for the system.
@hostname the system hostname, as set
with sethostname().
@osrelease the release number of the OS
as defined in /sys/conf/newvers.sh
(e.g. "1.1" for NetBSD 1.1)....
@kernel_ident the kernel config file name
as defined in /sys/conf/newvers.sh
(e.g. "GENERIC" for the kernel
compiles from a kernel config named
"GENERIC")
@domainname the system domainname, as set
with setdomainname().
@ostype the name of the OS. For NetBSD,
this is "NetBSD". I've included
this so that if other operating
systems use this code, they can
fill in their own OS name.
This could, for instance,
make alternately booting NetBSD
and FreeBSD on a single disk
easier.
As noted, if this is picked up for NetBSD, it'd be nice to see
other OS's use the same magic strings and semantics, if not
the same implementation, if they desire similar functionality.
>How-To-Repeat:
[ Not applicable. ]
>Fix:
Apply the following patch. (NOTE: if you don't have
the patch in pr 1776 applied to your /bin/sh, it
will do bad things when traversing 'magic' symlinks.)
The files changed by the patch are:
sys/sys/mount.h
add MNT_MAGICLINKS flag.
sbin/mount/mntopts.h
sbin/mount/mount.c
add a mapping between the 'magiclinks' mount
option and the MNT_MAGICLINKS mount flag.
sys/kern/vfs_lookup.c
do magic link substitution for symlinks on file
systems that have MNT_MAGICLINKS set.
sys/kern/vfs_syscalls.c
Recognize the MNT_MAGICLINKS options, and
allow it to be used.
sys/kern/init_main.c
implement ROOTFS_MAGICLINKS.
sys/conf/newvers.sh
add 'kernel_ident' variable, to support
@kernel_ident.
bin/ln/symlink.7
lib/libc/sys/mount.2
sbin/mount/mount.8
documentation.
Index: bin/ln/symlink.7
===================================================================
RCS file: /a/cvsroot/src/bin/ln/symlink.7,v
retrieving revision 1.3
diff -c -r1.3 symlink.7
*** symlink.7 1995/03/21 09:06:13 1.3
--- symlink.7 1995/11/23 01:04:57
***************
*** 412,417 ****
--- 412,464 ----
or
.Fl P
options.
+ .Sh MAGIC SYMLINKS
+ Symlinks in file systems with the
+ .Li MNT_MAGICLINKS
+ flag set have
+ .Dq magic
+ patterns in symlinks expanded. Those patterns begin with
+ .Dq @
+ (an at-sign), and end at the end of the pathname component
+ (i.e. at the next
+ .Dq / ,
+ or at the end of the symbolic link if there are no more slashes).
+ The following patterns are supported:
+ .Bl -tag -width @machine_arch
+ .It @domainname
+ Expands to the machine's domain name, as set by
+ .Xr setdomainname 3 .
+ .It @hostname
+ Expands to the machine's host name, as set by
+ .Xr sethostname 3 .
+ .It @kernel_ident
+ Expands to the name of the
+ .Xr config 8
+ file used to generate the running kernel.
+ .It @machine
+ Expands to the value of
+ .Li MACHINE
+ for the system (also, the same as
+ .Xr make 1 's
+ .Li ${MACHINE}
+ variable).
+ .It @machine_arch
+ Expands to the value of
+ .Li MACHINE_ARCH
+ for the system (also, the same as
+ .Xr make 1 's
+ .Li ${MACHINE_ARCH}
+ variable).
+ .It @osrelease
+ Expands to the operating system release of the running kernel.
+ .It @ostype
+ Expands to the operating system type of the running kernel.
+ (This will always be
+ .Dq NetBSD
+ for
+ NetBSD
+ systems.)
+ .El
.Sh SEE ALSO
.Xr chflags 1 ,
.Xr chgrp 1 ,
***************
*** 426,431 ****
--- 473,479 ----
.Xr rm 1 ,
.Xr tar 1 ,
.Xr lstat 2 ,
+ .Xr mount 2 ,
.Xr readlink 2 ,
.Xr rename 2 ,
.Xr unlink 2 ,
Index: lib/libc/sys/mount.2
===================================================================
RCS file: /a/cvsroot/src/lib/libc/sys/mount.2,v
retrieving revision 1.9
diff -c -r1.9 mount.2
*** mount.2 1995/10/12 15:41:07 1.9
--- mount.2 1995/11/23 01:04:58
***************
*** 78,83 ****
--- 78,89 ----
may be specified to
suppress default semantics which affect file system access.
.Bl -tag -width MNT_SYNCHRONOUS
+ .It Dv MNT_MACIGLINKS
+ Expand special strings (beginning with
+ .Dq @ )
+ when traversing symbolic links. See
+ .Xr symlink 7
+ for a list of supported strings.
.It Dv MNT_RDONLY
The file system should be treated as read-only;
Even the super-user may not write on it.
Index: sbin/mount/mntopts.h
===================================================================
RCS file: /a/cvsroot/src/sbin/mount/mntopts.h,v
retrieving revision 1.3
diff -c -r1.3 mntopts.h
*** mntopts.h 1995/03/18 14:56:59 1.3
--- mntopts.h 1995/11/23 01:04:58
***************
*** 43,48 ****
--- 43,49 ----
/* User-visible MNT_ flags. */
#define MOPT_ASYNC { "async", 0, MNT_ASYNC }
+ #define MOPT_MAGICLINKS { "magiclinks", 0, MNT_MAGICLINKS }
#define MOPT_NODEV { "dev", 1, MNT_NODEV }
#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC }
#define MOPT_NOSUID { "suid", 1, MNT_NOSUID }
***************
*** 74,79 ****
--- 75,81 ----
MOPT_USERQUOTA, \
MOPT_GROUPQUOTA, \
MOPT_FSTAB_COMPAT, \
+ MOPT_MAGICLINKS, \
MOPT_NODEV, \
MOPT_NOEXEC, \
MOPT_NOSUID, \
Index: sbin/mount/mount.8
===================================================================
RCS file: /a/cvsroot/src/sbin/mount/mount.8,v
retrieving revision 1.11
diff -c -r1.11 mount.8
*** mount.8 1995/07/12 06:23:21 1.11
--- mount.8 1995/11/23 01:04:58
***************
*** 128,133 ****
--- 128,139 ----
.Fl f ;
forces the revocation of write access when trying to downgrade
a filesystem mount status from read-write to read-only.
+ .It magiclinks
+ Expand special strings (beginning with
+ .Dq @ )
+ when traversing symbolic links. See
+ .Xr symlink 7
+ for a list of supported strings.
.It nodev
Do not interpret character or block special devices on the file system.
This option is useful for a server that has file systems containing
Index: sbin/mount/mount.c
===================================================================
RCS file: /a/cvsroot/src/sbin/mount/mount.c,v
retrieving revision 1.24
diff -c -r1.24 mount.c
*** mount.c 1995/11/18 03:34:29 1.24
--- mount.c 1995/11/23 01:04:58
***************
*** 90,95 ****
--- 90,96 ----
{ MNT_EXPORTANON, 1, "anon uid mapping" },
{ MNT_EXRDONLY, 1, "exported read-only" },
{ MNT_LOCAL, 0, "local" },
+ { MNT_MAGICLINKS, 0, "magiclinks" },
{ MNT_NODEV, 0, "nodev" },
{ MNT_NOEXEC, 0, "noexec" },
{ MNT_NOSUID, 0, "nosuid" },
Index: sys/conf/newvers.sh
===================================================================
RCS file: /a/cvsroot/src/sys/conf/newvers.sh,v
retrieving revision 1.17
diff -c -r1.17 newvers.sh
*** newvers.sh 1994/07/10 22:26:35 1.17
--- newvers.sh 1995/11/23 01:05:00
***************
*** 54,58 ****
--- 54,59 ----
"char version[] = \
\"${ost} ${osr} (${id}) #${v}: ${t}\\n ${u}@${h}:${d}\\n\";" \
>> vers.c
+ echo "char kernel_ident[] = \"${id}\";" >> vers.c
echo `expr ${v} + 1` > version
Index: sys/kern/init_main.c
===================================================================
RCS file: /a/cvsroot/src/sys/kern/init_main.c,v
retrieving revision 1.78
diff -c -r1.78 init_main.c
*** init_main.c 1995/10/07 06:28:05 1.78
--- init_main.c 1995/11/23 01:05:00
***************
*** 296,301 ****
--- 296,304 ----
if ((*mountroot)())
panic("cannot mount root");
mountlist.cqh_first->mnt_flag |= MNT_ROOTFS;
+ #ifdef ROOTFS_MAGICLINKS
+ mountlist.cqh_first->mnt_flag |= MNT_MAGICLINKS;
+ #endif
mountlist.cqh_first->mnt_op->vfs_refcount++;
/* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
Index: sys/kern/vfs_lookup.c
===================================================================
RCS file: /a/cvsroot/src/sys/kern/vfs_lookup.c,v
retrieving revision 1.15
diff -c -r1.15 vfs_lookup.c
*** vfs_lookup.c 1995/03/08 01:20:50 1.15
--- vfs_lookup.c 1995/11/23 01:05:00
***************
*** 42,47 ****
--- 42,48 ----
#include <sys/param.h>
#include <sys/systm.h>
+ #include <sys/kernel.h>
#include <sys/syslimits.h>
#include <sys/time.h>
#include <sys/namei.h>
***************
*** 56,61 ****
--- 57,64 ----
#include <sys/ktrace.h>
#endif
+ int symlink_magic __P((char *cp, int *len));
+
/*
* Convert a pathname into a pointer to a locked inode.
*
***************
*** 184,190 ****
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
! if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
if (ndp->ni_pathlen > 1)
free(cp, M_NAMEI);
error = ENAMETOOLONG;
--- 187,199 ----
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
! /*
! * Do symlink substitution, if appropriate, and
! * check length for potential overflow.
! */
! if ((ndp->ni_vp->v_mount->mnt_flag & MNT_MAGICLINKS
! && symlink_magic(cp, &linklen)) ||
! (linklen + ndp->ni_pathlen >= MAXPATHLEN)) {
if (ndp->ni_pathlen > 1)
free(cp, M_NAMEI);
error = ENAMETOOLONG;
***************
*** 205,210 ****
--- 214,307 ----
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
return (error);
+ }
+
+ /*
+ * Substitute replacement text for 'magic' strings in symlinks.
+ * Returns 0 if successful, and returns non-zero if an error
+ * occurs. (Currently, the only possible error is running
+ * out of temporary pathname space.)
+ *
+ * Looks for "@<string>" and "@<string>/", where <string> is a
+ * recognized 'magic' string. Replaces the "@<string>" with
+ * the appropriate replacement text. (Note that in some
+ * cases the replacement text may have zero length.)
+ *
+ * This would have been table driven, but the variance in
+ * replacement strings (& replacement string lengths) made
+ * that impractical.
+ */
+
+ #define MATCH(str) \
+ ((i + (sizeof(str) - 1) == *len) || \
+ ((i + (sizeof(str) - 1) < *len) && \
+ (cp[i + sizeof(str) - 1] == '/'))) && \
+ !strncmp((str), &cp[i], sizeof(str) - 1)
+
+ #define SUBSTITUTE(m, s, sl) \
+ if ((newlen + (sl)) > MAXPATHLEN) \
+ return (1); \
+ i += sizeof(m) - 1; \
+ bcopy((s), &tmp[newlen], (sl)); \
+ newlen += (sl); \
+ change = 1;
+
+ int
+ symlink_magic(cp, len)
+ char *cp;
+ int *len;
+ {
+ char tmp[MAXPATHLEN];
+ int change, i, newlen;
+
+ for (change = i = newlen = 0; i < *len; ) {
+ if (cp[i] != '@')
+ tmp[newlen++] = cp[i++];
+ else {
+ i++;
+ /*
+ * The following checks should be ordered
+ * according to frequency of use.
+ */
+ if (MATCH("machine_arch")) {
+ SUBSTITUTE("machine_arch", MACHINE_ARCH,
+ sizeof(MACHINE_ARCH) - 1);
+ } else if (MATCH("machine")) {
+ SUBSTITUTE("machine", MACHINE,
+ sizeof(MACHINE) - 1);
+ } else if (MATCH("hostname")) {
+ SUBSTITUTE("hostname", hostname,
+ hostnamelen);
+ } else if (MATCH("osrelease")) {
+ extern char osrelease[];
+
+ SUBSTITUTE("osrelease", osrelease,
+ strlen(osrelease));
+ } else if (MATCH("kernel_ident")) {
+ extern char kernel_ident[];
+
+ SUBSTITUTE("kernel_ident", kernel_ident,
+ strlen(kernel_ident));
+ } else if (MATCH("domainname")) {
+ SUBSTITUTE("domainname", domainname,
+ domainnamelen);
+ } else if (MATCH("ostype")) {
+ extern char ostype[];
+
+ SUBSTITUTE("ostype", ostype,
+ strlen(ostype));
+ } else
+ tmp[newlen++] = '@';
+ }
+ }
+
+ if (!change)
+ return (0);
+
+ bcopy(tmp, cp, newlen);
+ *len = newlen;
+
+ return (0);
}
/*
Index: sys/kern/vfs_syscalls.c
===================================================================
RCS file: /a/cvsroot/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.59
diff -c -r1.59 vfs_syscalls.c
*** vfs_syscalls.c 1995/11/11 22:00:18 1.59
--- vfs_syscalls.c 1995/11/23 01:05:02
***************
*** 228,236 ****
else if (mp->mnt_flag & MNT_RDONLY)
mp->mnt_flag |= MNT_WANTRDWR;
mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
! MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC |
! MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);
/*
* Mount the filesystem.
*/
--- 228,237 ----
else if (mp->mnt_flag & MNT_RDONLY)
mp->mnt_flag |= MNT_WANTRDWR;
mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
! MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_MAGICLINKS);
mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC |
! MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC |
! MNT_MAGICLINKS);
/*
* Mount the filesystem.
*/
Index: sys/sys/mount.h
===================================================================
RCS file: /a/cvsroot/src/sys/sys/mount.h,v
retrieving revision 1.44
diff -c -r1.44 mount.h
*** mount.h 1995/11/11 22:00:21 1.44
--- mount.h 1995/11/23 01:05:03
***************
*** 119,125 ****
};
/*
! * Mount flags.
*
* Unmount uses MNT_FORCE flag.
*/
--- 119,125 ----
};
/*
! * Mount flags: FLAGS MAY BE OUT OF ORDER, for backward compatibility.
*
* Unmount uses MNT_FORCE flag.
*/
***************
*** 130,135 ****
--- 130,136 ----
#define MNT_NODEV 0x00000010 /* don't interpret special files */
#define MNT_UNION 0x00000020 /* union with underlying filesystem */
#define MNT_ASYNC 0x00000040 /* file system written asynchronously */
+ #define MNT_MAGICLINKS 0x00008000 /* interpret symlinks for magic names */
/*
* exported mount flags.
>Audit-Trail:
>Unformatted: