Commit 1ae78a14 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.4-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull ksmbd server updates from Steve French:

 - SMB3.1.1 negotiate context fixes and cleanup

 - new lock_rename_child VFS helper

 - ksmbd fix to avoid unlink race and to use the new VFS helper to avoid
   rename race

* tag '6.4-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix racy issue from using ->d_parent and ->d_name
  ksmbd: remove unused compression negotiate ctx packing
  ksmbd: avoid duplicate negotiate ctx offset increments
  ksmbd: set NegotiateContextCount once instead of every inc
  fs: introduce lock_rename_child() helper
  ksmbd: remove internal.h include
parents 4e1c80ae 74d7970f
...@@ -59,8 +59,6 @@ extern int finish_clean_context(struct fs_context *fc); ...@@ -59,8 +59,6 @@ extern int finish_clean_context(struct fs_context *fc);
*/ */
extern int filename_lookup(int dfd, struct filename *name, unsigned flags, extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
struct path *path, struct path *root); struct path *path, struct path *root);
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
int do_rmdir(int dfd, struct filename *name); int do_rmdir(int dfd, struct filename *name);
int do_unlinkat(int dfd, struct filename *name); int do_unlinkat(int dfd, struct filename *name);
int may_linkat(struct mnt_idmap *idmap, const struct path *link); int may_linkat(struct mnt_idmap *idmap, const struct path *link);
......
This diff is collapsed.
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,9 +114,9 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, ...@@ -116,9 +114,9 @@ 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,
unsigned int flags, unsigned int flags,
...@@ -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
...@@ -2980,20 +3010,10 @@ static inline int may_create(struct mnt_idmap *idmap, ...@@ -2980,20 +3010,10 @@ static inline int may_create(struct mnt_idmap *idmap,
return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC); return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
} }
/* static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
* p1 and p2 should be directories on the same fs.
*/
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
{ {
struct dentry *p; struct dentry *p;
if (p1 == p2) {
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
return NULL;
}
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
p = d_ancestor(p2, p1); p = d_ancestor(p2, p1);
if (p) { if (p) {
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
...@@ -3012,8 +3032,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ...@@ -3012,8 +3032,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2); inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2);
return NULL; return NULL;
} }
/*
* p1 and p2 should be directories on the same fs.
*/
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
{
if (p1 == p2) {
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
return NULL;
}
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
return lock_two_directories(p1, p2);
}
EXPORT_SYMBOL(lock_rename); EXPORT_SYMBOL(lock_rename);
/*
* c1 and p2 should be on the same fs.
*/
struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
{
if (READ_ONCE(c1->d_parent) == p2) {
/*
* hopefully won't need to touch ->s_vfs_rename_mutex at all.
*/
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
/*
* now that p2 is locked, nobody can move in or out of it,
* so the test below is safe.
*/
if (likely(c1->d_parent == p2))
return NULL;
/*
* c1 got moved out of p2 while we'd been taking locks;
* unlock and fall back to slow case.
*/
inode_unlock(p2->d_inode);
}
mutex_lock(&c1->d_sb->s_vfs_rename_mutex);
/*
* nobody can move out of any directories on this fs.
*/
if (likely(c1->d_parent != p2))
return lock_two_directories(c1->d_parent, p2);
/*
* c1 got moved into p2 while we were taking locks;
* we need p2 locked and ->s_vfs_rename_mutex unlocked,
* for consistency with lock_rename().
*/
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
return NULL;
}
EXPORT_SYMBOL(lock_rename_child);
void unlock_rename(struct dentry *p1, struct dentry *p2) void unlock_rename(struct dentry *p1, struct dentry *p2)
{ {
inode_unlock(p1->d_inode); inode_unlock(p1->d_inode);
...@@ -3806,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, ...@@ -3806,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;
...@@ -4166,7 +4243,7 @@ int do_rmdir(int dfd, struct filename *name) ...@@ -4166,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;
...@@ -4299,7 +4376,7 @@ int do_unlinkat(int dfd, struct filename *name) ...@@ -4299,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)) {
...@@ -4863,7 +4940,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, ...@@ -4863,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;
...@@ -4871,7 +4949,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, ...@@ -4871,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,20 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags, ...@@ -57,12 +57,20 @@ 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 *,
unsigned int, struct path *);
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
...@@ -81,6 +89,7 @@ extern int follow_down(struct path *path, unsigned int flags); ...@@ -81,6 +89,7 @@ extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *); extern int follow_up(struct path *);
extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern struct dentry *lock_rename(struct dentry *, struct dentry *);
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
extern void unlock_rename(struct dentry *, struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *);
extern int __must_check nd_jump_link(const struct path *path); extern int __must_check nd_jump_link(const struct path *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