Messages in this thread Patch in this message | | | Subject | Re: [PATCH] permit fs->get_sb() to return alternative root | Date | Fri, 28 May 2004 09:52:48 +0100 | From | David Howells <> |
| |
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/
| |