Move XFS's support for 64bit inodes in NFS filehandles to libfs. In detail: - add the FILEID_INO64_FLAG and thei64 case in struct fid to exportfs.h - deal with the 64bit inode cases in generic_fh_to_dentry and generic_fh_to_parent by adding them to the switch case table and rewriting the min filehandle size checks based on a readable helper - move export_encode_fh to libfs as generic_encode_fh32 and rewrite it to be a similar style as the other exportfs functions in libfs - add a new generic_encode_fh64 which is identical to generic_encode_fh32 except for dealing with 64bit inode filehandles XFS is switched over to the new code, switching over other filesystems like ext4 is left to their maintainers but should be rather trivial by looking at fs/xfs/linux-2.6/xfs_export.c Signed-off-by: Christoph Hellwig <hch@xxxxxx> Index: linux-2.6/fs/xfs/linux-2.6/xfs_export.c =================================================================== --- linux-2.6.orig/fs/xfs/linux-2.6/xfs_export.c 2008-08-11 09:28:47.000000000 -0300 +++ linux-2.6/fs/xfs/linux-2.6/xfs_export.c 2008-08-11 09:47:24.000000000 -0300 @@ -25,91 +25,10 @@ #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" -#include "xfs_export.h" #include "xfs_vnodeops.h" #include "xfs_bmap_btree.h" #include "xfs_inode.h" -#include "xfs_vfsops.h" -/* - * Note that we only accept fileids which are long enough rather than allow - * the parent generation number to default to zero. XFS considers zero a - * valid generation number not an invalid/wildcard value. - */ -static int xfs_fileid_length(int fileid_type) -{ - switch (fileid_type) { - case FILEID_INO32_GEN: - return 2; - case FILEID_INO32_GEN_PARENT: - return 4; - case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: - return 3; - case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: - return 6; - } - return 255; /* invalid */ -} - -STATIC int -xfs_fs_encode_fh( - struct dentry *dentry, - __u32 *fh, - int *max_len, - int connectable) -{ - struct fid *fid = (struct fid *)fh; - struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fh; - struct inode *inode = dentry->d_inode; - int fileid_type; - int len; - - /* Directories don't need their parent encoded, they have ".." */ - if (S_ISDIR(inode->i_mode) || !connectable) - fileid_type = FILEID_INO32_GEN; - else - fileid_type = FILEID_INO32_GEN_PARENT; - - /* filesystem may contain 64bit inode numbers */ - if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS)) - fileid_type |= XFS_FILEID_TYPE_64FLAG; - - /* - * Only encode if there is enough space given. In practice - * this means we can't export a filesystem with 64bit inodes - * over NFSv2 with the subtree_check export option; the other - * seven combinations work. The real answer is "don't use v2". - */ - len = xfs_fileid_length(fileid_type); - if (*max_len < len) - return 255; - *max_len = len; - - switch (fileid_type) { - case FILEID_INO32_GEN_PARENT: - spin_lock(&dentry->d_lock); - fid->i32.parent_ino = dentry->d_parent->d_inode->i_ino; - fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation; - spin_unlock(&dentry->d_lock); - /*FALLTHRU*/ - case FILEID_INO32_GEN: - fid->i32.ino = inode->i_ino; - fid->i32.gen = inode->i_generation; - break; - case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: - spin_lock(&dentry->d_lock); - fid64->parent_ino = dentry->d_parent->d_inode->i_ino; - fid64->parent_gen = dentry->d_parent->d_inode->i_generation; - spin_unlock(&dentry->d_lock); - /*FALLTHRU*/ - case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: - fid64->ino = inode->i_ino; - fid64->gen = inode->i_generation; - break; - } - - return fileid_type; -} STATIC struct inode * xfs_nfs_get_inode( @@ -146,65 +65,35 @@ STATIC struct dentry * xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fileid_type) { - struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; - struct inode *inode = NULL; - struct dentry *result; - - if (fh_len < xfs_fileid_length(fileid_type)) + /* + * We only accept fileids which are long enough rather than allow + * the parent generation number to default to zero. XFS considers + * zero a valid generation number not an invalid/wildcard value. + */ + if (fileid_type == FILEID_INO32_GEN_PARENT && fh_len == 3) return NULL; - switch (fileid_type) { - case FILEID_INO32_GEN_PARENT: - case FILEID_INO32_GEN: - inode = xfs_nfs_get_inode(sb, fid->i32.ino, fid->i32.gen); - break; - case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: - case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: - inode = xfs_nfs_get_inode(sb, fid64->ino, fid64->gen); - break; - } - - if (!inode) - return NULL; - if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return generic_fh_to_dentry(sb, fid, fh_len, fileid_type, + xfs_nfs_get_inode); } STATIC struct dentry * xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fileid_type) { - struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; - struct inode *inode = NULL; - struct dentry *result; - - switch (fileid_type) { - case FILEID_INO32_GEN_PARENT: - inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino, - fid->i32.parent_gen); - break; - case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: - inode = xfs_nfs_get_inode(sb, fid64->parent_ino, - fid64->parent_gen); - break; - } + return generic_fh_to_parent(sb, fid, fh_len, fileid_type, + xfs_nfs_get_inode); +} - if (!inode) - return NULL; - if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; +STATIC int +xfs_fs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, int connectable) +{ + struct xfs_mount *mp = XFS_M(dentry->d_inode->i_sb); + struct fid *fid = (struct fid *)fh; + + if (mp->m_flags & XFS_MOUNT_SMALL_INUMS) + return generic_encode_fh32(dentry, fid, max_len, connectable); + return generic_encode_fh64(dentry, fid, max_len, connectable); } STATIC struct dentry * Index: linux-2.6/fs/xfs/linux-2.6/xfs_export.h =================================================================== --- linux-2.6.orig/fs/xfs/linux-2.6/xfs_export.h 2008-08-11 09:28:47.000000000 -0300 +++ /dev/null 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_EXPORT_H__ -#define __XFS_EXPORT_H__ - -/* - * Common defines for code related to exporting XFS filesystems over NFS. - * - * The NFS fileid goes out on the wire as an array of - * 32bit unsigned ints in host order. There are 5 possible - * formats. - * - * (1) fileid_type=0x00 - * (no fileid data; handled by the generic code) - * - * (2) fileid_type=0x01 - * inode-num - * generation - * - * (3) fileid_type=0x02 - * inode-num - * generation - * parent-inode-num - * parent-generation - * - * (4) fileid_type=0x81 - * inode-num-lo32 - * inode-num-hi32 - * generation - * - * (5) fileid_type=0x82 - * inode-num-lo32 - * inode-num-hi32 - * generation - * parent-inode-num-lo32 - * parent-inode-num-hi32 - * parent-generation - * - * Note, the NFS filehandle also includes an fsid portion which - * may have an inode number in it. That number is hardcoded to - * 32bits and there is no way for XFS to intercept it. In - * practice this means when exporting an XFS filesystem with 64bit - * inodes you should either export the mountpoint (rather than - * a subdirectory) or use the "fsid" export option. - */ - -struct xfs_fid64 { - u64 ino; - u32 gen; - u64 parent_ino; - u32 parent_gen; -} __attribute__((packed)); - -/* This flag goes on the wire. Don't play with it. */ -#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */ - -#endif /* __XFS_EXPORT_H__ */ Index: linux-2.6/include/linux/exportfs.h =================================================================== --- linux-2.6.orig/include/linux/exportfs.h 2008-08-11 09:47:16.000000000 -0300 +++ linux-2.6/include/linux/exportfs.h 2008-08-11 09:47:24.000000000 -0300 @@ -46,6 +46,12 @@ enum fid_type { * 32 bit parent block number, 32 bit parent generation number */ FILEID_UDF_WITH_PARENT = 0x52, + + /* + * Flag to state we have 64bit inode numbers. + * This flag is ored into the 32bit filehande types. + */ + FILEID_INO64_FLAG = 0x80, }; struct fid { @@ -56,6 +62,12 @@ struct fid { u32 parent_ino; u32 parent_gen; } i32; + struct { + u64 ino; + u32 gen; + u64 parent_ino; + u32 parent_gen; + } i64; struct { u32 block; u16 partref; @@ -148,5 +160,9 @@ extern struct dentry *generic_fh_to_dent extern struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type, struct inode *(*get_inode) (struct super_block *sb, u64 ino, u32 gen)); +extern int generic_encode_fh32(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable); +extern int generic_encode_fh64(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable); #endif /* LINUX_EXPORTFS_H */ Index: linux-2.6/fs/libfs.c =================================================================== --- linux-2.6.orig/fs/libfs.c 2008-08-11 09:47:16.000000000 -0300 +++ linux-2.6/fs/libfs.c 2008-08-11 09:47:38.000000000 -0300 @@ -754,6 +754,31 @@ static struct dentry *exportfs_d_alloc(s return dentry; } +static int fileid_length(int fileid_type) +{ + switch (fileid_type) { + case FILEID_INO32_GEN: + return 2; + case FILEID_INO32_GEN_PARENT: + return 4; + case FILEID_INO32_GEN | FILEID_INO64_FLAG: + return 3; + case FILEID_INO32_GEN_PARENT | FILEID_INO64_FLAG: + return 6; + } + return 255; /* invalid */ +} + +/* + * Old kernel used to send out file handles with the parent inode + * encoded but without a generation number for the parent. We still + * have to accept these odd handles, but we will never generate them. + */ +static inline int no_parent_gen_hack(int fileid_type, int fh_len) +{ + return fileid_type == FILEID_INO32_GEN_PARENT && fh_len == 3; +} + /** * generic_fh_to_dentry - generic helper for the fh_to_dentry export operation * @sb: filesystem to do the file handle conversion on @@ -772,7 +797,8 @@ struct dentry *generic_fh_to_dentry(stru { struct inode *inode = NULL; - if (fh_len < 2) + if (fh_len < fileid_length(fh_type) && + !no_parent_gen_hack(fh_type, fh_len)) return NULL; switch (fh_type) { @@ -780,6 +806,10 @@ struct dentry *generic_fh_to_dentry(stru case FILEID_INO32_GEN_PARENT: inode = get_inode(sb, fid->i32.ino, fid->i32.gen); break; + case FILEID_INO32_GEN_PARENT | FILEID_INO64_FLAG: + case FILEID_INO32_GEN | FILEID_INO64_FLAG: + inode = get_inode(sb, fid->i64.ino, fid->i64.gen); + break; } return exportfs_d_alloc(inode); @@ -787,7 +817,7 @@ struct dentry *generic_fh_to_dentry(stru EXPORT_SYMBOL_GPL(generic_fh_to_dentry); /** - * generic_fh_to_dentry - generic helper for the fh_to_parent export operation + * generic_fh_to_parent - generic helper for the fh_to_parent export operation * @sb: filesystem to do the file handle conversion on * @fid: file handle to convert * @fh_len: length of the file handle in bytes @@ -805,20 +835,127 @@ struct dentry *generic_fh_to_parent(stru { struct inode *inode = NULL; - if (fh_len <= 2) - return NULL; - switch (fh_type) { case FILEID_INO32_GEN_PARENT: inode = get_inode(sb, fid->i32.parent_ino, (fh_len > 3 ? fid->i32.parent_gen : 0)); break; + case FILEID_INO32_GEN_PARENT | FILEID_INO64_FLAG: + inode = get_inode(sb, fid->i64.parent_ino, fid->i64.parent_gen); + break; } return exportfs_d_alloc(inode); } EXPORT_SYMBOL_GPL(generic_fh_to_parent); +/** + * generic_encode_fh32 - generic helper for the encode_fh export operation + * @dentry: the dentry to encode + * @fh: where to store the file handle fragment + * @max_len: maximum length to store there + * @connectable: whether to store parent information + * + * This is the generic helper to encode file handles for filesystem that + * use a <ino, generation> tuple to locate an object on disk. Both the + * inode number and the generation are encoded as 32bits integers. + */ +int generic_encode_fh32(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable) +{ + struct inode *inode = dentry->d_inode; + int fileid_type; + int len; + + /* + * Directories don't need their parent encoded, they have ".." + */ + if (connectable && !S_ISDIR(inode->i_mode)) + fileid_type = FILEID_INO32_GEN_PARENT; + else + fileid_type = FILEID_INO32_GEN; + + /* + * Only encode if there is enough space given. There should always + * be enough space for a 32bit file handle. + */ + len = fileid_length(fileid_type); + if (*max_len < len) + return 255; + + switch (fileid_type) { + case FILEID_INO32_GEN_PARENT: + spin_lock(&dentry->d_lock); + fid->i32.parent_ino = dentry->d_parent->d_inode->i_ino; + fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation; + spin_unlock(&dentry->d_lock); + /*FALLTHRU*/ + case FILEID_INO32_GEN: + fid->i32.ino = inode->i_ino; + fid->i32.gen = inode->i_generation; + break; + } + + *max_len = len; + return fileid_type; +} +EXPORT_SYMBOL_GPL(generic_encode_fh32); + +/** + * generic_encode_fh64 - generic helper for the encode_fh export operation + * @dentry: the dentry to encode + * @fh: where to store the file handle fragment + * @max_len: maximum length to store there + * @connectable: whether to store parent information + * + * This is the generic helper to encode file handles for filesystem that + * use a <ino, generation> tuple to locate an object on disk. The + * inode number is encoded as 64bit integer, and the generation is + * encoded as 32bits integer. + */ +int generic_encode_fh64(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable) +{ + struct inode *inode = dentry->d_inode; + int fileid_type; + int len; + + /* + * Directories don't need their parent encoded, they have ".." + */ + if (connectable && !S_ISDIR(inode->i_mode)) + fileid_type = FILEID_INO32_GEN_PARENT | FILEID_INO64_FLAG; + else + fileid_type = FILEID_INO32_GEN | FILEID_INO64_FLAG; + + /* + * Only encode if there is enough space given. In practice + * this means we can't export a filesystem with 64bit inodes + * over NFSv2 with the subtree_check export option; the other + * seven combinations work. The real answer is "don't use v2". + */ + len = fileid_length(fileid_type); + if (*max_len < len) + return 255; + + switch (fileid_type) { + case FILEID_INO32_GEN_PARENT | FILEID_INO64_FLAG: + spin_lock(&dentry->d_lock); + fid->i64.parent_ino = dentry->d_parent->d_inode->i_ino; + fid->i64.parent_gen = dentry->d_parent->d_inode->i_generation; + spin_unlock(&dentry->d_lock); + /*FALLTHRU*/ + case FILEID_INO32_GEN | FILEID_INO64_FLAG: + fid->i64.ino = inode->i_ino; + fid->i64.gen = inode->i_generation; + break; + } + + *max_len = len; + return fileid_type; +} +EXPORT_SYMBOL_GPL(generic_encode_fh64); + EXPORT_SYMBOL(dcache_dir_close); EXPORT_SYMBOL(dcache_dir_lseek); EXPORT_SYMBOL(dcache_dir_open); Index: linux-2.6/fs/exportfs/expfs.c =================================================================== --- linux-2.6.orig/fs/exportfs/expfs.c 2008-08-11 09:28:47.000000000 -0300 +++ linux-2.6/fs/exportfs/expfs.c 2008-08-11 09:47:24.000000000 -0300 @@ -299,46 +299,6 @@ out: return error; } -/** - * export_encode_fh - default export_operations->encode_fh function - * @dentry: the dentry to encode - * @fh: where to store the file handle fragment - * @max_len: maximum length to store there - * @connectable: whether to store parent information - * - * This default encode_fh function assumes that the 32 inode number - * is suitable for locating an inode, and that the generation number - * can be used to check that it is still valid. It places them in the - * filehandle fragment where export_decode_fh expects to find them. - */ -static int export_encode_fh(struct dentry *dentry, struct fid *fid, - int *max_len, int connectable) -{ - struct inode * inode = dentry->d_inode; - int len = *max_len; - int type = FILEID_INO32_GEN; - - if (len < 2 || (connectable && len < 4)) - return 255; - - len = 2; - fid->i32.ino = inode->i_ino; - fid->i32.gen = inode->i_generation; - if (connectable && !S_ISDIR(inode->i_mode)) { - struct inode *parent; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent->d_inode; - fid->i32.parent_ino = parent->i_ino; - fid->i32.parent_gen = parent->i_generation; - spin_unlock(&dentry->d_lock); - len = 4; - type = FILEID_INO32_GEN_PARENT; - } - *max_len = len; - return type; -} - int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, int connectable) { @@ -348,7 +308,7 @@ int exportfs_encode_fh(struct dentry *de if (nop->encode_fh) error = nop->encode_fh(dentry, fid->raw, max_len, connectable); else - error = export_encode_fh(dentry, fid, max_len, connectable); + error = generic_encode_fh32(dentry, fid, max_len, connectable); return error; } -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html