Commit 74d7970f authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: fix racy issue from using ->d_parent and ->d_name

Al pointed out that ksmbd has racy issue from using ->d_parent and ->d_name
in ksmbd_vfs_unlink and smb2_vfs_rename(). and use new lock_rename_child()
to lock stable parent while underlying rename racy.
Introduce vfs_path_parent_lookup helper to avoid out of share access and
export vfs functions like the following ones to use
vfs_path_parent_lookup().
 - rename __lookup_hash() to lookup_one_qstr_excl().
 - export lookup_one_qstr_excl().
 - export getname_kernel() and putname().

vfs_path_parent_lookup() is used for parent lookup of destination file
using absolute pathname given from FILE_RENAME_INFORMATION request.
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent af36c51e
...@@ -2408,7 +2408,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, ...@@ -2408,7 +2408,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
return rc; return rc;
} }
rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0);
if (rc) { if (rc) {
pr_err("cannot get linux path (%s), err = %d\n", pr_err("cannot get linux path (%s), err = %d\n",
name, rc); name, rc);
...@@ -2699,8 +2699,10 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2699,8 +2699,10 @@ int smb2_open(struct ksmbd_work *work)
goto err_out1; goto err_out1;
} }
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
if (!rc) { if (!rc) {
file_present = true;
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
/* /*
* If file exists with under flags, return access * If file exists with under flags, return access
...@@ -2709,7 +2711,6 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2709,7 +2711,6 @@ int smb2_open(struct ksmbd_work *work)
if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || if (req->CreateDisposition == FILE_OVERWRITE_IF_LE ||
req->CreateDisposition == FILE_OPEN_IF_LE) { req->CreateDisposition == FILE_OPEN_IF_LE) {
rc = -EACCES; rc = -EACCES;
path_put(&path);
goto err_out; goto err_out;
} }
...@@ -2717,26 +2718,23 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2717,26 +2718,23 @@ int smb2_open(struct ksmbd_work *work)
ksmbd_debug(SMB, ksmbd_debug(SMB,
"User does not have write permission\n"); "User does not have write permission\n");
rc = -EACCES; rc = -EACCES;
path_put(&path);
goto err_out; goto err_out;
} }
} else if (d_is_symlink(path.dentry)) { } else if (d_is_symlink(path.dentry)) {
rc = -EACCES; rc = -EACCES;
path_put(&path);
goto err_out; goto err_out;
} }
}
if (rc) { file_present = true;
idmap = mnt_idmap(path.mnt);
} else {
if (rc != -ENOENT) if (rc != -ENOENT)
goto err_out; goto err_out;
ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
name, rc); name, rc);
rc = 0; rc = 0;
} else {
file_present = true;
idmap = mnt_idmap(path.mnt);
} }
if (stream_name) { if (stream_name) {
if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) {
if (s_type == DATA_STREAM) { if (s_type == DATA_STREAM) {
...@@ -2864,8 +2862,9 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2864,8 +2862,9 @@ int smb2_open(struct ksmbd_work *work)
if ((daccess & FILE_DELETE_LE) || if ((daccess & FILE_DELETE_LE) ||
(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) {
rc = ksmbd_vfs_may_delete(idmap, rc = inode_permission(idmap,
path.dentry); d_inode(path.dentry->d_parent),
MAY_EXEC | MAY_WRITE);
if (rc) if (rc)
goto err_out; goto err_out;
} }
...@@ -3236,10 +3235,13 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3236,10 +3235,13 @@ int smb2_open(struct ksmbd_work *work)
} }
err_out: err_out:
if (file_present || created) if (file_present || created) {
path_put(&path); inode_unlock(d_inode(path.dentry->d_parent));
dput(path.dentry);
}
ksmbd_revert_fsids(work); ksmbd_revert_fsids(work);
err_out1: err_out1:
if (rc) { if (rc) {
if (rc == -EINVAL) if (rc == -EINVAL)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
...@@ -5390,44 +5392,19 @@ int smb2_echo(struct ksmbd_work *work) ...@@ -5390,44 +5392,19 @@ int smb2_echo(struct ksmbd_work *work)
static int smb2_rename(struct ksmbd_work *work, static int smb2_rename(struct ksmbd_work *work,
struct ksmbd_file *fp, struct ksmbd_file *fp,
struct mnt_idmap *idmap,
struct smb2_file_rename_info *file_info, struct smb2_file_rename_info *file_info,
struct nls_table *local_nls) struct nls_table *local_nls)
{ {
struct ksmbd_share_config *share = fp->tcon->share_conf; struct ksmbd_share_config *share = fp->tcon->share_conf;
char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; char *new_name = NULL;
char *pathname = NULL; int rc, flags = 0;
struct path path;
bool file_present = true;
int rc;
ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n");
pathname = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathname)
return -ENOMEM;
abs_oldname = file_path(fp->filp, pathname, PATH_MAX);
if (IS_ERR(abs_oldname)) {
rc = -EINVAL;
goto out;
}
old_name = strrchr(abs_oldname, '/');
if (old_name && old_name[1] != '\0') {
old_name++;
} else {
ksmbd_debug(SMB, "can't get last component in path %s\n",
abs_oldname);
rc = -ENOENT;
goto out;
}
new_name = smb2_get_name(file_info->FileName, new_name = smb2_get_name(file_info->FileName,
le32_to_cpu(file_info->FileNameLength), le32_to_cpu(file_info->FileNameLength),
local_nls); local_nls);
if (IS_ERR(new_name)) { if (IS_ERR(new_name))
rc = PTR_ERR(new_name); return PTR_ERR(new_name);
goto out;
}
if (strchr(new_name, ':')) { if (strchr(new_name, ':')) {
int s_type; int s_type;
...@@ -5453,7 +5430,7 @@ static int smb2_rename(struct ksmbd_work *work, ...@@ -5453,7 +5430,7 @@ static int smb2_rename(struct ksmbd_work *work,
if (rc) if (rc)
goto out; goto out;
rc = ksmbd_vfs_setxattr(idmap, rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp),
fp->filp->f_path.dentry, fp->filp->f_path.dentry,
xattr_stream_name, xattr_stream_name,
NULL, 0, 0); NULL, 0, 0);
...@@ -5468,46 +5445,17 @@ static int smb2_rename(struct ksmbd_work *work, ...@@ -5468,46 +5445,17 @@ static int smb2_rename(struct ksmbd_work *work,
} }
ksmbd_debug(SMB, "new name %s\n", new_name); ksmbd_debug(SMB, "new name %s\n", new_name);
rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
if (rc) {
if (rc != -ENOENT)
goto out;
file_present = false;
} else {
path_put(&path);
}
if (ksmbd_share_veto_filename(share, new_name)) { if (ksmbd_share_veto_filename(share, new_name)) {
rc = -ENOENT; rc = -ENOENT;
ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name);
goto out; goto out;
} }
if (file_info->ReplaceIfExists) { if (!file_info->ReplaceIfExists)
if (file_present) { flags = RENAME_NOREPLACE;
rc = ksmbd_vfs_remove_file(work, new_name);
if (rc) {
if (rc != -ENOTEMPTY)
rc = -EINVAL;
ksmbd_debug(SMB, "cannot delete %s, rc %d\n",
new_name, rc);
goto out;
}
}
} else {
if (file_present &&
strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) {
rc = -EEXIST;
ksmbd_debug(SMB,
"cannot rename already existing file\n");
goto out;
}
}
rc = ksmbd_vfs_fp_rename(work, fp, new_name); rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags);
out: out:
kfree(pathname);
if (!IS_ERR(new_name))
kfree(new_name); kfree(new_name);
return rc; return rc;
} }
...@@ -5548,18 +5496,17 @@ static int smb2_create_link(struct ksmbd_work *work, ...@@ -5548,18 +5496,17 @@ static int smb2_create_link(struct ksmbd_work *work,
} }
ksmbd_debug(SMB, "target name is %s\n", target_name); ksmbd_debug(SMB, "target name is %s\n", target_name);
rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
&path, 0);
if (rc) { if (rc) {
if (rc != -ENOENT) if (rc != -ENOENT)
goto out; goto out;
file_present = false; file_present = false;
} else {
path_put(&path);
} }
if (file_info->ReplaceIfExists) { if (file_info->ReplaceIfExists) {
if (file_present) { if (file_present) {
rc = ksmbd_vfs_remove_file(work, link_name); rc = ksmbd_vfs_remove_file(work, &path);
if (rc) { if (rc) {
rc = -EINVAL; rc = -EINVAL;
ksmbd_debug(SMB, "cannot delete %s\n", ksmbd_debug(SMB, "cannot delete %s\n",
...@@ -5579,6 +5526,10 @@ static int smb2_create_link(struct ksmbd_work *work, ...@@ -5579,6 +5526,10 @@ static int smb2_create_link(struct ksmbd_work *work,
if (rc) if (rc)
rc = -EINVAL; rc = -EINVAL;
out: out:
if (file_present) {
inode_unlock(d_inode(path.dentry->d_parent));
path_put(&path);
}
if (!IS_ERR(link_name)) if (!IS_ERR(link_name))
kfree(link_name); kfree(link_name);
kfree(pathname); kfree(pathname);
...@@ -5756,12 +5707,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5756,12 +5707,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
struct smb2_file_rename_info *rename_info, struct smb2_file_rename_info *rename_info,
unsigned int buf_len) unsigned int buf_len)
{ {
struct mnt_idmap *idmap;
struct ksmbd_file *parent_fp;
struct dentry *parent;
struct dentry *dentry = fp->filp->f_path.dentry;
int ret;
if (!(fp->daccess & FILE_DELETE_LE)) { if (!(fp->daccess & FILE_DELETE_LE)) {
pr_err("no right to delete : 0x%x\n", fp->daccess); pr_err("no right to delete : 0x%x\n", fp->daccess);
return -EACCES; return -EACCES;
...@@ -5771,32 +5716,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5771,32 +5716,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
le32_to_cpu(rename_info->FileNameLength)) le32_to_cpu(rename_info->FileNameLength))
return -EINVAL; return -EINVAL;
idmap = file_mnt_idmap(fp->filp); if (!le32_to_cpu(rename_info->FileNameLength))
if (ksmbd_stream_fd(fp)) return -EINVAL;
goto next;
parent = dget_parent(dentry);
ret = ksmbd_vfs_lock_parent(idmap, parent, dentry);
if (ret) {
dput(parent);
return ret;
}
parent_fp = ksmbd_lookup_fd_inode(d_inode(parent));
inode_unlock(d_inode(parent));
dput(parent);
if (parent_fp) { return smb2_rename(work, fp, rename_info, work->conn->local_nls);
if (parent_fp->daccess & FILE_DELETE_LE) {
pr_err("parent dir is opened with delete access\n");
ksmbd_fd_put(work, parent_fp);
return -ESHARE;
}
ksmbd_fd_put(work, parent_fp);
}
next:
return smb2_rename(work, fp, idmap, rename_info,
work->conn->local_nls);
} }
static int set_file_disposition_info(struct ksmbd_file *fp, static int set_file_disposition_info(struct ksmbd_file *fp,
......
This diff is collapsed.
...@@ -71,9 +71,7 @@ struct ksmbd_kstat { ...@@ -71,9 +71,7 @@ struct ksmbd_kstat {
__le32 file_attributes; __le32 file_attributes;
}; };
int ksmbd_vfs_lock_parent(struct mnt_idmap *idmap, struct dentry *parent, int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
struct dentry *child);
int ksmbd_vfs_may_delete(struct mnt_idmap *idmap, struct dentry *dentry);
int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, int ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap,
struct dentry *dentry, __le32 *daccess); struct dentry *dentry, __le32 *daccess);
int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
...@@ -84,12 +82,12 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -84,12 +82,12 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
char *buf, size_t count, loff_t *pos, bool sync, char *buf, size_t count, loff_t *pos, bool sync,
ssize_t *written); ssize_t *written);
int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path);
int ksmbd_vfs_link(struct ksmbd_work *work, int ksmbd_vfs_link(struct ksmbd_work *work,
const char *oldname, const char *newname); const char *oldname, const char *newname);
int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat);
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
char *newname); char *newname, int flags);
int ksmbd_vfs_truncate(struct ksmbd_work *work, int ksmbd_vfs_truncate(struct ksmbd_work *work,
struct ksmbd_file *fp, loff_t size); struct ksmbd_file *fp, loff_t size);
struct srv_copychunk; struct srv_copychunk;
...@@ -116,8 +114,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, ...@@ -116,8 +114,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
size_t *xattr_stream_name_size, int s_type); size_t *xattr_stream_name_size, int s_type);
int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
struct dentry *dentry, char *attr_name); struct dentry *dentry, char *attr_name);
int ksmbd_vfs_kern_path(struct ksmbd_work *work, int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
char *name, unsigned int flags, struct path *path, unsigned int flags, struct path *path,
bool caseless); bool caseless);
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name, const char *name,
...@@ -131,8 +129,7 @@ struct file_allocated_range_buffer; ...@@ -131,8 +129,7 @@ struct file_allocated_range_buffer;
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
struct file_allocated_range_buffer *ranges, struct file_allocated_range_buffer *ranges,
unsigned int in_count, unsigned int *out_count); unsigned int in_count, unsigned int *out_count);
int ksmbd_vfs_unlink(struct mnt_idmap *idmap, struct dentry *dir, int ksmbd_vfs_unlink(struct file *filp);
struct dentry *dentry);
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
struct mnt_idmap *idmap, struct mnt_idmap *idmap,
......
...@@ -244,7 +244,6 @@ void ksmbd_release_inode_hash(void) ...@@ -244,7 +244,6 @@ void ksmbd_release_inode_hash(void)
static void __ksmbd_inode_close(struct ksmbd_file *fp) static void __ksmbd_inode_close(struct ksmbd_file *fp)
{ {
struct dentry *dir, *dentry;
struct ksmbd_inode *ci = fp->f_ci; struct ksmbd_inode *ci = fp->f_ci;
int err; int err;
struct file *filp; struct file *filp;
...@@ -263,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) ...@@ -263,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
if (atomic_dec_and_test(&ci->m_count)) { if (atomic_dec_and_test(&ci->m_count)) {
write_lock(&ci->m_lock); write_lock(&ci->m_lock);
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
dentry = filp->f_path.dentry;
dir = dentry->d_parent;
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
write_unlock(&ci->m_lock); write_unlock(&ci->m_lock);
ksmbd_vfs_unlink(file_mnt_idmap(filp), dir, dentry); ksmbd_vfs_unlink(filp);
write_lock(&ci->m_lock); write_lock(&ci->m_lock);
} }
write_unlock(&ci->m_lock); write_unlock(&ci->m_lock);
......
...@@ -254,6 +254,7 @@ getname_kernel(const char * filename) ...@@ -254,6 +254,7 @@ getname_kernel(const char * filename)
return result; return result;
} }
EXPORT_SYMBOL(getname_kernel);
void putname(struct filename *name) void putname(struct filename *name)
{ {
...@@ -271,6 +272,7 @@ void putname(struct filename *name) ...@@ -271,6 +272,7 @@ void putname(struct filename *name)
} else } else
__putname(name); __putname(name);
} }
EXPORT_SYMBOL(putname);
/** /**
* check_acl - perform ACL permission checking * check_acl - perform ACL permission checking
...@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name, ...@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name,
* when directory is guaranteed to have no in-lookup children * when directory is guaranteed to have no in-lookup children
* at all. * at all.
*/ */
static struct dentry *__lookup_hash(const struct qstr *name, struct dentry *lookup_one_qstr_excl(const struct qstr *name,
struct dentry *base, unsigned int flags) struct dentry *base,
unsigned int flags)
{ {
struct dentry *dentry = lookup_dcache(name, base, flags); struct dentry *dentry = lookup_dcache(name, base, flags);
struct dentry *old; struct dentry *old;
...@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name, ...@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name,
} }
return dentry; return dentry;
} }
EXPORT_SYMBOL(lookup_one_qstr_excl);
static struct dentry *lookup_fast(struct nameidata *nd) static struct dentry *lookup_fast(struct nameidata *nd)
{ {
...@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags, ...@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
} }
/* Note: this does not consume "name" */ /* Note: this does not consume "name" */
static int filename_parentat(int dfd, struct filename *name, static int __filename_parentat(int dfd, struct filename *name,
unsigned int flags, struct path *parent, unsigned int flags, struct path *parent,
struct qstr *last, int *type) struct qstr *last, int *type,
const struct path *root)
{ {
int retval; int retval;
struct nameidata nd; struct nameidata nd;
if (IS_ERR(name)) if (IS_ERR(name))
return PTR_ERR(name); return PTR_ERR(name);
set_nameidata(&nd, dfd, name, NULL); set_nameidata(&nd, dfd, name, root);
retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
if (unlikely(retval == -ECHILD)) if (unlikely(retval == -ECHILD))
retval = path_parentat(&nd, flags, parent); retval = path_parentat(&nd, flags, parent);
...@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name, ...@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name,
return retval; return retval;
} }
static int filename_parentat(int dfd, struct filename *name,
unsigned int flags, struct path *parent,
struct qstr *last, int *type)
{
return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
}
/* does lookup, returns the object with parent locked */ /* does lookup, returns the object with parent locked */
static struct dentry *__kern_path_locked(struct filename *name, struct path *path) static struct dentry *__kern_path_locked(struct filename *name, struct path *path)
{ {
...@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat ...@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
d = __lookup_hash(&last, path->dentry, 0); d = lookup_one_qstr_excl(&last, path->dentry, 0);
if (IS_ERR(d)) { if (IS_ERR(d)) {
inode_unlock(path->dentry->d_inode); inode_unlock(path->dentry->d_inode);
path_put(path); path_put(path);
...@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path) ...@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
} }
EXPORT_SYMBOL(kern_path); EXPORT_SYMBOL(kern_path);
/**
* vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair
* @filename: filename structure
* @flags: lookup flags
* @parent: pointer to struct path to fill
* @last: last component
* @type: type of the last component
* @root: pointer to struct path of the base directory
*/
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
struct path *parent, struct qstr *last, int *type,
const struct path *root)
{
return __filename_parentat(AT_FDCWD, filename, flags, parent, last,
type, root);
}
EXPORT_SYMBOL(vfs_path_parent_lookup);
/** /**
* vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
* @dentry: pointer to dentry of the base directory * @dentry: pointer to dentry of the base directory
...@@ -3852,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, ...@@ -3852,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
if (last.name[last.len] && !want_dir) if (last.name[last.len] && !want_dir)
create_flags = 0; create_flags = 0;
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags); dentry = lookup_one_qstr_excl(&last, path->dentry,
reval_flag | create_flags);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto unlock; goto unlock;
...@@ -4212,7 +4243,7 @@ int do_rmdir(int dfd, struct filename *name) ...@@ -4212,7 +4243,7 @@ int do_rmdir(int dfd, struct filename *name)
goto exit2; goto exit2;
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
dentry = __lookup_hash(&last, path.dentry, lookup_flags); dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto exit3; goto exit3;
...@@ -4345,7 +4376,7 @@ int do_unlinkat(int dfd, struct filename *name) ...@@ -4345,7 +4376,7 @@ int do_unlinkat(int dfd, struct filename *name)
goto exit2; goto exit2;
retry_deleg: retry_deleg:
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
dentry = __lookup_hash(&last, path.dentry, lookup_flags); dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
...@@ -4909,7 +4940,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, ...@@ -4909,7 +4940,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
retry_deleg: retry_deleg:
trap = lock_rename(new_path.dentry, old_path.dentry); trap = lock_rename(new_path.dentry, old_path.dentry);
old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
lookup_flags);
error = PTR_ERR(old_dentry); error = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry)) if (IS_ERR(old_dentry))
goto exit3; goto exit3;
...@@ -4917,7 +4949,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, ...@@ -4917,7 +4949,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
error = -ENOENT; error = -ENOENT;
if (d_is_negative(old_dentry)) if (d_is_negative(old_dentry))
goto exit4; goto exit4;
new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
lookup_flags | target_flags);
error = PTR_ERR(new_dentry); error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry)) if (IS_ERR(new_dentry))
goto exit4; goto exit4;
......
...@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags, ...@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
return user_path_at_empty(dfd, name, flags, path, NULL); return user_path_at_empty(dfd, name, flags, path, NULL);
} }
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
struct dentry *base,
unsigned int flags);
extern int kern_path(const char *, unsigned, struct path *); extern int kern_path(const char *, unsigned, struct path *);
extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int); extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
extern void done_path_create(struct path *, struct dentry *); extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *kern_path_locked(const char *, struct path *);
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
struct path *parent, struct qstr *last, int *type,
const struct path *root);
int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *,
unsigned int, struct path *); unsigned int, struct path *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment