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 */ /* nfs4namespace.c */
#ifdef CONFIG_NFS_V4 #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 #else
static inline 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); 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); extern void nfs_sb_deactive(struct super_block *sb);
/* namespace.c */ /* namespace.c */
extern char *nfs_path(const char *base, extern char *nfs_path(char **p, struct dentry *dentry,
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen); char *buffer, ssize_t buflen);
extern struct vfsmount *nfs_d_automount(struct path *path); 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 * Determine the device name as a string
*/ */
static inline char *nfs_devname(const struct vfsmount *mnt_parent, static inline char *nfs_devname(struct dentry *dentry,
const struct dentry *dentry,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root, char *dummy;
dentry, buffer, buflen); 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); static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
int nfs_mountpoint_expiry_timeout = 500 * HZ; int nfs_mountpoint_expiry_timeout = 500 * HZ;
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_do_submount(struct super_block *sb,
const struct dentry *dentry, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fh *fh,
struct nfs_fattr *fattr); struct nfs_fattr *fattr);
/* /*
* nfs_path - reconstruct the path given an arbitrary dentry * nfs_path - reconstruct the path given an arbitrary dentry
* @base - arbitrary string to prepend to the path * @base - used to return pointer to the end of devname part of path
* @droot - pointer to root dentry for mountpoint
* @dentry - pointer to dentry * @dentry - pointer to dentry
* @buffer - result buffer * @buffer - result buffer
* @buflen - length of buffer * @buflen - length of buffer
* *
* Helper function for constructing the path from the * Helper function for constructing the server pathname
* root dentry to an arbitrary hashed dentry. * by arbitrary hashed dentry.
* *
* This is mainly for use in figuring out the path on the * 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, char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen)
{ {
char *end; char *end;
int namelen; int namelen;
unsigned seq; unsigned seq;
const char *base;
rename_retry: rename_retry:
end = buffer+buflen; end = buffer+buflen;
@ -60,7 +58,10 @@ rename_retry:
seq = read_seqbegin(&rename_lock); seq = read_seqbegin(&rename_lock);
rcu_read_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; namelen = dentry->d_name.len;
buflen -= namelen + 1; buflen -= namelen + 1;
if (buflen < 0) if (buflen < 0)
@ -68,27 +69,47 @@ rename_retry:
end -= namelen; end -= namelen;
memcpy(end, dentry->d_name.name, namelen); memcpy(end, dentry->d_name.name, namelen);
*--end = '/'; *--end = '/';
spin_unlock(&dentry->d_lock);
dentry = dentry->d_parent; 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; goto rename_retry;
}
if (*end != '/') { if (*end != '/') {
if (--buflen < 0) if (--buflen < 0) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
goto Elong; goto Elong;
}
*--end = '/'; *--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); namelen = strlen(base);
/* Strip off excess slashes in base string */ /* Strip off excess slashes in base string */
while (namelen > 0 && base[namelen - 1] == '/') while (namelen > 0 && base[namelen - 1] == '/')
namelen--; namelen--;
buflen -= namelen; buflen -= namelen;
if (buflen < 0) if (buflen < 0) {
spin_lock(&dentry->d_lock);
rcu_read_unlock();
goto Elong; goto Elong;
}
end -= namelen; end -= namelen;
memcpy(end, base, namelen); memcpy(end, base, namelen);
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
return end; return end;
Elong_unlock: Elong_unlock:
spin_lock(&dentry->d_lock);
rcu_read_unlock(); rcu_read_unlock();
if (read_seqretry(&rename_lock, seq)) if (read_seqretry(&rename_lock, seq))
goto rename_retry; goto rename_retry;
@ -143,9 +164,9 @@ struct vfsmount *nfs_d_automount(struct path *path)
} }
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) 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 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)) if (IS_ERR(mnt))
goto out; 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 * 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 * @dentry - parent directory
* @fh - filehandle for new root dentry * @fh - filehandle for new root dentry
* @fattr - attributes for new root inode * @fattr - attributes for new root inode
* *
*/ */
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_do_submount(struct super_block *sb,
const struct dentry *dentry, struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fh *fh,
struct nfs_fattr *fattr) struct nfs_fattr *fattr)
{ {
struct nfs_clone_mount mountdata = { struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb, .sb = sb,
.dentry = dentry, .dentry = dentry,
.fh = fh, .fh = fh,
.fattr = fattr, .fattr = fattr,
@ -237,11 +258,11 @@ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
dentry->d_name.name); dentry->d_name.name);
if (page == NULL) if (page == NULL)
goto out; goto out;
devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); devname = nfs_devname(dentry, page, PAGE_SIZE);
mnt = (struct vfsmount *)devname; mnt = (struct vfsmount *)devname;
if (IS_ERR(devname)) if (IS_ERR(devname))
goto free_page; 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:
free_page((unsigned long)page); free_page((unsigned long)page);
out: out:

View File

@ -54,33 +54,29 @@ Elong:
/* /*
* Determine the mount path as a string * Determine the mount path as a string
*/ */
static char *nfs4_path(const struct vfsmount *mnt_parent, static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
const struct dentry *dentry,
char *buffer, ssize_t buflen)
{ {
const char *srvpath; char *limit;
char *path = nfs_path(&limit, dentry, buffer, buflen);
srvpath = strchr(mnt_parent->mnt_devname, ':'); if (!IS_ERR(path)) {
if (srvpath) char *colon = strchr(path, ':');
srvpath++; if (colon && colon < limit)
else path = colon + 1;
srvpath = mnt_parent->mnt_devname; }
return path;
return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen);
} }
/* /*
* Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
* believe to be the server path to this dentry * believe to be the server path to this dentry
*/ */
static int nfs4_validate_fspath(const struct vfsmount *mnt_parent, static int nfs4_validate_fspath(struct dentry *dentry,
const struct dentry *dentry,
const struct nfs4_fs_locations *locations, const struct nfs4_fs_locations *locations,
char *page, char *page2) char *page, char *page2)
{ {
const char *path, *fs_path; 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)) if (IS_ERR(path))
return PTR_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 * 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 * @dentry - parent directory
* @locations - array of NFSv4 server location information * @locations - array of NFSv4 server location information
* *
*/ */
static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_follow_referral(struct super_block *sb,
const struct dentry *dentry, struct dentry *dentry,
const struct nfs4_fs_locations *locations) const struct nfs4_fs_locations *locations)
{ {
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct nfs_clone_mount mountdata = { struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb, .sb = sb,
.dentry = dentry, .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; char *page = NULL, *page2 = NULL;
int loc, error; int loc, error;
@ -198,7 +194,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
goto out; goto out;
/* Ensure fs path is a prefix of current dentry path */ /* 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) { if (error < 0) {
mnt = ERR_PTR(error); mnt = ERR_PTR(error);
goto out; goto out;
@ -225,11 +221,10 @@ out:
/* /*
* nfs_do_refmount - handle crossing a referral on server * nfs_do_refmount - handle crossing a referral on server
* @mnt_parent - mountpoint of referral
* @dentry - dentry 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 vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent; 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) fs_locations->fs_path.ncomponents <= 0)
goto out_free; goto out_free;
mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations); mnt = nfs_follow_referral(sb, dentry, fs_locations);
out_free: out_free:
__free_page(page); __free_page(page);
kfree(fs_locations); 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; 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 *page = (char *) __get_free_page(GFP_KERNEL);
char *devname, *tmp; char *devname, *tmp;
char *dummy;
if (page == NULL) if (page == NULL)
return; return;
devname = nfs_path(path->mnt->mnt_devname, devname = nfs_path(&dummy, dentry, page, PAGE_SIZE);
path->mnt->mnt_root, path->dentry,
page, PAGE_SIZE);
if (IS_ERR(devname)) if (IS_ERR(devname))
goto out_freepage; goto out_freepage;
tmp = kstrdup(devname, GFP_KERNEL); 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); mnt_target->mnt_root = dget(nd->path.dentry);
/* Correct the device pathname */ /* Correct the device pathname */
nfs_fix_devname(&nd->path, mnt_target); nfs_fix_devname(nd->path.dentry, mnt_target);
path_put(&nd->path); path_put(&nd->path);
kfree(nd); kfree(nd);