vfs: add lookup_open()

Split out lookup + maybe create from do_last().  This is the part under i_mutex
protection.

The function is called lookup_open() and returns a filp even though the open
part is not used yet.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Miklos Szeredi 2012-06-05 15:10:15 +02:00 committed by Al Viro
parent 7157486541
commit d58ffd35c1

View File

@ -2196,6 +2196,60 @@ static inline int open_to_namei_flags(int flag)
return flag;
}
/*
* Lookup, maybe create and open the last component
*
* Must be called with i_mutex held on parent.
*
* Returns open file or NULL on success, error otherwise. NULL means no open
* was performed, only lookup.
*/
static struct file *lookup_open(struct nameidata *nd, struct path *path,
const struct open_flags *op,
int *want_write, bool *created)
{
struct dentry *dir = nd->path.dentry;
struct dentry *dentry;
int error;
*created = false;
dentry = lookup_hash(nd);
if (IS_ERR(dentry))
return ERR_CAST(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 nameidata_to_filp().
*/
error = mnt_want_write(nd->path.mnt);
if (error)
goto out_dput;
*want_write = 1;
*created = true;
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto out_dput;
error = vfs_create(dir->d_inode, dentry, mode, nd);
if (error)
goto out_dput;
}
path->dentry = dentry;
path->mnt = nd->path.mnt;
return NULL;
out_dput:
dput(dentry);
return ERR_PTR(error);
}
/*
* Handle the last step of open()
*/
@ -2203,13 +2257,13 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
const struct open_flags *op, const char *pathname)
{
struct dentry *dir = nd->path.dentry;
struct dentry *dentry;
int open_flag = op->open_flag;
int will_truncate = open_flag & O_TRUNC;
int want_write = 0;
int acc_mode = op->acc_mode;
struct file *filp;
struct inode *inode;
bool created;
int symlink_ok = 0;
struct path save_parent = { .dentry = NULL, .mnt = NULL };
bool retried = false;
@ -2277,53 +2331,24 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
retry_lookup:
mutex_lock(&dir->d_inode->i_mutex);
filp = lookup_open(nd, path, op, &want_write, &created);
mutex_unlock(&dir->d_inode->i_mutex);
dentry = lookup_hash(nd);
error = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
if (IS_ERR(filp))
goto out;
path->dentry = dentry;
path->mnt = nd->path.mnt;
/* Negative dentry, just create the file */
if (!dentry->d_inode && (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 nameidata_to_filp().
*/
error = mnt_want_write(nd->path.mnt);
if (error)
goto exit_mutex_unlock;
want_write = 1;
if (created) {
/* Don't check for write permission, don't truncate */
open_flag &= ~O_TRUNC;
will_truncate = 0;
acc_mode = MAY_OPEN;
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto exit_mutex_unlock;
error = vfs_create(dir->d_inode, dentry, mode, nd);
if (error)
goto exit_mutex_unlock;
mutex_unlock(&dir->d_inode->i_mutex);
dput(nd->path.dentry);
nd->path.dentry = dentry;
path_to_nameidata(path, nd);
goto common;
}
/*
* It already exists.
*/
mutex_unlock(&dir->d_inode->i_mutex);
audit_inode(pathname, path->dentry);
error = -EEXIST;
@ -2432,8 +2457,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
terminate_walk(nd);
return filp;
exit_mutex_unlock:
mutex_unlock(&dir->d_inode->i_mutex);
exit_dput:
path_put_conditional(path, nd);
exit: