linux/fs/xfs/xfs_attr_remote.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
   3 * Copyright (c) 2013 Red Hat, Inc.
   4 * All Rights Reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it would be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write the Free Software Foundation,
  17 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18 */
  19#include "xfs.h"
  20#include "xfs_fs.h"
  21#include "xfs_types.h"
  22#include "xfs_bit.h"
  23#include "xfs_log.h"
  24#include "xfs_trans.h"
  25#include "xfs_trans_priv.h"
  26#include "xfs_sb.h"
  27#include "xfs_ag.h"
  28#include "xfs_mount.h"
  29#include "xfs_error.h"
  30#include "xfs_da_btree.h"
  31#include "xfs_bmap_btree.h"
  32#include "xfs_dinode.h"
  33#include "xfs_inode.h"
  34#include "xfs_alloc.h"
  35#include "xfs_inode_item.h"
  36#include "xfs_bmap.h"
  37#include "xfs_bmap_util.h"
  38#include "xfs_attr.h"
  39#include "xfs_attr_leaf.h"
  40#include "xfs_attr_remote.h"
  41#include "xfs_trans_space.h"
  42#include "xfs_trace.h"
  43#include "xfs_cksum.h"
  44#include "xfs_buf_item.h"
  45
  46#define ATTR_RMTVALUE_MAPSIZE   1       /* # of map entries at once */
  47
  48/*
  49 * Each contiguous block has a header, so it is not just a simple attribute
  50 * length to FSB conversion.
  51 */
  52int
  53xfs_attr3_rmt_blocks(
  54        struct xfs_mount *mp,
  55        int             attrlen)
  56{
  57        if (xfs_sb_version_hascrc(&mp->m_sb)) {
  58                int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
  59                return (attrlen + buflen - 1) / buflen;
  60        }
  61        return XFS_B_TO_FSB(mp, attrlen);
  62}
  63
  64/*
  65 * Checking of the remote attribute header is split into two parts. The verifier
  66 * does CRC, location and bounds checking, the unpacking function checks the
  67 * attribute parameters and owner.
  68 */
  69static bool
  70xfs_attr3_rmt_hdr_ok(
  71        struct xfs_mount        *mp,
  72        void                    *ptr,
  73        xfs_ino_t               ino,
  74        uint32_t                offset,
  75        uint32_t                size,
  76        xfs_daddr_t             bno)
  77{
  78        struct xfs_attr3_rmt_hdr *rmt = ptr;
  79
  80        if (bno != be64_to_cpu(rmt->rm_blkno))
  81                return false;
  82        if (offset != be32_to_cpu(rmt->rm_offset))
  83                return false;
  84        if (size != be32_to_cpu(rmt->rm_bytes))
  85                return false;
  86        if (ino != be64_to_cpu(rmt->rm_owner))
  87                return false;
  88
  89        /* ok */
  90        return true;
  91}
  92
  93static bool
  94xfs_attr3_rmt_verify(
  95        struct xfs_mount        *mp,
  96        void                    *ptr,
  97        int                     fsbsize,
  98        xfs_daddr_t             bno)
  99{
 100        struct xfs_attr3_rmt_hdr *rmt = ptr;
 101
 102        if (!xfs_sb_version_hascrc(&mp->m_sb))
 103                return false;
 104        if (rmt->rm_magic != cpu_to_be32(XFS_ATTR3_RMT_MAGIC))
 105                return false;
 106        if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_uuid))
 107                return false;
 108        if (be64_to_cpu(rmt->rm_blkno) != bno)
 109                return false;
 110        if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
 111                return false;
 112        if (be32_to_cpu(rmt->rm_offset) +
 113                                be32_to_cpu(rmt->rm_bytes) >= XATTR_SIZE_MAX)
 114                return false;
 115        if (rmt->rm_owner == 0)
 116                return false;
 117
 118        return true;
 119}
 120
 121static void
 122xfs_attr3_rmt_read_verify(
 123        struct xfs_buf  *bp)
 124{
 125        struct xfs_mount *mp = bp->b_target->bt_mount;
 126        char            *ptr;
 127        int             len;
 128        bool            corrupt = false;
 129        xfs_daddr_t     bno;
 130
 131        /* no verification of non-crc buffers */
 132        if (!xfs_sb_version_hascrc(&mp->m_sb))
 133                return;
 134
 135        ptr = bp->b_addr;
 136        bno = bp->b_bn;
 137        len = BBTOB(bp->b_length);
 138        ASSERT(len >= XFS_LBSIZE(mp));
 139
 140        while (len > 0) {
 141                if (!xfs_verify_cksum(ptr, XFS_LBSIZE(mp),
 142                                      XFS_ATTR3_RMT_CRC_OFF)) {
 143                        corrupt = true;
 144                        break;
 145                }
 146                if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
 147                        corrupt = true;
 148                        break;
 149                }
 150                len -= XFS_LBSIZE(mp);
 151                ptr += XFS_LBSIZE(mp);
 152                bno += mp->m_bsize;
 153        }
 154
 155        if (corrupt) {
 156                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
 157                xfs_buf_ioerror(bp, EFSCORRUPTED);
 158        } else
 159                ASSERT(len == 0);
 160}
 161
 162static void
 163xfs_attr3_rmt_write_verify(
 164        struct xfs_buf  *bp)
 165{
 166        struct xfs_mount *mp = bp->b_target->bt_mount;
 167        struct xfs_buf_log_item *bip = bp->b_fspriv;
 168        char            *ptr;
 169        int             len;
 170        xfs_daddr_t     bno;
 171
 172        /* no verification of non-crc buffers */
 173        if (!xfs_sb_version_hascrc(&mp->m_sb))
 174                return;
 175
 176        ptr = bp->b_addr;
 177        bno = bp->b_bn;
 178        len = BBTOB(bp->b_length);
 179        ASSERT(len >= XFS_LBSIZE(mp));
 180
 181        while (len > 0) {
 182                if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
 183                        XFS_CORRUPTION_ERROR(__func__,
 184                                            XFS_ERRLEVEL_LOW, mp, bp->b_addr);
 185                        xfs_buf_ioerror(bp, EFSCORRUPTED);
 186                        return;
 187                }
 188                if (bip) {
 189                        struct xfs_attr3_rmt_hdr *rmt;
 190
 191                        rmt = (struct xfs_attr3_rmt_hdr *)ptr;
 192                        rmt->rm_lsn = cpu_to_be64(bip->bli_item.li_lsn);
 193                }
 194                xfs_update_cksum(ptr, XFS_LBSIZE(mp), XFS_ATTR3_RMT_CRC_OFF);
 195
 196                len -= XFS_LBSIZE(mp);
 197                ptr += XFS_LBSIZE(mp);
 198                bno += mp->m_bsize;
 199        }
 200        ASSERT(len == 0);
 201}
 202
 203const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
 204        .verify_read = xfs_attr3_rmt_read_verify,
 205        .verify_write = xfs_attr3_rmt_write_verify,
 206};
 207
 208STATIC int
 209xfs_attr3_rmt_hdr_set(
 210        struct xfs_mount        *mp,
 211        void                    *ptr,
 212        xfs_ino_t               ino,
 213        uint32_t                offset,
 214        uint32_t                size,
 215        xfs_daddr_t             bno)
 216{
 217        struct xfs_attr3_rmt_hdr *rmt = ptr;
 218
 219        if (!xfs_sb_version_hascrc(&mp->m_sb))
 220                return 0;
 221
 222        rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC);
 223        rmt->rm_offset = cpu_to_be32(offset);
 224        rmt->rm_bytes = cpu_to_be32(size);
 225        uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_uuid);
 226        rmt->rm_owner = cpu_to_be64(ino);
 227        rmt->rm_blkno = cpu_to_be64(bno);
 228
 229        return sizeof(struct xfs_attr3_rmt_hdr);
 230}
 231
 232/*
 233 * Helper functions to copy attribute data in and out of the one disk extents
 234 */
 235STATIC int
 236xfs_attr_rmtval_copyout(
 237        struct xfs_mount *mp,
 238        struct xfs_buf  *bp,
 239        xfs_ino_t       ino,
 240        int             *offset,
 241        int             *valuelen,
 242        __uint8_t       **dst)
 243{
 244        char            *src = bp->b_addr;
 245        xfs_daddr_t     bno = bp->b_bn;
 246        int             len = BBTOB(bp->b_length);
 247
 248        ASSERT(len >= XFS_LBSIZE(mp));
 249
 250        while (len > 0 && *valuelen > 0) {
 251                int hdr_size = 0;
 252                int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp));
 253
 254                byte_cnt = min(*valuelen, byte_cnt);
 255
 256                if (xfs_sb_version_hascrc(&mp->m_sb)) {
 257                        if (!xfs_attr3_rmt_hdr_ok(mp, src, ino, *offset,
 258                                                  byte_cnt, bno)) {
 259                                xfs_alert(mp,
 260"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
 261                                        bno, *offset, byte_cnt, ino);
 262                                return EFSCORRUPTED;
 263                        }
 264                        hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
 265                }
 266
 267                memcpy(*dst, src + hdr_size, byte_cnt);
 268
 269                /* roll buffer forwards */
 270                len -= XFS_LBSIZE(mp);
 271                src += XFS_LBSIZE(mp);
 272                bno += mp->m_bsize;
 273
 274                /* roll attribute data forwards */
 275                *valuelen -= byte_cnt;
 276                *dst += byte_cnt;
 277                *offset += byte_cnt;
 278        }
 279        return 0;
 280}
 281
 282STATIC void
 283xfs_attr_rmtval_copyin(
 284        struct xfs_mount *mp,
 285        struct xfs_buf  *bp,
 286        xfs_ino_t       ino,
 287        int             *offset,
 288        int             *valuelen,
 289        __uint8_t       **src)
 290{
 291        char            *dst = bp->b_addr;
 292        xfs_daddr_t     bno = bp->b_bn;
 293        int             len = BBTOB(bp->b_length);
 294
 295        ASSERT(len >= XFS_LBSIZE(mp));
 296
 297        while (len > 0 && *valuelen > 0) {
 298                int hdr_size;
 299                int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp));
 300
 301                byte_cnt = min(*valuelen, byte_cnt);
 302                hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
 303                                                 byte_cnt, bno);
 304
 305                memcpy(dst + hdr_size, *src, byte_cnt);
 306
 307                /*
 308                 * If this is the last block, zero the remainder of it.
 309                 * Check that we are actually the last block, too.
 310                 */
 311                if (byte_cnt + hdr_size < XFS_LBSIZE(mp)) {
 312                        ASSERT(*valuelen - byte_cnt == 0);
 313                        ASSERT(len == XFS_LBSIZE(mp));
 314                        memset(dst + hdr_size + byte_cnt, 0,
 315                                        XFS_LBSIZE(mp) - hdr_size - byte_cnt);
 316                }
 317
 318                /* roll buffer forwards */
 319                len -= XFS_LBSIZE(mp);
 320                dst += XFS_LBSIZE(mp);
 321                bno += mp->m_bsize;
 322
 323                /* roll attribute data forwards */
 324                *valuelen -= byte_cnt;
 325                *src += byte_cnt;
 326                *offset += byte_cnt;
 327        }
 328}
 329
 330/*
 331 * Read the value associated with an attribute from the out-of-line buffer
 332 * that we stored it in.
 333 */
 334int
 335xfs_attr_rmtval_get(
 336        struct xfs_da_args      *args)
 337{
 338        struct xfs_bmbt_irec    map[ATTR_RMTVALUE_MAPSIZE];
 339        struct xfs_mount        *mp = args->dp->i_mount;
 340        struct xfs_buf          *bp;
 341        xfs_dablk_t             lblkno = args->rmtblkno;
 342        __uint8_t               *dst = args->value;
 343        int                     valuelen = args->valuelen;
 344        int                     nmap;
 345        int                     error;
 346        int                     blkcnt = args->rmtblkcnt;
 347        int                     i;
 348        int                     offset = 0;
 349
 350        trace_xfs_attr_rmtval_get(args);
 351
 352        ASSERT(!(args->flags & ATTR_KERNOVAL));
 353
 354        while (valuelen > 0) {
 355                nmap = ATTR_RMTVALUE_MAPSIZE;
 356                error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
 357                                       blkcnt, map, &nmap,
 358                                       XFS_BMAPI_ATTRFORK);
 359                if (error)
 360                        return error;
 361                ASSERT(nmap >= 1);
 362
 363                for (i = 0; (i < nmap) && (valuelen > 0); i++) {
 364                        xfs_daddr_t     dblkno;
 365                        int             dblkcnt;
 366
 367                        ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
 368                               (map[i].br_startblock != HOLESTARTBLOCK));
 369                        dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
 370                        dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
 371                        error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
 372                                                   dblkno, dblkcnt, 0, &bp,
 373                                                   &xfs_attr3_rmt_buf_ops);
 374                        if (error)
 375                                return error;
 376
 377                        error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
 378                                                        &offset, &valuelen,
 379                                                        &dst);
 380                        xfs_buf_relse(bp);
 381                        if (error)
 382                                return error;
 383
 384                        /* roll attribute extent map forwards */
 385                        lblkno += map[i].br_blockcount;
 386                        blkcnt -= map[i].br_blockcount;
 387                }
 388        }
 389        ASSERT(valuelen == 0);
 390        return 0;
 391}
 392
 393/*
 394 * Write the value associated with an attribute into the out-of-line buffer
 395 * that we have defined for it.
 396 */
 397int
 398xfs_attr_rmtval_set(
 399        struct xfs_da_args      *args)
 400{
 401        struct xfs_inode        *dp = args->dp;
 402        struct xfs_mount        *mp = dp->i_mount;
 403        struct xfs_bmbt_irec    map;
 404        xfs_dablk_t             lblkno;
 405        xfs_fileoff_t           lfileoff = 0;
 406        __uint8_t               *src = args->value;
 407        int                     blkcnt;
 408        int                     valuelen;
 409        int                     nmap;
 410        int                     error;
 411        int                     offset = 0;
 412
 413        trace_xfs_attr_rmtval_set(args);
 414
 415        /*
 416         * Find a "hole" in the attribute address space large enough for
 417         * us to drop the new attribute's value into. Because CRC enable
 418         * attributes have headers, we can't just do a straight byte to FSB
 419         * conversion and have to take the header space into account.
 420         */
 421        blkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen);
 422        error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
 423                                                   XFS_ATTR_FORK);
 424        if (error)
 425                return error;
 426
 427        args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
 428        args->rmtblkcnt = blkcnt;
 429
 430        /*
 431         * Roll through the "value", allocating blocks on disk as required.
 432         */
 433        while (blkcnt > 0) {
 434                int     committed;
 435
 436                /*
 437                 * Allocate a single extent, up to the size of the value.
 438                 */
 439                xfs_bmap_init(args->flist, args->firstblock);
 440                nmap = 1;
 441                error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
 442                                  blkcnt,
 443                                  XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
 444                                  args->firstblock, args->total, &map, &nmap,
 445                                  args->flist);
 446                if (!error) {
 447                        error = xfs_bmap_finish(&args->trans, args->flist,
 448                                                &committed);
 449                }
 450                if (error) {
 451                        ASSERT(committed);
 452                        args->trans = NULL;
 453                        xfs_bmap_cancel(args->flist);
 454                        return(error);
 455                }
 456
 457                /*
 458                 * bmap_finish() may have committed the last trans and started
 459                 * a new one.  We need the inode to be in all transactions.
 460                 */
 461                if (committed)
 462                        xfs_trans_ijoin(args->trans, dp, 0);
 463
 464                ASSERT(nmap == 1);
 465                ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
 466                       (map.br_startblock != HOLESTARTBLOCK));
 467                lblkno += map.br_blockcount;
 468                blkcnt -= map.br_blockcount;
 469
 470                /*
 471                 * Start the next trans in the chain.
 472                 */
 473                error = xfs_trans_roll(&args->trans, dp);
 474                if (error)
 475                        return (error);
 476        }
 477
 478        /*
 479         * Roll through the "value", copying the attribute value to the
 480         * already-allocated blocks.  Blocks are written synchronously
 481         * so that we can know they are all on disk before we turn off
 482         * the INCOMPLETE flag.
 483         */
 484        lblkno = args->rmtblkno;
 485        blkcnt = args->rmtblkcnt;
 486        valuelen = args->valuelen;
 487        while (valuelen > 0) {
 488                struct xfs_buf  *bp;
 489                xfs_daddr_t     dblkno;
 490                int             dblkcnt;
 491
 492                ASSERT(blkcnt > 0);
 493
 494                xfs_bmap_init(args->flist, args->firstblock);
 495                nmap = 1;
 496                error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno,
 497                                       blkcnt, &map, &nmap,
 498                                       XFS_BMAPI_ATTRFORK);
 499                if (error)
 500                        return(error);
 501                ASSERT(nmap == 1);
 502                ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
 503                       (map.br_startblock != HOLESTARTBLOCK));
 504
 505                dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
 506                dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
 507
 508                bp = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, 0);
 509                if (!bp)
 510                        return ENOMEM;
 511                bp->b_ops = &xfs_attr3_rmt_buf_ops;
 512
 513                xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
 514                                       &valuelen, &src);
 515
 516                error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */
 517                xfs_buf_relse(bp);
 518                if (error)
 519                        return error;
 520
 521
 522                /* roll attribute extent map forwards */
 523                lblkno += map.br_blockcount;
 524                blkcnt -= map.br_blockcount;
 525        }
 526        ASSERT(valuelen == 0);
 527        return 0;
 528}
 529
 530/*
 531 * Remove the value associated with an attribute by deleting the
 532 * out-of-line buffer that it is stored on.
 533 */
 534int
 535xfs_attr_rmtval_remove(
 536        struct xfs_da_args      *args)
 537{
 538        struct xfs_mount        *mp = args->dp->i_mount;
 539        xfs_dablk_t             lblkno;
 540        int                     blkcnt;
 541        int                     error;
 542        int                     done;
 543
 544        trace_xfs_attr_rmtval_remove(args);
 545
 546        /*
 547         * Roll through the "value", invalidating the attribute value's blocks.
 548         */
 549        lblkno = args->rmtblkno;
 550        blkcnt = args->rmtblkcnt;
 551        while (blkcnt > 0) {
 552                struct xfs_bmbt_irec    map;
 553                struct xfs_buf          *bp;
 554                xfs_daddr_t             dblkno;
 555                int                     dblkcnt;
 556                int                     nmap;
 557
 558                /*
 559                 * Try to remember where we decided to put the value.
 560                 */
 561                nmap = 1;
 562                error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
 563                                       blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK);
 564                if (error)
 565                        return(error);
 566                ASSERT(nmap == 1);
 567                ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
 568                       (map.br_startblock != HOLESTARTBLOCK));
 569
 570                dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
 571                dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
 572
 573                /*
 574                 * If the "remote" value is in the cache, remove it.
 575                 */
 576                bp = xfs_incore(mp->m_ddev_targp, dblkno, dblkcnt, XBF_TRYLOCK);
 577                if (bp) {
 578                        xfs_buf_stale(bp);
 579                        xfs_buf_relse(bp);
 580                        bp = NULL;
 581                }
 582
 583                lblkno += map.br_blockcount;
 584                blkcnt -= map.br_blockcount;
 585        }
 586
 587        /*
 588         * Keep de-allocating extents until the remote-value region is gone.
 589         */
 590        lblkno = args->rmtblkno;
 591        blkcnt = args->rmtblkcnt;
 592        done = 0;
 593        while (!done) {
 594                int committed;
 595
 596                xfs_bmap_init(args->flist, args->firstblock);
 597                error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
 598                                    XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
 599                                    1, args->firstblock, args->flist,
 600                                    &done);
 601                if (!error) {
 602                        error = xfs_bmap_finish(&args->trans, args->flist,
 603                                                &committed);
 604                }
 605                if (error) {
 606                        ASSERT(committed);
 607                        args->trans = NULL;
 608                        xfs_bmap_cancel(args->flist);
 609                        return error;
 610                }
 611
 612                /*
 613                 * bmap_finish() may have committed the last trans and started
 614                 * a new one.  We need the inode to be in all transactions.
 615                 */
 616                if (committed)
 617                        xfs_trans_ijoin(args->trans, args->dp, 0);
 618
 619                /*
 620                 * Close out trans and start the next one in the chain.
 621                 */
 622                error = xfs_trans_roll(&args->trans, args->dp);
 623                if (error)
 624                        return (error);
 625        }
 626        return(0);
 627}
 628