2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0
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
*/
# include "xfs.h"
2019-06-29 05:25:35 +03:00
# include "xfs_shared.h"
2013-10-23 03:51:50 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-04-17 02:20:36 +04:00
# include "xfs_mount.h"
2013-08-12 14:49:37 +04:00
# 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_inode.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2010-02-17 23:05:16 +03:00
# include "xfs_inode_item.h"
2012-10-08 14:56:11 +04:00
# include "xfs_icache.h"
2015-02-16 03:49:23 +03:00
# include "xfs_pnfs.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
{
2021-08-19 04:46:52 +03:00
struct xfs_mount * mp = XFS_M ( inode - > i_sb ) ;
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
/*
2020-08-05 18:49:58 +03:00
* If the filesystem may contain 64 bit inode numbers , we need
2010-11-19 16:38:49 +03: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-19 04:46:52 +03:00
if ( ! xfs_has_small_inums ( mp ) | | xfs_is_inode32 ( mp ) )
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 ;
2021-04-21 01:54:36 +03:00
fallthrough ;
2007-10-22 03:42:11 +04:00
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 ;
2021-04-21 01:54:36 +03:00
fallthrough ;
2007-10-22 03:42:11 +04:00
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 )
2018-03-22 01:09:32 +03:00
{
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 ) {
2018-06-05 20:09:34 +03:00
2009-01-01 22:21:16 +03:00
/*
* EINVAL means the inode cluster doesn ' t exist anymore .
2018-06-05 20:09:34 +03: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 22:21:16 +03:00
* We don ' t use ESTALE directly down the chain to not
* confuse applications using bulkstat that expect EINVAL .
*/
2018-06-05 20:09:34 +03:00
switch ( error ) {
case - EINVAL :
case - ENOENT :
case - EFSCORRUPTED :
2014-06-25 08:58:08 +04:00
error = - ESTALE ;
2018-06-05 20:09:34 +03:00
break ;
default :
break ;
}
2014-06-25 08:58:08 +04:00
return ERR_PTR ( error ) ;
2009-01-01 22:21:16 +03:00
}
2007-12-18 08:26:55 +03:00
2016-02-09 08:54:58 +03:00
if ( VFS_I ( ip ) - > i_generation ! = generation ) {
2018-07-25 22:52:32 +03:00
xfs_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
2015-03-18 01:25:59 +03:00
error = xfs_lookup ( XFS_I ( d_inode ( child ) ) , & xfs_name_dotdot , & cip , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( unlikely ( error ) )
2014-06-25 08:58:08 +04:00
return ERR_PTR ( error ) ;
2005-04-17 02:20:36 +04:00
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 )
{
2020-04-03 21:45:37 +03:00
return xfs_log_force_inode ( XFS_I ( inode ) ) ;
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 ,
2016-07-08 16:53:20 +03:00
# ifdef CONFIG_EXPORTFS_BLOCK_OPS
2015-02-16 03:49:23 +03:00
. get_uuid = xfs_fs_get_uuid ,
. map_blocks = xfs_fs_map_blocks ,
. commit_blocks = xfs_fs_commit_blocks ,
# endif
2005-04-17 02:20:36 +04:00
} ;