*BSD News Article 12696


Return to BSD News archive

Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!sun-barr!news2me.EBay.Sun.COM!exodus.Eng.Sun.COM!sun!amdcad!BitBlocks.com!bvs
From: bvs@BitBlocks.com (Bakul Shah)
Newsgroups: comp.os.386bsd.development
Subject: Re: A challenge to all true kernel hackers - conditional symlinks.
Message-ID: <C3ow4H.FID@BitBlocks.com>
Date: 10 Mar 93 20:12:16 GMT
References: <JKH.93Mar9214944@whisker.lotus.ie>
Organization: Bit Blocks, Inc.
Lines: 124

jkh@whisker.lotus.ie (Jordan K. Hubbard) writes:

>Finally (this is leading somewhere, honest) you had Apollo's
>beautifully simple, yet very powerful, mechanism for doing symlinks,
>which was to allow environment variables to be imbeded anywhere within
>a link name.  I.E.:

>	ln -s /usr/doc /usr/src/documents/$(LANG)

>Depending on the setting of the user's environment variable "LANG", /usr/doc
>might then point to /usr/src/documents/german, /usr/src/documents/french,
>etc.

Currently $, ( and ) are ordinary filename characters.  I
wouldn't want any special meaning ascribed to these three chars
and some other way of gaining the same functionality would be
preferable.

Though, the basic idea has merit (i.e. I like it!) and can be
done without further bloating the kernel.  A variant of open()
will allow you to do this and more.  Here is a sketch of this
idea.

- Create a new system call open1() that takes a file-descriptor
  of a directory and a path. Change things so that each process gets
  file-descriptors for `/' and the current directory (perhaps as
  `well-known' constants).

- open1(fd, path, path_size, flags, mode) will resolve the path
  relative to the directory `fd'.  If the entire path can be
  resolved, it returns a file-descriptor of the opened file and a
  null path.

- If only part of the path can be resolved, open1() returns the
  fd of the last component that could be mapped to a directory
  and the unresolved portion of the path.  Now the caller
  (typically a library routine) can do what it wants with the
  rest of the path.  If you want to implement conditional
  symlinks as above, it can look up the environment, replace the
  env. var with its value and call open1 again with the new path
  and fd.  If you want to splice in result of a command in the
  path, you can do that too.

Given open1(), our old friend open() can be safely removed from
the kernel.  It can be implemented as a library routine with
something like

open(char * path, int flags, int mode) {
	char buffer[LOTSASPACE];
	char buffer1[LOTSASPACE];
	int fd = CWD;
	extern int errno;
	char save;
	
	for (;;) { 
		int dd;
		/* Figure out where to start the search */
		if (path[0] == '/') {
			while (path[0] == '/')
				path++;
			dd = ROOT;
		} else
			dd = fd;
		strcpy(buffer, path);

		fd = open1(dd, buffer, sizeof buffer, flags, mode);

		if (fd < 0)			/* error? */
			return -1;

		if (strlen(buffer) == 0)	/* are we done? */
			return fd;

		if (buffer[0] == '$') {		/* env. variable? */
			/* splice in the var value before remaining path */
			char * var = ++buffer;
			while (*buffer && *buffer != '/')
				buffer++;
			save = *buffer;
			*buffer = '\0';
			var = getenv(var);
			if (!var)
				break;
			*buffer = save;
			sprintf(buffer1, "%s%s", var, buffer);
		} else if (buffer[0] == '`') {	/* command? */
			/* splice in the command result before remaining path */
			FILE * stream;
			char * command = ++buffer;

			while (*buffer && *buffer != '/')
				buffer++;
			save = *buffer;
			stream = popen(command);
			if (!stream)
				break;
			fread(buffer1, sizeof buffer1, 1, stream);
			/* XXX check we got the entire path */
			pclose(stream);
			strcat(buffer1, buffer);
		} else
			break;
		/* the newly doctored path is what we will work with now */
		path = buffer1;
	}
	/* here in case of an error */
	if (fd != CWD)
		close(fd);
	errno = ENOENT;
	return -1;
}

Like I said, this is merely a sketch; a real design will have to
take care of all sorts of details.  Implementing open1()
shouldn't be that hard though.  I would prefer a file mode for
such things instead of usurping $ etc. as normal chars.  In this
case you'd check the mode of returned fd instead of prefix of
returned path.  Also note that with this scheme even code for
following symlinks can be removed from the kernel.  On the negative
side you make a few more system calls.

Comments? 

Bakul Shah <bvs@BitBlocks.com>