mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 14:44:10 +08:00
nfsd4: don't do lookup within readdir in recovery code
The main nfsd code was recently modified to no longer do lookups from withing the readdir callback, to avoid locking problems on certain filesystems. This (rather hacky, and overdue for replacement) NFSv4 recovery code has the same problem. Fix it to build up a list of names (instead of dentries) and do the lookups afterwards. Reported symptoms were a deadlock in the xfs code (called from nfsd4_recdir_load), with /var/lib/nfs on xfs. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Reported-by: David Warren <warren@atmos.washington.edu>
This commit is contained in:
parent
a1c8c4d1ff
commit
05f4f678b0
@ -182,36 +182,26 @@ out_unlock:
|
||||
|
||||
typedef int (recdir_func)(struct dentry *, struct dentry *);
|
||||
|
||||
struct dentry_list {
|
||||
struct dentry *dentry;
|
||||
struct name_list {
|
||||
char name[HEXDIR_LEN];
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct dentry_list_arg {
|
||||
struct list_head dentries;
|
||||
struct dentry *parent;
|
||||
};
|
||||
|
||||
static int
|
||||
nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
|
||||
nfsd4_build_namelist(void *arg, const char *name, int namlen,
|
||||
loff_t offset, u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct dentry_list_arg *dla = arg;
|
||||
struct list_head *dentries = &dla->dentries;
|
||||
struct dentry *parent = dla->parent;
|
||||
struct dentry *dentry;
|
||||
struct dentry_list *child;
|
||||
struct list_head *names = arg;
|
||||
struct name_list *entry;
|
||||
|
||||
if (name && isdotent(name, namlen))
|
||||
if (namlen != HEXDIR_LEN - 1)
|
||||
return 0;
|
||||
dentry = lookup_one_len(name, parent, namlen);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
child = kmalloc(sizeof(*child), GFP_KERNEL);
|
||||
if (child == NULL)
|
||||
entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
|
||||
if (entry == NULL)
|
||||
return -ENOMEM;
|
||||
child->dentry = dentry;
|
||||
list_add(&child->list, dentries);
|
||||
memcpy(entry->name, name, HEXDIR_LEN - 1);
|
||||
entry->name[HEXDIR_LEN - 1] = '\0';
|
||||
list_add(&entry->list, names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -220,11 +210,9 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
|
||||
{
|
||||
const struct cred *original_cred;
|
||||
struct file *filp;
|
||||
struct dentry_list_arg dla = {
|
||||
.parent = dir,
|
||||
};
|
||||
struct list_head *dentries = &dla.dentries;
|
||||
struct dentry_list *child;
|
||||
LIST_HEAD(names);
|
||||
struct name_list *entry;
|
||||
struct dentry *dentry;
|
||||
int status;
|
||||
|
||||
if (!rec_dir_init)
|
||||
@ -233,31 +221,34 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
|
||||
status = nfs4_save_creds(&original_cred);
|
||||
if (status < 0)
|
||||
return status;
|
||||
INIT_LIST_HEAD(dentries);
|
||||
|
||||
filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
|
||||
current_cred());
|
||||
status = PTR_ERR(filp);
|
||||
if (IS_ERR(filp))
|
||||
goto out;
|
||||
INIT_LIST_HEAD(dentries);
|
||||
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
|
||||
status = vfs_readdir(filp, nfsd4_build_namelist, &names);
|
||||
fput(filp);
|
||||
while (!list_empty(dentries)) {
|
||||
child = list_entry(dentries->next, struct dentry_list, list);
|
||||
status = f(dir, child->dentry);
|
||||
while (!list_empty(&names)) {
|
||||
entry = list_entry(names.next, struct name_list, list);
|
||||
|
||||
dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
|
||||
if (IS_ERR(dentry)) {
|
||||
status = PTR_ERR(dentry);
|
||||
goto out;
|
||||
}
|
||||
status = f(dir, dentry);
|
||||
dput(dentry);
|
||||
if (status)
|
||||
goto out;
|
||||
list_del(&child->list);
|
||||
dput(child->dentry);
|
||||
kfree(child);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
out:
|
||||
while (!list_empty(dentries)) {
|
||||
child = list_entry(dentries->next, struct dentry_list, list);
|
||||
list_del(&child->list);
|
||||
dput(child->dentry);
|
||||
kfree(child);
|
||||
while (!list_empty(&names)) {
|
||||
entry = list_entry(names.next, struct name_list, list);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
nfs4_reset_creds(original_cred);
|
||||
return status;
|
||||
|
Loading…
Reference in New Issue
Block a user