Commit 05f4f678 authored by J. Bruce Fields's avatar J. Bruce Fields

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: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
Reported-by: default avatarDavid Warren <warren@atmos.washington.edu>
parent a1c8c4d1
...@@ -182,36 +182,26 @@ out_unlock: ...@@ -182,36 +182,26 @@ out_unlock:
typedef int (recdir_func)(struct dentry *, struct dentry *); typedef int (recdir_func)(struct dentry *, struct dentry *);
struct dentry_list { struct name_list {
struct dentry *dentry; char name[HEXDIR_LEN];
struct list_head list; struct list_head list;
}; };
struct dentry_list_arg {
struct list_head dentries;
struct dentry *parent;
};
static int 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) loff_t offset, u64 ino, unsigned int d_type)
{ {
struct dentry_list_arg *dla = arg; struct list_head *names = arg;
struct list_head *dentries = &dla->dentries; struct name_list *entry;
struct dentry *parent = dla->parent;
struct dentry *dentry;
struct dentry_list *child;
if (name && isdotent(name, namlen)) if (namlen != HEXDIR_LEN - 1)
return 0; return 0;
dentry = lookup_one_len(name, parent, namlen); entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
if (IS_ERR(dentry)) if (entry == NULL)
return PTR_ERR(dentry);
child = kmalloc(sizeof(*child), GFP_KERNEL);
if (child == NULL)
return -ENOMEM; return -ENOMEM;
child->dentry = dentry; memcpy(entry->name, name, HEXDIR_LEN - 1);
list_add(&child->list, dentries); entry->name[HEXDIR_LEN - 1] = '\0';
list_add(&entry->list, names);
return 0; return 0;
} }
...@@ -220,11 +210,9 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) ...@@ -220,11 +210,9 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
{ {
const struct cred *original_cred; const struct cred *original_cred;
struct file *filp; struct file *filp;
struct dentry_list_arg dla = { LIST_HEAD(names);
.parent = dir, struct name_list *entry;
}; struct dentry *dentry;
struct list_head *dentries = &dla.dentries;
struct dentry_list *child;
int status; int status;
if (!rec_dir_init) if (!rec_dir_init)
...@@ -233,31 +221,34 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) ...@@ -233,31 +221,34 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
status = nfs4_save_creds(&original_cred); status = nfs4_save_creds(&original_cred);
if (status < 0) if (status < 0)
return status; return status;
INIT_LIST_HEAD(dentries);
filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY, filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
current_cred()); current_cred());
status = PTR_ERR(filp); status = PTR_ERR(filp);
if (IS_ERR(filp)) if (IS_ERR(filp))
goto out; goto out;
INIT_LIST_HEAD(dentries); status = vfs_readdir(filp, nfsd4_build_namelist, &names);
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
fput(filp); fput(filp);
while (!list_empty(dentries)) { while (!list_empty(&names)) {
child = list_entry(dentries->next, struct dentry_list, list); entry = list_entry(names.next, struct name_list, list);
status = f(dir, child->dentry);
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) if (status)
goto out; goto out;
list_del(&child->list); list_del(&entry->list);
dput(child->dentry); kfree(entry);
kfree(child);
} }
out: out:
while (!list_empty(dentries)) { while (!list_empty(&names)) {
child = list_entry(dentries->next, struct dentry_list, list); entry = list_entry(names.next, struct name_list, list);
list_del(&child->list); list_del(&entry->list);
dput(child->dentry); kfree(entry);
kfree(child);
} }
nfs4_reset_creds(original_cred); nfs4_reset_creds(original_cred);
return status; return status;
......
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