[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-fsdevel
Subject:    Re: [PATCH] permit fs->get_sb() to return alternative root
From:       David Howells <dhowells () redhat ! com>
Date:       2004-05-28 8:52:48
Message-ID: 19883.1085734368 () redhat ! com
[Download RAW message or body]

> P=E5 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 on NFS:-/

David



["nfs-266.diff" (text/plain)]

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

-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic