Browse Source

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fixes from Al Viro:
 "These four commits are obvious fixes (a couple of fdget_pos()-related
  ones from Eric Biggers, prepend_name() fix, missing checks for false
  negatives from __lookup_mnt() in fs/namei.c)"

For now I'm pulling just the four obvious fixes, there's another four
pending in Al's 'for-linus' branch wrt the mnt_hash list that were more
involved.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  rcuwalk: recheck mount_lock after mountpoint crossing attempts
  make prepend_name() work correctly when called with negative *buflen
  vfs: Don't let __fdget_pos() get FMODE_PATH files
  vfs: atomic f_pos access in llseek()
Linus Torvalds 11 years ago
parent
commit
3e79d97828
4 changed files with 21 additions and 35 deletions
  1. 2 2
      fs/dcache.c
  2. 4 15
      fs/file.c
  3. 13 16
      fs/namei.c
  4. 2 2
      fs/read_write.c

+ 2 - 2
fs/dcache.c

@@ -2833,9 +2833,9 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
 	u32 dlen = ACCESS_ONCE(name->len);
 	u32 dlen = ACCESS_ONCE(name->len);
 	char *p;
 	char *p;
 
 
-	if (*buflen < dlen + 1)
-		return -ENAMETOOLONG;
 	*buflen -= dlen + 1;
 	*buflen -= dlen + 1;
+	if (*buflen < 0)
+		return -ENAMETOOLONG;
 	p = *buffer -= dlen + 1;
 	p = *buffer -= dlen + 1;
 	*p++ = '/';
 	*p++ = '/';
 	while (dlen--) {
 	while (dlen--) {

+ 4 - 15
fs/file.c

@@ -713,27 +713,16 @@ unsigned long __fdget_raw(unsigned int fd)
 
 
 unsigned long __fdget_pos(unsigned int fd)
 unsigned long __fdget_pos(unsigned int fd)
 {
 {
-	struct files_struct *files = current->files;
-	struct file *file;
-	unsigned long v;
-
-	if (atomic_read(&files->count) == 1) {
-		file = __fcheck_files(files, fd);
-		v = 0;
-	} else {
-		file = __fget(fd, 0);
-		v = FDPUT_FPUT;
-	}
-	if (!file)
-		return 0;
+	unsigned long v = __fdget(fd);
+	struct file *file = (struct file *)(v & ~3);
 
 
-	if (file->f_mode & FMODE_ATOMIC_POS) {
+	if (file && (file->f_mode & FMODE_ATOMIC_POS)) {
 		if (file_count(file) > 1) {
 		if (file_count(file) > 1) {
 			v |= FDPUT_POS_UNLOCK;
 			v |= FDPUT_POS_UNLOCK;
 			mutex_lock(&file->f_pos_lock);
 			mutex_lock(&file->f_pos_lock);
 		}
 		}
 	}
 	}
-	return v | (unsigned long)file;
+	return v;
 }
 }
 
 
 /*
 /*

+ 13 - 16
fs/namei.c

@@ -1109,7 +1109,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 			return false;
 			return false;
 
 
 		if (!d_mountpoint(path->dentry))
 		if (!d_mountpoint(path->dentry))
-			break;
+			return true;
 
 
 		mounted = __lookup_mnt(path->mnt, path->dentry);
 		mounted = __lookup_mnt(path->mnt, path->dentry);
 		if (!mounted)
 		if (!mounted)
@@ -1125,20 +1125,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 		 */
 		 */
 		*inode = path->dentry->d_inode;
 		*inode = path->dentry->d_inode;
 	}
 	}
-	return true;
-}
-
-static void follow_mount_rcu(struct nameidata *nd)
-{
-	while (d_mountpoint(nd->path.dentry)) {
-		struct mount *mounted;
-		mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
-		if (!mounted)
-			break;
-		nd->path.mnt = &mounted->mnt;
-		nd->path.dentry = mounted->mnt.mnt_root;
-		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
-	}
+	return read_seqretry(&mount_lock, nd->m_seq);
 }
 }
 
 
 static int follow_dotdot_rcu(struct nameidata *nd)
 static int follow_dotdot_rcu(struct nameidata *nd)
@@ -1166,7 +1153,17 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 			break;
 			break;
 		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
 		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
 	}
 	}
-	follow_mount_rcu(nd);
+	while (d_mountpoint(nd->path.dentry)) {
+		struct mount *mounted;
+		mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
+		if (!mounted)
+			break;
+		nd->path.mnt = &mounted->mnt;
+		nd->path.dentry = mounted->mnt.mnt_root;
+		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
+		if (!read_seqretry(&mount_lock, nd->m_seq))
+			goto failed;
+	}
 	nd->inode = nd->path.dentry->d_inode;
 	nd->inode = nd->path.dentry->d_inode;
 	return 0;
 	return 0;
 
 

+ 2 - 2
fs/read_write.c

@@ -307,7 +307,7 @@ SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
 		unsigned int, whence)
 		unsigned int, whence)
 {
 {
 	int retval;
 	int retval;
-	struct fd f = fdget(fd);
+	struct fd f = fdget_pos(fd);
 	loff_t offset;
 	loff_t offset;
 
 
 	if (!f.file)
 	if (!f.file)
@@ -327,7 +327,7 @@ SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
 			retval = 0;
 			retval = 0;
 	}
 	}
 out_putf:
 out_putf:
-	fdput(f);
+	fdput_pos(f);
 	return retval;
 	return retval;
 }
 }
 #endif
 #endif