18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/befs/datastream.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on portions of file.c by Makoto Kato <m_kato@ga2.so-net.ne.jp> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Many thanks to Dominic Giampaolo, author of "Practical File System 108c2ecf20Sopenharmony_ci * Design with the Be File System", for such a helpful book. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "befs.h" 198c2ecf20Sopenharmony_ci#include "datastream.h" 208c2ecf20Sopenharmony_ci#include "io.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciconst befs_inode_addr BAD_IADDR = { 0, 0, 0 }; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int befs_find_brun_direct(struct super_block *sb, 258c2ecf20Sopenharmony_ci const befs_data_stream *data, 268c2ecf20Sopenharmony_ci befs_blocknr_t blockno, befs_block_run *run); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int befs_find_brun_indirect(struct super_block *sb, 298c2ecf20Sopenharmony_ci const befs_data_stream *data, 308c2ecf20Sopenharmony_ci befs_blocknr_t blockno, 318c2ecf20Sopenharmony_ci befs_block_run *run); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int befs_find_brun_dblindirect(struct super_block *sb, 348c2ecf20Sopenharmony_ci const befs_data_stream *data, 358c2ecf20Sopenharmony_ci befs_blocknr_t blockno, 368c2ecf20Sopenharmony_ci befs_block_run *run); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * befs_read_datastream - get buffer_head containing data, starting from pos. 408c2ecf20Sopenharmony_ci * @sb: Filesystem superblock 418c2ecf20Sopenharmony_ci * @ds: datastream to find data with 428c2ecf20Sopenharmony_ci * @pos: start of data 438c2ecf20Sopenharmony_ci * @off: offset of data in buffer_head->b_data 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Returns pointer to buffer_head containing data starting with offset @off, 468c2ecf20Sopenharmony_ci * if you don't need to know offset just set @off = NULL. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistruct buffer_head * 498c2ecf20Sopenharmony_cibefs_read_datastream(struct super_block *sb, const befs_data_stream *ds, 508c2ecf20Sopenharmony_ci befs_off_t pos, uint *off) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct buffer_head *bh; 538c2ecf20Sopenharmony_ci befs_block_run run; 548c2ecf20Sopenharmony_ci befs_blocknr_t block; /* block coresponding to pos */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s %llu", __func__, pos); 578c2ecf20Sopenharmony_ci block = pos >> BEFS_SB(sb)->block_shift; 588c2ecf20Sopenharmony_ci if (off) 598c2ecf20Sopenharmony_ci *off = pos - (block << BEFS_SB(sb)->block_shift); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) { 628c2ecf20Sopenharmony_ci befs_error(sb, "BeFS: Error finding disk addr of block %lu", 638c2ecf20Sopenharmony_ci (unsigned long)block); 648c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s ERROR", __func__); 658c2ecf20Sopenharmony_ci return NULL; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci bh = befs_bread_iaddr(sb, run); 688c2ecf20Sopenharmony_ci if (!bh) { 698c2ecf20Sopenharmony_ci befs_error(sb, "BeFS: Error reading block %lu from datastream", 708c2ecf20Sopenharmony_ci (unsigned long)block); 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s read data, starting at %llu", __func__, pos); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return bh; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * befs_fblock2brun - give back block run for fblock 818c2ecf20Sopenharmony_ci * @sb: the superblock 828c2ecf20Sopenharmony_ci * @data: datastream to read from 838c2ecf20Sopenharmony_ci * @fblock: the blocknumber with the file position to find 848c2ecf20Sopenharmony_ci * @run: The found run is passed back through this pointer 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Takes a file position and gives back a brun who's starting block 878c2ecf20Sopenharmony_ci * is block number fblock of the file. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * Returns BEFS_OK or BEFS_ERR. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Calls specialized functions for each of the three possible 928c2ecf20Sopenharmony_ci * datastream regions. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ciint 958c2ecf20Sopenharmony_cibefs_fblock2brun(struct super_block *sb, const befs_data_stream *data, 968c2ecf20Sopenharmony_ci befs_blocknr_t fblock, befs_block_run *run) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int err; 998c2ecf20Sopenharmony_ci befs_off_t pos = fblock << BEFS_SB(sb)->block_shift; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (pos < data->max_direct_range) { 1028c2ecf20Sopenharmony_ci err = befs_find_brun_direct(sb, data, fblock, run); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci } else if (pos < data->max_indirect_range) { 1058c2ecf20Sopenharmony_ci err = befs_find_brun_indirect(sb, data, fblock, run); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci } else if (pos < data->max_double_indirect_range) { 1088c2ecf20Sopenharmony_ci err = befs_find_brun_dblindirect(sb, data, fblock, run); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci } else { 1118c2ecf20Sopenharmony_ci befs_error(sb, 1128c2ecf20Sopenharmony_ci "befs_fblock2brun() was asked to find block %lu, " 1138c2ecf20Sopenharmony_ci "which is not mapped by the datastream\n", 1148c2ecf20Sopenharmony_ci (unsigned long)fblock); 1158c2ecf20Sopenharmony_ci err = BEFS_ERR; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return err; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * befs_read_lsmylink - read long symlink from datastream. 1228c2ecf20Sopenharmony_ci * @sb: Filesystem superblock 1238c2ecf20Sopenharmony_ci * @ds: Datastream to read from 1248c2ecf20Sopenharmony_ci * @buff: Buffer in which to place long symlink data 1258c2ecf20Sopenharmony_ci * @len: Length of the long symlink in bytes 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * Returns the number of bytes read 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cisize_t 1308c2ecf20Sopenharmony_cibefs_read_lsymlink(struct super_block *sb, const befs_data_stream *ds, 1318c2ecf20Sopenharmony_ci void *buff, befs_off_t len) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci befs_off_t bytes_read = 0; /* bytes readed */ 1348c2ecf20Sopenharmony_ci u16 plen; 1358c2ecf20Sopenharmony_ci struct buffer_head *bh; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s length: %llu", __func__, len); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci while (bytes_read < len) { 1408c2ecf20Sopenharmony_ci bh = befs_read_datastream(sb, ds, bytes_read, NULL); 1418c2ecf20Sopenharmony_ci if (!bh) { 1428c2ecf20Sopenharmony_ci befs_error(sb, "BeFS: Error reading datastream block " 1438c2ecf20Sopenharmony_ci "starting from %llu", bytes_read); 1448c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s ERROR", __func__); 1458c2ecf20Sopenharmony_ci return bytes_read; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci plen = ((bytes_read + BEFS_SB(sb)->block_size) < len) ? 1498c2ecf20Sopenharmony_ci BEFS_SB(sb)->block_size : len - bytes_read; 1508c2ecf20Sopenharmony_ci memcpy(buff + bytes_read, bh->b_data, plen); 1518c2ecf20Sopenharmony_ci brelse(bh); 1528c2ecf20Sopenharmony_ci bytes_read += plen; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s read %u bytes", __func__, (unsigned int) 1568c2ecf20Sopenharmony_ci bytes_read); 1578c2ecf20Sopenharmony_ci return bytes_read; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * befs_count_blocks - blocks used by a file 1628c2ecf20Sopenharmony_ci * @sb: Filesystem superblock 1638c2ecf20Sopenharmony_ci * @ds: Datastream of the file 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Counts the number of fs blocks that the file represented by 1668c2ecf20Sopenharmony_ci * inode occupies on the filesystem, counting both regular file 1678c2ecf20Sopenharmony_ci * data and filesystem metadata (and eventually attribute data 1688c2ecf20Sopenharmony_ci * when we support attributes) 1698c2ecf20Sopenharmony_ci*/ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cibefs_blocknr_t 1728c2ecf20Sopenharmony_cibefs_count_blocks(struct super_block *sb, const befs_data_stream *ds) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci befs_blocknr_t blocks; 1758c2ecf20Sopenharmony_ci befs_blocknr_t datablocks; /* File data blocks */ 1768c2ecf20Sopenharmony_ci befs_blocknr_t metablocks; /* FS metadata blocks */ 1778c2ecf20Sopenharmony_ci struct befs_sb_info *befs_sb = BEFS_SB(sb); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s", __func__); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci datablocks = ds->size >> befs_sb->block_shift; 1828c2ecf20Sopenharmony_ci if (ds->size & (befs_sb->block_size - 1)) 1838c2ecf20Sopenharmony_ci datablocks += 1; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci metablocks = 1; /* Start with 1 block for inode */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* Size of indirect block */ 1888c2ecf20Sopenharmony_ci if (ds->size > ds->max_direct_range) 1898c2ecf20Sopenharmony_ci metablocks += ds->indirect.len; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * Double indir block, plus all the indirect blocks it maps. 1938c2ecf20Sopenharmony_ci * In the double-indirect range, all block runs of data are 1948c2ecf20Sopenharmony_ci * BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know 1958c2ecf20Sopenharmony_ci * how many data block runs are in the double-indirect region, 1968c2ecf20Sopenharmony_ci * and from that we know how many indirect blocks it takes to 1978c2ecf20Sopenharmony_ci * map them. We assume that the indirect blocks are also 1988c2ecf20Sopenharmony_ci * BEFS_DBLINDIR_BRUN_LEN blocks long. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) { 2018c2ecf20Sopenharmony_ci uint dbl_bytes; 2028c2ecf20Sopenharmony_ci uint dbl_bruns; 2038c2ecf20Sopenharmony_ci uint indirblocks; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dbl_bytes = 2068c2ecf20Sopenharmony_ci ds->max_double_indirect_range - ds->max_indirect_range; 2078c2ecf20Sopenharmony_ci dbl_bruns = 2088c2ecf20Sopenharmony_ci dbl_bytes / (befs_sb->block_size * BEFS_DBLINDIR_BRUN_LEN); 2098c2ecf20Sopenharmony_ci indirblocks = dbl_bruns / befs_iaddrs_per_block(sb); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci metablocks += ds->double_indirect.len; 2128c2ecf20Sopenharmony_ci metablocks += indirblocks; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci blocks = datablocks + metablocks; 2168c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s %u blocks", __func__, (unsigned int)blocks); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return blocks; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * befs_find_brun_direct - find a direct block run in the datastream 2238c2ecf20Sopenharmony_ci * @sb: the superblock 2248c2ecf20Sopenharmony_ci * @data: the datastream 2258c2ecf20Sopenharmony_ci * @blockno: the blocknumber to find 2268c2ecf20Sopenharmony_ci * @run: The found run is passed back through this pointer 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * Finds the block run that starts at file block number blockno 2298c2ecf20Sopenharmony_ci * in the file represented by the datastream data, if that 2308c2ecf20Sopenharmony_ci * blockno is in the direct region of the datastream. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * Return value is BEFS_OK if the blockrun is found, BEFS_ERR 2338c2ecf20Sopenharmony_ci * otherwise. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Algorithm: 2368c2ecf20Sopenharmony_ci * Linear search. Checks each element of array[] to see if it 2378c2ecf20Sopenharmony_ci * contains the blockno-th filesystem block. This is necessary 2388c2ecf20Sopenharmony_ci * because the block runs map variable amounts of data. Simply 2398c2ecf20Sopenharmony_ci * keeps a count of the number of blocks searched so far (sum), 2408c2ecf20Sopenharmony_ci * incrementing this by the length of each block run as we come 2418c2ecf20Sopenharmony_ci * across it. Adds sum to *count before returning (this is so 2428c2ecf20Sopenharmony_ci * you can search multiple arrays that are logicaly one array, 2438c2ecf20Sopenharmony_ci * as in the indirect region code). 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * When/if blockno is found, if blockno is inside of a block 2468c2ecf20Sopenharmony_ci * run as stored on disk, we offset the start and length members 2478c2ecf20Sopenharmony_ci * of the block run, so that blockno is the start and len is 2488c2ecf20Sopenharmony_ci * still valid (the run ends in the same place). 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int 2518c2ecf20Sopenharmony_cibefs_find_brun_direct(struct super_block *sb, const befs_data_stream *data, 2528c2ecf20Sopenharmony_ci befs_blocknr_t blockno, befs_block_run *run) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int i; 2558c2ecf20Sopenharmony_ci const befs_block_run *array = data->direct; 2568c2ecf20Sopenharmony_ci befs_blocknr_t sum; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS; 2618c2ecf20Sopenharmony_ci sum += array[i].len, i++) { 2628c2ecf20Sopenharmony_ci if (blockno >= sum && blockno < sum + (array[i].len)) { 2638c2ecf20Sopenharmony_ci int offset = blockno - sum; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci run->allocation_group = array[i].allocation_group; 2668c2ecf20Sopenharmony_ci run->start = array[i].start + offset; 2678c2ecf20Sopenharmony_ci run->len = array[i].len - offset; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s, " 2708c2ecf20Sopenharmony_ci "found %lu at direct[%d]", __func__, 2718c2ecf20Sopenharmony_ci (unsigned long)blockno, i); 2728c2ecf20Sopenharmony_ci return BEFS_OK; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci befs_error(sb, "%s failed to find file block %lu", __func__, 2778c2ecf20Sopenharmony_ci (unsigned long)blockno); 2788c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s ERROR", __func__); 2798c2ecf20Sopenharmony_ci return BEFS_ERR; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/** 2838c2ecf20Sopenharmony_ci * befs_find_brun_indirect - find a block run in the datastream 2848c2ecf20Sopenharmony_ci * @sb: the superblock 2858c2ecf20Sopenharmony_ci * @data: the datastream 2868c2ecf20Sopenharmony_ci * @blockno: the blocknumber to find 2878c2ecf20Sopenharmony_ci * @run: The found run is passed back through this pointer 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * Finds the block run that starts at file block number blockno 2908c2ecf20Sopenharmony_ci * in the file represented by the datastream data, if that 2918c2ecf20Sopenharmony_ci * blockno is in the indirect region of the datastream. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Return value is BEFS_OK if the blockrun is found, BEFS_ERR 2948c2ecf20Sopenharmony_ci * otherwise. 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * Algorithm: 2978c2ecf20Sopenharmony_ci * For each block in the indirect run of the datastream, read 2988c2ecf20Sopenharmony_ci * it in and search through it for search_blk. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * XXX: 3018c2ecf20Sopenharmony_ci * Really should check to make sure blockno is inside indirect 3028c2ecf20Sopenharmony_ci * region. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic int 3058c2ecf20Sopenharmony_cibefs_find_brun_indirect(struct super_block *sb, 3068c2ecf20Sopenharmony_ci const befs_data_stream *data, 3078c2ecf20Sopenharmony_ci befs_blocknr_t blockno, 3088c2ecf20Sopenharmony_ci befs_block_run *run) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int i, j; 3118c2ecf20Sopenharmony_ci befs_blocknr_t sum = 0; 3128c2ecf20Sopenharmony_ci befs_blocknr_t indir_start_blk; 3138c2ecf20Sopenharmony_ci befs_blocknr_t search_blk; 3148c2ecf20Sopenharmony_ci struct buffer_head *indirblock; 3158c2ecf20Sopenharmony_ci befs_disk_block_run *array; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci befs_block_run indirect = data->indirect; 3188c2ecf20Sopenharmony_ci befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect); 3198c2ecf20Sopenharmony_ci int arraylen = befs_iaddrs_per_block(sb); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s, find %lu", __func__, (unsigned long)blockno); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift; 3248c2ecf20Sopenharmony_ci search_blk = blockno - indir_start_blk; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Examine blocks of the indirect run one at a time */ 3278c2ecf20Sopenharmony_ci for (i = 0; i < indirect.len; i++) { 3288c2ecf20Sopenharmony_ci indirblock = sb_bread(sb, indirblockno + i); 3298c2ecf20Sopenharmony_ci if (indirblock == NULL) { 3308c2ecf20Sopenharmony_ci befs_error(sb, "---> %s failed to read " 3318c2ecf20Sopenharmony_ci "disk block %lu from the indirect brun", 3328c2ecf20Sopenharmony_ci __func__, (unsigned long)indirblockno + i); 3338c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s ERROR", __func__); 3348c2ecf20Sopenharmony_ci return BEFS_ERR; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci array = (befs_disk_block_run *) indirblock->b_data; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (j = 0; j < arraylen; ++j) { 3408c2ecf20Sopenharmony_ci int len = fs16_to_cpu(sb, array[j].len); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (search_blk >= sum && search_blk < sum + len) { 3438c2ecf20Sopenharmony_ci int offset = search_blk - sum; 3448c2ecf20Sopenharmony_ci run->allocation_group = 3458c2ecf20Sopenharmony_ci fs32_to_cpu(sb, array[j].allocation_group); 3468c2ecf20Sopenharmony_ci run->start = 3478c2ecf20Sopenharmony_ci fs16_to_cpu(sb, array[j].start) + offset; 3488c2ecf20Sopenharmony_ci run->len = 3498c2ecf20Sopenharmony_ci fs16_to_cpu(sb, array[j].len) - offset; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci brelse(indirblock); 3528c2ecf20Sopenharmony_ci befs_debug(sb, 3538c2ecf20Sopenharmony_ci "<--- %s found file block " 3548c2ecf20Sopenharmony_ci "%lu at indirect[%d]", __func__, 3558c2ecf20Sopenharmony_ci (unsigned long)blockno, 3568c2ecf20Sopenharmony_ci j + (i * arraylen)); 3578c2ecf20Sopenharmony_ci return BEFS_OK; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci sum += len; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci brelse(indirblock); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* Only fallthrough is an error */ 3668c2ecf20Sopenharmony_ci befs_error(sb, "BeFS: %s failed to find " 3678c2ecf20Sopenharmony_ci "file block %lu", __func__, (unsigned long)blockno); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci befs_debug(sb, "<--- %s ERROR", __func__); 3708c2ecf20Sopenharmony_ci return BEFS_ERR; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/** 3748c2ecf20Sopenharmony_ci * befs_find_brun_dblindirect - find a block run in the datastream 3758c2ecf20Sopenharmony_ci * @sb: the superblock 3768c2ecf20Sopenharmony_ci * @data: the datastream 3778c2ecf20Sopenharmony_ci * @blockno: the blocknumber to find 3788c2ecf20Sopenharmony_ci * @run: The found run is passed back through this pointer 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * Finds the block run that starts at file block number blockno 3818c2ecf20Sopenharmony_ci * in the file represented by the datastream data, if that 3828c2ecf20Sopenharmony_ci * blockno is in the double-indirect region of the datastream. 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Return value is BEFS_OK if the blockrun is found, BEFS_ERR 3858c2ecf20Sopenharmony_ci * otherwise. 3868c2ecf20Sopenharmony_ci * 3878c2ecf20Sopenharmony_ci * Algorithm: 3888c2ecf20Sopenharmony_ci * The block runs in the double-indirect region are different. 3898c2ecf20Sopenharmony_ci * They are always allocated 4 fs blocks at a time, so each 3908c2ecf20Sopenharmony_ci * block run maps a constant amount of file data. This means 3918c2ecf20Sopenharmony_ci * that we can directly calculate how many block runs into the 3928c2ecf20Sopenharmony_ci * double-indirect region we need to go to get to the one that 3938c2ecf20Sopenharmony_ci * maps a particular filesystem block. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * We do this in two stages. First we calculate which of the 3968c2ecf20Sopenharmony_ci * inode addresses in the double-indirect block will point us 3978c2ecf20Sopenharmony_ci * to the indirect block that contains the mapping for the data, 3988c2ecf20Sopenharmony_ci * then we calculate which of the inode addresses in that 3998c2ecf20Sopenharmony_ci * indirect block maps the data block we are after. 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Oh, and once we've done that, we actually read in the blocks 4028c2ecf20Sopenharmony_ci * that contain the inode addresses we calculated above. Even 4038c2ecf20Sopenharmony_ci * though the double-indirect run may be several blocks long, 4048c2ecf20Sopenharmony_ci * we can calculate which of those blocks will contain the index 4058c2ecf20Sopenharmony_ci * we are after and only read that one. We then follow it to 4068c2ecf20Sopenharmony_ci * the indirect block and perform a similar process to find 4078c2ecf20Sopenharmony_ci * the actual block run that maps the data block we are interested 4088c2ecf20Sopenharmony_ci * in. 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Then we offset the run as in befs_find_brun_array() and we are 4118c2ecf20Sopenharmony_ci * done. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_cistatic int 4148c2ecf20Sopenharmony_cibefs_find_brun_dblindirect(struct super_block *sb, 4158c2ecf20Sopenharmony_ci const befs_data_stream *data, 4168c2ecf20Sopenharmony_ci befs_blocknr_t blockno, 4178c2ecf20Sopenharmony_ci befs_block_run *run) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int dblindir_indx; 4208c2ecf20Sopenharmony_ci int indir_indx; 4218c2ecf20Sopenharmony_ci int offset; 4228c2ecf20Sopenharmony_ci int dbl_which_block; 4238c2ecf20Sopenharmony_ci int which_block; 4248c2ecf20Sopenharmony_ci int dbl_block_indx; 4258c2ecf20Sopenharmony_ci int block_indx; 4268c2ecf20Sopenharmony_ci off_t dblindir_leftover; 4278c2ecf20Sopenharmony_ci befs_blocknr_t blockno_at_run_start; 4288c2ecf20Sopenharmony_ci struct buffer_head *dbl_indir_block; 4298c2ecf20Sopenharmony_ci struct buffer_head *indir_block; 4308c2ecf20Sopenharmony_ci befs_block_run indir_run; 4318c2ecf20Sopenharmony_ci befs_disk_inode_addr *iaddr_array; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci befs_blocknr_t indir_start_blk = 4348c2ecf20Sopenharmony_ci data->max_indirect_range >> BEFS_SB(sb)->block_shift; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci off_t dbl_indir_off = blockno - indir_start_blk; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* number of data blocks mapped by each of the iaddrs in 4398c2ecf20Sopenharmony_ci * the indirect block pointed to by the double indirect block 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci size_t iblklen = BEFS_DBLINDIR_BRUN_LEN; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* number of data blocks mapped by each of the iaddrs in 4448c2ecf20Sopenharmony_ci * the double indirect block 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci size_t diblklen = iblklen * befs_iaddrs_per_block(sb) 4478c2ecf20Sopenharmony_ci * BEFS_DBLINDIR_BRUN_LEN; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci befs_debug(sb, "---> %s find %lu", __func__, (unsigned long)blockno); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* First, discover which of the double_indir->indir blocks 4528c2ecf20Sopenharmony_ci * contains pos. Then figure out how much of pos that 4538c2ecf20Sopenharmony_ci * accounted for. Then discover which of the iaddrs in 4548c2ecf20Sopenharmony_ci * the indirect block contains pos. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci dblindir_indx = dbl_indir_off / diblklen; 4588c2ecf20Sopenharmony_ci dblindir_leftover = dbl_indir_off % diblklen; 4598c2ecf20Sopenharmony_ci indir_indx = dblindir_leftover / diblklen; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* Read double indirect block */ 4628c2ecf20Sopenharmony_ci dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb); 4638c2ecf20Sopenharmony_ci if (dbl_which_block > data->double_indirect.len) { 4648c2ecf20Sopenharmony_ci befs_error(sb, "The double-indirect index calculated by " 4658c2ecf20Sopenharmony_ci "%s, %d, is outside the range " 4668c2ecf20Sopenharmony_ci "of the double-indirect block", __func__, 4678c2ecf20Sopenharmony_ci dblindir_indx); 4688c2ecf20Sopenharmony_ci return BEFS_ERR; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci dbl_indir_block = 4728c2ecf20Sopenharmony_ci sb_bread(sb, iaddr2blockno(sb, &data->double_indirect) + 4738c2ecf20Sopenharmony_ci dbl_which_block); 4748c2ecf20Sopenharmony_ci if (dbl_indir_block == NULL) { 4758c2ecf20Sopenharmony_ci befs_error(sb, "%s couldn't read the " 4768c2ecf20Sopenharmony_ci "double-indirect block at blockno %lu", __func__, 4778c2ecf20Sopenharmony_ci (unsigned long) 4788c2ecf20Sopenharmony_ci iaddr2blockno(sb, &data->double_indirect) + 4798c2ecf20Sopenharmony_ci dbl_which_block); 4808c2ecf20Sopenharmony_ci return BEFS_ERR; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dbl_block_indx = 4848c2ecf20Sopenharmony_ci dblindir_indx - (dbl_which_block * befs_iaddrs_per_block(sb)); 4858c2ecf20Sopenharmony_ci iaddr_array = (befs_disk_inode_addr *) dbl_indir_block->b_data; 4868c2ecf20Sopenharmony_ci indir_run = fsrun_to_cpu(sb, iaddr_array[dbl_block_indx]); 4878c2ecf20Sopenharmony_ci brelse(dbl_indir_block); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Read indirect block */ 4908c2ecf20Sopenharmony_ci which_block = indir_indx / befs_iaddrs_per_block(sb); 4918c2ecf20Sopenharmony_ci if (which_block > indir_run.len) { 4928c2ecf20Sopenharmony_ci befs_error(sb, "The indirect index calculated by " 4938c2ecf20Sopenharmony_ci "%s, %d, is outside the range " 4948c2ecf20Sopenharmony_ci "of the indirect block", __func__, indir_indx); 4958c2ecf20Sopenharmony_ci return BEFS_ERR; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci indir_block = 4998c2ecf20Sopenharmony_ci sb_bread(sb, iaddr2blockno(sb, &indir_run) + which_block); 5008c2ecf20Sopenharmony_ci if (indir_block == NULL) { 5018c2ecf20Sopenharmony_ci befs_error(sb, "%s couldn't read the indirect block " 5028c2ecf20Sopenharmony_ci "at blockno %lu", __func__, (unsigned long) 5038c2ecf20Sopenharmony_ci iaddr2blockno(sb, &indir_run) + which_block); 5048c2ecf20Sopenharmony_ci return BEFS_ERR; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci block_indx = indir_indx - (which_block * befs_iaddrs_per_block(sb)); 5088c2ecf20Sopenharmony_ci iaddr_array = (befs_disk_inode_addr *) indir_block->b_data; 5098c2ecf20Sopenharmony_ci *run = fsrun_to_cpu(sb, iaddr_array[block_indx]); 5108c2ecf20Sopenharmony_ci brelse(indir_block); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci blockno_at_run_start = indir_start_blk; 5138c2ecf20Sopenharmony_ci blockno_at_run_start += diblklen * dblindir_indx; 5148c2ecf20Sopenharmony_ci blockno_at_run_start += iblklen * indir_indx; 5158c2ecf20Sopenharmony_ci offset = blockno - blockno_at_run_start; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci run->start += offset; 5188c2ecf20Sopenharmony_ci run->len -= offset; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci befs_debug(sb, "Found file block %lu in double_indirect[%d][%d]," 5218c2ecf20Sopenharmony_ci " double_indirect_leftover = %lu", (unsigned long) 5228c2ecf20Sopenharmony_ci blockno, dblindir_indx, indir_indx, dblindir_leftover); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return BEFS_OK; 5258c2ecf20Sopenharmony_ci} 526