[PATCH 2/3] enhanced ESTALE error handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi.

This patch adds handling for the error, ESTALE, to the system
calls which take pathnames as arguments.  The algorithm used
is to detect that an ESTALE error has occurred during an
operation subsequent to the lookup process and then to unwind
appropriately and then to perform the lookup process again.
Eventually, either the lookup process will return an error
or a valid dentry/inode combination and then operation can
succeed or fail based on its own merits.

A partial list of the updated system calls are stat, stat64,
lstat, lstat64, mkdir, link, open, access, chmod, chown,
readlink, utime, utimes, chdir, chroot, rename, exec, mknod,
statfs, inotify, setxattr, getxattr, and listxattr.  Due to
common code factoring, other system calls may have been
included too, but were not explicitly tested.

   Thanx...

      ps

Signed-off-by: Peter Staubach <staubach@xxxxxxxxxx>
--- linux-2.6.23.i686/fs/namei.c.org
+++ linux-2.6.23.i686/fs/namei.c
@@ -1956,6 +1986,7 @@ asmlinkage long sys_mknodat(int dfd, con
 	if (IS_ERR(tmp))
 		return PTR_ERR(tmp);
 
+top:
 	error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
 	if (error)
 		goto out;
@@ -1986,6 +2017,8 @@ asmlinkage long sys_mknodat(int dfd, con
 	}
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	putname(tmp);
 
@@ -2021,8 +2054,8 @@ int vfs_mkdir(struct inode *dir, struct 
 
 asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
 {
-	int error = 0;
-	char * tmp;
+	int error;
+	char *tmp;
 	struct dentry *dentry;
 	struct nameidata nd;
 
@@ -2031,6 +2064,7 @@ asmlinkage long sys_mkdirat(int dfd, con
 	if (IS_ERR(tmp))
 		goto out_err;
 
+top:
 	error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
 	if (error)
 		goto out;
@@ -2046,6 +2080,8 @@ asmlinkage long sys_mkdirat(int dfd, con
 out_unlock:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	putname(tmp);
 out_err:
@@ -2125,23 +2161,24 @@ static long do_rmdir(int dfd, const char
 	struct nameidata nd;
 
 	name = getname(pathname);
-	if(IS_ERR(name))
+	if (IS_ERR(name))
 		return PTR_ERR(name);
 
+top:
 	error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
 	if (error)
 		goto exit;
 
-	switch(nd.last_type) {
-		case LAST_DOTDOT:
-			error = -ENOTEMPTY;
-			goto exit1;
-		case LAST_DOT:
-			error = -EINVAL;
-			goto exit1;
-		case LAST_ROOT:
-			error = -EBUSY;
-			goto exit1;
+	switch (nd.last_type) {
+	case LAST_DOTDOT:
+		error = -ENOTEMPTY;
+		goto exit1;
+	case LAST_DOT:
+		error = -EINVAL;
+		goto exit1;
+	case LAST_ROOT:
+		error = -EBUSY;
+		goto exit1;
 	}
 	mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 	dentry = lookup_hash(&nd);
@@ -2154,6 +2191,8 @@ exit2:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 exit1:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 exit:
 	putname(name);
 	return error;
@@ -2206,12 +2245,14 @@ static long do_unlinkat(int dfd, const c
 	char * name;
 	struct dentry *dentry;
 	struct nameidata nd;
-	struct inode *inode = NULL;
+	struct inode *inode;
 
 	name = getname(pathname);
 	if(IS_ERR(name))
 		return PTR_ERR(name);
 
+top:
+	inode = NULL;
 	error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
 	if (error)
 		goto exit;
@@ -2237,6 +2278,8 @@ static long do_unlinkat(int dfd, const c
 		iput(inode);	/* truncate the inode here */
 exit1:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 exit:
 	putname(name);
 	return error;
@@ -2301,6 +2344,7 @@ asmlinkage long sys_symlinkat(const char
 	if (IS_ERR(to))
 		goto out_putname;
 
+top:
 	error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
 	if (error)
 		goto out;
@@ -2314,6 +2358,8 @@ asmlinkage long sys_symlinkat(const char
 out_unlock:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	putname(to);
 out_putname:
@@ -2389,6 +2435,7 @@ asmlinkage long sys_linkat(int olddfd, c
 	if (IS_ERR(to))
 		return PTR_ERR(to);
 
+top:
 	error = __user_walk_fd(olddfd, oldname,
 			       flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
 			       &old_nd);
@@ -2408,6 +2455,11 @@ asmlinkage long sys_linkat(int olddfd, c
 	dput(new_dentry);
 out_unlock:
 	mutex_unlock(&nd.dentry->d_inode->i_mutex);
+	if (error == -ESTALE) {
+		path_release(&nd);
+		path_release(&old_nd);
+		goto top;
+	}
 out_release:
 	path_release(&nd);
 out:
@@ -2578,6 +2630,7 @@ static int do_rename(int olddfd, const c
 	struct dentry * trap;
 	struct nameidata oldnd, newnd;
 
+top:
 	error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
 	if (error)
 		goto exit;
@@ -2638,6 +2691,11 @@ exit4:
 	dput(old_dentry);
 exit3:
 	unlock_rename(new_dir, old_dir);
+	if (error == -ESTALE) {
+		path_release(&newnd);
+		path_release(&oldnd);
+		goto top;
+	}
 exit2:
 	path_release(&newnd);
 exit1:
--- linux-2.6.23.i686/fs/open.c.org
+++ linux-2.6.23.i686/fs/open.c
@@ -124,6 +124,7 @@ asmlinkage long sys_statfs(const char __
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct statfs tmp;
@@ -131,6 +132,8 @@ asmlinkage long sys_statfs(const char __
 		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
 			error = -EFAULT;
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
@@ -143,6 +146,7 @@ asmlinkage long sys_statfs64(const char 
 
 	if (sz != sizeof(*buf))
 		return -EINVAL;
+top:
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct statfs64 tmp;
@@ -150,6 +154,8 @@ asmlinkage long sys_statfs64(const char 
 		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
 			error = -EFAULT;
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
@@ -230,6 +236,7 @@ static long do_sys_truncate(const char _
 	if (length < 0)	/* sorry, but loff_t says... */
 		goto out;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (error)
 		goto out;
@@ -278,6 +285,8 @@ put_write_and_out:
 	put_write_access(inode);
 dput_and_out:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -448,21 +457,24 @@ asmlinkage long sys_faccessat(int dfd, c
 	else
 		current->cap_effective = current->cap_permitted;
 
+top:
 	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
 	if (res)
 		goto out;
 
 	res = vfs_permission(&nd, mode);
 	/* SuS v2 requires we report a read only fs too */
-	if(res || !(mode & S_IWOTH) ||
+	if (res || !(mode & S_IWOTH) ||
 	   special_file(nd.dentry->d_inode->i_mode))
 		goto out_path_release;
 
-	if(IS_RDONLY(nd.dentry->d_inode))
+	if (IS_RDONLY(nd.dentry->d_inode))
 		res = -EROFS;
 
 out_path_release:
 	path_release(&nd);
+	if (res == -ESTALE)
+		goto top;
 out:
 	current->fsuid = old_fsuid;
 	current->fsgid = old_fsgid;
@@ -481,6 +493,7 @@ asmlinkage long sys_chdir(const char __u
 	struct nameidata nd;
 	int error;
 
+top:
 	error = __user_walk(filename,
 			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
 	if (error)
@@ -494,6 +507,8 @@ asmlinkage long sys_chdir(const char __u
 
 dput_and_out:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -533,6 +548,7 @@ asmlinkage long sys_chroot(const char __
 	struct nameidata nd;
 	int error;
 
+top:
 	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
 	if (error)
 		goto out;
@@ -550,6 +566,8 @@ asmlinkage long sys_chroot(const char __
 	error = 0;
 dput_and_out:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -599,6 +617,7 @@ asmlinkage long sys_fchmodat(int dfd, co
 	int error;
 	struct iattr newattrs;
 
+top:
 	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
 	if (error)
 		goto out;
@@ -622,6 +641,8 @@ asmlinkage long sys_fchmodat(int dfd, co
 
 dput_and_out:
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -672,11 +693,14 @@ asmlinkage long sys_chown(const char __u
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk(filename, &nd);
 	if (error)
 		goto out;
 	error = chown_common(nd.dentry, user, group);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -692,11 +716,14 @@ asmlinkage long sys_fchownat(int dfd, co
 		goto out;
 
 	follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
+top:
 	error = __user_walk_fd(dfd, filename, follow, &nd);
 	if (error)
 		goto out;
 	error = chown_common(nd.dentry, user, group);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -706,11 +733,14 @@ asmlinkage long sys_lchown(const char __
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk_link(filename, &nd);
 	if (error)
 		goto out;
 	error = chown_common(nd.dentry, user, group);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 out:
 	return error;
 }
@@ -819,16 +849,22 @@ static struct file *do_filp_open(int dfd
 {
 	int namei_flags, error;
 	struct nameidata nd;
+	struct file *res;
 
+top:
 	namei_flags = flags;
 	if ((namei_flags+1) & O_ACCMODE)
 		namei_flags++;
 
 	error = open_namei(dfd, filename, namei_flags, mode, &nd);
-	if (!error)
-		return nameidata_to_filp(&nd, flags);
+	if (error)
+		return ERR_PTR(error);
+
+	res = nameidata_to_filp(&nd, flags);
+	if (IS_ERR(res) && res == ERR_PTR(-ESTALE))
+		goto top;
+	return res;
 
-	return ERR_PTR(error);
 }
 
 struct file *filp_open(const char *filename, int flags, int mode)
--- linux-2.6.23.i686/fs/inotify_user.c.org
+++ linux-2.6.23.i686/fs/inotify_user.c
@@ -346,13 +346,17 @@ static int find_inode(const char __user 
 {
 	int error;
 
+top:
 	error = __user_walk(dirname, flags, nd);
 	if (error)
 		return error;
 	/* you can only watch an inode if you have read permissions on it */
 	error = vfs_permission(nd, MAY_READ);
-	if (error)
+	if (error) {
 		path_release(nd);
+		if (error == -ESTALE)
+			goto top;
+	}
 	return error;
 }
 
--- linux-2.6.23.i686/fs/stat.c.org
+++ linux-2.6.23.i686/fs/stat.c
@@ -60,10 +60,13 @@ int vfs_stat_fd(int dfd, char __user *na
 	struct nameidata nd;
 	int error;
 
+top:
 	error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd);
 	if (!error) {
 		error = vfs_getattr(nd.mnt, nd.dentry, stat);
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
@@ -80,10 +83,13 @@ int vfs_lstat_fd(int dfd, char __user *n
 	struct nameidata nd;
 	int error;
 
+top:
 	error = __user_walk_fd(dfd, name, 0, &nd);
 	if (!error) {
 		error = vfs_getattr(nd.mnt, nd.dentry, stat);
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
@@ -300,6 +306,7 @@ asmlinkage long sys_readlinkat(int dfd, 
 	if (bufsiz <= 0)
 		return -EINVAL;
 
+top:
 	error = __user_walk_fd(dfd, path, 0, &nd);
 	if (!error) {
 		struct inode * inode = nd.dentry->d_inode;
@@ -313,6 +320,8 @@ asmlinkage long sys_readlinkat(int dfd, 
 			}
 		}
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
--- linux-2.6.23.i686/fs/exec.c.org
+++ linux-2.6.23.i686/fs/exec.c
@@ -107,6 +107,7 @@ asmlinkage long sys_uselib(const char __
 	struct nameidata nd;
 	int error;
 
+top:
 	error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
 	if (error)
 		goto out;
@@ -149,6 +150,8 @@ out:
 exit:
 	release_open_intent(&nd);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	goto out;
 }
 
@@ -648,14 +651,16 @@ struct file *open_exec(const char *name)
 	int err;
 	struct file *file;
 
-	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
+top:
+	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd,
+				FMODE_READ|FMODE_EXEC);
 	file = ERR_PTR(err);
 
 	if (!err) {
 		struct inode *inode = nd.dentry->d_inode;
 		file = ERR_PTR(-EACCES);
 		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
+			err = vfs_permission(&nd, MAY_EXEC);
 			file = ERR_PTR(err);
 			if (!err) {
 				file = nameidata_to_filp(&nd, O_RDONLY);
@@ -665,15 +670,17 @@ struct file *open_exec(const char *name)
 						fput(file);
 						file = ERR_PTR(err);
 					}
-				}
-out:
+				} else if (file == ERR_PTR(-ESTALE))
+					goto top;
 				return file;
 			}
 		}
 		release_open_intent(&nd);
 		path_release(&nd);
+		if (err == -ESTALE)
+			goto top;
 	}
-	goto out;
+	return file;
 }
 
 EXPORT_SYMBOL(open_exec);
--- linux-2.6.23.i686/fs/utimes.c.org
+++ linux-2.6.23.i686/fs/utimes.c
@@ -79,6 +79,7 @@ long do_utimes(int dfd, char __user *fil
 			goto out;
 		dentry = f->f_path.dentry;
 	} else {
+top:
 		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
 		if (error)
 			goto out;
@@ -136,8 +137,11 @@ long do_utimes(int dfd, char __user *fil
 dput_and_out:
 	if (f)
 		fput(f);
-	else
+	else {
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
+	}
 out:
 	return error;
 }
--- linux-2.6.23.i686/fs/compat.c.org
+++ linux-2.6.23.i686/fs/compat.c
@@ -238,6 +238,7 @@ asmlinkage long compat_sys_statfs(const 
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct kstatfs tmp;
@@ -245,6 +246,8 @@ asmlinkage long compat_sys_statfs(const 
 		if (!error)
 			error = put_compat_statfs(buf, &tmp);
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
@@ -306,6 +309,7 @@ asmlinkage long compat_sys_statfs64(cons
 	if (sz != sizeof(*buf))
 		return -EINVAL;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (!error) {
 		struct kstatfs tmp;
@@ -313,6 +317,8 @@ asmlinkage long compat_sys_statfs64(cons
 		if (!error)
 			error = put_compat_statfs64(buf, &tmp);
 		path_release(&nd);
+		if (error == -ESTALE)
+			goto top;
 	}
 	return error;
 }
--- linux-2.6.23.i686/fs/xattr.c.org
+++ linux-2.6.23.i686/fs/xattr.c
@@ -232,11 +232,14 @@ sys_setxattr(char __user *path, char __u
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
 	error = setxattr(nd.dentry, name, value, size, flags);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -247,11 +250,14 @@ sys_lsetxattr(char __user *path, char __
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
 	error = setxattr(nd.dentry, name, value, size, flags);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -317,11 +323,14 @@ sys_getxattr(char __user *path, char __u
 	struct nameidata nd;
 	ssize_t error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
 	error = getxattr(nd.dentry, name, value, size);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -332,11 +341,14 @@ sys_lgetxattr(char __user *path, char __
 	struct nameidata nd;
 	ssize_t error;
 
+top:
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
 	error = getxattr(nd.dentry, name, value, size);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -391,11 +403,14 @@ sys_listxattr(char __user *path, char __
 	struct nameidata nd;
 	ssize_t error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
 	error = listxattr(nd.dentry, list, size);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -405,11 +420,14 @@ sys_llistxattr(char __user *path, char _
 	struct nameidata nd;
 	ssize_t error;
 
+top:
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
 	error = listxattr(nd.dentry, list, size);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -452,11 +470,14 @@ sys_removexattr(char __user *path, char 
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk(path, &nd);
 	if (error)
 		return error;
 	error = removexattr(nd.dentry, name);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 
@@ -466,11 +487,14 @@ sys_lremovexattr(char __user *path, char
 	struct nameidata nd;
 	int error;
 
+top:
 	error = user_path_walk_link(path, &nd);
 	if (error)
 		return error;
 	error = removexattr(nd.dentry, name);
 	path_release(&nd);
+	if (error == -ESTALE)
+		goto top;
 	return error;
 }
 

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux