2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2004 - 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* 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 .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* 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
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
# include "xfs_types.h"
# include "xfs_log.h"
# include "xfs_trans.h"
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_mount.h"
2013-08-12 14:49:37 +04:00
# include "xfs_da_btree.h"
# include "xfs_dir2_format.h"
# include "xfs_dir2.h"
2005-04-17 02:20:36 +04:00
# include "xfs_export.h"
2007-08-29 04:58:01 +04:00
# include "xfs_bmap_btree.h"
# include "xfs_inode.h"
2010-02-17 23:05:16 +03:00
# include "xfs_inode_item.h"
2010-06-24 05:51:19 +04:00
# include "xfs_trace.h"
2012-10-08 14:56:11 +04:00
# include "xfs_icache.h"
2005-04-17 02:20:36 +04:00
/*
2007-10-22 03:42:11 +04:00
* 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 .
2005-04-17 02:20:36 +04:00
*/
2007-10-22 03:42:11 +04:00
static int xfs_fileid_length ( int fileid_type )
2005-04-17 02:20:36 +04:00
{
2007-10-22 03:42:11 +04:00
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 ;
2005-04-17 02:20:36 +04:00
}
2013-02-17 10:48:11 +04:00
return FILEID_INVALID ;
2005-04-17 02:20:36 +04:00
}
STATIC int
2006-03-14 06:06:18 +03:00
xfs_fs_encode_fh (
2012-04-02 22:34:06 +04:00
struct inode * inode ,
__u32 * fh ,
int * max_len ,
struct inode * parent )
2005-04-17 02:20:36 +04:00
{
2007-10-22 03:42:11 +04:00
struct fid * fid = ( struct fid * ) fh ;
struct xfs_fid64 * fid64 = ( struct xfs_fid64 * ) fh ;
int fileid_type ;
2005-04-17 02:20:36 +04:00
int len ;
/* Directories don't need their parent encoded, they have ".." */
2012-04-02 22:34:06 +04:00
if ( ! parent )
2007-10-22 03:42:11 +04:00
fileid_type = FILEID_INO32_GEN ;
else
fileid_type = FILEID_INO32_GEN_PARENT ;
2010-11-19 16:38:49 +03:00
/*
* If the the filesystem may contain 64 bit inode numbers , we need
* to use larger file handles that can represent them .
*
* While we only allocate inodes that do not fit into 32 bits any
* large enough filesystem may contain them , thus the slightly
* confusing looking conditional below .
*/
if ( ! ( XFS_M ( inode - > i_sb ) - > m_flags & XFS_MOUNT_SMALL_INUMS ) | |
( XFS_M ( inode - > i_sb ) - > m_flags & XFS_MOUNT_32BITINODES ) )
2007-10-22 03:42:11 +04:00
fileid_type | = XFS_FILEID_TYPE_64FLAG ;
2005-04-17 02:20:36 +04:00
/*
* Only encode if there is enough space given . In practice
* this means we can ' t export a filesystem with 64 bit inodes
* over NFSv2 with the subtree_check export option ; the other
* seven combinations work . The real answer is " don't use v2 " .
*/
2007-10-22 03:42:11 +04:00
len = xfs_fileid_length ( fileid_type ) ;
2011-01-29 16:13:25 +03:00
if ( * max_len < len ) {
* max_len = len ;
2013-02-17 10:48:11 +04:00
return FILEID_INVALID ;
2011-01-29 16:13:25 +03:00
}
2005-04-17 02:20:36 +04:00
* max_len = len ;
2007-10-22 03:42:11 +04:00
switch ( fileid_type ) {
case FILEID_INO32_GEN_PARENT :
2012-04-02 22:34:06 +04:00
fid - > i32 . parent_ino = XFS_I ( parent ) - > i_ino ;
fid - > i32 . parent_gen = parent - > i_generation ;
2007-10-22 03:42:11 +04:00
/*FALLTHRU*/
case FILEID_INO32_GEN :
2011-11-30 12:58:18 +04:00
fid - > i32 . ino = XFS_I ( inode ) - > i_ino ;
2007-10-22 03:42:11 +04:00
fid - > i32 . gen = inode - > i_generation ;
break ;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG :
2012-04-02 22:34:06 +04:00
fid64 - > parent_ino = XFS_I ( parent ) - > i_ino ;
fid64 - > parent_gen = parent - > i_generation ;
2007-10-22 03:42:11 +04:00
/*FALLTHRU*/
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG :
2011-11-30 12:58:18 +04:00
fid64 - > ino = XFS_I ( inode ) - > i_ino ;
2007-10-22 03:42:11 +04:00
fid64 - > gen = inode - > i_generation ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 03:42:11 +04:00
return fileid_type ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 03:42:11 +04:00
STATIC struct inode *
xfs_nfs_get_inode (
2005-04-17 02:20:36 +04:00
struct super_block * sb ,
2007-10-22 03:42:11 +04:00
u64 ino ,
u32 generation )
{
2007-12-18 08:26:55 +03:00
xfs_mount_t * mp = XFS_M ( sb ) ;
xfs_inode_t * ip ;
2005-04-17 02:20:36 +04:00
int error ;
2007-12-18 08:26:55 +03:00
/*
* NFS can sometimes send requests for ino 0. Fail them gracefully .
*/
if ( ino = = 0 )
return ERR_PTR ( - ESTALE ) ;
2005-04-17 02:20:36 +04:00
2009-01-01 22:21:16 +03:00
/*
2010-06-24 05:15:47 +04:00
* The XFS_IGET_UNTRUSTED means that an invalid inode number is just
* fine and not an indication of a corrupted filesystem as clients can
* send invalid file handles and we have to handle it gracefully . .
2009-01-01 22:21:16 +03:00
*/
2010-06-24 05:51:19 +04:00
error = xfs_iget ( mp , NULL , ino , XFS_IGET_UNTRUSTED , 0 , & ip ) ;
2009-01-01 22:21:16 +03:00
if ( error ) {
/*
* EINVAL means the inode cluster doesn ' t exist anymore .
* This implies the filehandle is stale , so we should
* translate it here .
* We don ' t use ESTALE directly down the chain to not
* confuse applications using bulkstat that expect EINVAL .
*/
2011-07-15 00:50:36 +04:00
if ( error = = EINVAL | | error = = ENOENT )
2009-01-01 22:21:16 +03:00
error = ESTALE ;
2007-10-22 03:42:11 +04:00
return ERR_PTR ( - error ) ;
2009-01-01 22:21:16 +03:00
}
2007-12-18 08:26:55 +03:00
2008-04-22 11:33:40 +04:00
if ( ip - > i_d . di_gen ! = generation ) {
2010-06-24 05:51:19 +04:00
IRELE ( ip ) ;
2011-07-15 00:50:36 +04:00
return ERR_PTR ( - ESTALE ) ;
2007-12-18 08:26:55 +03:00
}
2007-10-22 03:42:11 +04:00
2008-08-13 09:45:15 +04:00
return VFS_I ( ip ) ;
2007-10-22 03:42:11 +04:00
}
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 ;
if ( fh_len < xfs_fileid_length ( fileid_type ) )
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 ;
}
2008-08-11 17:49:04 +04:00
return d_obtain_alias ( inode ) ;
2007-10-22 03:42:11 +04:00
}
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 ;
tmpfs,ceph,gfs2,isofs,reiserfs,xfs: fix fh_len checking
Fuzzing with trinity oopsed on the 1st instruction of shmem_fh_to_dentry(),
u64 inum = fid->raw[2];
which is unhelpfully reported as at the end of shmem_alloc_inode():
BUG: unable to handle kernel paging request at ffff880061cd3000
IP: [<ffffffff812190d0>] shmem_alloc_inode+0x40/0x40
Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC
Call Trace:
[<ffffffff81488649>] ? exportfs_decode_fh+0x79/0x2d0
[<ffffffff812d77c3>] do_handle_open+0x163/0x2c0
[<ffffffff812d792c>] sys_open_by_handle_at+0xc/0x10
[<ffffffff83a5f3f8>] tracesys+0xe1/0xe6
Right, tmpfs is being stupid to access fid->raw[2] before validating that
fh_len includes it: the buffer kmalloc'ed by do_sys_name_to_handle() may
fall at the end of a page, and the next page not be present.
But some other filesystems (ceph, gfs2, isofs, reiserfs, xfs) are being
careless about fh_len too, in fh_to_dentry() and/or fh_to_parent(), and
could oops in the same way: add the missing fh_len checks to those.
Reported-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Sage Weil <sage@inktank.com>
Cc: Steven Whitehouse <swhiteho@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: stable@vger.kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2012-10-08 07:32:51 +04:00
if ( fh_len < xfs_fileid_length ( fileid_type ) )
return NULL ;
2007-10-22 03:42:11 +04:00
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 ;
}
2008-08-11 17:49:04 +04:00
return d_obtain_alias ( inode ) ;
2005-04-17 02:20:36 +04:00
}
STATIC struct dentry *
2006-03-14 06:06:18 +03:00
xfs_fs_get_parent (
2005-04-17 02:20:36 +04:00
struct dentry * child )
{
int error ;
2008-03-06 05:46:25 +03:00
struct xfs_inode * cip ;
2005-04-17 02:20:36 +04:00
2008-05-21 10:58:22 +04:00
error = xfs_lookup ( XFS_I ( child - > d_inode ) , & xfs_name_dotdot , & cip , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( unlikely ( error ) )
return ERR_PTR ( - error ) ;
2008-08-11 17:49:04 +04:00
return d_obtain_alias ( VFS_I ( cip ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-02-17 23:05:16 +03:00
STATIC int
xfs_fs_nfs_commit_metadata (
struct inode * inode )
{
struct xfs_inode * ip = XFS_I ( inode ) ;
struct xfs_mount * mp = ip - > i_mount ;
2011-09-19 00:47:50 +04:00
xfs_lsn_t lsn = 0 ;
2010-02-17 23:05:16 +03:00
xfs_ilock ( ip , XFS_ILOCK_SHARED ) ;
2011-09-19 00:47:50 +04:00
if ( xfs_ipincount ( ip ) )
lsn = ip - > i_itemp - > ili_last_lsn ;
2010-02-17 23:05:16 +03:00
xfs_iunlock ( ip , XFS_ILOCK_SHARED ) ;
2011-09-19 00:47:50 +04:00
if ( ! lsn )
return 0 ;
return _xfs_log_force_lsn ( mp , lsn , XFS_LOG_SYNC , NULL ) ;
2010-02-17 23:05:16 +03:00
}
2007-10-22 03:42:17 +04:00
const struct export_operations xfs_export_operations = {
2006-03-14 06:06:18 +03:00
. encode_fh = xfs_fs_encode_fh ,
2007-10-22 03:42:11 +04:00
. fh_to_dentry = xfs_fs_fh_to_dentry ,
. fh_to_parent = xfs_fs_fh_to_parent ,
2006-03-14 06:06:18 +03:00
. get_parent = xfs_fs_get_parent ,
2010-02-17 23:05:16 +03:00
. commit_metadata = xfs_fs_nfs_commit_metadata ,
2005-04-17 02:20:36 +04:00
} ;