lkml.org 
[lkml]   [2004]   [May]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
SubjectRe: [PATCH] permit fs->get_sb() to return alternative root
Date
From
Content-Type: text/plain; charset=ISO-8859-1text/plain; charset=US-ASCII
Content-Transfer-Encoding: quoted-printable


> 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



--Multipart_Thu_May_20_12:22:58_2004-1
Content-Type: text/plain; type=patch
Content-Disposition: attachment; filename="nfs-266.diff"
Content-Transfer-Encoding: 7bit

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
--Multipart_Thu_May_20_12:22:58_2004-1--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 14:03    [W:0.293 / U:0.144 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site