linux/fs/befs/datastream.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * linux/fs/befs/datastream.c
   4 *
   5 * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com>
   6 *
   7 * Based on portions of file.c by Makoto Kato <m_kato@ga2.so-net.ne.jp>
   8 *
   9 * Many thanks to Dominic Giampaolo, author of "Practical File System
  10 * Design with the Be File System", for such a helpful book.
  11 *
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/buffer_head.h>
  16#include <linux/string.h>
  17
  18#include "befs.h"
  19#include "datastream.h"
  20#include "io.h"
  21
  22const befs_inode_addr BAD_IADDR = { 0, 0, 0 };
  23
  24static int befs_find_brun_direct(struct super_block *sb,
  25                                 const befs_data_stream *data,
  26                                 befs_blocknr_t blockno, befs_block_run *run);
  27
  28static int befs_find_brun_indirect(struct super_block *sb,
  29                                   const befs_data_stream *data,
  30                                   befs_blocknr_t blockno,
  31                                   befs_block_run *run);
  32
  33static int befs_find_brun_dblindirect(struct super_block *sb,
  34                                      const befs_data_stream *data,
  35                                      befs_blocknr_t blockno,
  36                                      befs_block_run *run);
  37
  38/**
  39 * befs_read_datastream - get buffer_head containing data, starting from pos.
  40 * @sb: Filesystem superblock
  41 * @ds: datastream to find data with
  42 * @pos: start of data
  43 * @off: offset of data in buffer_head->b_data
  44 *
  45 * Returns pointer to buffer_head containing data starting with offset @off,
  46 * if you don't need to know offset just set @off = NULL.
  47 */
  48struct buffer_head *
  49befs_read_datastream(struct super_block *sb, const befs_data_stream *ds,
  50                     befs_off_t pos, uint *off)
  51{
  52        struct buffer_head *bh;
  53        befs_block_run run;
  54        befs_blocknr_t block;   /* block coresponding to pos */
  55
  56        befs_debug(sb, "---> %s %llu", __func__, pos);
  57        block = pos >> BEFS_SB(sb)->block_shift;
  58        if (off)
  59                *off = pos - (block << BEFS_SB(sb)->block_shift);
  60
  61        if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) {
  62                befs_error(sb, "BeFS: Error finding disk addr of block %lu",
  63                           (unsigned long)block);
  64                befs_debug(sb, "<--- %s ERROR", __func__);
  65                return NULL;
  66        }
  67        bh = befs_bread_iaddr(sb, run);
  68        if (!bh) {
  69                befs_error(sb, "BeFS: Error reading block %lu from datastream",
  70                           (unsigned long)block);
  71                return NULL;
  72        }
  73
  74        befs_debug(sb, "<--- %s read data, starting at %llu", __func__, pos);
  75
  76        return bh;
  77}
  78
  79/**
  80 * befs_fblock2brun - give back block run for fblock
  81 * @sb: the superblock
  82 * @data: datastream to read from
  83 * @fblock: the blocknumber with the file position to find
  84 * @run: The found run is passed back through this pointer
  85 *
  86 * Takes a file position and gives back a brun who's starting block
  87 * is block number fblock of the file.
  88 *
  89 * Returns BEFS_OK or BEFS_ERR.
  90 *
  91 * Calls specialized functions for each of the three possible
  92 * datastream regions.
  93 */
  94int
  95befs_fblock2brun(struct super_block *sb, const befs_data_stream *data,
  96                 befs_blocknr_t fblock, befs_block_run *run)
  97{
  98        int err;
  99        befs_off_t pos = fblock << BEFS_SB(sb)->block_shift;
 100
 101        if (pos < data->max_direct_range) {
 102                err = befs_find_brun_direct(sb, data, fblock, run);
 103
 104        } else if (pos < data->max_indirect_range) {
 105                err = befs_find_brun_indirect(sb, data, fblock, run);
 106
 107        } else if (pos < data->max_double_indirect_range) {
 108                err = befs_find_brun_dblindirect(sb, data, fblock, run);
 109
 110        } else {
 111                befs_error(sb,
 112                           "befs_fblock2brun() was asked to find block %lu, "
 113                           "which is not mapped by the datastream\n",
 114                           (unsigned long)fblock);
 115                err = BEFS_ERR;
 116        }
 117        return err;
 118}
 119
 120/**
 121 * befs_read_lsmylink - read long symlink from datastream.
 122 * @sb: Filesystem superblock
 123 * @ds: Datastream to read from
 124 * @buff: Buffer in which to place long symlink data
 125 * @len: Length of the long symlink in bytes
 126 *
 127 * Returns the number of bytes read
 128 */
 129size_t
 130befs_read_lsymlink(struct super_block *sb, const befs_data_stream *ds,
 131                   void *buff, befs_off_t len)
 132{
 133        befs_off_t bytes_read = 0;      /* bytes readed */
 134        u16 plen;
 135        struct buffer_head *bh;
 136
 137        befs_debug(sb, "---> %s length: %llu", __func__, len);
 138
 139        while (bytes_read < len) {
 140                bh = befs_read_datastream(sb, ds, bytes_read, NULL);
 141                if (!bh) {
 142                        befs_error(sb, "BeFS: Error reading datastream block "
 143                                   "starting from %llu", bytes_read);
 144                        befs_debug(sb, "<--- %s ERROR", __func__);
 145                        return bytes_read;
 146
 147                }
 148                plen = ((bytes_read + BEFS_SB(sb)->block_size) < len) ?
 149                    BEFS_SB(sb)->block_size : len - bytes_read;
 150                memcpy(buff + bytes_read, bh->b_data, plen);
 151                brelse(bh);
 152                bytes_read += plen;
 153        }
 154
 155        befs_debug(sb, "<--- %s read %u bytes", __func__, (unsigned int)
 156                   bytes_read);
 157        return bytes_read;
 158}
 159
 160/**
 161 * befs_count_blocks - blocks used by a file
 162 * @sb: Filesystem superblock
 163 * @ds: Datastream of the file
 164 *
 165 * Counts the number of fs blocks that the file represented by
 166 * inode occupies on the filesystem, counting both regular file
 167 * data and filesystem metadata (and eventually attribute data
 168 * when we support attributes)
 169*/
 170
 171befs_blocknr_t
 172befs_count_blocks(struct super_block *sb, const befs_data_stream *ds)
 173{
 174        befs_blocknr_t blocks;
 175        befs_blocknr_t datablocks;      /* File data blocks */
 176        befs_blocknr_t metablocks;      /* FS metadata blocks */
 177        struct befs_sb_info *befs_sb = BEFS_SB(sb);
 178
 179        befs_debug(sb, "---> %s", __func__);
 180
 181        datablocks = ds->size >> befs_sb->block_shift;
 182        if (ds->size & (befs_sb->block_size - 1))
 183                datablocks += 1;
 184
 185        metablocks = 1;         /* Start with 1 block for inode */
 186
 187        /* Size of indirect block */
 188        if (ds->size > ds->max_direct_range)
 189                metablocks += ds->indirect.len;
 190
 191        /*
 192         * Double indir block, plus all the indirect blocks it maps.
 193         * In the double-indirect range, all block runs of data are
 194         * BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know
 195         * how many data block runs are in the double-indirect region,
 196         * and from that we know how many indirect blocks it takes to
 197         * map them. We assume that the indirect blocks are also
 198         * BEFS_DBLINDIR_BRUN_LEN blocks long.
 199         */
 200        if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) {
 201                uint dbl_bytes;
 202                uint dbl_bruns;
 203                uint indirblocks;
 204
 205                dbl_bytes =
 206                    ds->max_double_indirect_range - ds->max_indirect_range;
 207                dbl_bruns =
 208                    dbl_bytes / (befs_sb->block_size * BEFS_DBLINDIR_BRUN_LEN);
 209                indirblocks = dbl_bruns / befs_iaddrs_per_block(sb);
 210
 211                metablocks += ds->double_indirect.len;
 212                metablocks += indirblocks;
 213        }
 214
 215        blocks = datablocks + metablocks;
 216        befs_debug(sb, "<--- %s %u blocks", __func__, (unsigned int)blocks);
 217
 218        return blocks;
 219}
 220
 221/**
 222 * befs_find_brun_direct - find a direct block run in the datastream
 223 * @sb: the superblock
 224 * @data: the datastream
 225 * @blockno: the blocknumber to find
 226 * @run: The found run is passed back through this pointer
 227 *
 228 * Finds the block run that starts at file block number blockno
 229 * in the file represented by the datastream data, if that
 230 * blockno is in the direct region of the datastream.
 231 *
 232 * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
 233 * otherwise.
 234 *
 235 * Algorithm:
 236 * Linear search. Checks each element of array[] to see if it
 237 * contains the blockno-th filesystem block. This is necessary
 238 * because the block runs map variable amounts of data. Simply
 239 * keeps a count of the number of blocks searched so far (sum),
 240 * incrementing this by the length of each block run as we come
 241 * across it. Adds sum to *count before returning (this is so
 242 * you can search multiple arrays that are logicaly one array,
 243 * as in the indirect region code).
 244 *
 245 * When/if blockno is found, if blockno is inside of a block
 246 * run as stored on disk, we offset the start and length members
 247 * of the block run, so that blockno is the start and len is
 248 * still valid (the run ends in the same place).
 249 */
 250static int
 251befs_find_brun_direct(struct super_block *sb, const befs_data_stream *data,
 252                      befs_blocknr_t blockno, befs_block_run *run)
 253{
 254        int i;
 255        const befs_block_run *array = data->direct;
 256        befs_blocknr_t sum;
 257
 258        befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno);
 259
 260        for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS;
 261             sum += array[i].len, i++) {
 262                if (blockno >= sum && blockno < sum + (array[i].len)) {
 263                        int offset = blockno - sum;
 264
 265                        run->allocation_group = array[i].allocation_group;
 266                        run->start = array[i].start + offset;
 267                        run->len = array[i].len - offset;
 268
 269                        befs_debug(sb, "---> %s, "
 270                                   "found %lu at direct[%d]", __func__,
 271                                   (unsigned long)blockno, i);
 272                        return BEFS_OK;
 273                }
 274        }
 275
 276        befs_error(sb, "%s failed to find file block %lu", __func__,
 277                   (unsigned long)blockno);
 278        befs_debug(sb, "---> %s ERROR", __func__);
 279        return BEFS_ERR;
 280}
 281
 282/**
 283 * befs_find_brun_indirect - find a block run in the datastream
 284 * @sb: the superblock
 285 * @data: the datastream
 286 * @blockno: the blocknumber to find
 287 * @run: The found run is passed back through this pointer
 288 *
 289 * Finds the block run that starts at file block number blockno
 290 * in the file represented by the datastream data, if that
 291 * blockno is in the indirect region of the datastream.
 292 *
 293 * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
 294 * otherwise.
 295 *
 296 * Algorithm:
 297 * For each block in the indirect run of the datastream, read
 298 * it in and search through it for search_blk.
 299 *
 300 * XXX:
 301 * Really should check to make sure blockno is inside indirect
 302 * region.
 303 */
 304static int
 305befs_find_brun_indirect(struct super_block *sb,
 306                        const befs_data_stream *data,
 307                        befs_blocknr_t blockno,
 308                        befs_block_run *run)
 309{
 310        int i, j;
 311        befs_blocknr_t sum = 0;
 312        befs_blocknr_t indir_start_blk;
 313        befs_blocknr_t search_blk;
 314        struct buffer_head *indirblock;
 315        befs_disk_block_run *array;
 316
 317        befs_block_run indirect = data->indirect;
 318        befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect);
 319        int arraylen = befs_iaddrs_per_block(sb);
 320
 321        befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno);
 322
 323        indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift;
 324        search_blk = blockno - indir_start_blk;
 325
 326        /* Examine blocks of the indirect run one at a time */
 327        for (i = 0; i < indirect.len; i++) {
 328                indirblock = sb_bread(sb, indirblockno + i);
 329                if (indirblock == NULL) {
 330                        befs_error(sb, "---> %s failed to read "
 331                                   "disk block %lu from the indirect brun",
 332                                   __func__, (unsigned long)indirblockno + i);
 333                        befs_debug(sb, "<--- %s ERROR", __func__);
 334                        return BEFS_ERR;
 335                }
 336
 337                array = (befs_disk_block_run *) indirblock->b_data;
 338
 339                for (j = 0; j < arraylen; ++j) {
 340                        int len = fs16_to_cpu(sb, array[j].len);
 341
 342                        if (search_blk >= sum && search_blk < sum + len) {
 343                                int offset = search_blk - sum;
 344                                run->allocation_group =
 345                                    fs32_to_cpu(sb, array[j].allocation_group);
 346                                run->start =
 347                                    fs16_to_cpu(sb, array[j].start) + offset;
 348                                run->len =
 349                                    fs16_to_cpu(sb, array[j].len) - offset;
 350
 351                                brelse(indirblock);
 352                                befs_debug(sb,
 353                                           "<--- %s found file block "
 354                                           "%lu at indirect[%d]", __func__,
 355                                           (unsigned long)blockno,
 356                                           j + (i * arraylen));
 357                                return BEFS_OK;
 358                        }
 359                        sum += len;
 360                }
 361
 362                brelse(indirblock);
 363        }
 364
 365        /* Only fallthrough is an error */
 366        befs_error(sb, "BeFS: %s failed to find "
 367                   "file block %lu", __func__, (unsigned long)blockno);
 368
 369        befs_debug(sb, "<--- %s ERROR", __func__);
 370        return BEFS_ERR;
 371}
 372
 373/**
 374 * befs_find_brun_dblindirect - find a block run in the datastream
 375 * @sb: the superblock
 376 * @data: the datastream
 377 * @blockno: the blocknumber to find
 378 * @run: The found run is passed back through this pointer
 379 *
 380 * Finds the block run that starts at file block number blockno
 381 * in the file represented by the datastream data, if that
 382 * blockno is in the double-indirect region of the datastream.
 383 *
 384 * Return value is BEFS_OK if the blockrun is found, BEFS_ERR
 385 * otherwise.
 386 *
 387 * Algorithm:
 388 * The block runs in the double-indirect region are different.
 389 * They are always allocated 4 fs blocks at a time, so each
 390 * block run maps a constant amount of file data. This means
 391 * that we can directly calculate how many block runs into the
 392 * double-indirect region we need to go to get to the one that
 393 * maps a particular filesystem block.
 394 *
 395 * We do this in two stages. First we calculate which of the
 396 * inode addresses in the double-indirect block will point us
 397 * to the indirect block that contains the mapping for the data,
 398 * then we calculate which of the inode addresses in that
 399 * indirect block maps the data block we are after.
 400 *
 401 * Oh, and once we've done that, we actually read in the blocks
 402 * that contain the inode addresses we calculated above. Even
 403 * though the double-indirect run may be several blocks long,
 404 * we can calculate which of those blocks will contain the index
 405 * we are after and only read that one. We then follow it to
 406 * the indirect block and perform a similar process to find
 407 * the actual block run that maps the data block we are interested
 408 * in.
 409 *
 410 * Then we offset the run as in befs_find_brun_array() and we are
 411 * done.
 412 */
 413static int
 414befs_find_brun_dblindirect(struct super_block *sb,
 415                           const befs_data_stream *data,
 416                           befs_blocknr_t blockno,
 417                           befs_block_run *run)
 418{
 419        int dblindir_indx;
 420        int indir_indx;
 421        int offset;
 422        int dbl_which_block;
 423        int which_block;
 424        int dbl_block_indx;
 425        int block_indx;
 426        off_t dblindir_leftover;
 427        befs_blocknr_t blockno_at_run_start;
 428        struct buffer_head *dbl_indir_block;
 429        struct buffer_head *indir_block;
 430        befs_block_run indir_run;
 431        befs_disk_inode_addr *iaddr_array;
 432
 433        befs_blocknr_t indir_start_blk =
 434            data->max_indirect_range >> BEFS_SB(sb)->block_shift;
 435
 436        off_t dbl_indir_off = blockno - indir_start_blk;
 437
 438        /* number of data blocks mapped by each of the iaddrs in
 439         * the indirect block pointed to by the double indirect block
 440         */
 441        size_t iblklen = BEFS_DBLINDIR_BRUN_LEN;
 442
 443        /* number of data blocks mapped by each of the iaddrs in
 444         * the double indirect block
 445         */
 446        size_t diblklen = iblklen * befs_iaddrs_per_block(sb)
 447            * BEFS_DBLINDIR_BRUN_LEN;
 448
 449        befs_debug(sb, "---> %s find %lu", __func__, (unsigned long)blockno);
 450
 451        /* First, discover which of the double_indir->indir blocks
 452         * contains pos. Then figure out how much of pos that
 453         * accounted for. Then discover which of the iaddrs in
 454         * the indirect block contains pos.
 455         */
 456
 457        dblindir_indx = dbl_indir_off / diblklen;
 458        dblindir_leftover = dbl_indir_off % diblklen;
 459        indir_indx = dblindir_leftover / diblklen;
 460
 461        /* Read double indirect block */
 462        dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb);
 463        if (dbl_which_block > data->double_indirect.len) {
 464                befs_error(sb, "The double-indirect index calculated by "
 465                           "%s, %d, is outside the range "
 466                           "of the double-indirect block", __func__,
 467                           dblindir_indx);
 468                return BEFS_ERR;
 469        }
 470
 471        dbl_indir_block =
 472            sb_bread(sb, iaddr2blockno(sb, &data->double_indirect) +
 473                                        dbl_which_block);
 474        if (dbl_indir_block == NULL) {
 475                befs_error(sb, "%s couldn't read the "
 476                           "double-indirect block at blockno %lu", __func__,
 477                           (unsigned long)
 478                           iaddr2blockno(sb, &data->double_indirect) +
 479                           dbl_which_block);
 480                return BEFS_ERR;
 481        }
 482
 483        dbl_block_indx =
 484            dblindir_indx - (dbl_which_block * befs_iaddrs_per_block(sb));
 485        iaddr_array = (befs_disk_inode_addr *) dbl_indir_block->b_data;
 486        indir_run = fsrun_to_cpu(sb, iaddr_array[dbl_block_indx]);
 487        brelse(dbl_indir_block);
 488
 489        /* Read indirect block */
 490        which_block = indir_indx / befs_iaddrs_per_block(sb);
 491        if (which_block > indir_run.len) {
 492                befs_error(sb, "The indirect index calculated by "
 493                           "%s, %d, is outside the range "
 494                           "of the indirect block", __func__, indir_indx);
 495                return BEFS_ERR;
 496        }
 497
 498        indir_block =
 499            sb_bread(sb, iaddr2blockno(sb, &indir_run) + which_block);
 500        if (indir_block == NULL) {
 501                befs_error(sb, "%s couldn't read the indirect block "
 502                           "at blockno %lu", __func__, (unsigned long)
 503                           iaddr2blockno(sb, &indir_run) + which_block);
 504                return BEFS_ERR;
 505        }
 506
 507        block_indx = indir_indx - (which_block * befs_iaddrs_per_block(sb));
 508        iaddr_array = (befs_disk_inode_addr *) indir_block->b_data;
 509        *run = fsrun_to_cpu(sb, iaddr_array[block_indx]);
 510        brelse(indir_block);
 511
 512        blockno_at_run_start = indir_start_blk;
 513        blockno_at_run_start += diblklen * dblindir_indx;
 514        blockno_at_run_start += iblklen * indir_indx;
 515        offset = blockno - blockno_at_run_start;
 516
 517        run->start += offset;
 518        run->len -= offset;
 519
 520        befs_debug(sb, "Found file block %lu in double_indirect[%d][%d],"
 521                   " double_indirect_leftover = %lu", (unsigned long)
 522                   blockno, dblindir_indx, indir_indx, dblindir_leftover);
 523
 524        return BEFS_OK;
 525}
 526