From: David Howells <dhowells@redhat.com>
To: Trond Myklebust <trond.myklebust@fys.uio.no>
Cc: Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk>,
Linus Torvalds <torvalds@osdl.org>,
linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH] permit fs->get_sb() to return alternative root
Date: Thu, 20 May 2004 12:22:59 +0100 [thread overview]
Message-ID: <19818.1085052179@redhat.com> (raw)
In-Reply-To: <1084905576.5215.150.camel@lade.trondhjem.org>
[-- Attachment #1: Type: text/plain, Size: 1052 bytes --]
> På ty , 18/05/2004 klokka 13:48, skreiv
> viro@parcelfarce.linux.theplanet.co.uk:
> > Details, please. Preferably with a patch that could be read...
>
> Needs work (and adaptation to NFSv4), but the basic algorithm would go
> along the lines of the following
Okay... I've worked on your patch somewhat. The attached patch permits
superblock sharing:
[root@host135 root]# mount hades:/hades /mnt
[root@host135 root]# mount hades:/usr /afs
[root@host135 root]# mount hades:/hades /x
[root@host135 root]# mount hades:/ /y
[root@host135 root]# stat /mnt /afs /x /y /y/hades | grep ^Device
Device: eh/14d Inode: 3925361 Links: 46
Device: eh/14d Inode: 327041 Links: 19
Device: eh/14d Inode: 3925361 Links: 46
Device: eh/14d Inode: 2 Links: 33
Device: eh/14d Inode: 3925361 Links: 46
And on an NFS export from a different server:
[root@host135 root]# stat /home/dhowells/Mail | grep ^Device
Device: 10h/16d Inode: 3620912 Links: 40
However, df doesn't work:-/
David
[-- Attachment #2: nfs-266.diff --]
[-- Type: application/octet-stream, Size: 11833 bytes --]
diff -uNr linux-2.6.6-getsb/fs/dcache.c linux-2.6.6-nfs/fs/dcache.c
--- linux-2.6.6-getsb/fs/dcache.c 2004-05-17 15:07:41.000000000 +0100
+++ linux-2.6.6-nfs/fs/dcache.c 2004-05-19 17:12:25.000000000 +0100
@@ -1247,6 +1247,23 @@
}
/**
+ * d_materialise_dentry - connect a disconnected dentry into the tree
+ * @dentry: dentry to replace
+ * @anon: dentry to place into the tree
+ *
+ * Prepare an anonymous dentry for life in the superblock's dentry tree as a
+ * named dentry in place of the dentry to be replaced.
+ */
+void d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
+{
+ switch_names(dentry, anon);
+ do_switch(dentry->d_name.len, anon->d_name.len);
+ do_switch(dentry->d_name.hash, anon->d_name.hash);
+ do_switch(dentry->d_parent, anon->d_parent);
+ anon->d_flags &= ~DCACHE_DISCONNECTED;
+}
+
+/**
* d_path - return the path of a dentry
* @dentry: dentry to report
* @vfsmnt: vfsmnt to which the dentry belongs
@@ -1659,6 +1676,7 @@
EXPORT_SYMBOL(d_invalidate);
EXPORT_SYMBOL(d_lookup);
EXPORT_SYMBOL(d_move);
+EXPORT_SYMBOL(d_materialise_dentry);
EXPORT_SYMBOL(d_path);
EXPORT_SYMBOL(d_prune_aliases);
EXPORT_SYMBOL(d_rehash);
diff -uNr linux-2.6.6-getsb/fs/nfs/dir.c linux-2.6.6-nfs/fs/nfs/dir.c
--- linux-2.6.6-getsb/fs/nfs/dir.c 2004-05-17 15:07:34.000000000 +0100
+++ linux-2.6.6-nfs/fs/nfs/dir.c 2004-05-19 17:18:40.000000000 +0100
@@ -710,10 +710,12 @@
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
+ struct dentry *anon;
struct inode *inode = NULL;
int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
+ int found_alias = 0;
dfprintk(VFS, "NFS: lookup(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -746,6 +748,30 @@
inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
if (!inode)
goto out_unlock;
+
+ /* Search for directory aliases arising from multiple mounts from one server */
+ if (S_ISDIR(inode->i_mode) && (anon = d_find_alias(inode))) {
+ spin_lock(&anon->d_lock);
+ /* Is this a mountpoint that we could splice into our tree? */
+ if (IS_ROOT(anon)) {
+ /* Yes! Convert into an ordinary dentry */
+ d_materialise_dentry(dentry, anon);
+ found_alias = 1;
+ } else if (anon->d_name.len == dentry->d_name.len &&
+ !memcmp(anon->d_name.name, dentry->d_name.name, dentry->d_name.len) &&
+ dentry->d_parent == anon->d_parent)
+ found_alias = 1;
+ spin_unlock(&anon->d_lock);
+ if (found_alias) {
+ d_drop(anon);
+ iput(inode);
+ d_rehash(anon);
+ return anon;
+ }
+ /* Doh! Server appears to be aliasing directories */
+ dput(anon);
+ }
+
no_entry:
error = 0;
d_add(dentry, inode);
diff -uNr linux-2.6.6-getsb/fs/nfs/inode.c linux-2.6.6-nfs/fs/nfs/inode.c
--- linux-2.6.6-getsb/fs/nfs/inode.c 2004-05-18 13:14:47.000000000 +0100
+++ linux-2.6.6-nfs/fs/nfs/inode.c 2004-05-20 11:33:11.518537943 +0100
@@ -228,33 +228,90 @@
/*
* Obtain the root inode of the file system.
*/
-static struct inode *
-nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
+static int
+nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, struct nfs_fsinfo *fsinfo,
+ struct dentry **_root)
{
- struct nfs_server *server = NFS_SB(sb);
- struct inode *rooti;
- int error;
+ struct nfs_server *server = NFS_SB(sb);
+ struct dentry *root;
+ struct inode *inode;
+ int error;
- error = server->rpc_ops->getroot(server, rootfh, fsinfo);
+ error = server->rpc_ops->getroot(server, mntfh, fsinfo);
if (error < 0) {
printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
- return ERR_PTR(error);
+ return error;
}
- rooti = nfs_fhget(sb, rootfh, fsinfo->fattr);
- if (!rooti)
- return ERR_PTR(-ENOMEM);
- return rooti;
+ inode = nfs_fhget(sb, mntfh, fsinfo->fattr);
+ if (!inode) {
+ printk("nfs_get_another_root: get root inode failed\n");
+ return -ENOMEM;
+ }
+
+ /* Root dentries start off anonymous */
+ root = d_alloc_anon(inode);
+ if (!root) {
+ printk("nfs_get_another_root: get root dentry failed\n");
+ iput(inode);
+ return -ENOMEM;
+ }
+
+ if (!root->d_op)
+ root->d_op = server->rpc_ops->dentry_ops;
+ *_root = root;
+ return 0;
+}
+
+/*
+ * Get the root dentry for a new mountpoint in an existing superblock
+ */
+static int
+nfs_get_another_root(struct super_block *sb, struct nfs_fh *mntfh, struct dentry **_root)
+{
+ struct nfs_fattr fattr;
+ struct nfs_fsinfo fsinfo = {
+ .fattr = &fattr,
+ };
+
+ return nfs_get_root(sb, mntfh, &fsinfo, _root);
+}
+
+/*
+ * create a dummy root dentry with dummy inode
+ */
+static int
+nfs_make_dummy_root(struct super_block *sb)
+{
+ struct dentry *root;
+ struct inode *inode;
+
+ inode = new_inode(sb);
+ if (!inode)
+ return -ENOMEM;
+
+ inode->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ iput(inode);
+ return -ENOMEM;
+ }
+
+ sb->s_root = root;
+ return 0;
}
/*
* Do NFS version-independent mount processing, and sanity checking
*/
static int
-nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
+nfs_sb_init(struct super_block *sb, struct nfs_fh *mntfh, rpc_authflavor_t authflavor,
+ struct dentry **_root)
{
struct nfs_server *server;
- struct inode *root_inode;
struct nfs_fattr fattr;
struct nfs_fsinfo fsinfo = {
.fattr = &fattr,
@@ -263,26 +320,29 @@
.fattr = &fattr,
};
+ int error;
+
/* We probably want something more informative here */
snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev));
- server = NFS_SB(sb);
+ sb->s_magic = NFS_SUPER_MAGIC;
- sb->s_magic = NFS_SUPER_MAGIC;
+ error = nfs_make_dummy_root(sb);
+ if (error < 0)
+ return error;
- root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
- /* Did getting the root inode fail? */
- if (IS_ERR(root_inode))
- goto out_no_root;
- sb->s_root = d_alloc_root(root_inode);
- if (!sb->s_root)
- goto out_no_root;
+ error = nfs_get_root(sb, mntfh, &fsinfo, _root);
+ if (error < 0) {
+ dput(sb->s_root);
+ sb->s_root = NULL;
+ return error;
+ }
- sb->s_root->d_op = server->rpc_ops->dentry_ops;
+ server = NFS_SB(sb);
/* Get some general file system info */
if (server->namelen == 0 &&
- server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
+ server->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
server->namelen = pathinfo.max_namelen;
/* Work out a lot of parameters */
if (server->rsize == 0)
@@ -335,12 +395,6 @@
/* We're airborne Set socket buffersize */
rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
return 0;
- /* Yargs. It didn't work out. */
-out_no_root:
- printk("nfs_read_super: get root inode failed\n");
- if (!IS_ERR(root_inode))
- iput(root_inode);
- return -EINVAL;
}
/*
@@ -398,7 +452,8 @@
* daemon. We stash these away in the private superblock fields.
*/
static int
-nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
+nfs_fill_super(struct super_block *sb, struct nfs_fh *mntfh, struct nfs_mount_data *data,
+ int silent, struct dentry **_root)
{
struct nfs_server *server;
int err = -EIO;
@@ -473,7 +528,7 @@
}
sb->s_op = &nfs_sops;
- err = nfs_sb_init(sb, authflavor);
+ err = nfs_sb_init(sb, mntfh, authflavor, _root);
if (err != 0)
goto out_noinit;
@@ -1284,6 +1339,11 @@
/*
* File system information
+ * - superblocks are indexed on server only - all inodes, dentries, etc. associated with a
+ * particular server are held in the same superblock
+ * - NFS superblocks can have several effective roots to the dentry tree
+ * - directory type roots are spliced into the tree when a path from one root reaches the root
+ * of another (see nfs_lookup())
*/
static int nfs_set_super(struct super_block *s, void *data)
@@ -1301,7 +1361,7 @@
return 0;
if (old->addr.sin_port != server->addr.sin_port)
return 0;
- return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh));
+ return 1;
}
static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
@@ -1310,7 +1370,7 @@
int error;
struct nfs_server *server;
struct super_block *s;
- struct nfs_fh *root;
+ struct nfs_fh mntfh;
struct nfs_mount_data *data = raw_data;
if (!data) {
@@ -1325,10 +1385,9 @@
/* Zero out the NFS state stuff */
init_nfsv4_state(server);
- root = &server->fh;
- memcpy(root, &data->root, sizeof(*root));
- if (root->size < sizeof(root->data))
- memset(root->data+root->size, 0, sizeof(root->data)-root->size);
+ memcpy(&mntfh, &data->root, sizeof(mntfh));
+ if (mntfh.size < sizeof(mntfh.data))
+ memset(mntfh.data+mntfh.size, 0, sizeof(mntfh.data)-mntfh.size);
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
@@ -1339,15 +1398,15 @@
data->bsize = 0;
if (data->version < 4) {
data->flags &= ~NFS_MOUNT_VER3;
- memset(root, 0, sizeof(*root));
- root->size = NFS2_FHSIZE;
- memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+ memset(&mntfh, 0, sizeof(mntfh));
+ mntfh.size = NFS2_FHSIZE;
+ memcpy(mntfh.data, data->old_root.data, NFS2_FHSIZE);
}
if (data->version < 5)
data->flags &= ~NFS_MOUNT_SECFLAVOUR;
}
- if (root->size > sizeof(root->data)) {
+ if (mntfh.size > sizeof(mntfh.data)) {
printk("nfs_get_sb: invalid root filehandle\n");
kfree(server);
return ERR_PTR(-EINVAL);
@@ -1360,22 +1419,33 @@
return ERR_PTR(-EINVAL);
}
+ /* Get a superblock - note that we may end up sharing one that already exists */
s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
-
- if (IS_ERR(s) || s->s_root) {
+ if (IS_ERR(s)) {
kfree(server);
return s;
}
- s->s_flags = flags;
+ if (!s->s_root) {
+ s->s_flags = flags;
- error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
- if (error) {
- up_write(&s->s_umount);
- deactivate_super(s);
- return ERR_PTR(error);
+ error = nfs_fill_super(s, &mntfh, data, flags & MS_VERBOSE ? 1 : 0, _root);
+ if (error) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ return ERR_PTR(error);
+ }
+ s->s_flags |= MS_ACTIVE;
}
- s->s_flags |= MS_ACTIVE;
+ else {
+ error = nfs_get_another_root(s, &mntfh, _root);
+ if (error) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ return ERR_PTR(error);
+ }
+ }
+
return s;
}
diff -uNr linux-2.6.6-getsb/include/linux/dcache.h linux-2.6.6-nfs/include/linux/dcache.h
--- linux-2.6.6-getsb/include/linux/dcache.h 2004-05-17 15:08:42.000000000 +0100
+++ linux-2.6.6-nfs/include/linux/dcache.h 2004-05-19 17:13:03.000000000 +0100
@@ -198,6 +198,7 @@
*/
extern void d_instantiate(struct dentry *, struct inode *);
extern void d_delete(struct dentry *);
+extern void d_materialise_dentry(struct dentry *, struct dentry *);
/* allocate/de-allocate */
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
diff -uNr linux-2.6.6-getsb/include/linux/nfs_fs_sb.h linux-2.6.6-nfs/include/linux/nfs_fs_sb.h
--- linux-2.6.6-getsb/include/linux/nfs_fs_sb.h 2004-05-17 15:08:42.000000000 +0100
+++ linux-2.6.6-nfs/include/linux/nfs_fs_sb.h 2004-05-19 19:45:38.000000000 +0100
@@ -26,14 +26,14 @@
unsigned int acdirmax;
unsigned int namelen;
char * hostname; /* remote hostname */
- struct nfs_fh fh;
+// struct nfs_fh fh;
struct sockaddr_in addr;
#ifdef CONFIG_NFS_V4
/* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address.
*/
char ip_addr[16];
- char * mnt_path;
+// char * mnt_path;
struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */
struct list_head nfs4_siblings; /* List of other nfs_server structs
* that share the same clientid
next prev parent reply other threads:[~2004-05-20 11:23 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-05-18 14:14 [PATCH] permit fs->get_sb() to return alternative root David Howells
2004-05-18 14:43 ` Trond Myklebust
[not found] ` <20040518152426.GY17014@parcelfarce.linux.theplanet.co.uk>
[not found] ` <1084895260.5215.22.camel@lade.trondhjem.org>
[not found] ` <20040518162812.GZ17014@parcelfarce.linux.theplanet.co.uk>
[not found] ` <1084898393.5215.43.camel@lade.trondhjem.org>
[not found] ` <Pine.LNX.4.58.0405180948190.25502@ppc970.osdl.org>
[not found] ` <1084899447.5215.49.camel@lade.trondhjem.org>
[not found] ` <20040518170410.GA17014@parcelfarce.linux.theplanet.co.uk>
[not found] ` <1084902342.5215.94.camel@lade.trondhjem.org>
[not found] ` <20040518174808.GD17014@parcelfarce.linux.theplanet.co.uk>
[not found] ` <1084905576.5215.150.camel@lade.trondhjem.org>
2004-05-20 11:22 ` David Howells [this message]
2004-05-28 8:52 ` David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=19818.1085052179@redhat.com \
--to=dhowells@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=torvalds@osdl.org \
--cc=trond.myklebust@fys.uio.no \
--cc=viro@parcelfarce.linux.theplanet.co.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.