2013-08-12 20:49:40 +10:00
/*
* Copyright ( c ) 2000 - 2006 Silicon Graphics , Inc .
* Copyright ( c ) 2012 - 2013 Red Hat , 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
*/
# include "xfs.h"
# include "xfs_fs.h"
# include "xfs_format.h"
# include "xfs_log.h"
# include "xfs_trans.h"
2013-08-12 20:49:55 +10:00
# include "xfs_ag.h"
2013-08-12 20:49:40 +10:00
# include "xfs_sb.h"
# include "xfs_mount.h"
# include "xfs_bmap_btree.h"
# include "xfs_inode.h"
# include "xfs_error.h"
# include "xfs_trace.h"
# include "xfs_symlink.h"
# include "xfs_cksum.h"
# include "xfs_buf_item.h"
/*
* Each contiguous block has a header , so it is not just a simple pathlen
* to FSB conversion .
*/
int
xfs_symlink_blocks (
struct xfs_mount * mp ,
int pathlen )
{
int buflen = XFS_SYMLINK_BUF_SPACE ( mp , mp - > m_sb . sb_blocksize ) ;
return ( pathlen + buflen - 1 ) / buflen ;
}
int
xfs_symlink_hdr_set (
struct xfs_mount * mp ,
xfs_ino_t ino ,
uint32_t offset ,
uint32_t size ,
struct xfs_buf * bp )
{
struct xfs_dsymlink_hdr * dsl = bp - > b_addr ;
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return 0 ;
dsl - > sl_magic = cpu_to_be32 ( XFS_SYMLINK_MAGIC ) ;
dsl - > sl_offset = cpu_to_be32 ( offset ) ;
dsl - > sl_bytes = cpu_to_be32 ( size ) ;
uuid_copy ( & dsl - > sl_uuid , & mp - > m_sb . sb_uuid ) ;
dsl - > sl_owner = cpu_to_be64 ( ino ) ;
dsl - > sl_blkno = cpu_to_be64 ( bp - > b_bn ) ;
bp - > b_ops = & xfs_symlink_buf_ops ;
return sizeof ( struct xfs_dsymlink_hdr ) ;
}
/*
* Checking of the symlink header is split into two parts . the verifier does
* CRC , location and bounds checking , the unpacking function checks the path
* parameters and owner .
*/
bool
xfs_symlink_hdr_ok (
struct xfs_mount * mp ,
xfs_ino_t ino ,
uint32_t offset ,
uint32_t size ,
struct xfs_buf * bp )
{
struct xfs_dsymlink_hdr * dsl = bp - > b_addr ;
if ( offset ! = be32_to_cpu ( dsl - > sl_offset ) )
return false ;
if ( size ! = be32_to_cpu ( dsl - > sl_bytes ) )
return false ;
if ( ino ! = be64_to_cpu ( dsl - > sl_owner ) )
return false ;
/* ok */
return true ;
}
static bool
xfs_symlink_verify (
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
struct xfs_dsymlink_hdr * dsl = bp - > b_addr ;
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return false ;
if ( dsl - > sl_magic ! = cpu_to_be32 ( XFS_SYMLINK_MAGIC ) )
return false ;
if ( ! uuid_equal ( & dsl - > sl_uuid , & mp - > m_sb . sb_uuid ) )
return false ;
if ( bp - > b_bn ! = be64_to_cpu ( dsl - > sl_blkno ) )
return false ;
if ( be32_to_cpu ( dsl - > sl_offset ) +
be32_to_cpu ( dsl - > sl_bytes ) > = MAXPATHLEN )
return false ;
if ( dsl - > sl_owner = = 0 )
return false ;
return true ;
}
static void
xfs_symlink_read_verify (
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
/* no verification of non-crc buffers */
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return ;
if ( ! xfs_verify_cksum ( bp - > b_addr , BBTOB ( bp - > b_length ) ,
offsetof ( struct xfs_dsymlink_hdr , sl_crc ) ) | |
! xfs_symlink_verify ( bp ) ) {
XFS_CORRUPTION_ERROR ( __func__ , XFS_ERRLEVEL_LOW , mp , bp - > b_addr ) ;
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
}
}
static void
xfs_symlink_write_verify (
struct xfs_buf * bp )
{
struct xfs_mount * mp = bp - > b_target - > bt_mount ;
struct xfs_buf_log_item * bip = bp - > b_fspriv ;
/* no verification of non-crc buffers */
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) )
return ;
if ( ! xfs_symlink_verify ( bp ) ) {
XFS_CORRUPTION_ERROR ( __func__ , XFS_ERRLEVEL_LOW , mp , bp - > b_addr ) ;
xfs_buf_ioerror ( bp , EFSCORRUPTED ) ;
return ;
}
if ( bip ) {
struct xfs_dsymlink_hdr * dsl = bp - > b_addr ;
dsl - > sl_lsn = cpu_to_be64 ( bip - > bli_item . li_lsn ) ;
}
xfs_update_cksum ( bp - > b_addr , BBTOB ( bp - > b_length ) ,
offsetof ( struct xfs_dsymlink_hdr , sl_crc ) ) ;
}
const struct xfs_buf_ops xfs_symlink_buf_ops = {
. verify_read = xfs_symlink_read_verify ,
. verify_write = xfs_symlink_write_verify ,
} ;
void
xfs_symlink_local_to_remote (
struct xfs_trans * tp ,
struct xfs_buf * bp ,
struct xfs_inode * ip ,
struct xfs_ifork * ifp )
{
struct xfs_mount * mp = ip - > i_mount ;
char * buf ;
if ( ! xfs_sb_version_hascrc ( & mp - > m_sb ) ) {
bp - > b_ops = NULL ;
memcpy ( bp - > b_addr , ifp - > if_u1 . if_data , ifp - > if_bytes ) ;
return ;
}
/*
* As this symlink fits in an inode literal area , it must also fit in
* the smallest buffer the filesystem supports .
*/
ASSERT ( BBTOB ( bp - > b_length ) > =
ifp - > if_bytes + sizeof ( struct xfs_dsymlink_hdr ) ) ;
bp - > b_ops = & xfs_symlink_buf_ops ;
buf = bp - > b_addr ;
buf + = xfs_symlink_hdr_set ( mp , ip - > i_ino , 0 , ifp - > if_bytes , bp ) ;
memcpy ( buf , ifp - > if_u1 . if_data , ifp - > if_bytes ) ;
}