2018-06-05 19:42:14 -07:00
// SPDX-License-Identifier: GPL-2.0
2005-04-16 15:20:36 -07:00
/*
2005-11-02 14:58:39 +11:00
* Copyright ( c ) 2004 - 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-16 15:20:36 -07:00
*/
# include "xfs.h"
2019-06-28 19:25:35 -07:00
# include "xfs_shared.h"
2013-10-23 10:51:50 +11:00
# include "xfs_format.h"
2013-10-23 10:50:10 +11:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-04-16 15:20:36 -07:00
# include "xfs_mount.h"
2013-08-12 20:49:37 +10:00
# include "xfs_dir2.h"
2005-04-16 15:20:36 -07:00
# include "xfs_export.h"
2007-08-29 10:58:01 +10:00
# include "xfs_inode.h"
2013-10-23 10:50:10 +11:00
# include "xfs_trans.h"
2010-02-17 14:05:16 -06:00
# include "xfs_inode_item.h"
2012-10-08 21:56:11 +11:00
# include "xfs_icache.h"
2015-02-16 11:49:23 +11:00
# include "xfs_pnfs.h"
2005-04-16 15:20:36 -07:00
/*
2007-10-21 16:42:11 -07: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-16 15:20:36 -07:00
*/
2007-10-21 16:42:11 -07:00
static int xfs_fileid_length ( int fileid_type )
2005-04-16 15:20:36 -07:00
{
2007-10-21 16:42:11 -07: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-16 15:20:36 -07:00
}
2013-02-17 15:48:11 +09:00
return FILEID_INVALID ;
2005-04-16 15:20:36 -07:00
}
STATIC int
2006-03-14 14:06:18 +11:00
xfs_fs_encode_fh (
2012-04-02 14:34:06 -04:00
struct inode * inode ,
__u32 * fh ,
int * max_len ,
struct inode * parent )
2005-04-16 15:20:36 -07:00
{
2021-08-18 18:46:52 -07:00
struct xfs_mount * mp = XFS_M ( inode - > i_sb ) ;
2007-10-21 16:42:11 -07:00
struct fid * fid = ( struct fid * ) fh ;
struct xfs_fid64 * fid64 = ( struct xfs_fid64 * ) fh ;
int fileid_type ;
2005-04-16 15:20:36 -07:00
int len ;
/* Directories don't need their parent encoded, they have ".." */
2012-04-02 14:34:06 -04:00
if ( ! parent )
2007-10-21 16:42:11 -07:00
fileid_type = FILEID_INO32_GEN ;
else
fileid_type = FILEID_INO32_GEN_PARENT ;
2010-11-19 13:38:49 +00:00
/*
2020-08-05 08:49:58 -07:00
* If the filesystem may contain 64 bit inode numbers , we need
2010-11-19 13:38:49 +00:00
* 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 .
*/
2021-08-18 18:46:52 -07:00
if ( ! xfs_has_small_inums ( mp ) | | xfs_is_inode32 ( mp ) )
2007-10-21 16:42:11 -07:00
fileid_type | = XFS_FILEID_TYPE_64FLAG ;
2005-04-16 15:20:36 -07: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-21 16:42:11 -07:00
len = xfs_fileid_length ( fileid_type ) ;
2011-01-29 18:43:25 +05:30
if ( * max_len < len ) {
* max_len = len ;
2013-02-17 15:48:11 +09:00
return FILEID_INVALID ;
2011-01-29 18:43:25 +05:30
}
2005-04-16 15:20:36 -07:00
* max_len = len ;
2007-10-21 16:42:11 -07:00
switch ( fileid_type ) {
case FILEID_INO32_GEN_PARENT :
2012-04-02 14:34:06 -04:00
fid - > i32 . parent_ino = XFS_I ( parent ) - > i_ino ;
fid - > i32 . parent_gen = parent - > i_generation ;
2021-04-20 17:54:36 -05:00
fallthrough ;
2007-10-21 16:42:11 -07:00
case FILEID_INO32_GEN :
2011-11-30 08:58:18 +00:00
fid - > i32 . ino = XFS_I ( inode ) - > i_ino ;
2007-10-21 16:42:11 -07:00
fid - > i32 . gen = inode - > i_generation ;
break ;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG :
2012-04-02 14:34:06 -04:00
fid64 - > parent_ino = XFS_I ( parent ) - > i_ino ;
fid64 - > parent_gen = parent - > i_generation ;
2021-04-20 17:54:36 -05:00
fallthrough ;
2007-10-21 16:42:11 -07:00
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG :
2011-11-30 08:58:18 +00:00
fid64 - > ino = XFS_I ( inode ) - > i_ino ;
2007-10-21 16:42:11 -07:00
fid64 - > gen = inode - > i_generation ;
break ;
2005-04-16 15:20:36 -07:00
}
2007-10-21 16:42:11 -07:00
return fileid_type ;
2005-04-16 15:20:36 -07:00
}
2007-10-21 16:42:11 -07:00
STATIC struct inode *
xfs_nfs_get_inode (
2005-04-16 15:20:36 -07:00
struct super_block * sb ,
2007-10-21 16:42:11 -07:00
u64 ino ,
u32 generation )
2018-03-21 15:09:32 -07:00
{
2007-12-18 16:26:55 +11:00
xfs_mount_t * mp = XFS_M ( sb ) ;
xfs_inode_t * ip ;
2005-04-16 15:20:36 -07:00
int error ;
2007-12-18 16:26:55 +11:00
/*
* NFS can sometimes send requests for ino 0. Fail them gracefully .
*/
if ( ino = = 0 )
return ERR_PTR ( - ESTALE ) ;
2005-04-16 15:20:36 -07:00
2009-01-01 14:21:16 -05:00
/*
2010-06-24 11:15:47 +10: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 14:21:16 -05:00
*/
2010-06-24 11:51:19 +10:00
error = xfs_iget ( mp , NULL , ino , XFS_IGET_UNTRUSTED , 0 , & ip ) ;
2009-01-01 14:21:16 -05:00
if ( error ) {
2018-06-05 10:09:34 -07:00
2009-01-01 14:21:16 -05:00
/*
* EINVAL means the inode cluster doesn ' t exist anymore .
2018-06-05 10:09:34 -07:00
* EFSCORRUPTED means the metadata pointing to the inode cluster
* or the inode cluster itself is corrupt . This implies the
* filehandle is stale , so we should translate it here .
2009-01-01 14:21:16 -05:00
* We don ' t use ESTALE directly down the chain to not
* confuse applications using bulkstat that expect EINVAL .
*/
2018-06-05 10:09:34 -07:00
switch ( error ) {
case - EINVAL :
case - ENOENT :
case - EFSCORRUPTED :
2014-06-25 14:58:08 +10:00
error = - ESTALE ;
2018-06-05 10:09:34 -07:00
break ;
default :
break ;
}
2014-06-25 14:58:08 +10:00
return ERR_PTR ( error ) ;
2009-01-01 14:21:16 -05:00
}
2007-12-18 16:26:55 +11:00
2016-02-09 16:54:58 +11:00
if ( VFS_I ( ip ) - > i_generation ! = generation ) {
2018-07-25 12:52:32 -07:00
xfs_irele ( ip ) ;
2011-07-14 20:50:36 +00:00
return ERR_PTR ( - ESTALE ) ;
2007-12-18 16:26:55 +11:00
}
2007-10-21 16:42:11 -07:00
2008-08-13 15:45:15 +10:00
return VFS_I ( ip ) ;
2007-10-21 16:42:11 -07: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 15:49:04 +02:00
return d_obtain_alias ( inode ) ;
2007-10-21 16:42:11 -07: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-07 20:32:51 -07:00
if ( fh_len < xfs_fileid_length ( fileid_type ) )
return NULL ;
2007-10-21 16:42:11 -07: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 15:49:04 +02:00
return d_obtain_alias ( inode ) ;
2005-04-16 15:20:36 -07:00
}
STATIC struct dentry *
2006-03-14 14:06:18 +11:00
xfs_fs_get_parent (
2005-04-16 15:20:36 -07:00
struct dentry * child )
{
int error ;
2008-03-06 13:46:25 +11:00
struct xfs_inode * cip ;
2005-04-16 15:20:36 -07:00
2015-03-17 22:25:59 +00:00
error = xfs_lookup ( XFS_I ( d_inode ( child ) ) , & xfs_name_dotdot , & cip , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( unlikely ( error ) )
2014-06-25 14:58:08 +10:00
return ERR_PTR ( error ) ;
2005-04-16 15:20:36 -07:00
2008-08-11 15:49:04 +02:00
return d_obtain_alias ( VFS_I ( cip ) ) ;
2005-04-16 15:20:36 -07:00
}
2010-02-17 14:05:16 -06:00
STATIC int
xfs_fs_nfs_commit_metadata (
struct inode * inode )
{
2020-04-03 11:45:37 -07:00
return xfs_log_force_inode ( XFS_I ( inode ) ) ;
2010-02-17 14:05:16 -06:00
}
2007-10-21 16:42:17 -07:00
const struct export_operations xfs_export_operations = {
2006-03-14 14:06:18 +11:00
. encode_fh = xfs_fs_encode_fh ,
2007-10-21 16:42:11 -07:00
. fh_to_dentry = xfs_fs_fh_to_dentry ,
. fh_to_parent = xfs_fs_fh_to_parent ,
2006-03-14 14:06:18 +11:00
. get_parent = xfs_fs_get_parent ,
2010-02-17 14:05:16 -06:00
. commit_metadata = xfs_fs_nfs_commit_metadata ,
2016-07-08 09:53:20 -04:00
# ifdef CONFIG_EXPORTFS_BLOCK_OPS
2015-02-16 11:49:23 +11:00
. get_uuid = xfs_fs_get_uuid ,
. map_blocks = xfs_fs_map_blocks ,
. commit_blocks = xfs_fs_commit_blocks ,
# endif
2005-04-16 15:20:36 -07:00
} ;