nfs: make nfs_path() work without vfsmount

part 3: now we have everything to get nfs_path() just by dentry -
just follow to (disconnected) root and pick the rest of the thing
there.

Start killing propagation of struct vfsmount * on the paths that
used to bring it to nfs_path().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2011-03-16 06:26:11 -04:00
parent b1942c5f8c
commit b514f872f8
4 changed files with 74 additions and 62 deletions

View File

@ -163,10 +163,10 @@ static inline void nfs_fs_proc_exit(void)
/* nfs4namespace.c */
#ifdef CONFIG_NFS_V4
extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
extern struct vfsmount *nfs_do_refmount(struct super_block *sb, struct dentry *dentry);
#else
static inline
struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
struct vfsmount *nfs_do_refmount(struct super_block *sb, struct dentry *dentry)
{
return ERR_PTR(-ENOENT);
}
@ -247,9 +247,7 @@ extern void nfs_sb_active(struct super_block *sb);
extern void nfs_sb_deactive(struct super_block *sb);
/* namespace.c */
extern char *nfs_path(const char *base,
const struct dentry *droot,
const struct dentry *dentry,
extern char *nfs_path(char **p, struct dentry *dentry,
char *buffer, ssize_t buflen);
extern struct vfsmount *nfs_d_automount(struct path *path);
@ -290,12 +288,11 @@ extern int _nfs4_call_sync_session(struct nfs_server *server,
/*
* Determine the device name as a string
*/
static inline char *nfs_devname(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
static inline char *nfs_devname(struct dentry *dentry,
char *buffer, ssize_t buflen)
{
return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root,
dentry, buffer, buflen);
char *dummy;
return nfs_path(&dummy, dentry, buffer, buflen);
}
/*

View File

@ -25,33 +25,31 @@ static LIST_HEAD(nfs_automount_list);
static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
int nfs_mountpoint_expiry_timeout = 500 * HZ;
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
static struct vfsmount *nfs_do_submount(struct super_block *sb,
struct dentry *dentry,
struct nfs_fh *fh,
struct nfs_fattr *fattr);
/*
* nfs_path - reconstruct the path given an arbitrary dentry
* @base - arbitrary string to prepend to the path
* @droot - pointer to root dentry for mountpoint
* @base - used to return pointer to the end of devname part of path
* @dentry - pointer to dentry
* @buffer - result buffer
* @buflen - length of buffer
*
* Helper function for constructing the path from the
* root dentry to an arbitrary hashed dentry.
* Helper function for constructing the server pathname
* by arbitrary hashed dentry.
*
* This is mainly for use in figuring out the path on the
* server side when automounting on top of an existing partition.
* server side when automounting on top of an existing partition
* and in generating /proc/mounts and friends.
*/
char *nfs_path(const char *base,
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen)
char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
{
char *end;
int namelen;
unsigned seq;
const char *base;
rename_retry:
end = buffer+buflen;
@ -60,7 +58,10 @@ char *nfs_path(const char *base,
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
while (!IS_ROOT(dentry) && dentry != droot) {
while (1) {
spin_lock(&dentry->d_lock);
if (IS_ROOT(dentry))
break;
namelen = dentry->d_name.len;
buflen -= namelen + 1;
if (buflen < 0)
@ -68,27 +69,47 @@ char *nfs_path(const char *base,
end -= namelen;
memcpy(end, dentry->d_name.name, namelen);
*--end = '/';
spin_unlock(&dentry->d_lock);
dentry = dentry->d_parent;
}
rcu_read_unlock();
if (read_seqretry(&rename_lock, seq))
if (read_seqretry(&rename_lock, seq)) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
goto rename_retry;
}
if (*end != '/') {
if (--buflen < 0)
if (--buflen < 0) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
goto Elong;
}
*--end = '/';
}
*p = end;
base = dentry->d_fsdata;
if (!base) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
WARN_ON(1);
return end;
}
namelen = strlen(base);
/* Strip off excess slashes in base string */
while (namelen > 0 && base[namelen - 1] == '/')
namelen--;
buflen -= namelen;
if (buflen < 0)
if (buflen < 0) {
spin_lock(&dentry->d_lock);
rcu_read_unlock();
goto Elong;
}
end -= namelen;
memcpy(end, base, namelen);
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
return end;
Elong_unlock:
spin_lock(&dentry->d_lock);
rcu_read_unlock();
if (read_seqretry(&rename_lock, seq))
goto rename_retry;
@ -143,9 +164,9 @@ struct vfsmount *nfs_d_automount(struct path *path)
}
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
mnt = nfs_do_refmount(path->mnt, path->dentry);
mnt = nfs_do_refmount(path->mnt->mnt_sb, path->dentry);
else
mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr);
mnt = nfs_do_submount(path->mnt->mnt_sb, path->dentry, fh, fattr);
if (IS_ERR(mnt))
goto out;
@ -209,19 +230,19 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
/**
* nfs_do_submount - set up mountpoint when crossing a filesystem boundary
* @mnt_parent - mountpoint of parent directory
* @sb - superblock of parent directory
* @dentry - parent directory
* @fh - filehandle for new root dentry
* @fattr - attributes for new root inode
*
*/
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
static struct vfsmount *nfs_do_submount(struct super_block *sb,
struct dentry *dentry,
struct nfs_fh *fh,
struct nfs_fattr *fattr)
{
struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb,
.sb = sb,
.dentry = dentry,
.fh = fh,
.fattr = fattr,
@ -237,11 +258,11 @@ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
dentry->d_name.name);
if (page == NULL)
goto out;
devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE);
devname = nfs_devname(dentry, page, PAGE_SIZE);
mnt = (struct vfsmount *)devname;
if (IS_ERR(devname))
goto free_page;
mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata);
mnt = nfs_do_clone_mount(NFS_SB(sb), devname, &mountdata);
free_page:
free_page((unsigned long)page);
out:

View File

@ -54,33 +54,29 @@ static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
/*
* Determine the mount path as a string
*/
static char *nfs4_path(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
char *buffer, ssize_t buflen)
static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
{
const char *srvpath;
srvpath = strchr(mnt_parent->mnt_devname, ':');
if (srvpath)
srvpath++;
else
srvpath = mnt_parent->mnt_devname;
return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen);
char *limit;
char *path = nfs_path(&limit, dentry, buffer, buflen);
if (!IS_ERR(path)) {
char *colon = strchr(path, ':');
if (colon && colon < limit)
path = colon + 1;
}
return path;
}
/*
* Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
* believe to be the server path to this dentry
*/
static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
static int nfs4_validate_fspath(struct dentry *dentry,
const struct nfs4_fs_locations *locations,
char *page, char *page2)
{
const char *path, *fs_path;
path = nfs4_path(mnt_parent, dentry, page, PAGE_SIZE);
path = nfs4_path(dentry, page, PAGE_SIZE);
if (IS_ERR(path))
return PTR_ERR(path);
@ -165,20 +161,20 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
/**
* nfs_follow_referral - set up mountpoint when hitting a referral on moved error
* @mnt_parent - mountpoint of parent directory
* @sb - superblock of parent directory
* @dentry - parent directory
* @locations - array of NFSv4 server location information
*
*/
static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
const struct dentry *dentry,
static struct vfsmount *nfs_follow_referral(struct super_block *sb,
struct dentry *dentry,
const struct nfs4_fs_locations *locations)
{
struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb,
.sb = sb,
.dentry = dentry,
.authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor,
.authflavor = NFS_SB(sb)->client->cl_auth->au_flavor,
};
char *page = NULL, *page2 = NULL;
int loc, error;
@ -198,7 +194,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
goto out;
/* Ensure fs path is a prefix of current dentry path */
error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2);
error = nfs4_validate_fspath(dentry, locations, page, page2);
if (error < 0) {
mnt = ERR_PTR(error);
goto out;
@ -225,11 +221,10 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
/*
* nfs_do_refmount - handle crossing a referral on server
* @mnt_parent - mountpoint of referral
* @dentry - dentry of referral
*
*/
struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
struct vfsmount *nfs_do_refmount(struct super_block *sb, struct dentry *dentry)
{
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent;
@ -262,7 +257,7 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr
fs_locations->fs_path.ncomponents <= 0)
goto out_free;
mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations);
mnt = nfs_follow_referral(sb, dentry, fs_locations);
out_free:
__free_page(page);
kfree(fs_locations);

View File

@ -2771,16 +2771,15 @@ static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
return root_mnt;
}
static void nfs_fix_devname(const struct path *path, struct vfsmount *mnt)
static void nfs_fix_devname(struct dentry *dentry, struct vfsmount *mnt)
{
char *page = (char *) __get_free_page(GFP_KERNEL);
char *devname, *tmp;
char *dummy;
if (page == NULL)
return;
devname = nfs_path(path->mnt->mnt_devname,
path->mnt->mnt_root, path->dentry,
page, PAGE_SIZE);
devname = nfs_path(&dummy, dentry, page, PAGE_SIZE);
if (IS_ERR(devname))
goto out_freepage;
tmp = kstrdup(devname, GFP_KERNEL);
@ -2894,7 +2893,7 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt,
mnt_target->mnt_root = dget(nd->path.dentry);
/* Correct the device pathname */
nfs_fix_devname(&nd->path, mnt_target);
nfs_fix_devname(nd->path.dentry, mnt_target);
path_put(&nd->path);
kfree(nd);