LCOV - code coverage report
Current view: top level - fs/xfs/libxfs - xfs_symlink_remote.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 178 206 86.4 %
Date: 2023-07-31 20:08:27 Functions: 12 12 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Copyright (c) 2000-2006 Silicon Graphics, Inc.
       4             :  * Copyright (c) 2012-2013 Red Hat, Inc.
       5             :  * All rights reserved.
       6             :  */
       7             : #include "xfs.h"
       8             : #include "xfs_fs.h"
       9             : #include "xfs_format.h"
      10             : #include "xfs_log_format.h"
      11             : #include "xfs_shared.h"
      12             : #include "xfs_trans_resv.h"
      13             : #include "xfs_mount.h"
      14             : #include "xfs_inode.h"
      15             : #include "xfs_error.h"
      16             : #include "xfs_trans.h"
      17             : #include "xfs_buf_item.h"
      18             : #include "xfs_log.h"
      19             : #include "xfs_symlink_remote.h"
      20             : #include "xfs_bit.h"
      21             : #include "xfs_bmap.h"
      22             : #include "xfs_health.h"
      23             : 
      24             : /*
      25             :  * Each contiguous block has a header, so it is not just a simple pathlen
      26             :  * to FSB conversion.
      27             :  */
      28             : int
      29    27174398 : xfs_symlink_blocks(
      30             :         struct xfs_mount *mp,
      31             :         int             pathlen)
      32             : {
      33    27174398 :         int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
      34             : 
      35   244464368 :         return (pathlen + buflen - 1) / buflen;
      36             : }
      37             : 
      38             : int
      39    22044422 : xfs_symlink_hdr_set(
      40             :         struct xfs_mount        *mp,
      41             :         xfs_ino_t               ino,
      42             :         uint32_t                offset,
      43             :         uint32_t                size,
      44             :         struct xfs_buf          *bp)
      45             : {
      46    22044422 :         struct xfs_dsymlink_hdr *dsl = bp->b_addr;
      47             : 
      48    22044422 :         if (!xfs_has_crc(mp))
      49             :                 return 0;
      50             : 
      51    22044422 :         memset(dsl, 0, sizeof(struct xfs_dsymlink_hdr));
      52    22044422 :         dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC);
      53    22044422 :         dsl->sl_offset = cpu_to_be32(offset);
      54    22044422 :         dsl->sl_bytes = cpu_to_be32(size);
      55    22044422 :         uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid);
      56    22044185 :         dsl->sl_owner = cpu_to_be64(ino);
      57    22044185 :         dsl->sl_blkno = cpu_to_be64(xfs_buf_daddr(bp));
      58    22044185 :         bp->b_ops = &xfs_symlink_buf_ops;
      59             : 
      60    22044185 :         return sizeof(struct xfs_dsymlink_hdr);
      61             : }
      62             : 
      63             : /*
      64             :  * Checking of the symlink header is split into two parts. the verifier does
      65             :  * CRC, location and bounds checking, the unpacking function checks the path
      66             :  * parameters and owner.
      67             :  */
      68             : bool
      69   216773933 : xfs_symlink_hdr_ok(
      70             :         xfs_ino_t               ino,
      71             :         uint32_t                offset,
      72             :         uint32_t                size,
      73             :         struct xfs_buf          *bp)
      74             : {
      75   216773933 :         struct xfs_dsymlink_hdr *dsl = bp->b_addr;
      76             : 
      77   433547866 :         if (offset != be32_to_cpu(dsl->sl_offset))
      78             :                 return false;
      79   434392038 :         if (size != be32_to_cpu(dsl->sl_bytes))
      80             :                 return false;
      81   217218570 :         if (ino != be64_to_cpu(dsl->sl_owner))
      82           0 :                 return false;
      83             : 
      84             :         /* ok */
      85             :         return true;
      86             : }
      87             : 
      88             : static xfs_failaddr_t
      89     3690637 : xfs_symlink_verify(
      90             :         struct xfs_buf          *bp)
      91             : {
      92     3690637 :         struct xfs_mount        *mp = bp->b_mount;
      93     3690637 :         struct xfs_dsymlink_hdr *dsl = bp->b_addr;
      94             : 
      95     3690637 :         if (!xfs_has_crc(mp))
      96           0 :                 return __this_address;
      97     3690637 :         if (!xfs_verify_magic(bp, dsl->sl_magic))
      98           0 :                 return __this_address;
      99     3690637 :         if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid))
     100           0 :                 return __this_address;
     101     3690637 :         if (xfs_buf_daddr(bp) != be64_to_cpu(dsl->sl_blkno))
     102           1 :                 return __this_address;
     103     7381272 :         if (be32_to_cpu(dsl->sl_offset) +
     104     3690636 :                                 be32_to_cpu(dsl->sl_bytes) >= XFS_SYMLINK_MAXLEN)
     105           0 :                 return __this_address;
     106     3690636 :         if (dsl->sl_owner == 0)
     107           0 :                 return __this_address;
     108     3690636 :         if (!xfs_log_check_lsn(mp, be64_to_cpu(dsl->sl_lsn)))
     109           0 :                 return __this_address;
     110             : 
     111             :         return NULL;
     112             : }
     113             : 
     114             : static void
     115      905868 : xfs_symlink_read_verify(
     116             :         struct xfs_buf  *bp)
     117             : {
     118      905868 :         struct xfs_mount *mp = bp->b_mount;
     119      905868 :         xfs_failaddr_t  fa;
     120             : 
     121             :         /* no verification of non-crc buffers */
     122      905868 :         if (!xfs_has_crc(mp))
     123             :                 return;
     124             : 
     125      905868 :         if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF))
     126           2 :                 xfs_verifier_error(bp, -EFSBADCRC, __this_address);
     127             :         else {
     128      905865 :                 fa = xfs_symlink_verify(bp);
     129      905865 :                 if (fa)
     130           0 :                         xfs_verifier_error(bp, -EFSCORRUPTED, fa);
     131             :         }
     132             : }
     133             : 
     134             : static void
     135     2778808 : xfs_symlink_write_verify(
     136             :         struct xfs_buf  *bp)
     137             : {
     138     2778808 :         struct xfs_mount *mp = bp->b_mount;
     139     2778808 :         struct xfs_buf_log_item *bip = bp->b_log_item;
     140     2778808 :         xfs_failaddr_t          fa;
     141             : 
     142             :         /* no verification of non-crc buffers */
     143     2778808 :         if (!xfs_has_crc(mp))
     144             :                 return;
     145             : 
     146     2778808 :         fa = xfs_symlink_verify(bp);
     147     2778808 :         if (fa) {
     148           0 :                 xfs_verifier_error(bp, -EFSCORRUPTED, fa);
     149           0 :                 return;
     150             :         }
     151             : 
     152     2778808 :         if (bip) {
     153     2778808 :                 struct xfs_dsymlink_hdr *dsl = bp->b_addr;
     154     2778808 :                 dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn);
     155             :         }
     156     2778808 :         xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF);
     157             : }
     158             : 
     159             : const struct xfs_buf_ops xfs_symlink_buf_ops = {
     160             :         .name = "xfs_symlink",
     161             :         .magic = { 0, cpu_to_be32(XFS_SYMLINK_MAGIC) },
     162             :         .verify_read = xfs_symlink_read_verify,
     163             :         .verify_write = xfs_symlink_write_verify,
     164             :         .verify_struct = xfs_symlink_verify,
     165             : };
     166             : 
     167             : void
     168        1043 : xfs_symlink_local_to_remote(
     169             :         struct xfs_trans        *tp,
     170             :         struct xfs_buf          *bp,
     171             :         struct xfs_inode        *ip,
     172             :         struct xfs_ifork        *ifp,
     173             :         void                    *priv)
     174             : {
     175        1043 :         struct xfs_mount        *mp = ip->i_mount;
     176        1043 :         char                    *buf;
     177             : 
     178        1043 :         xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF);
     179             : 
     180        1043 :         if (!xfs_has_crc(mp)) {
     181           0 :                 bp->b_ops = NULL;
     182           0 :                 memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
     183           0 :                 xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
     184           0 :                 return;
     185             :         }
     186             : 
     187             :         /*
     188             :          * As this symlink fits in an inode literal area, it must also fit in
     189             :          * the smallest buffer the filesystem supports.
     190             :          */
     191        1043 :         ASSERT(BBTOB(bp->b_length) >=
     192             :                         ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr));
     193             : 
     194        1043 :         bp->b_ops = &xfs_symlink_buf_ops;
     195             : 
     196        1043 :         buf = bp->b_addr;
     197        1043 :         buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp);
     198        2086 :         memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes);
     199        1043 :         xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsymlink_hdr) +
     200        1043 :                                         ifp->if_bytes - 1);
     201             : }
     202             : 
     203             : /*
     204             :  * Verify the in-memory consistency of an inline symlink data fork. This
     205             :  * does not do on-disk format checks.
     206             :  */
     207             : xfs_failaddr_t
     208     6619828 : xfs_symlink_sf_verify_struct(
     209             :         void                    *sfp,
     210             :         int64_t                 size)
     211             : {
     212     6619828 :         char                    *endp = sfp + size;
     213             : 
     214             :         /*
     215             :          * Zero length symlinks should never occur in memory as they are
     216             :          * never allowed to exist on disk.
     217             :          */
     218     6619828 :         if (!size)
     219           0 :                 return __this_address;
     220             : 
     221             :         /* No negative sizes or overly long symlink targets. */
     222     6619828 :         if (size < 0 || size > XFS_SYMLINK_MAXLEN)
     223           0 :                 return __this_address;
     224             : 
     225             :         /* No NULLs in the target either. */
     226    13239656 :         if (memchr(sfp, 0, size - 1))
     227           2 :                 return __this_address;
     228             : 
     229             :         /* We /did/ null-terminate the buffer, right? */
     230     6619826 :         if (*endp != 0)
     231           0 :                 return __this_address;
     232             :         return NULL;
     233             : }
     234             : 
     235             : xfs_failaddr_t
     236     6619807 : xfs_symlink_shortform_verify(
     237             :         struct xfs_inode        *ip)
     238             : {
     239     6619807 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
     240             : 
     241     6619807 :         ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
     242             : 
     243     6619807 :         return xfs_symlink_sf_verify_struct(ifp->if_u1.if_data, ifp->if_bytes);
     244             : }
     245             : 
     246             : /* Read a remote symlink target into the buffer. */
     247             : int
     248   217263012 : xfs_symlink_remote_read(
     249             :         struct xfs_inode        *ip,
     250             :         char                    *link)
     251             : {
     252   217263012 :         struct xfs_mount        *mp = ip->i_mount;
     253   217263012 :         struct xfs_bmbt_irec    mval[XFS_SYMLINK_MAPS];
     254   217263012 :         struct xfs_buf          *bp;
     255   217263012 :         xfs_daddr_t             d;
     256   217263012 :         char                    *cur_chunk;
     257   217263012 :         int                     pathlen = ip->i_disk_size;
     258   217263012 :         int                     nmaps = XFS_SYMLINK_MAPS;
     259   217263012 :         int                     byte_cnt;
     260   217263012 :         int                     n;
     261   217263012 :         int                     error = 0;
     262   217263012 :         int                     fsblocks = 0;
     263   217263012 :         int                     offset;
     264             : 
     265   217263012 :         ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
     266             : 
     267   217289970 :         fsblocks = xfs_symlink_blocks(mp, pathlen);
     268   217289970 :         error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0);
     269   217293481 :         if (error)
     270           0 :                 goto out;
     271             : 
     272             :         offset = 0;
     273   434635825 :         for (n = 0; n < nmaps; n++) {
     274   217292229 :                 d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
     275   217292229 :                 byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
     276             : 
     277   217292229 :                 error = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0,
     278             :                                 &bp, &xfs_symlink_buf_ops);
     279   217198515 :                 if (xfs_metadata_is_sick(error))
     280           2 :                         xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK);
     281   217198515 :                 if (error)
     282           2 :                         return error;
     283   217198513 :                 byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
     284   217198513 :                 if (pathlen < byte_cnt)
     285             :                         byte_cnt = pathlen;
     286             : 
     287   217198513 :                 cur_chunk = bp->b_addr;
     288   217198513 :                 if (xfs_has_crc(mp)) {
     289   217207405 :                         if (!xfs_symlink_hdr_ok(ip->i_ino, offset,
     290             :                                                         byte_cnt, bp)) {
     291           0 :                                 xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK);
     292           0 :                                 error = -EFSCORRUPTED;
     293           0 :                                 xfs_alert(mp,
     294             : "symlink header does not match required off/len/owner (0x%x/0x%x,0x%llx)",
     295             :                                         offset, byte_cnt, ip->i_ino);
     296           0 :                                 xfs_buf_relse(bp);
     297           0 :                                 goto out;
     298             : 
     299             :                         }
     300             : 
     301   217207405 :                         cur_chunk += sizeof(struct xfs_dsymlink_hdr);
     302             :                 }
     303             : 
     304   434397026 :                 memcpy(link + offset, cur_chunk, byte_cnt);
     305             : 
     306   217198513 :                 pathlen -= byte_cnt;
     307   217198513 :                 offset += byte_cnt;
     308             : 
     309   217198513 :                 xfs_buf_relse(bp);
     310             :         }
     311   217343596 :         ASSERT(pathlen == 0);
     312             : 
     313   217343596 :         link[ip->i_disk_size] = '\0';
     314   217343596 :         error = 0;
     315             : 
     316             :  out:
     317             :         return error;
     318             : }
     319             : 
     320             : /* Write the symlink target into the inode. */
     321             : int
     322    27157510 : __xfs_symlink_write_target(
     323             :         struct xfs_trans        *tp,
     324             :         struct xfs_inode        *ip,
     325             :         xfs_ino_t               owner,
     326             :         const char              *target_path,
     327             :         int                     pathlen,
     328             :         xfs_fsblock_t           fs_blocks,
     329             :         uint                    resblks)
     330             : {
     331    27157510 :         struct xfs_bmbt_irec    mval[XFS_SYMLINK_MAPS];
     332    27157510 :         struct xfs_mount        *mp = tp->t_mountp;
     333    27157510 :         const char              *cur_chunk;
     334    27157510 :         struct xfs_buf          *bp;
     335    27157510 :         xfs_daddr_t             d;
     336    27157510 :         int                     byte_cnt;
     337    27157510 :         int                     nmaps;
     338    27157510 :         int                     offset = 0;
     339    27157510 :         int                     n;
     340    27157510 :         int                     error;
     341             : 
     342             :         /*
     343             :          * If the symlink will fit into the inode, write it inline.
     344             :          */
     345    27157510 :         if (pathlen <= xfs_inode_data_fork_size(ip)) {
     346     5112648 :                 xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen);
     347             : 
     348     5112635 :                 ip->i_disk_size = pathlen;
     349     5112635 :                 ip->i_df.if_format = XFS_DINODE_FMT_LOCAL;
     350     5112635 :                 xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
     351     5112635 :                 return 0;
     352             :         }
     353             : 
     354    22044862 :         nmaps = XFS_SYMLINK_MAPS;
     355    22044862 :         error = xfs_bmapi_write(tp, ip, 0, fs_blocks, XFS_BMAPI_METADATA,
     356             :                         resblks, mval, &nmaps);
     357    22042060 :         if (error)
     358             :                 return error;
     359             : 
     360    22042863 :         ip->i_disk_size = pathlen;
     361    22042863 :         xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
     362             : 
     363    22042863 :         cur_chunk = target_path;
     364    22042863 :         offset = 0;
     365    66127305 :         for (n = 0; n < nmaps; n++) {
     366    22040648 :                 char    *buf;
     367             : 
     368    22040648 :                 d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
     369    22040648 :                 byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
     370    22040648 :                 error = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
     371    22040648 :                                 BTOBB(byte_cnt), 0, &bp);
     372    22043567 :                 if (error)
     373           0 :                         return error;
     374    22043567 :                 bp->b_ops = &xfs_symlink_buf_ops;
     375             : 
     376    22043567 :                 byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
     377    22043567 :                 byte_cnt = min(byte_cnt, pathlen);
     378             : 
     379    22043567 :                 buf = bp->b_addr;
     380    22043567 :                 buf += xfs_symlink_hdr_set(mp, owner, offset, byte_cnt, bp);
     381             : 
     382    44084902 :                 memcpy(buf, cur_chunk, byte_cnt);
     383             : 
     384    22042451 :                 cur_chunk += byte_cnt;
     385    22042451 :                 pathlen -= byte_cnt;
     386    22042451 :                 offset += byte_cnt;
     387             : 
     388    22042451 :                 xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF);
     389    22043573 :                 xfs_trans_log_buf(tp, bp, 0, (buf + byte_cnt - 1) -
     390    22043573 :                                                 (char *)bp->b_addr);
     391             :         }
     392    22043794 :         ASSERT(pathlen == 0);
     393             :         return 0;
     394             : }
     395             : 
     396             : /* Remove all the blocks from a symlink and invalidate buffers. */
     397             : int
     398    20976302 : xfs_symlink_remote_truncate(
     399             :         struct xfs_trans        *tp,
     400             :         struct xfs_inode        *ip)
     401             : {
     402    20976302 :         struct xfs_bmbt_irec    mval[XFS_SYMLINK_MAPS];
     403    20976302 :         struct xfs_mount        *mp = tp->t_mountp;
     404    20976302 :         struct xfs_buf          *bp;
     405    20976302 :         int                     nmaps = XFS_SYMLINK_MAPS;
     406    20976302 :         int                     done = 0;
     407    20976302 :         int                     i;
     408    20976302 :         int                     error;
     409             : 
     410             :         /* Read mappings and invalidate buffers. */
     411    20976302 :         error = xfs_bmapi_read(ip, 0, XFS_MAX_FILEOFF, mval, &nmaps, 0);
     412    20975709 :         if (error)
     413             :                 return error;
     414             : 
     415    41951397 :         for (i = 0; i < nmaps; i++) {
     416    62927251 :                 if (!xfs_bmap_is_real_extent(&mval[i]))
     417             :                         break;
     418             : 
     419    20975698 :                 error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
     420    20975698 :                                 XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
     421    20975698 :                                 XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0,
     422             :                                 &bp);
     423    20974475 :                 if (error)
     424           0 :                         return error;
     425             : 
     426    20974475 :                 xfs_trans_binval(tp, bp);
     427             :         }
     428             : 
     429             :         /* Unmap the remote blocks. */
     430    20975699 :         error = xfs_bunmapi(tp, ip, 0, XFS_MAX_FILEOFF, 0, nmaps, &done);
     431    20975564 :         if (error)
     432             :                 return error;
     433    20975564 :         if (!done) {
     434           0 :                 ASSERT(done);
     435           0 :                 xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK);
     436           0 :                 return -EFSCORRUPTED;
     437             :         }
     438             : 
     439    20975564 :         xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
     440    20975564 :         return 0;
     441             : }

Generated by: LCOV version 1.14