120 lines
3.3 KiB
Diff
120 lines
3.3 KiB
Diff
diff --git a/fs/internal.h b/fs/internal.h
|
|
--- a/fs/internal.h
|
|
+++ b/fs/internal.h
|
|
@@ -204,6 +204,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
|
|
int flag);
|
|
int chown_common(const struct path *path, uid_t user, gid_t group);
|
|
extern int vfs_open(const struct path *, struct file *);
|
|
+int vfs_open_consume(struct path *, struct file *);
|
|
|
|
/*
|
|
* inode.c
|
|
diff --git a/fs/namei.c b/fs/namei.c
|
|
--- a/fs/namei.c
|
|
+++ b/fs/namei.c
|
|
@@ -4652,6 +4652,7 @@ static const char *open_last_lookups(struct nameidata *nd,
|
|
static int do_open(struct nameidata *nd,
|
|
struct file *file, const struct open_flags *op)
|
|
{
|
|
+ struct vfsmount *mnt;
|
|
struct mnt_idmap *idmap;
|
|
int open_flag = op->open_flag;
|
|
bool do_truncate;
|
|
@@ -4693,11 +4694,17 @@ static int do_open(struct nameidata *nd,
|
|
error = mnt_want_write(nd->path.mnt);
|
|
if (error)
|
|
return error;
|
|
+ /*
|
|
+ * A dedicated reference is needed because after the call to
|
|
+ * vfs_open_consume() we no longer own the reference in nd->path.mnt
|
|
+ * while we need to undo write acess below.
|
|
+ */
|
|
+ mnt = mntget(nd->path.mnt);
|
|
do_truncate = true;
|
|
}
|
|
error = may_open(idmap, &nd->path, acc_mode, open_flag);
|
|
if (!error && !(file->f_mode & FMODE_OPENED))
|
|
- error = vfs_open(&nd->path, file);
|
|
+ error = vfs_open_consume(&nd->path, file);
|
|
if (!error)
|
|
error = security_file_post_open(file, op->acc_mode);
|
|
if (!error && do_truncate)
|
|
@@ -4706,8 +4713,10 @@ static int do_open(struct nameidata *nd,
|
|
WARN_ON(1);
|
|
error = -EINVAL;
|
|
}
|
|
- if (do_truncate)
|
|
- mnt_drop_write(nd->path.mnt);
|
|
+ if (do_truncate) {
|
|
+ mnt_drop_write(mnt);
|
|
+ mntput(mnt);
|
|
+ }
|
|
return error;
|
|
}
|
|
|
|
diff --git a/fs/open.c b/fs/open.c
|
|
--- a/fs/open.c
|
|
+++ b/fs/open.c
|
|
@@ -882,6 +882,11 @@ static inline int file_get_write_access(struct file *f)
|
|
return error;
|
|
}
|
|
|
|
+/*
|
|
+ * Populate struct file
|
|
+ *
|
|
+ * NOTE: it assumes f_path is populated and consumes the caller's reference.
|
|
+ */
|
|
static int do_dentry_open(struct file *f,
|
|
int (*open)(struct inode *, struct file *))
|
|
{
|
|
@@ -889,7 +894,6 @@ static int do_dentry_open(struct file *f,
|
|
struct inode *inode = f->f_path.dentry->d_inode;
|
|
int error;
|
|
|
|
- path_get(&f->f_path);
|
|
f->f_inode = inode;
|
|
f->f_mapping = inode->i_mapping;
|
|
f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
|
|
@@ -1006,6 +1010,7 @@ int finish_open(struct file *file, struct dentry *dentry,
|
|
BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */
|
|
|
|
file->__f_path.dentry = dentry;
|
|
+ path_get(&file->f_path);
|
|
return do_dentry_open(file, open);
|
|
}
|
|
EXPORT_SYMBOL(finish_open);
|
|
@@ -1049,6 +1054,7 @@ int vfs_open(const struct path *path, struct file *file)
|
|
int ret;
|
|
|
|
file->__f_path = *path;
|
|
+ path_get(&file->f_path);
|
|
ret = do_dentry_open(file, NULL);
|
|
if (!ret) {
|
|
/*
|
|
@@ -1061,6 +1067,25 @@ int vfs_open(const struct path *path, struct file *file)
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * vfs_open_consume - open the file at the given path and consume the reference
|
|
+ * @path: path to open
|
|
+ * @file: newly allocated file with f_flag initialized
|
|
+ */
|
|
+int vfs_open_consume(struct path *path, struct file *file)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ file->__f_path = *path;
|
|
+ path->mnt = NULL;
|
|
+ path->dentry = NULL;
|
|
+ ret = do_dentry_open(file, NULL);
|
|
+ if (!ret) {
|
|
+ fsnotify_open(file);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
struct file *dentry_open(const struct path *path, int flags,
|
|
const struct cred *cred)
|
|
{
|