return -ECHILD;
}
-/**
- * release_open_intent - free up open intent resources
- * @nd: pointer to nameidata
- */
-void release_open_intent(struct nameidata *nd)
-{
- struct file *file = nd->intent.open.file;
-
- if (file && !IS_ERR(file)) {
- if (file->f_path.dentry == NULL)
- put_filp(file);
- else
- fput(file);
- }
-}
-
static inline int d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
return dentry->d_op->d_revalidate(dentry, nd);
}
static struct file *atomic_open(struct nameidata *nd, struct dentry *dentry,
- struct path *path, const struct open_flags *op,
- int *want_write, bool need_lookup,
+ struct path *path, struct opendata *od,
+ const struct open_flags *op,
+ bool *want_write, bool need_lookup,
bool *created)
{
struct inode *dir = nd->path.dentry->d_inode;
umode_t mode;
int error;
int acc_mode;
- struct opendata od;
struct file *filp;
int create_error = 0;
struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
(open_flag & O_ACCMODE) != O_RDONLY) {
error = mnt_want_write(nd->path.mnt);
if (!error) {
- *want_write = 1;
+ *want_write = true;
} else if (!(open_flag & O_CREAT)) {
/*
* No O_CREATE -> atomicity not a requirement -> fall
if (nd->flags & LOOKUP_DIRECTORY)
open_flag |= O_DIRECTORY;
- od.dentry = DENTRY_NOT_SET;
- od.mnt = nd->path.mnt;
- od.filp = &nd->intent.open.file;
- filp = dir->i_op->atomic_open(dir, dentry, &od, open_flag, mode,
+ od->dentry = DENTRY_NOT_SET;
+ od->mnt = nd->path.mnt;
+ filp = dir->i_op->atomic_open(dir, dentry, od, open_flag, mode,
created);
if (IS_ERR(filp)) {
- if (WARN_ON(od.dentry != DENTRY_NOT_SET))
- dput(od.dentry);
+ if (WARN_ON(od->dentry != DENTRY_NOT_SET))
+ dput(od->dentry);
if (create_error && PTR_ERR(filp) == -ENOENT)
filp = ERR_PTR(create_error);
}
if (!filp) {
- if (WARN_ON(od.dentry == DENTRY_NOT_SET)) {
+ if (WARN_ON(od->dentry == DENTRY_NOT_SET)) {
filp = ERR_PTR(-EIO);
goto out;
}
- if (od.dentry) {
+ if (od->dentry) {
dput(dentry);
- dentry = od.dentry;
+ dentry = od->dentry;
}
goto looked_up;
}
* here.
*/
error = may_open(&filp->f_path, acc_mode, open_flag);
- if (error)
- goto out_fput;
-
- error = open_check_o_direct(filp);
- if (error)
- goto out_fput;
+ if (error) {
+ fput(filp);
+ filp = ERR_PTR(error);
+ }
out:
dput(dentry);
return filp;
-out_fput:
- fput(filp);
- filp = ERR_PTR(error);
- goto out;
-
no_open:
if (need_lookup) {
dentry = lookup_real(dir, dentry, nd);
* was performed, only lookup.
*/
static struct file *lookup_open(struct nameidata *nd, struct path *path,
+ struct opendata *od,
const struct open_flags *op,
- int *want_write, bool *created)
+ bool *want_write, bool *created)
{
struct dentry *dir = nd->path.dentry;
struct inode *dir_inode = dir->d_inode;
goto out_no_open;
if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {
- return atomic_open(nd, dentry, path, op, want_write,
+ return atomic_open(nd, dentry, path, od, op, want_write,
need_lookup, created);
}
* 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 nameidata_to_filp().
+ * the 'struct file' in finish_open().
*/
error = mnt_want_write(nd->path.mnt);
if (error)
goto out_dput;
- *want_write = 1;
+ *want_write = true;
*created = true;
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
* Handle the last step of open()
*/
static struct file *do_last(struct nameidata *nd, struct path *path,
- const struct open_flags *op, const char *pathname)
+ struct opendata *od, const struct open_flags *op,
+ const char *pathname)
{
struct dentry *dir = nd->path.dentry;
int open_flag = op->open_flag;
- int will_truncate = open_flag & O_TRUNC;
- int want_write = 0;
+ bool will_truncate = (open_flag & O_TRUNC) != 0;
+ bool want_write = false;
int acc_mode = op->acc_mode;
struct file *filp;
struct inode *inode;
bool created;
- int symlink_ok = 0;
+ bool symlink_ok = false;
struct path save_parent = { .dentry = NULL, .mnt = NULL };
bool retried = false;
int error;
error = -EISDIR;
goto exit;
}
- goto ok;
+ goto finish_open;
case LAST_BIND:
error = complete_walk(nd);
if (error)
return ERR_PTR(error);
audit_inode(pathname, dir);
- goto ok;
+ goto finish_open;
}
if (!(open_flag & O_CREAT)) {
if (nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
- symlink_ok = 1;
+ symlink_ok = true;
/* we _can_ be in RCU mode here */
error = lookup_fast(nd, &nd->last, path, &inode);
if (likely(!error))
retry_lookup:
mutex_lock(&dir->d_inode->i_mutex);
- filp = lookup_open(nd, path, op, &want_write, &created);
+ filp = lookup_open(nd, path, od, op, &want_write, &created);
mutex_unlock(&dir->d_inode->i_mutex);
if (filp) {
goto out;
if (created || !S_ISREG(filp->f_path.dentry->d_inode->i_mode))
- will_truncate = 0;
+ will_truncate = false;
audit_inode(pathname, filp->f_path.dentry);
goto opened;
if (created) {
/* Don't check for write permission, don't truncate */
open_flag &= ~O_TRUNC;
- will_truncate = 0;
+ will_truncate = false;
acc_mode = MAY_OPEN;
path_to_nameidata(path, nd);
- goto common;
+ goto finish_open_created;
}
/*
*/
if (want_write) {
mnt_drop_write(nd->path.mnt);
- want_write = 0;
+ want_write = false;
}
error = -EEXIST;
if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup)
goto exit;
audit_inode(pathname, nd->path.dentry);
-ok:
+finish_open:
if (!S_ISREG(nd->inode->i_mode))
- will_truncate = 0;
+ will_truncate = false;
if (will_truncate) {
error = mnt_want_write(nd->path.mnt);
if (error)
goto exit;
- want_write = 1;
+ want_write = true;
}
-common:
+finish_open_created:
error = may_open(&nd->path, acc_mode, open_flag);
if (error)
goto exit;
- filp = nameidata_to_filp(nd);
- if (filp == ERR_PTR(-EOPENSTALE) && save_parent.dentry && !retried) {
- BUG_ON(save_parent.dentry != dir);
- path_put(&nd->path);
- nd->path = save_parent;
- nd->inode = dir->d_inode;
- save_parent.mnt = NULL;
- save_parent.dentry = NULL;
- if (want_write) {
- mnt_drop_write(nd->path.mnt);
- want_write = 0;
- }
- retried = true;
- goto retry_lookup;
+ od->mnt = nd->path.mnt;
+ filp = finish_open(od, nd->path.dentry, NULL);
+ if (IS_ERR(filp)) {
+ if (filp == ERR_PTR(-EOPENSTALE))
+ goto stale_open;
+ goto out;
}
opened:
- if (!IS_ERR(filp)) {
- error = ima_file_check(filp, op->acc_mode);
- if (error) {
- fput(filp);
- filp = ERR_PTR(error);
- }
- }
- if (!IS_ERR(filp)) {
- if (will_truncate) {
- error = handle_truncate(filp);
- if (error) {
- fput(filp);
- filp = ERR_PTR(error);
- }
- }
+ error = open_check_o_direct(filp);
+ if (error)
+ goto exit_fput;
+ error = ima_file_check(filp, op->acc_mode);
+ if (error)
+ goto exit_fput;
+
+ if (will_truncate) {
+ error = handle_truncate(filp);
+ if (error)
+ goto exit_fput;
}
out:
if (want_write)
exit:
filp = ERR_PTR(error);
goto out;
+exit_fput:
+ fput(filp);
+ goto exit;
+
+stale_open:
+ /* If no saved parent or already retried then can't retry */
+ if (!save_parent.dentry || retried)
+ goto out;
+
+ BUG_ON(save_parent.dentry != dir);
+ path_put(&nd->path);
+ nd->path = save_parent;
+ nd->inode = dir->d_inode;
+ save_parent.mnt = NULL;
+ save_parent.dentry = NULL;
+ if (want_write) {
+ mnt_drop_write(nd->path.mnt);
+ want_write = false;
+ }
+ retried = true;
+ goto retry_lookup;
}
static struct file *path_openat(int dfd, const char *pathname,
struct nameidata *nd, const struct open_flags *op, int flags)
{
struct file *base = NULL;
- struct file *filp;
+ struct opendata od;
+ struct file *res;
struct path path;
int error;
- filp = get_empty_filp();
- if (!filp)
+ od.filp = get_empty_filp();
+ if (!od.filp)
return ERR_PTR(-ENFILE);
- filp->f_flags = op->open_flag;
- nd->intent.open.file = filp;
- nd->intent.open.flags = open_to_namei_flags(op->open_flag);
- nd->intent.open.create_mode = op->mode;
+ od.filp->f_flags = op->open_flag;
error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(error))
if (unlikely(error))
goto out_filp;
- filp = do_last(nd, &path, op, pathname);
- while (unlikely(!filp)) { /* trailing symlink */
+ res = do_last(nd, &path, &od, op, pathname);
+ while (unlikely(!res)) { /* trailing symlink */
struct path link = path;
void *cookie;
if (!(nd->flags & LOOKUP_FOLLOW)) {
path_put_conditional(&path, nd);
path_put(&nd->path);
- filp = ERR_PTR(-ELOOP);
+ res = ERR_PTR(-ELOOP);
break;
}
nd->flags |= LOOKUP_PARENT;
error = follow_link(&link, nd, &cookie);
if (unlikely(error))
goto out_filp;
- filp = do_last(nd, &path, op, pathname);
+ res = do_last(nd, &path, &od, op, pathname);
put_link(nd, &link, cookie);
}
out:
path_put(&nd->root);
if (base)
fput(base);
- release_open_intent(nd);
- if (filp == ERR_PTR(-EOPENSTALE)) {
+ if (od.filp) {
+ BUG_ON(od.filp->f_path.dentry);
+ put_filp(od.filp);
+ }
+ if (res == ERR_PTR(-EOPENSTALE)) {
if (flags & LOOKUP_RCU)
- filp = ERR_PTR(-ECHILD);
+ res = ERR_PTR(-ECHILD);
else
- filp = ERR_PTR(-ESTALE);
+ res = ERR_PTR(-ESTALE);
}
- return filp;
+ return res;
out_filp:
- filp = ERR_PTR(error);
+ res = ERR_PTR(error);
goto out;
}
goto out;
nd.flags &= ~LOOKUP_PARENT;
nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
- nd.intent.open.flags = O_EXCL;
/*
* Do the final lookup.