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