PATCH: Almost flat link resolution

Adam D. Bradley (artdodge@cs.bu.edu)
Wed, 29 Apr 1998 23:51:06 -0400 (EDT)


Following is a patch that attempts to flatten the link resolution done
by the VFS through lookup_dentry() and do_follow_link().

I used a few tricks in combination to guarantee (I think) that nothing
that works now will break under the new scheme. Particularly:
(1) The follow_link / readlink disparity:
I changed the prototype for readlink to accept an additional
integer pointer, for "needs_followlink". If this value is set
to 0, then the value returned by readlink can be used for name
lookups. If set to non-zero, then the value returned by
readlink should be interpreted as "decorative", and
follow_link must still be used for actual lookups. This
ensures that all of the /proc magic continues to work.
(2) Link limits
There is now an explicit counter for the number of links that
have been traversed. This value is placed in
task_struct->link_count. Another value is added to
task_struct to assume the (misnomered) role link_count used to
play.
(3) Recursion can still happen, but.......
Recursion can only occur under one of two conditions:
(a) When the filesystem sets "needs_followlink" and calls
lookup_dentry from within its follow_link()
(b) When a symbolic link's value will not fit into the
name-construction buffer.
This case can be disabled by defining
"NO_RECURSIVE_EXPANDS"
Recursion is limited to 5 times (just as before), using the
new task_struct->recursion_count. (This is what
task_struct->link_count used to do.)

Caveats:
--------

This approach does a good amount of string-copying, and dynamically
allocates two one-page buffers for the resolution process. It can no
doubt also be optimized in lots of ways. This is just a
proof-of-concept, with the hopes that it'll generate some constructive
criticism, other ideas, etc.

I'm a little unclear on when I'm allowed to return ELOOP as opposed to
ENAMETOOLONG; can someone quote the POSIX/Unix98 spec on this for me?

This patch is running on my 2.1.98 system without problems. Speed is
negligibly (within 5%) slower than with the vanilla lookup scheme
(tested with multiple repeated "find / -follow"s, "ls -LR /"s, and
similar things.)

Comments/flames/etc welcomed.

(PS - I didn't touch the stuff in the asm-sparc and asm-sparc64, the
comment says asm_offsets.h is auto-generated so I left it alone...)

diff -u --recursive --new-file linux-2.1.vanilla/fs/affs/symlink.c linux/fs/affs/symlink.c
--- linux-2.1.vanilla/fs/affs/symlink.c Wed Jan 14 23:51:44 1998
+++ linux/fs/affs/symlink.c Sat Apr 25 16:21:27 1998
@@ -19,7 +19,7 @@

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

-static int affs_readlink(struct dentry *, char *, int);
+static int affs_readlink(struct dentry *, char *, int, int *);
static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base);

struct inode_operations affs_symlink_inode_operations = {
@@ -44,7 +44,7 @@
};

static int
-affs_readlink(struct dentry *dentry, char *buffer, int buflen)
+affs_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
@@ -94,6 +94,7 @@
i++, j++;
}
affs_brelse(bh);
+ *needs_follow = 0;
return i;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- linux-2.1.vanilla/fs/autofs/symlink.c Wed Jan 14 23:51:14 1998
+++ linux/fs/autofs/symlink.c Sat Apr 25 16:22:25 1998
@@ -14,7 +14,7 @@
#include <linux/sched.h>
#include "autofs_i.h"

-static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
struct autofs_symlink *sl;
int len;
@@ -23,6 +23,7 @@
len = sl->len;
if (len > buflen) len = buflen;
copy_to_user(buffer, sl->data, len);
+ *needs_follow = 0;
return len;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/coda/symlink.c linux/fs/coda/symlink.c
--- linux-2.1.vanilla/fs/coda/symlink.c Sat Mar 7 13:35:19 1998
+++ linux/fs/coda/symlink.c Sat Apr 25 16:23:33 1998
@@ -24,7 +24,7 @@
#include <linux/coda_fs_i.h>
#include <linux/coda_cache.h>

-static int coda_readlink(struct dentry *de, char *buffer, int length);
+static int coda_readlink(struct dentry *de, char *buffer, int length, int *needs_follow);
static struct dentry *coda_follow_link(struct dentry *, struct dentry *);

struct inode_operations coda_symlink_inode_operations = {
@@ -50,7 +50,7 @@
NULL /* revalidate */
};

-static int coda_readlink(struct dentry *de, char *buffer, int length)
+static int coda_readlink(struct dentry *de, char *buffer, int length, int *needs_follow)
{
struct inode *inode = de->d_inode;
int len;
@@ -81,6 +81,8 @@
}
if ( buf )
CODA_FREE(buf, len);
+
+ *needs_follow = 0;
return error;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- linux-2.1.vanilla/fs/ext2/symlink.c Wed Jan 14 23:51:14 1998
+++ linux/fs/ext2/symlink.c Sat Apr 25 16:24:22 1998
@@ -24,7 +24,7 @@
#include <linux/mm.h>
#include <linux/stat.h>

-static int ext2_readlink (struct dentry *, char *, int);
+static int ext2_readlink (struct dentry *, char *, int, int *);
static struct dentry *ext2_follow_link(struct dentry *, struct dentry *);

/*
@@ -74,7 +74,7 @@
return base;
}

-static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
+static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head * bh = NULL;
@@ -104,5 +104,7 @@
UPDATE_ATIME(inode);
if (bh)
brelse (bh);
+
+ *needs_follow = 0;
return i;
}
diff -u --recursive --new-file linux-2.1.vanilla/fs/isofs/symlink.c linux/fs/isofs/symlink.c
--- linux-2.1.vanilla/fs/isofs/symlink.c Wed Jan 14 23:51:15 1998
+++ linux/fs/isofs/symlink.c Sat Apr 25 16:25:45 1998
@@ -18,7 +18,7 @@

#include <asm/uaccess.h>

-static int isofs_readlink(struct dentry *, char *, int);
+static int isofs_readlink(struct dentry *, char *, int, int *);
static struct dentry * isofs_follow_link(struct dentry *, struct dentry *);

/*
@@ -44,7 +44,7 @@
NULL /* permission */
};

-static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen)
+static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
char * pnt;
int i;
@@ -62,6 +62,8 @@
if (copy_to_user(buffer, pnt, i))
i = -EFAULT;
kfree(pnt);
+
+ *needs_follow = 0;
return i;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/minix/symlink.c linux/fs/minix/symlink.c
--- linux-2.1.vanilla/fs/minix/symlink.c Wed Jan 14 23:51:16 1998
+++ linux/fs/minix/symlink.c Sat Apr 25 16:26:19 1998
@@ -14,7 +14,7 @@

#include <asm/uaccess.h>

-static int minix_readlink(struct dentry *, char *, int);
+static int minix_readlink(struct dentry *, char *, int, int *);
static struct dentry *minix_follow_link(struct dentry *, struct dentry *);

/*
@@ -57,7 +57,7 @@
return base;
}

-static int minix_readlink(struct dentry * dentry, char * buffer, int buflen)
+static int minix_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
struct buffer_head * bh;
int i;
@@ -74,5 +74,7 @@
put_user(c,buffer++);
}
brelse(bh);
+
+ *needs_follow = 0;
return i;
}
diff -u --recursive --new-file linux-2.1.vanilla/fs/namei.c linux/fs/namei.c
--- linux-2.1.vanilla/fs/namei.c Wed Jan 14 23:51:46 1998
+++ linux/fs/namei.c Sat Apr 25 18:52:13 1998
@@ -303,13 +303,13 @@
struct inode * inode = dentry->d_inode;

if (inode && inode->i_op && inode->i_op->follow_link) {
- if (current->link_count < 5) {
+ if (current->recursion_count < 5) {
struct dentry * result;

- current->link_count++;
+ current->recursion_count++;
/* This eats the base */
result = inode->i_op->follow_link(dentry, base);
- current->link_count--;
+ current->recursion_count--;
dput(dentry);
return result;
}
@@ -333,69 +333,118 @@
}

/*
- * Name resolution.
+ * Name resolution
*
* This is the basic name resolution function, turning a pathname
* into the final dentry.
+ *
+ * Reworked by Adam D. Bradley <artdodge@cs.bu.edu> 4/98
+ * to be non-recursive whenever possible.
+ * This is done with a few tricks:
+ * 1) Try to re-write pathnames using "literal" symlinks
+ * (still defer to follow_link if readlink reports
+ * "needs_follow" as true)
+ * 2) If the buffer runs out of space, recurse via do_follow_link.
+ * The trick is how to correctly limit both symlink depth
+ * _and_ recursion. The traditional "current->link_count"
+ * has been replaced with "current->recursion_count", and
+ * "current->link_count" now actually counts symlinks
+ * traversed.
+ * reset current->link_count in do_lookup_dentry wrapper:
*/
+#define MAX_FOLLOW_LINKS 32
+
+struct dentry * do_lookup_dentry(const char * name, struct dentry * base, int follow_link);
+
struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link)
{
+ struct dentry *retval = do_lookup_dentry(name, base, follow_link);
+
+ /* All recursion passes through do_follow_link, so this is correct: */
+ if (current->recursion_count == 0) {
+ /* reset the link counter, since we're about to return a final result */
+ current->link_count = 0;
+ }
+
+ return retval;
+}
+
+struct dentry * do_lookup_dentry(const char * name, struct dentry * base, int follow_link)
+{
struct dentry * dentry;
+ char *namestack = kmalloc(PATH_MAX+1, GFP_KERNEL);
+ char *namep;
+ char *resolving = kmalloc(PATH_MAX+1, GFP_KERNEL);
+ mm_segment_t fs;
+
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " called on <%s>, follow_link=%d\n", name, follow_link);
+#endif
+
+ *resolving = '\0';
+
+ namep = namestack + PATH_MAX - strlen(name);
+ strcpy(namep, name);

- if (*name == '/') {
+ if (*namep == '/') {
if (base)
- dput(base);
+ dput (base);
base = dget(current->fs->root);
do {
- name++;
- } while (*name == '/');
+ namep++;
+ } while (*namep == '/');
} else if (!base) {
base = dget(current->fs->pwd);
}

- if (!*name)
- goto return_base;
+ if (!*namep)
+ goto n_return_base;

- /* At this point we know we have a real path component. */
- for(;;) {
+ /* At this point, namep points to the beginning of a path component */
+ for (;;) {
int err;
unsigned long hash;
struct qstr this;
struct inode *inode;
char c, follow;
+ char *insert;
+ int need_follow_link;

- dentry = ERR_PTR(-ENOENT);
- inode = base->d_inode;
- if (!inode)
- break;
-
- dentry = ERR_PTR(-ENOTDIR);
- if (!inode->i_op || !inode->i_op->lookup)
- break;
+#ifdef ADB_FS_DEBUG
+ printk(KERNEL_DEBUG __FUNCTION__ " string is now <%s>\n", namep);
+#endif

+ inode = base->d_inode;
+ if (!inode) {
+ dentry = ERR_PTR(-ENOENT);
+ goto n_return_dentry;
+ }
+ if (!inode->i_op || !inode->i_op->lookup) {
+ dentry = ERR_PTR(-ENOTDIR);
+ goto n_return_dentry;
+ }
err = permission(inode, MAY_EXEC);
- dentry = ERR_PTR(err);
- if (err)
- break;
+ if (err) {
+ dentry = ERR_PTR(err);
+ goto n_return_dentry;
+ }

- this.name = name;
- c = *name;
+ this.name = namep;
+ c = *namep;

hash = init_name_hash();
do {
hash = partial_name_hash(c, hash);
- c = *++name;
+ c = *++namep;
} while (c && (c != '/'));
- this.len = name - (const char *) this.name;
+ this.len = namep - (const char *) this.name;
this.hash = end_name_hash(hash);

- /* remove trailing slashes? */
- follow = follow_link;
- if (c) {
- follow |= c;
- do {
- c = *++name;
- } while (c == '/');
+ follow = follow_link | c;
+ insert = namep;
+
+ while (*namep == '/') {
+ namep++;
}

/*
@@ -407,36 +456,114 @@
error = base->d_op->d_hash(base, &this);
if (error < 0) {
dentry = ERR_PTR(error);
- break;
+ goto n_return_dentry;
}
}

- /* This does the actual lookups.. */
+ /* Do the actual lookups */
dentry = reserved_lookup(base, &this);
if (!dentry) {
dentry = cached_lookup(base, &this);
if (!dentry) {
dentry = real_lookup(base, &this);
if (IS_ERR(dentry))
- break;
+ goto n_return_dentry;
}
}

- /* Check mountpoints.. */
+ /* Check if this is a mountpoint */
dentry = follow_mount(dentry);

if (!follow)
- break;
+ goto n_return_dentry;

- base = do_follow_link(base, dentry);
- if (c && !IS_ERR(base))
- continue;
+ if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode) &&
+ dentry->d_inode->i_op && dentry->d_inode->i_op->readlink) {
+ /* If this is a link that we might be able to resolve flatly */
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " string is now <%s>, trying to literalize symlink\n", namep);
+#endif
+ fs = get_fs();
+ set_fs(get_ds());
+ err = dentry->d_inode->i_op->readlink(dentry, resolving, PATH_MAX, &need_follow_link);
+ set_fs(fs);
+
+ if (++(current->link_count) > MAX_FOLLOW_LINKS) {
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " exceeded MAX_FOLLOW_LINKS, returning -ENAMETOOLONG\n");
+#endif
+ dput(dentry);

-return_base:
- return base;
- }
+ dentry = ERR_PTR(-ENAMETOOLONG);
+ goto n_return_dentry;
+ }
+
+ if (err > 0) {
+ if (need_follow_link) {
+ base = do_follow_link(base, dentry); /* old behavior */
+ } else {
+ dput(dentry); /* finished w/ symlink */
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " got it, resolving=[%s]=<%lx>, "
+ "namestack=<%lx> insert=<%lx> namep=<%lx> err=%d\n",
+ resolving, resolving, namestack, insert, namep, err);
+#endif
+ resolving[err] = '\0';
+ namep = insert - err;
+ if (namep < namestack) {
+#ifdef NO_RECURSIVE_EXPANDS
+ dentry = ERR_PTR(-ENAMETOOLONG);
+ goto n_return_dentry;
+#else
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " doing recursive lookup on <%s>, lsf=%d\n", resolving, current->link_count);
+#endif
+ base = do_follow_link(base,dentry); /* old behavior - limits depth */
+ for (namep=insert ; *namep=='/' ; namep++);
+#endif
+ } else {
+ strncpy(namep, resolving, err); /* won't copy trailing '\0' */
+#ifdef ADB_FS_DEBUG
+ printk(KERN_DEBUG __FUNCTION__ " updated name to <%s>\n", namep);
+#endif
+ if (*namep == '/') { /* symlink to root */
+ dput(base);
+ base = dget(current->fs->root);
+ do {
+ namep++;
+ } while (*namep=='/');
+ }
+ }
+ /* At this point, we want to re-start the for loop */
+ }
+ } else {
+ /* This shouldn't happen... */
+ dput (dentry);
+ printk(KERN_DEBUG __FUNCTION__ " dentry->d_inode->i_op->readlink returns %d\n", err);
+ dentry = ERR_PTR(err);
+ goto n_return_dentry;
+ }
+ } else {
+ base = do_follow_link(base, dentry); /* old behavior */
+ }
+
+ if (!(*namep) || IS_ERR(base))
+ goto n_return_base;
+
+ } /* infinite for loop */
+
+n_return_dentry:
+ kfree(namestack);
+ kfree(resolving);
dput(base);
+
return dentry;
+
+n_return_base:
+ kfree(namestack);
+ kfree(resolving);
+
+ return base;
}

/*
diff -u --recursive --new-file linux-2.1.vanilla/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- linux-2.1.vanilla/fs/nfs/symlink.c Wed Jan 14 23:51:17 1998
+++ linux/fs/nfs/symlink.c Sat Apr 25 16:28:21 1998
@@ -18,7 +18,7 @@

#include <asm/uaccess.h>

-static int nfs_readlink(struct dentry *, char *, int);
+static int nfs_readlink(struct dentry *, char *, int, int *);
static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);

/*
@@ -44,7 +44,7 @@
NULL /* permission */
};

-static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
int error;
unsigned int len;
@@ -64,6 +64,8 @@
error = len;
kfree(mem);
}
+
+ *needs_follow = 0;
return error;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/proc/link.c linux/fs/proc/link.c
--- linux-2.1.vanilla/fs/proc/link.c Fri Apr 10 22:34:14 1998
+++ linux/fs/proc/link.c Sat Apr 25 17:03:49 1998
@@ -16,7 +16,7 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>

-static int proc_readlink(struct dentry *, char *, int);
+static int proc_readlink(struct dentry *, char *, int, int *);
static struct dentry * proc_follow_link(struct dentry *, struct dentry *);

/*
@@ -136,7 +136,7 @@
* This pretty-prints the pathname of a dentry,
* clarifying sockets etc.
*/
-static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen)
+static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen, int *needs_follow)
{
struct inode * inode;
char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern;
@@ -165,10 +165,11 @@
dput(dentry);
copy_to_user(buffer, path, buflen);
free_page((unsigned long)tmp);
+ *needs_follow = -1;
return buflen;
}

-static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
+static int proc_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
int error;

@@ -177,7 +178,7 @@
if (!IS_ERR(dentry)) {
error = -ENOENT;
if (dentry) {
- error = do_proc_readlink(dentry, buffer, buflen);
+ error = do_proc_readlink(dentry, buffer, buflen, needs_follow);
}
}
return error;
diff -u --recursive --new-file linux-2.1.vanilla/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c
--- linux-2.1.vanilla/fs/proc/proc_devtree.c Sat Apr 18 15:59:34 1998
+++ linux/fs/proc/proc_devtree.c Sat Apr 25 16:38:21 1998
@@ -41,7 +41,7 @@
* and "@10" to it.
*/

-static int devtree_readlink(struct dentry *, char *, int);
+static int devtree_readlink(struct dentry *, char *, int, int *);
static struct dentry *devtree_follow_link(struct dentry *, struct dentry *);

struct inode_operations devtree_symlink_inode_operations = {
@@ -77,7 +77,7 @@
return lookup_dentry(link, base, 1);
}

-static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
struct inode *inode = dentry->d_inode;
struct proc_dir_entry * de;
@@ -91,6 +91,8 @@
linklen = buflen;
if (copy_to_user(buffer, link, linklen))
return -EFAULT;
+
+ *needs_follow = 0;
return linklen;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/proc/root.c linux/fs/proc/root.c
--- linux-2.1.vanilla/fs/proc/root.c Sat Apr 18 15:59:35 1998
+++ linux/fs/proc/root.c Sat Apr 25 16:40:37 1998
@@ -50,7 +50,7 @@
NULL /* can't fsync */
};

-int proc_readlink(struct dentry * dentry, char * buffer, int buflen);
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow);
struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base);

/*
@@ -368,7 +368,7 @@
/*
* /proc/self:
*/
-static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
int len;
char tmp[30];
@@ -377,6 +377,7 @@
if (buflen < len)
len = buflen;
copy_to_user(buffer, tmp, len);
+ *needs_follow = 0;
return len;
}

@@ -389,7 +390,7 @@
return lookup_dentry(tmp, base, 1);
}

-int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
struct inode *inode = dentry->d_inode;
struct proc_dir_entry * de;
@@ -410,6 +411,7 @@

copy_to_user(buffer, page, len);
free_page((unsigned long) page);
+ *needs_follow = 0;
return len;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/romfs/inode.c linux/fs/romfs/inode.c
--- linux-2.1.vanilla/fs/romfs/inode.c Fri Apr 10 22:34:15 1998
+++ linux/fs/romfs/inode.c Sat Apr 25 16:30:56 1998
@@ -426,7 +426,7 @@
}

static int
-romfs_readlink(struct dentry *dentry, char *buffer, int len)
+romfs_readlink(struct dentry *dentry, char *buffer, int len, int *needs_follow)
{
struct inode *inode = dentry->d_inode;
int mylen;
@@ -445,6 +445,7 @@
}
copy_to_user(buffer, buf, mylen);

+ *needs_follow = 0;
out:
return mylen;
}
diff -u --recursive --new-file linux-2.1.vanilla/fs/stat.c linux/fs/stat.c
--- linux-2.1.vanilla/fs/stat.c Sat Mar 7 13:35:22 1998
+++ linux/fs/stat.c Sat Apr 25 16:27:08 1998
@@ -259,6 +259,7 @@
{
struct dentry * dentry;
int error;
+ int scrap;

if (bufsiz <= 0)
return -EINVAL;
@@ -274,7 +275,7 @@
if (inode->i_op && inode->i_op->readlink &&
!(error = do_revalidate(dentry))) {
UPDATE_ATIME(inode);
- error = inode->i_op->readlink(dentry, buf, bufsiz);
+ error = inode->i_op->readlink(dentry, buf, bufsiz, &scrap);
}
dput(dentry);
}
diff -u --recursive --new-file linux-2.1.vanilla/fs/sysv/symlink.c linux/fs/sysv/symlink.c
vv--- linux-2.1.vanilla/fs/sysv/symlink.c Wed Jan 14 23:51:18
1998
+++ linux/fs/sysv/symlink.c Sat Apr 25 16:31:43 1998
@@ -20,7 +20,7 @@

#include <asm/uaccess.h>

-static int sysv_readlink(struct dentry *, char *, int);
+static int sysv_readlink(struct dentry *, char *, int, int *);
static struct dentry *sysv_follow_link(struct dentry *, struct dentry *);

/*
@@ -63,7 +63,7 @@
return base;
}

-static int sysv_readlink(struct dentry * dentry, char * buffer, int buflen)
+static int sysv_readlink(struct dentry * dentry, char * buffer, int buflen, int *)
{
struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
@@ -83,5 +83,6 @@
put_user(c,buffer++);
}
brelse(bh);
+ *needs_follow = 0;
return i;
}
diff -u --recursive --new-file linux-2.1.vanilla/fs/ufs/ufs_symlink.c linux/fs/ufs/ufs_symlink.c
--- linux-2.1.vanilla/fs/ufs/ufs_symlink.c Fri Feb 20 23:57:26 1998
+++ linux/fs/ufs/ufs_symlink.c Sat Apr 25 16:32:17 1998
@@ -22,7 +22,7 @@
extern int ufs_bmap (struct inode *, int);

static int
-ufs_readlink(struct dentry * dentry, char * buffer, int buflen)
+ufs_readlink(struct dentry * dentry, char * buffer, int buflen, int *needs_follow)
{
struct inode * inode = dentry->d_inode;
struct super_block * sb = inode->i_sb;
@@ -68,6 +68,7 @@
put_user (c, buffer++);
}
brelse (bh);
+ *needs_follow = 0;
return i;
}

diff -u --recursive --new-file linux-2.1.vanilla/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- linux-2.1.vanilla/fs/umsdos/symlink.c Thu Mar 19 17:46:59 1998
+++ linux/fs/umsdos/symlink.c Sat Apr 25 16:33:58 1998
@@ -86,7 +86,7 @@



-static int UMSDOS_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int UMSDOS_readlink(struct dentry *dentry, char *buffer, int buflen, int *needs_follow)
{
int ret;

@@ -95,8 +95,8 @@
PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
/* dput(dentry); / * FIXME /mn/ */
Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
- return ret;
-
+ *needs_follow = 0;
+ return ret;
}

/* this one mostly stolen from romfs :) */
diff -u --recursive --new-file linux-2.1.vanilla/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.1.vanilla/include/linux/fs.h Sat Apr 18 18:23:13 1998
+++ linux/include/linux/fs.h Sat Apr 25 16:53:45 1998
@@ -603,7 +603,7 @@
int (*mknod) (struct inode *,struct dentry *,int,int);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
- int (*readlink) (struct dentry *, char *,int);
+ int (*readlink) (struct dentry *, char *,int, int *);
struct dentry * (*follow_link) (struct dentry *, struct dentry *);
int (*readpage) (struct file *, struct page *);
int (*writepage) (struct file *, struct page *);
diff -u --recursive --new-file linux-2.1.vanilla/include/linux/sched.h linux/include/linux/sched.h
--- linux-2.1.vanilla/include/linux/sched.h Sat Apr 18 18:23:14 1998
+++ linux/include/linux/sched.h Sat Apr 25 18:05:27 1998
@@ -268,6 +268,7 @@
char comm[16];
/* file system info */
int link_count;
+ int recursion_count;
struct tty_struct *tty; /* NULL if no tty */
/* ipc stuff */
struct sem_undo *semundo;
@@ -358,7 +359,7 @@
/* rlimits */ INIT_RLIMITS, \
/* math */ 0, \
/* comm */ "swapper", \
-/* fs info */ 0,NULL, \
+/* fs info */ 0,0,NULL, \
/* ipc */ NULL, NULL, \
/* tss */ INIT_TSS, \
/* fs */ &init_fs, \

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