Index: src/bin/ln/symlink.7 =================================================================== RCS file: /ncvs/src/bin/ln/symlink.7,v retrieving revision 1.30 diff -u -b -r1.30 symlink.7 --- src/bin/ln/symlink.7 13 Feb 2005 22:25:09 -0000 1.30 +++ src/bin/ln/symlink.7 4 Nov 2006 08:26:57 -0000 @@ -430,6 +430,91 @@ follows all symbolic links, regardless of their type, whether specified on the command line or encountered in the tree walk. +.Sh MAGIC SYMLINKS +Symlinks with the +.Dq magic +patterns in names expanded, if +.Nm vfs.magiclinks +variable set to 1. +Those patterns begin with +.Dq @ +.Pq an at-sign , +and end at the end of the pathname component +.Po +i.e. at the next +.Dq / , +or at the end of the symbolic link if there are no more slashes +.Pc . +.Pp +To illustrate the pattern matching rules, assume that +.Dq @foo +is a valid magic string: +.Pp +.Bl -tag -width @foo/barxxxxx -offset indent -compact +.It @foo +would be matched +.It @foo/bar +would be matched +.It bar@foo +would be matched +.It @foobar +would not be matched +.El +.Pp +Magic strings may also be delimited with +.Sq { +and +.Sq } +characters, allowing for more complex patterns in symbolic links such as: +.Bd -literal -offset indent +@{var1}-@{var2}.@{var3} +.Ed +.Pp +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 1 +file used to generate the running kernel +.Po +equivalent to the output of +.Dq sysctl -n kern.ident +.Pc . +.It @machine +Expands to the value of +.Li MACHINE +for the system +.Po +equivalent to the output of +.Dq uname -m +.Pc . +.It @machine_arch +Expands to the value of +.Li MACHINE_ARCH +for the system +.Po +equivalent to the output of +.Dq uname -p +.Pc . +.It @osrelease +Expands to the operating system release of the running kernel +.Po +equivalent to the output of +.Dq uname -r +.Pc . +.It @ostype +Expands to the operating system type of the running kernel +.Po +equivalent to the output of +.Dq uname -s +.Pc . +.El .Sh SEE ALSO .Xr chflags 1 , .Xr chgrp 1 , Index: src/sys/kern/vfs_lookup.c =================================================================== RCS file: /ncvs/src/sys/kern/vfs_lookup.c,v retrieving revision 1.95 diff -u -b -r1.95 vfs_lookup.c --- src/sys/kern/vfs_lookup.c 14 Sep 2006 17:57:02 -0000 1.95 +++ src/sys/kern/vfs_lookup.c 4 Nov 2006 08:27:20 -0000 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,131 @@ SYSCTL_INT(_vfs, OID_AUTO, lookup_shared, CTLFLAG_RW, &lookup_shared, 0, "Enables/Disables shared locks for path name translation"); +#ifdef MAGICLINKS +static int vfs_magiclinks = 1; +#else +static int vfs_magiclinks = 0; +#endif +SYSCTL_INT(_vfs, OID_AUTO, magiclinks, CTLFLAG_RW, &vfs_magiclinks, 0, + "Whether \"magic\" symlinks are expanded"); + +/* + * 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 "@" and "@/", where is a + * recognized 'magic' string. Replaces the "@" 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 (and replacement string lengths) made + * that impractical. + */ +#define VNL(x) \ + (sizeof(x) - 1) + +#define VO '{' +#define VC '}' + +#define MATCH(str) \ + ((termchar == '/' && i + VNL(str) == *len) || \ + (i + VNL(str) < *len && \ + cp[i + VNL(str)] == termchar)) && \ + !strncmp((str), &cp[i], VNL(str)) + +#define SUBSTITUTE(m, s, sl) \ + if ((newlen + (sl)) > MAXPATHLEN) \ + return (1); \ + i += VNL(m); \ + if (termchar != '/') \ + i++; \ + bcopy((s), &tmp[newlen], (sl)); \ + newlen += (sl); \ + change = 1; \ + termchar = '/'; + +#define PNBUF_GET() uma_zalloc(namei_zone, M_WAITOK) +#define PNBUF_PUT(item) uma_zfree(namei_zone, (item)) + +static int +symlink_magic(struct thread *td, char *cp, int *len) +{ + char *tmp; + int change, i, newlen; + int termchar = '/'; + + tmp = PNBUF_GET(); + for (change = i = newlen = 0; i < *len; ) { + if (cp[i] != '@') { + tmp[newlen++] = cp[i++]; + continue; + } + + i++; + + /* Check for @{var} syntax. */ + if (cp[i] == VO) { + termchar = VC; + 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")) { + char hname[MAXHOSTNAMELEN]; + getcredhostname(td->td_ucred, hname, sizeof(hname)); + SUBSTITUTE("hostname", hname, + strlen(hname)); + } else if (MATCH("osrelease")) { + SUBSTITUTE("osrelease", osrelease, + strlen(osrelease)); +/* } else if (MATCH("emul")) { + SUBSTITUTE("emul", p->p_emul->e_name, + strlen(p->p_emul->e_name)); */ + } else if (MATCH("kernel_ident")) { + SUBSTITUTE("kernel_ident", kern_ident, + strlen(kern_ident)); + } else if (MATCH("domainname")) { + SUBSTITUTE("domainname", domainname, + strlen(domainname)); + } else if (MATCH("ostype")) { + SUBSTITUTE("ostype", ostype, + strlen(ostype)); + } else { + tmp[newlen++] = '@'; + if (termchar == VC) + tmp[newlen++] = VO; + } + } + + if (change) { + bcopy(tmp, cp, newlen); + *len = newlen; + } + PNBUF_PUT(tmp); + + return (0); +} + +#undef VNL +#undef VO +#undef VC +#undef MATCH +#undef SUBSTITUTE +#undef PNBUF_GET +#undef PNBUF_PUT + /* * Convert a pathname into a pointer to a locked vnode. * @@ -275,7 +401,13 @@ error = ENOENT; break; } - if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { + /* + * Do symlink substitution, if appropriate, and + * check length for potential overflow. + */ + if ((vfs_magiclinks && + symlink_magic(td, cp, &linklen)) || + (linklen + ndp->ni_pathlen >= MAXPATHLEN)) { if (ndp->ni_pathlen > 1) uma_zfree(namei_zone, cp); error = ENAMETOOLONG;