Messages in this thread | | | Date | Mon, 20 Apr 1998 12:18:05 -0400 (EDT) | From | "Adam D. Bradley" <> | Subject | Re: PATCH: smart symlink loop detection. |
| |
Ok, I spent the weekend kludging around with ways of doing flat symlink resolution.
I took a look at how FreeBSD does it... apparently, when in their equivalent of lookup_dentry(), when they discover a symlink component in the pathname they overwrite it in-place with the symlink's "value", then re-start the resolution process using the new name (w/o changing "base"). If expanding a symlink would cause the buffer to overflow, they return -ENAMETOOLONG. Simple, graceful, makes sense, and allows "max-symlinks" to be implemented as a simple, flat counter/countdown. So I coded up a new version of lookup_dentry() that does this insatead, and it seems to work most of the time.
There's just one problem: I need to call inode->i_op->readlink() to get the "value" of a symlink. ext2_readlink (among others I suspect) uses copy_to_user(), which _sometimes_ returns -EFAULT when trying to write the symlink's text to the buffers I'm using. (This happens w/ all sorts of buffers: static char[]'s in the fn, kmalloc(PATH_MAX+1, GFP_{KERNEL,USER}), and get_free_page(GFP_{KERNEL,USER}).) My guess is it doesn't like writing to kernel-space buffers. BSD handles this using a flag in their "uio" structure. I'm sure this can be done in Linux, there's just something obvious I'm missing.
Another nit-pick - since there's no way for me to know (w/o using fs-specific code) the length of a symlink's value until I've copied it (return of i_op->readlink), I need to use two buffers. If there were a way to know beforehand, I could dispense with that second buffer (a second page of memory) and do everything in-place. This would, however, probably require a new i_op.
I also thought a little more about the linked list implementation, and it seems to me now a lot kludgier than when I first imagined it. Consider a list built from: struct name_list_node { struct qstr name; struct dentry *dentry; struct name_list *next; struct name_list *prev; } Presumably we can have the char * in qstr point to the d_name value of dentry, which saves a few kmalloc's, and reference counting should be handled correctly as long as (when we're done) we walk the list, dput the dentries and free the name_list_nodes. It also could be done w/ singly or doubly linked lists; doing inserts correctly with single-links requires a few extra copies, though. Either way, it's expensive compared with in-place string stuff, and still requires a one-page buffer to be allocated for getting symlink values in addition to the countless smaller kmalloc's for nodes.
Thoughts? Comments? Flames?
Adam -- You crucify all honesty \\Adam D. Bradley artdodge@cs.bu.edu No signs you see do you believe \\Boston University Computer Science And all your words just twist and turn\\ Grad Student and Linux Hacker Reviving just to crash and burn \\ <>< ---------> Why can't you listen as love screams everywhere? <--------
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu
| |