|
@@ -2028,6 +2028,8 @@ static int link_path_walk(const char *name, struct nameidata *nd)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
+ if (IS_ERR(name))
|
|
|
+ return PTR_ERR(name);
|
|
|
while (*name=='/')
|
|
|
name++;
|
|
|
if (!*name)
|
|
@@ -2125,12 +2127,15 @@ OK:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* must be paired with terminate_walk() */
|
|
|
static const char *path_init(struct nameidata *nd, unsigned flags)
|
|
|
{
|
|
|
const char *s = nd->name->name;
|
|
|
|
|
|
if (!*s)
|
|
|
flags &= ~LOOKUP_RCU;
|
|
|
+ if (flags & LOOKUP_RCU)
|
|
|
+ rcu_read_lock();
|
|
|
|
|
|
nd->last_type = LAST_ROOT; /* if there are only slashes... */
|
|
|
nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
|
|
@@ -2143,7 +2148,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
|
|
nd->path = nd->root;
|
|
|
nd->inode = inode;
|
|
|
if (flags & LOOKUP_RCU) {
|
|
|
- rcu_read_lock();
|
|
|
nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
|
|
|
nd->root_seq = nd->seq;
|
|
|
nd->m_seq = read_seqbegin(&mount_lock);
|
|
@@ -2159,21 +2163,15 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
|
|
|
|
|
nd->m_seq = read_seqbegin(&mount_lock);
|
|
|
if (*s == '/') {
|
|
|
- if (flags & LOOKUP_RCU)
|
|
|
- rcu_read_lock();
|
|
|
set_root(nd);
|
|
|
if (likely(!nd_jump_root(nd)))
|
|
|
return s;
|
|
|
- nd->root.mnt = NULL;
|
|
|
- rcu_read_unlock();
|
|
|
return ERR_PTR(-ECHILD);
|
|
|
} else if (nd->dfd == AT_FDCWD) {
|
|
|
if (flags & LOOKUP_RCU) {
|
|
|
struct fs_struct *fs = current->fs;
|
|
|
unsigned seq;
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
-
|
|
|
do {
|
|
|
seq = read_seqcount_begin(&fs->seq);
|
|
|
nd->path = fs->pwd;
|
|
@@ -2195,16 +2193,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
|
|
|
|
|
dentry = f.file->f_path.dentry;
|
|
|
|
|
|
- if (*s) {
|
|
|
- if (!d_can_lookup(dentry)) {
|
|
|
- fdput(f);
|
|
|
- return ERR_PTR(-ENOTDIR);
|
|
|
- }
|
|
|
+ if (*s && unlikely(!d_can_lookup(dentry))) {
|
|
|
+ fdput(f);
|
|
|
+ return ERR_PTR(-ENOTDIR);
|
|
|
}
|
|
|
|
|
|
nd->path = f.file->f_path;
|
|
|
if (flags & LOOKUP_RCU) {
|
|
|
- rcu_read_lock();
|
|
|
nd->inode = nd->path.dentry->d_inode;
|
|
|
nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
|
|
|
} else {
|
|
@@ -2272,24 +2267,15 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
|
|
|
const char *s = path_init(nd, flags);
|
|
|
int err;
|
|
|
|
|
|
- if (IS_ERR(s))
|
|
|
- return PTR_ERR(s);
|
|
|
-
|
|
|
- if (unlikely(flags & LOOKUP_DOWN)) {
|
|
|
+ if (unlikely(flags & LOOKUP_DOWN) && !IS_ERR(s)) {
|
|
|
err = handle_lookup_down(nd);
|
|
|
- if (unlikely(err < 0)) {
|
|
|
- terminate_walk(nd);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ if (unlikely(err < 0))
|
|
|
+ s = ERR_PTR(err);
|
|
|
}
|
|
|
|
|
|
while (!(err = link_path_walk(s, nd))
|
|
|
&& ((err = lookup_last(nd)) > 0)) {
|
|
|
s = trailing_symlink(nd);
|
|
|
- if (IS_ERR(s)) {
|
|
|
- err = PTR_ERR(s);
|
|
|
- break;
|
|
|
- }
|
|
|
}
|
|
|
if (!err)
|
|
|
err = complete_walk(nd);
|
|
@@ -2336,10 +2322,7 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
|
|
|
struct path *parent)
|
|
|
{
|
|
|
const char *s = path_init(nd, flags);
|
|
|
- int err;
|
|
|
- if (IS_ERR(s))
|
|
|
- return PTR_ERR(s);
|
|
|
- err = link_path_walk(s, nd);
|
|
|
+ int err = link_path_walk(s, nd);
|
|
|
if (!err)
|
|
|
err = complete_walk(nd);
|
|
|
if (!err) {
|
|
@@ -2666,15 +2649,10 @@ path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path)
|
|
|
{
|
|
|
const char *s = path_init(nd, flags);
|
|
|
int err;
|
|
|
- if (IS_ERR(s))
|
|
|
- return PTR_ERR(s);
|
|
|
+
|
|
|
while (!(err = link_path_walk(s, nd)) &&
|
|
|
(err = mountpoint_last(nd)) > 0) {
|
|
|
s = trailing_symlink(nd);
|
|
|
- if (IS_ERR(s)) {
|
|
|
- err = PTR_ERR(s);
|
|
|
- break;
|
|
|
- }
|
|
|
}
|
|
|
if (!err) {
|
|
|
*path = nd->path;
|
|
@@ -3027,17 +3005,16 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
|
|
|
* Returns 0 if successful. The file will have been created and attached to
|
|
|
* @file by the filesystem calling finish_open().
|
|
|
*
|
|
|
- * Returns 1 if the file was looked up only or didn't need creating. The
|
|
|
- * caller will need to perform the open themselves. @path will have been
|
|
|
- * updated to point to the new dentry. This may be negative.
|
|
|
+ * If the file was looked up only or didn't need creating, FMODE_OPENED won't
|
|
|
+ * be set. The caller will need to perform the open themselves. @path will
|
|
|
+ * have been updated to point to the new dentry. This may be negative.
|
|
|
*
|
|
|
* Returns an error code otherwise.
|
|
|
*/
|
|
|
static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
|
|
struct path *path, struct file *file,
|
|
|
const struct open_flags *op,
|
|
|
- int open_flag, umode_t mode,
|
|
|
- int *opened)
|
|
|
+ int open_flag, umode_t mode)
|
|
|
{
|
|
|
struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
|
|
|
struct inode *dir = nd->path.dentry->d_inode;
|
|
@@ -3052,39 +3029,38 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
|
|
file->f_path.dentry = DENTRY_NOT_SET;
|
|
|
file->f_path.mnt = nd->path.mnt;
|
|
|
error = dir->i_op->atomic_open(dir, dentry, file,
|
|
|
- open_to_namei_flags(open_flag),
|
|
|
- mode, opened);
|
|
|
+ open_to_namei_flags(open_flag), mode);
|
|
|
d_lookup_done(dentry);
|
|
|
if (!error) {
|
|
|
- /*
|
|
|
- * We didn't have the inode before the open, so check open
|
|
|
- * permission here.
|
|
|
- */
|
|
|
- int acc_mode = op->acc_mode;
|
|
|
- if (*opened & FILE_CREATED) {
|
|
|
- WARN_ON(!(open_flag & O_CREAT));
|
|
|
- fsnotify_create(dir, dentry);
|
|
|
- acc_mode = 0;
|
|
|
- }
|
|
|
- error = may_open(&file->f_path, acc_mode, open_flag);
|
|
|
- if (WARN_ON(error > 0))
|
|
|
- error = -EINVAL;
|
|
|
- } else if (error > 0) {
|
|
|
- if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
|
|
|
+ if (file->f_mode & FMODE_OPENED) {
|
|
|
+ /*
|
|
|
+ * We didn't have the inode before the open, so check open
|
|
|
+ * permission here.
|
|
|
+ */
|
|
|
+ int acc_mode = op->acc_mode;
|
|
|
+ if (file->f_mode & FMODE_CREATED) {
|
|
|
+ WARN_ON(!(open_flag & O_CREAT));
|
|
|
+ fsnotify_create(dir, dentry);
|
|
|
+ acc_mode = 0;
|
|
|
+ }
|
|
|
+ error = may_open(&file->f_path, acc_mode, open_flag);
|
|
|
+ if (WARN_ON(error > 0))
|
|
|
+ error = -EINVAL;
|
|
|
+ } else if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
|
|
|
error = -EIO;
|
|
|
} else {
|
|
|
if (file->f_path.dentry) {
|
|
|
dput(dentry);
|
|
|
dentry = file->f_path.dentry;
|
|
|
}
|
|
|
- if (*opened & FILE_CREATED)
|
|
|
+ if (file->f_mode & FMODE_CREATED)
|
|
|
fsnotify_create(dir, dentry);
|
|
|
if (unlikely(d_is_negative(dentry))) {
|
|
|
error = -ENOENT;
|
|
|
} else {
|
|
|
path->dentry = dentry;
|
|
|
path->mnt = nd->path.mnt;
|
|
|
- return 1;
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -3095,25 +3071,22 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
|
|
/*
|
|
|
* Look up and maybe create and open the last component.
|
|
|
*
|
|
|
- * Must be called with i_mutex held on parent.
|
|
|
- *
|
|
|
- * Returns 0 if the file was successfully atomically created (if necessary) and
|
|
|
- * opened. In this case the file will be returned attached to @file.
|
|
|
+ * Must be called with parent locked (exclusive in O_CREAT case).
|
|
|
*
|
|
|
- * Returns 1 if the file was not completely opened at this time, though lookups
|
|
|
- * and creations will have been performed and the dentry returned in @path will
|
|
|
- * be positive upon return if O_CREAT was specified. If O_CREAT wasn't
|
|
|
- * specified then a negative dentry may be returned.
|
|
|
+ * Returns 0 on success, that is, if
|
|
|
+ * the file was successfully atomically created (if necessary) and opened, or
|
|
|
+ * the file was not completely opened at this time, though lookups and
|
|
|
+ * creations were performed.
|
|
|
+ * These case are distinguished by presence of FMODE_OPENED on file->f_mode.
|
|
|
+ * In the latter case dentry returned in @path might be negative if O_CREAT
|
|
|
+ * hadn't been specified.
|
|
|
*
|
|
|
- * An error code is returned otherwise.
|
|
|
- *
|
|
|
- * FILE_CREATE will be set in @*opened if the dentry was created and will be
|
|
|
- * cleared otherwise prior to returning.
|
|
|
+ * An error code is returned on failure.
|
|
|
*/
|
|
|
static int lookup_open(struct nameidata *nd, struct path *path,
|
|
|
struct file *file,
|
|
|
const struct open_flags *op,
|
|
|
- bool got_write, int *opened)
|
|
|
+ bool got_write)
|
|
|
{
|
|
|
struct dentry *dir = nd->path.dentry;
|
|
|
struct inode *dir_inode = dir->d_inode;
|
|
@@ -3126,7 +3099,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
|
|
if (unlikely(IS_DEADDIR(dir_inode)))
|
|
|
return -ENOENT;
|
|
|
|
|
|
- *opened &= ~FILE_CREATED;
|
|
|
+ file->f_mode &= ~FMODE_CREATED;
|
|
|
dentry = d_lookup(dir, &nd->last);
|
|
|
for (;;) {
|
|
|
if (!dentry) {
|
|
@@ -3188,7 +3161,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
|
|
|
|
|
if (dir_inode->i_op->atomic_open) {
|
|
|
error = atomic_open(nd, dentry, path, file, op, open_flag,
|
|
|
- mode, opened);
|
|
|
+ mode);
|
|
|
if (unlikely(error == -ENOENT) && create_error)
|
|
|
error = create_error;
|
|
|
return error;
|
|
@@ -3211,7 +3184,7 @@ no_open:
|
|
|
|
|
|
/* Negative dentry, just create the file */
|
|
|
if (!dentry->d_inode && (open_flag & O_CREAT)) {
|
|
|
- *opened |= FILE_CREATED;
|
|
|
+ file->f_mode |= FMODE_CREATED;
|
|
|
audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
|
|
|
if (!dir_inode->i_op->create) {
|
|
|
error = -EACCES;
|
|
@@ -3230,7 +3203,7 @@ no_open:
|
|
|
out_no_open:
|
|
|
path->dentry = dentry;
|
|
|
path->mnt = nd->path.mnt;
|
|
|
- return 1;
|
|
|
+ return 0;
|
|
|
|
|
|
out_dput:
|
|
|
dput(dentry);
|
|
@@ -3241,8 +3214,7 @@ out_dput:
|
|
|
* Handle the last step of open()
|
|
|
*/
|
|
|
static int do_last(struct nameidata *nd,
|
|
|
- struct file *file, const struct open_flags *op,
|
|
|
- int *opened)
|
|
|
+ struct file *file, const struct open_flags *op)
|
|
|
{
|
|
|
struct dentry *dir = nd->path.dentry;
|
|
|
int open_flag = op->open_flag;
|
|
@@ -3308,17 +3280,17 @@ static int do_last(struct nameidata *nd,
|
|
|
inode_lock(dir->d_inode);
|
|
|
else
|
|
|
inode_lock_shared(dir->d_inode);
|
|
|
- error = lookup_open(nd, &path, file, op, got_write, opened);
|
|
|
+ error = lookup_open(nd, &path, file, op, got_write);
|
|
|
if (open_flag & O_CREAT)
|
|
|
inode_unlock(dir->d_inode);
|
|
|
else
|
|
|
inode_unlock_shared(dir->d_inode);
|
|
|
|
|
|
- if (error <= 0) {
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
+ if (error)
|
|
|
+ goto out;
|
|
|
|
|
|
- if ((*opened & FILE_CREATED) ||
|
|
|
+ if (file->f_mode & FMODE_OPENED) {
|
|
|
+ if ((file->f_mode & FMODE_CREATED) ||
|
|
|
!S_ISREG(file_inode(file)->i_mode))
|
|
|
will_truncate = false;
|
|
|
|
|
@@ -3326,7 +3298,7 @@ static int do_last(struct nameidata *nd,
|
|
|
goto opened;
|
|
|
}
|
|
|
|
|
|
- if (*opened & FILE_CREATED) {
|
|
|
+ if (file->f_mode & FMODE_CREATED) {
|
|
|
/* Don't check for write permission, don't truncate */
|
|
|
open_flag &= ~O_TRUNC;
|
|
|
will_truncate = false;
|
|
@@ -3395,20 +3367,15 @@ finish_open_created:
|
|
|
error = may_open(&nd->path, acc_mode, open_flag);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
- BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
|
|
|
- error = vfs_open(&nd->path, file, current_cred());
|
|
|
+ BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */
|
|
|
+ error = vfs_open(&nd->path, file);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
- *opened |= FILE_OPENED;
|
|
|
opened:
|
|
|
- error = open_check_o_direct(file);
|
|
|
- if (!error)
|
|
|
- error = ima_file_check(file, op->acc_mode, *opened);
|
|
|
+ error = ima_file_check(file, op->acc_mode);
|
|
|
if (!error && will_truncate)
|
|
|
error = handle_truncate(file);
|
|
|
out:
|
|
|
- if (unlikely(error) && (*opened & FILE_OPENED))
|
|
|
- fput(file);
|
|
|
if (unlikely(error > 0)) {
|
|
|
WARN_ON(1);
|
|
|
error = -EINVAL;
|
|
@@ -3458,7 +3425,7 @@ EXPORT_SYMBOL(vfs_tmpfile);
|
|
|
|
|
|
static int do_tmpfile(struct nameidata *nd, unsigned flags,
|
|
|
const struct open_flags *op,
|
|
|
- struct file *file, int *opened)
|
|
|
+ struct file *file)
|
|
|
{
|
|
|
struct dentry *child;
|
|
|
struct path path;
|
|
@@ -3480,12 +3447,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
|
|
|
if (error)
|
|
|
goto out2;
|
|
|
file->f_path.mnt = path.mnt;
|
|
|
- error = finish_open(file, child, NULL, opened);
|
|
|
- if (error)
|
|
|
- goto out2;
|
|
|
- error = open_check_o_direct(file);
|
|
|
- if (error)
|
|
|
- fput(file);
|
|
|
+ error = finish_open(file, child, NULL);
|
|
|
out2:
|
|
|
mnt_drop_write(path.mnt);
|
|
|
out:
|
|
@@ -3499,7 +3461,7 @@ static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
|
|
|
int error = path_lookupat(nd, flags, &path);
|
|
|
if (!error) {
|
|
|
audit_inode(nd->name, path.dentry, 0);
|
|
|
- error = vfs_open(&path, file, current_cred());
|
|
|
+ error = vfs_open(&path, file);
|
|
|
path_put(&path);
|
|
|
}
|
|
|
return error;
|
|
@@ -3508,59 +3470,40 @@ static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
|
|
|
static struct file *path_openat(struct nameidata *nd,
|
|
|
const struct open_flags *op, unsigned flags)
|
|
|
{
|
|
|
- const char *s;
|
|
|
struct file *file;
|
|
|
- int opened = 0;
|
|
|
int error;
|
|
|
|
|
|
- file = get_empty_filp();
|
|
|
+ file = alloc_empty_file(op->open_flag, current_cred());
|
|
|
if (IS_ERR(file))
|
|
|
return file;
|
|
|
|
|
|
- file->f_flags = op->open_flag;
|
|
|
-
|
|
|
if (unlikely(file->f_flags & __O_TMPFILE)) {
|
|
|
- error = do_tmpfile(nd, flags, op, file, &opened);
|
|
|
- goto out2;
|
|
|
- }
|
|
|
-
|
|
|
- if (unlikely(file->f_flags & O_PATH)) {
|
|
|
+ error = do_tmpfile(nd, flags, op, file);
|
|
|
+ } else if (unlikely(file->f_flags & O_PATH)) {
|
|
|
error = do_o_path(nd, flags, file);
|
|
|
- if (!error)
|
|
|
- opened |= FILE_OPENED;
|
|
|
- goto out2;
|
|
|
- }
|
|
|
-
|
|
|
- s = path_init(nd, flags);
|
|
|
- if (IS_ERR(s)) {
|
|
|
- put_filp(file);
|
|
|
- return ERR_CAST(s);
|
|
|
- }
|
|
|
- while (!(error = link_path_walk(s, nd)) &&
|
|
|
- (error = do_last(nd, file, op, &opened)) > 0) {
|
|
|
- nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
|
|
|
- s = trailing_symlink(nd);
|
|
|
- if (IS_ERR(s)) {
|
|
|
- error = PTR_ERR(s);
|
|
|
- break;
|
|
|
+ } else {
|
|
|
+ const char *s = path_init(nd, flags);
|
|
|
+ while (!(error = link_path_walk(s, nd)) &&
|
|
|
+ (error = do_last(nd, file, op)) > 0) {
|
|
|
+ nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
|
|
|
+ s = trailing_symlink(nd);
|
|
|
}
|
|
|
+ terminate_walk(nd);
|
|
|
}
|
|
|
- terminate_walk(nd);
|
|
|
-out2:
|
|
|
- if (!(opened & FILE_OPENED)) {
|
|
|
- BUG_ON(!error);
|
|
|
- put_filp(file);
|
|
|
+ if (likely(!error)) {
|
|
|
+ if (likely(file->f_mode & FMODE_OPENED))
|
|
|
+ return file;
|
|
|
+ WARN_ON(1);
|
|
|
+ error = -EINVAL;
|
|
|
}
|
|
|
- if (unlikely(error)) {
|
|
|
- if (error == -EOPENSTALE) {
|
|
|
- if (flags & LOOKUP_RCU)
|
|
|
- error = -ECHILD;
|
|
|
- else
|
|
|
- error = -ESTALE;
|
|
|
- }
|
|
|
- file = ERR_PTR(error);
|
|
|
+ fput(file);
|
|
|
+ if (error == -EOPENSTALE) {
|
|
|
+ if (flags & LOOKUP_RCU)
|
|
|
+ error = -ECHILD;
|
|
|
+ else
|
|
|
+ error = -ESTALE;
|
|
|
}
|
|
|
- return file;
|
|
|
+ return ERR_PTR(error);
|
|
|
}
|
|
|
|
|
|
struct file *do_filp_open(int dfd, struct filename *pathname,
|