浏览代码

lookup_open(): lift the "fallback to !O_CREAT" logics from atomic_open()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Al Viro 9 年之前
父节点
当前提交
1643b43fbd
共有 1 个文件被更改,包括 55 次插入89 次删除
  1. 55 89
      fs/namei.c

+ 55 - 89
fs/namei.c

@@ -2824,63 +2824,19 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
 static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 			struct path *path, struct file *file,
 			const struct open_flags *op,
-			bool got_write, bool need_lookup,
+			int open_flag, umode_t mode,
 			int *opened)
 {
 	struct inode *dir =  nd->path.dentry->d_inode;
-	unsigned open_flag = op->open_flag;
-	umode_t mode;
 	int error;
 	int acc_mode;
-	int create_error = 0;
 	struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
 	bool excl;
 
-	BUG_ON(dentry->d_inode);
-
-	mode = op->mode;
-	if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
-		mode &= ~current_umask();
-
 	excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
 	if (excl)
 		open_flag &= ~O_TRUNC;
 
-	/*
-	 * Checking write permission is tricky, bacuse we don't know if we are
-	 * going to actually need it: O_CREAT opens should work as long as the
-	 * file exists.  But checking existence breaks atomicity.  The trick is
-	 * to check access and if not granted clear O_CREAT from the flags.
-	 *
-	 * Another problem is returing the "right" error value (e.g. for an
-	 * O_EXCL open we want to return EEXIST not EROFS).
-	 */
-	if (open_flag & O_CREAT) {
-		if (unlikely(!got_write)) {
-			create_error = -EROFS;
-			if (open_flag & (O_EXCL | O_TRUNC)) {
-				/* Fall back and fail with the right error */
-				goto no_open;
-			}
-			/* No side effects, safe to clear O_CREAT */
-			open_flag &= ~O_CREAT;
-		} else {
-			create_error = may_o_create(&nd->path, dentry, mode);
-			if (create_error) {
-				if (open_flag & O_EXCL)
-					goto no_open;
-				open_flag &= ~O_CREAT;
-			}
-		}
-	} else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) &&
-		   unlikely(!got_write)) {
-		/*
-		 * No O_CREATE -> atomicity not a requirement -> fall
-		 * back to lookup + open
-		 */
-		goto no_open;
-	}
-
 	if (nd->flags & LOOKUP_DIRECTORY)
 		open_flag |= O_DIRECTORY;
 
@@ -2889,11 +2845,8 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 	error = dir->i_op->atomic_open(dir, dentry, file,
 				       open_to_namei_flags(open_flag),
 				       mode, opened);
-	if (error < 0) {
-		if (create_error && error == -ENOENT)
-			error = create_error;
+	if (error < 0)
 		goto out;
-	}
 
 	if (error) {	/* returned 1, that is */
 		if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
@@ -2906,7 +2859,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 		}
 		if (*opened & FILE_CREATED)
 			fsnotify_create(dir, dentry);
-		goto looked_up;
+		path->dentry = dentry;
+		path->mnt = nd->path.mnt;
+		return 1;
 	}
 
 	/*
@@ -2925,21 +2880,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 out:
 	dput(dentry);
 	return error;
-
-no_open:
-	if (need_lookup) {
-		dentry = lookup_real(dir, dentry, nd->flags);
-		if (IS_ERR(dentry))
-			return PTR_ERR(dentry);
-	}
-looked_up:
-	if (create_error && !dentry->d_inode) {
-		error = create_error;
-		goto out;
-	}
-	path->dentry = dentry;
-	path->mnt = nd->path.mnt;
-	return 1;
 }
 
 /*
@@ -2967,9 +2907,11 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 {
 	struct dentry *dir = nd->path.dentry;
 	struct inode *dir_inode = dir->d_inode;
+	int open_flag = op->open_flag;
 	struct dentry *dentry;
-	int error;
+	int error, create_error = 0;
 	bool need_lookup = false;
+	umode_t mode = op->mode;
 
 	if (unlikely(IS_DEADDIR(dir_inode)))
 		return -ENOENT;
@@ -2989,50 +2931,74 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 		goto out_no_open;
 	}
 
+	/*
+	 * Checking write permission is tricky, bacuse we don't know if we are
+	 * going to actually need it: O_CREAT opens should work as long as the
+	 * file exists.  But checking existence breaks atomicity.  The trick is
+	 * to check access and if not granted clear O_CREAT from the flags.
+	 *
+	 * Another problem is returing the "right" error value (e.g. for an
+	 * O_EXCL open we want to return EEXIST not EROFS).
+	 */
+	if (open_flag & O_CREAT) {
+		if (!IS_POSIXACL(dir->d_inode))
+			mode &= ~current_umask();
+		if (unlikely(!got_write)) {
+			create_error = -EROFS;
+			open_flag &= ~O_CREAT;
+			if (open_flag & (O_EXCL | O_TRUNC))
+				goto no_open;
+			/* No side effects, safe to clear O_CREAT */
+		} else {
+			create_error = may_o_create(&nd->path, dentry, mode);
+			if (create_error) {
+				open_flag &= ~O_CREAT;
+				if (open_flag & O_EXCL)
+					goto no_open;
+			}
+		}
+	} else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) &&
+		   unlikely(!got_write)) {
+		/*
+		 * No O_CREATE -> atomicity not a requirement -> fall
+		 * back to lookup + open
+		 */
+		goto no_open;
+	}
+
 	if (dir_inode->i_op->atomic_open) {
-		return atomic_open(nd, dentry, path, file, op, got_write,
-				   need_lookup, opened);
+		error = atomic_open(nd, dentry, path, file, op, open_flag,
+				    mode, opened);
+		if (unlikely(error == -ENOENT) && create_error)
+			error = create_error;
+		return error;
 	}
 
+no_open:
 	if (need_lookup) {
-		BUG_ON(dentry->d_inode);
-
 		dentry = lookup_real(dir_inode, dentry, nd->flags);
 		if (IS_ERR(dentry))
 			return PTR_ERR(dentry);
 	}
 
 	/* Negative dentry, just create the file */
-	if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
-		umode_t mode = op->mode;
-		if (!IS_POSIXACL(dir->d_inode))
-			mode &= ~current_umask();
-		/*
-		 * This write is needed to ensure that a
-		 * rw->ro transition does not occur between
-		 * the time when the file is created and when
-		 * a permanent write count is taken through
-		 * the 'struct file' in finish_open().
-		 */
-		if (!got_write) {
-			error = -EROFS;
-			goto out_dput;
-		}
+	if (!dentry->d_inode && (open_flag & O_CREAT)) {
 		*opened |= FILE_CREATED;
 		audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
-		error = may_o_create(&nd->path, dentry, mode);
-		if (error)
-			goto out_dput;
 		if (!dir_inode->i_op->create) {
 			error = -EACCES;
 			goto out_dput;
 		}
 		error = dir_inode->i_op->create(dir_inode, dentry, mode,
-						op->open_flag & O_EXCL);
+						open_flag & O_EXCL);
 		if (error)
 			goto out_dput;
 		fsnotify_create(dir_inode, dentry);
 	}
+	if (unlikely(create_error) && !dentry->d_inode) {
+		error = create_error;
+		goto out_dput;
+	}
 out_no_open:
 	path->dentry = dentry;
 	path->mnt = nd->path.mnt;