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-05-09 15:01:32.000000000 +0200 +++ linux-2.6/fs/xfs/linux-2.6/xfs_export.c 2008-05-09 15:02:23.000000000 +0200 @@ -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,33 @@ 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); + + return generic_encode_fh(dentry, (struct fid *)fh, max_len, connectable, + !(mp->m_flags & XFS_MOUNT_SMALL_INUMS)); } 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-05-09 15:01:32.000000000 +0200 +++ /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-05-09 15:01:32.000000000 +0200 +++ linux-2.6/include/linux/exportfs.h 2008-05-09 15:01:49.000000000 +0200 @@ -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,7 @@ 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_fh(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable, int ino64); #endif /* LINUX_EXPORTFS_H */ Index: linux-2.6/fs/libfs.c =================================================================== --- linux-2.6.orig/fs/libfs.c 2008-05-09 15:01:32.000000000 +0200 +++ linux-2.6/fs/libfs.c 2008-05-09 15:02:23.000000000 +0200 @@ -709,6 +709,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 @@ -727,13 +752,17 @@ 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) { case FILEID_INO32_GEN: case FILEID_INO32_GEN_PARENT: inode = get_inode(sb, fid->i32.ino, fid->i32.gen); + 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; } @@ -742,7 +771,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 @@ -760,20 +789,90 @@ 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_fh - 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 + * @ino64 encode 64bit inode numbers in the file handle + * + * This is the generic helper to encode file handles for filesystem that + * use a tuple to locate an object on disk. The + * generation is always encode in 32bits, and the inode is encoded + * in 32 or 64bits depending on the ino64 flag. + */ +int generic_encode_fh(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable, int ino64) +{ + 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; + + if (ino64) + fileid_type |= 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: + 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 | 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_fh); + + 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-05-09 15:01:32.000000000 +0200 +++ linux-2.6/fs/exportfs/expfs.c 2008-05-09 15:01:49.000000000 +0200 @@ -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_fh(dentry, fid, max_len, connectable, 0); return error; }