How to redirect a symbolic link is an interesting topic.
First of all, there are two cases to consider: either the symlink points to an absolute pathname (one that starts with a leading / character) or it doesn't and hence is relative.
For the absolute pathname, we need to forget about the current path leading up to the symbolic link, and replace the entire path up to and including the symbolic link with the contents of the symbolic link:
ln -s /tmp /ramdisk/tempfiles
In that case, when we resolve /ramdisk/tempfiles, we will redirect the symlink to /tmp. However, in the relative case:
ln -s ../resume.html resume.htm
When we resolve the relative symlink, we need to preserve the existing pathname up to the symlink, and replace only the symlink with its contents. So, in our example above, if the path was /ramdisk/old/resume.htm, we would replace the symlink, resume.htm, with its contents, ../resume.html, to get the pathname /ramdisk/old/../resume.html as the redirection result. Someone else is responsible for resolving /ramdisk/old/../resume.html into /ramdisk/resume.html.
In both cases, we preserve the contents (if any) after the symlink, and simply append that to the substituted value.
Here is the redirect_symlink() function presented with comments so that you can see what's going on:
static void redirect_symlink (resmgr_context_t *ctp, struct _io_connect *msg, cfs_attr_t *attr, des_t *components, int ncomponents) { int eflag; int ftype; char newpath [PATH_MAX]; int i; char *p; struct _io_connect_link_reply link_reply; // 1) set up variables i = 1; p = newpath; *p = 0; // 2) a relative path, do up to the symlink itself if (*attr -> type.symlinkdata != '/') { // 3) relative -- copy up to and including for (; i < (ncomponents - 1); i++) { strcat (p, components [i].name); p += strlen (p); strcat (p, "/"); p++; } } else { // 4) absolute, discard up to and including i = ncomponents - 1; } // 5) now substitute the content of the symlink strcat (p, attr -> type.symlinkdata); p += strlen (p); // skip the symlink itself now that we've substituted it i++; // 6) copy the rest of the pathname components, if any for (; components [i].name && i < PATH_MAX; i++) { strcat (p, "/"); strcat (p, components [i].name); p += strlen (p); } // 7) preserve these, wipe rest eflag = msg -> eflag; ftype = msg -> file_type; memset (&link_reply, 0, sizeof (link_reply)); // 8) set up the reply _IO_SET_CONNECT_RET (ctp, _IO_CONNECT_RET_LINK); link_reply.file_type = ftype; link_reply.eflag = eflag; link_reply.path_len = strlen (newpath) + 1; SETIOV (&ctp -> iov [0], &link_reply, sizeof (link_reply)); SETIOV (&ctp -> iov [1], newpath, link_reply.path_len); MsgReplyv (ctp -> rcvid, ctp -> status, ctp -> iov, 2); }
So basically, the main trick was in performing the symlink substitution, and setting the flag to indicate redirection.