2 * linux/fs/befs/datastream.c
4 * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com>
6 * Based on portions of file.c by Makoto Kato <m_kato@ga2.so-net.ne.jp>
8 * Many thanks to Dominic Giampaolo, author of "Practical File System
9 * Design with the Be File System", for such a helpful book.
13 #include <linux/kernel.h>
14 #include <linux/buffer_head.h>
15 #include <linux/string.h>
18 #include "datastream.h"
21 const befs_inode_addr BAD_IADDR
= { 0, 0, 0 };
23 static int befs_find_brun_direct(struct super_block
*sb
,
24 befs_data_stream
* data
,
25 befs_blocknr_t blockno
, befs_block_run
* run
);
27 static int befs_find_brun_indirect(struct super_block
*sb
,
28 befs_data_stream
* data
,
29 befs_blocknr_t blockno
,
30 befs_block_run
* run
);
32 static int befs_find_brun_dblindirect(struct super_block
*sb
,
33 befs_data_stream
* data
,
34 befs_blocknr_t blockno
,
35 befs_block_run
* run
);
38 * befs_read_datastream - get buffer_head containing data, starting from pos.
39 * @sb: Filesystem superblock
40 * @ds: datastrem to find data with
42 * @off: offset of data in buffer_head->b_data
44 * Returns pointer to buffer_head containing data starting with offset @off,
45 * if you don't need to know offset just set @off = NULL.
48 befs_read_datastream(struct super_block
*sb
, befs_data_stream
* ds
,
49 befs_off_t pos
, uint
* off
)
51 struct buffer_head
*bh
= NULL
;
53 befs_blocknr_t block
; /* block coresponding to pos */
55 befs_debug(sb
, "---> befs_read_datastream() %Lu", pos
);
56 block
= pos
>> BEFS_SB(sb
)->block_shift
;
58 *off
= pos
- (block
<< BEFS_SB(sb
)->block_shift
);
60 if (befs_fblock2brun(sb
, ds
, block
, &run
) != BEFS_OK
) {
61 befs_error(sb
, "BeFS: Error finding disk addr of block %lu",
63 befs_debug(sb
, "<--- befs_read_datastream() ERROR");
66 bh
= befs_bread_iaddr(sb
, run
);
68 befs_error(sb
, "BeFS: Error reading block %lu from datastream",
73 befs_debug(sb
, "<--- befs_read_datastream() read data, starting at %Lu",
80 * Takes a file position and gives back a brun who's starting block
81 * is block number fblock of the file.
83 * Returns BEFS_OK or BEFS_ERR.
85 * Calls specialized functions for each of the three possible
88 * 2001-11-15 Will Dyson
91 befs_fblock2brun(struct super_block
*sb
, befs_data_stream
* data
,
92 befs_blocknr_t fblock
, befs_block_run
* run
)
95 befs_off_t pos
= fblock
<< BEFS_SB(sb
)->block_shift
;
97 if (pos
< data
->max_direct_range
) {
98 err
= befs_find_brun_direct(sb
, data
, fblock
, run
);
100 } else if (pos
< data
->max_indirect_range
) {
101 err
= befs_find_brun_indirect(sb
, data
, fblock
, run
);
103 } else if (pos
< data
->max_double_indirect_range
) {
104 err
= befs_find_brun_dblindirect(sb
, data
, fblock
, run
);
108 "befs_fblock2brun() was asked to find block %lu, "
109 "which is not mapped by the datastream\n", fblock
);
116 * befs_read_lsmylink - read long symlink from datastream.
117 * @sb: Filesystem superblock
118 * @ds: Datastrem to read from
119 * @buf: Buffer in which to place long symlink data
120 * @len: Length of the long symlink in bytes
122 * Returns the number of bytes read
125 befs_read_lsymlink(struct super_block
* sb
, befs_data_stream
* ds
, void *buff
,
128 befs_off_t bytes_read
= 0; /* bytes readed */
130 struct buffer_head
*bh
= NULL
;
131 befs_debug(sb
, "---> befs_read_lsymlink() length: %Lu", len
);
133 while (bytes_read
< len
) {
134 bh
= befs_read_datastream(sb
, ds
, bytes_read
, NULL
);
136 befs_error(sb
, "BeFS: Error reading datastream block "
137 "starting from %Lu", bytes_read
);
138 befs_debug(sb
, "<--- befs_read_lsymlink() ERROR");
142 plen
= ((bytes_read
+ BEFS_SB(sb
)->block_size
) < len
) ?
143 BEFS_SB(sb
)->block_size
: len
- bytes_read
;
144 memcpy(buff
+ bytes_read
, bh
->b_data
, plen
);
149 befs_debug(sb
, "<--- befs_read_lsymlink() read %u bytes", bytes_read
);
154 * befs_count_blocks - blocks used by a file
155 * @sb: Filesystem superblock
156 * @ds: Datastream of the file
158 * Counts the number of fs blocks that the file represented by
159 * inode occupies on the filesystem, counting both regular file
160 * data and filesystem metadata (and eventually attribute data
161 * when we support attributes)
165 befs_count_blocks(struct super_block
* sb
, befs_data_stream
* ds
)
167 befs_blocknr_t blocks
;
168 befs_blocknr_t datablocks
; /* File data blocks */
169 befs_blocknr_t metablocks
; /* FS metadata blocks */
170 befs_sb_info
*befs_sb
= BEFS_SB(sb
);
172 befs_debug(sb
, "---> befs_count_blocks()");
174 datablocks
= ds
->size
>> befs_sb
->block_shift
;
175 if (ds
->size
& (befs_sb
->block_size
- 1))
178 metablocks
= 1; /* Start with 1 block for inode */
180 /* Size of indirect block */
181 if (ds
->size
> ds
->max_direct_range
)
182 metablocks
+= ds
->indirect
.len
;
185 Double indir block, plus all the indirect blocks it mapps
186 In the double-indirect range, all block runs of data are
187 BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know
188 how many data block runs are in the double-indirect region,
189 and from that we know how many indirect blocks it takes to
190 map them. We assume that the indirect blocks are also
191 BEFS_DBLINDIR_BRUN_LEN blocks long.
193 if (ds
->size
> ds
->max_indirect_range
&& ds
->max_indirect_range
!= 0) {
199 ds
->max_double_indirect_range
- ds
->max_indirect_range
;
201 dbl_bytes
/ (befs_sb
->block_size
* BEFS_DBLINDIR_BRUN_LEN
);
202 indirblocks
= dbl_bruns
/ befs_iaddrs_per_block(sb
);
204 metablocks
+= ds
->double_indirect
.len
;
205 metablocks
+= indirblocks
;
208 blocks
= datablocks
+ metablocks
;
209 befs_debug(sb
, "<--- befs_count_blocks() %u blocks", blocks
);
215 Finds the block run that starts at file block number blockno
216 in the file represented by the datastream data, if that
217 blockno is in the direct region of the datastream.
221 blockno: the blocknumber to find
222 run: The found run is passed back through this pointer
224 Return value is BEFS_OK if the blockrun is found, BEFS_ERR
228 Linear search. Checks each element of array[] to see if it
229 contains the blockno-th filesystem block. This is necessary
230 because the block runs map variable amounts of data. Simply
231 keeps a count of the number of blocks searched so far (sum),
232 incrementing this by the length of each block run as we come
233 across it. Adds sum to *count before returning (this is so
234 you can search multiple arrays that are logicaly one array,
235 as in the indirect region code).
237 When/if blockno is found, if blockno is inside of a block
238 run as stored on disk, we offset the start and length members
239 of the block run, so that blockno is the start and len is
240 still valid (the run ends in the same place).
242 2001-11-15 Will Dyson
245 befs_find_brun_direct(struct super_block
*sb
, befs_data_stream
* data
,
246 befs_blocknr_t blockno
, befs_block_run
* run
)
249 befs_block_run
*array
= data
->direct
;
251 befs_blocknr_t max_block
=
252 data
->max_direct_range
>> BEFS_SB(sb
)->block_shift
;
254 befs_debug(sb
, "---> befs_find_brun_direct(), find %lu", blockno
);
256 if (blockno
> max_block
) {
257 befs_error(sb
, "befs_find_brun_direct() passed block outside of"
262 for (i
= 0, sum
= 0; i
< BEFS_NUM_DIRECT_BLOCKS
;
263 sum
+= array
[i
].len
, i
++) {
264 if (blockno
>= sum
&& blockno
< sum
+ (array
[i
].len
)) {
265 int offset
= blockno
- sum
;
266 run
->allocation_group
= array
[i
].allocation_group
;
267 run
->start
= array
[i
].start
+ offset
;
268 run
->len
= array
[i
].len
- offset
;
270 befs_debug(sb
, "---> befs_find_brun_direct(), "
271 "found %lu at direct[%d]", blockno
, i
);
276 befs_debug(sb
, "---> befs_find_brun_direct() ERROR");
281 Finds the block run that starts at file block number blockno
282 in the file represented by the datastream data, if that
283 blockno is in the indirect region of the datastream.
287 blockno: the blocknumber to find
288 run: The found run is passed back through this pointer
290 Return value is BEFS_OK if the blockrun is found, BEFS_ERR
294 For each block in the indirect run of the datastream, read
295 it in and search through it for search_blk.
298 Really should check to make sure blockno is inside indirect
301 2001-11-15 Will Dyson
304 befs_find_brun_indirect(struct super_block
*sb
,
305 befs_data_stream
* data
, befs_blocknr_t blockno
,
306 befs_block_run
* run
)
309 befs_blocknr_t sum
= 0;
310 befs_blocknr_t indir_start_blk
;
311 befs_blocknr_t search_blk
;
312 struct buffer_head
*indirblock
;
313 befs_disk_block_run
*array
;
315 befs_block_run indirect
= data
->indirect
;
316 befs_blocknr_t indirblockno
= iaddr2blockno(sb
, &indirect
);
317 int arraylen
= befs_iaddrs_per_block(sb
);
319 befs_debug(sb
, "---> befs_find_brun_indirect(), find %lu", blockno
);
321 indir_start_blk
= data
->max_direct_range
>> BEFS_SB(sb
)->block_shift
;
322 search_blk
= blockno
- indir_start_blk
;
324 /* Examine blocks of the indirect run one at a time */
325 for (i
= 0; i
< indirect
.len
; i
++) {
326 indirblock
= befs_bread(sb
, indirblockno
+ i
);
327 if (indirblock
== NULL
) {
329 "---> befs_find_brun_indirect() failed to "
330 "read disk block %lu from the indirect brun",
335 array
= (befs_disk_block_run
*) indirblock
->b_data
;
337 for (j
= 0; j
< arraylen
; ++j
) {
338 int len
= fs16_to_cpu(sb
, array
[j
].len
);
340 if (search_blk
>= sum
&& search_blk
< sum
+ len
) {
341 int offset
= search_blk
- sum
;
342 run
->allocation_group
=
343 fs32_to_cpu(sb
, array
[j
].allocation_group
);
345 fs16_to_cpu(sb
, array
[j
].start
) + offset
;
347 fs16_to_cpu(sb
, array
[j
].len
) - offset
;
351 "<--- befs_find_brun_indirect() found "
352 "file block %lu at indirect[%d]",
353 blockno
, j
+ (i
* arraylen
));
362 /* Only fallthrough is an error */
363 befs_error(sb
, "BeFS: befs_find_brun_indirect() failed to find "
364 "file block %lu", blockno
);
366 befs_debug(sb
, "<--- befs_find_brun_indirect() ERROR");
371 Finds the block run that starts at file block number blockno
372 in the file represented by the datastream data, if that
373 blockno is in the double-indirect region of the datastream.
377 blockno: the blocknumber to find
378 run: The found run is passed back through this pointer
380 Return value is BEFS_OK if the blockrun is found, BEFS_ERR
384 The block runs in the double-indirect region are different.
385 They are always allocated 4 fs blocks at a time, so each
386 block run maps a constant amount of file data. This means
387 that we can directly calculate how many block runs into the
388 double-indirect region we need to go to get to the one that
389 maps a particular filesystem block.
391 We do this in two stages. First we calculate which of the
392 inode addresses in the double-indirect block will point us
393 to the indirect block that contains the mapping for the data,
394 then we calculate which of the inode addresses in that
395 indirect block maps the data block we are after.
397 Oh, and once we've done that, we actually read in the blocks
398 that contain the inode addresses we calculated above. Even
399 though the double-indirect run may be several blocks long,
400 we can calculate which of those blocks will contain the index
401 we are after and only read that one. We then follow it to
402 the indirect block and perform a similar process to find
403 the actual block run that maps the data block we are interested
406 Then we offset the run as in befs_find_brun_array() and we are
409 2001-11-15 Will Dyson
412 befs_find_brun_dblindirect(struct super_block
*sb
,
413 befs_data_stream
* data
, befs_blocknr_t blockno
,
414 befs_block_run
* run
)
423 off_t dblindir_leftover
;
424 befs_blocknr_t blockno_at_run_start
;
425 struct buffer_head
*dbl_indir_block
;
426 struct buffer_head
*indir_block
;
427 befs_block_run indir_run
;
428 befs_disk_inode_addr
*iaddr_array
= NULL
;
429 befs_sb_info
*befs_sb
= BEFS_SB(sb
);
431 befs_blocknr_t indir_start_blk
=
432 data
->max_indirect_range
>> befs_sb
->block_shift
;
434 off_t dbl_indir_off
= blockno
- indir_start_blk
;
436 /* number of data blocks mapped by each of the iaddrs in
437 * the indirect block pointed to by the double indirect block
439 size_t iblklen
= BEFS_DBLINDIR_BRUN_LEN
;
441 /* number of data blocks mapped by each of the iaddrs in
442 * the double indirect block
444 size_t diblklen
= iblklen
* befs_iaddrs_per_block(sb
)
445 * BEFS_DBLINDIR_BRUN_LEN
;
447 befs_debug(sb
, "---> befs_find_brun_dblindirect() find %lu", blockno
);
449 /* First, discover which of the double_indir->indir blocks
450 * contains pos. Then figure out how much of pos that
451 * accounted for. Then discover which of the iaddrs in
452 * the indirect block contains pos.
455 dblindir_indx
= dbl_indir_off
/ diblklen
;
456 dblindir_leftover
= dbl_indir_off
% diblklen
;
457 indir_indx
= dblindir_leftover
/ diblklen
;
459 /* Read double indirect block */
460 dbl_which_block
= dblindir_indx
/ befs_iaddrs_per_block(sb
);
461 if (dbl_which_block
> data
->double_indirect
.len
) {
462 befs_error(sb
, "The double-indirect index calculated by "
463 "befs_read_brun_dblindirect(), %d, is outside the range "
464 "of the double-indirect block", dblindir_indx
);
469 befs_bread(sb
, iaddr2blockno(sb
, &data
->double_indirect
) +
471 if (dbl_indir_block
== NULL
) {
472 befs_error(sb
, "befs_read_brun_dblindirect() couldn't read the "
473 "double-indirect block at blockno %lu",
475 &data
->double_indirect
) +
477 brelse(dbl_indir_block
);
482 dblindir_indx
- (dbl_which_block
* befs_iaddrs_per_block(sb
));
483 iaddr_array
= (befs_disk_inode_addr
*) dbl_indir_block
->b_data
;
484 indir_run
= fsrun_to_cpu(sb
, iaddr_array
[dbl_block_indx
]);
485 brelse(dbl_indir_block
);
488 /* Read indirect block */
489 which_block
= indir_indx
/ befs_iaddrs_per_block(sb
);
490 if (which_block
> indir_run
.len
) {
491 befs_error(sb
, "The indirect index calculated by "
492 "befs_read_brun_dblindirect(), %d, is outside the range "
493 "of the indirect block", indir_indx
);
498 befs_bread(sb
, iaddr2blockno(sb
, &indir_run
) + which_block
);
499 if (indir_block
== NULL
) {
500 befs_error(sb
, "befs_read_brun_dblindirect() couldn't read the "
501 "indirect block at blockno %lu",
502 iaddr2blockno(sb
, &indir_run
) + which_block
);
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
]);
513 blockno_at_run_start
= indir_start_blk
;
514 blockno_at_run_start
+= diblklen
* dblindir_indx
;
515 blockno_at_run_start
+= iblklen
* indir_indx
;
516 offset
= blockno
- blockno_at_run_start
;
518 run
->start
+= offset
;
521 befs_debug(sb
, "Found file block %lu in double_indirect[%d][%d],"
522 " double_indirect_leftover = %lu",
523 blockno
, dblindir_indx
, indir_indx
, dblindir_leftover
);