📄 fsck.c
字号:
if (a > 0x8000)
a = -a;
tw(tr(TR_FUNC, TrFsckLow, "age_distance(%d, %d) %d\n", x, y, a));
return a;
}
// For each ffs block, we initialise the basic bstat array information,
// namely the number of used bytes. Also, we locate the inodes block and if
// a previous operation was interrupted by a powerfail, we clean it up.
//
// We return EFFS_OK if all is fine. If a positive integer is returned, it
// denotes a block that needs to be cleaned by block_clean() once FFS
// has been properly intialized (we actually return the block number + 1
// because otherwise it would clash with EFFS_OK return code). If no inodes
// block is found or another error occurs, we return the error code.
bref_t blocks_fsck(void)
{
bref_t b, b_to_clean, b_inode_lost;
int age_valid;
age_t age_min, age_max, age_dist, age_dist_min, age_dist_max;
struct block_header_s *bhp;
struct block_header_old_s *obhp;
ttw(str(TTrInitLow, "blocks_fsck {" NL));
tw(tr(TR_BEGIN, TrFsck, "blocks_fsck() {\n"));
// initialize ages to the illegal/unset value
age_min = age_max = age_dist = 0;
fs.format = 0;
fs.inodes = -1;
fs.newinodes = -1;
b_inode_lost = -1;
b_to_clean = EFFS_OK;
for (b = 0; b < dev.numblocks; b++)
{
tlw(led_toggle(LED_DRV_INIT));
// read block flags from flash
bhp = (struct block_header_s *) offset2addr(dev.binfo[b].offset);
obhp = (struct block_header_old_s *) bhp;
bstat[b].used = dev.blocksize;
bstat[b].lost = bstat[b].used;
bstat[b].flags = bhp->flags;
bstat[b].objects = 0;
age_valid = 0;
if (bhp->magic_low != BLOCK_MAGIC_LOW ||
bhp->magic_high != BLOCK_MAGIC_HIGH) {
// The block magic as bad! It *could* be because the flash
// memory map is incorrect or because another application has
// spuriously written to the flash or ... who knows what. First
// we check to see if the reason is that we are dealing with a
// (really) old ffs format version.
if (obhp->magic_low == OLD_BLOCK_MAGIC_LOW &&
obhp->magic_high == OLD_FFS_FORMAT_VERSION) {
tw(tr(TR_FUNC, TrFsck, "OLD "));
fs.format = obhp->magic_high;
// We simulate that all the blocks are data blocks, in order
// to have some well-defined state that preformat() can work
// on. Later we will return EFFS_BADFORMAT and otherwise
// leave everything as it is, *without* modifying anything!
bstat[b].flags = BF_IS_DATA;
}
else {
// Quickly test if block is in empty state. We do not make a
// full check with block_used() because that takes too
// long --- we let preformat() do that.
if (bhp->magic_low == FLASH_NULL16 &&
bhp->magic_high == FLASH_NULL16 &&
bhp->age == FLASH_NULL16 &&
bhp->version == FLASH_NULL16 &&
bhp->flags == FLASH_NULL16)
{
bstat[b].used = 0;
bstat[b].lost = 0;
bstat[b].flags = BF_IS_EMPTY;
tw(tr(TR_FUNC, TrFsck, "EMPTY "));
}
else {
// If the block is not free, it is probably corrupted.
// Thus we reset its age and free it.
tw(tr(TR_FUNC, TrFsck, "magic = 0x%08x\n",
bhp->magic_low | (bhp->magic_high << 16)));
ffsdrv.write_halfword(&bhp->age, 0);
block_free(b);
tw(tr(TR_FUNC, TrFsck, "BAD "));
}
}
}
else {
fs.format = bhp->version;
age_valid = 1;
if (!is_block(b, BF_IS_FREE)) {
bstat[b].used = block_used(b);
bstat[b].lost = bstat[b].used - BHEADER_SIZE;
}
if (is_block(b, BF_IS_FREE)) {
// The only case where we do not call block_used() is
// when the block is truly free.
bstat[b].used = 0;
bstat[b].lost = 0;
tw(tr(TR_FUNC, TrFsck, "FREE "));
ttw(ttr(TTrInitLow, "FREE" NL));
}
else if (is_block(b, BF_IS_DATA)) {
tw(tr(TR_FUNC, TrFsck, "DATA "));
ttw(ttr(TTrInitLow, "DATA" NL));
}
else if (is_block(b, BF_IS_CLEANING)) {
// Here we schedule a block_clean(). Note that we can
// and do not execute the block cleaning now, as the info
// that block_clean() needs is not at all ready at this
// point in the initialization. So we set a flag and then
// clean the block at the end of ffs_initialize()
tw(tr(TR_FUNC, TrFsck, "CLEANING "));
ttw(ttr(TTrInitLow, "CLEANING" NL));
b_to_clean = b + 1;
}
else if (is_block(b, BF_IS_COPYING)) {
tw(tr(TR_FUNC, TrFsck, "COPYING "));
ttw(ttr(TTrInitLow, "COPYING" NL));
fs.newinodes = b;
}
else if (is_block(b, BF_IS_INODES)) {
tw(tr(TR_FUNC, TrFsck, "INODES "));
ttw(ttr(TTrInitLow, "INODES" NL));
fs.inodes = b;
}
else if (is_block(b, BF_IS_INODES_LOST)) {
tw(tr(TR_FUNC, TrFsck, "INODESLOST"));
ttw(ttr(TTrInitLow, "INODESLOST" NL));
b_inode_lost = b;
}
else {
block_free(b);
tw(tr(TR_FUNC, TrFsck, "INVALID "));
ttw(ttr(TTrInitLow, "INVALID" NL));
}
}
tw(tr(TR_NULL, TrFsck, " %2d: (0x%05x) %02x, used = %6d\n",
b, dev.binfo[b].offset, bstat[b].flags & 0xFF, bstat[b].used));
if (age_valid) {
if (age_min == 0) {
// Initialize minimum and maximum block ages
age_min = age_max = bhp->age;
tw(tr(TR_FUNC, TrFsckLow, "age_min/max = %d\n", age_min));
}
else {
age_dist_min = age_distance(bhp->age, age_min);
age_dist_max = age_distance(bhp->age, age_max);
if (age_dist_min > age_dist ||
age_dist_max > age_dist) {
if (age_dist_max > age_dist_min) {
age_dist = age_dist_max;
age_min = bhp->age;
tw(tr(TR_FUNC, TrFsckLow, "age_min = %d (dist = %d)\n",
age_min, age_dist));
}
else {
age_dist = age_dist_min;
age_max = bhp->age;
tw(tr(TR_FUNC, TrFsckLow, "age_max = %d (dist = %d)\n",
age_max, age_dist));
}
}
}
}
}
tlw(led_off(LED_DRV_INIT));
tw(tr(TR_FUNC, TrFsck, "age min, max, max-min = %d, %d, %d\n",
age_min, age_max, (uint16) (age_max-age_min)));
// If age_max is untouched is is because all blocks were in the 'Empty'
// state. In this case we let the age be as it is (0xFFFF).
if (age_max == 0)
age_max = age_min = BLOCK_AGE_MAX;
// Handle age wrap around thus ensuring fs.age_max is set correctly. We
// have to type-cast the whole computation, otherwise it will be
// incorrect.
if ((age_t) (age_max - age_min) > 0x8000) {
age_dist = age_max;
age_max = age_min;
age_min = age_dist;
}
// save maximum age found for the case of a bad block that is going to
// be reclaimed later on by blocks_reclaim()
fs.age_max = age_max;
tw(tr(TR_FUNC, TrFsck, "fs.format = 0x%04x\n", fs.format));
tw(tr(TR_FUNC, TrFsck, "fs.inodes, newinodes = %d, %d\n",
fs.inodes, fs.newinodes));
ttw(ttr(TTrInit, "fs.inodes, newinodes = %d, %d" NL,
fs.inodes, fs.newinodes));
tw(tr(TR_FUNC, TrFsck, "age min, max = %d, %d\n", age_min, age_max));
// If any blocks were in the EMPTY state, now is the time to bring them
// into the FREE state. Note that we must only do this *after*
// fs.age_max has been initialized.
for (b = 0; b < dev.numblocks; b++) {
if (is_block(b, BF_IS_EMPTY)) {
if ((bstat[b].used = block_used(b)) == 0)
block_preformat(b, 0);
else
block_free(b);
}
}
if (fs.inodes >= 0) { // Keep inode
if (fs.newinodes >= 0)
block_free(fs.newinodes); // Not finish copying
inodes_set(fs.inodes);
}
else { // No valid inode block
// Copying must have been finish
if (fs.newinodes >= 0 && b_inode_lost >= 0) {
fs.inodes = b_inode_lost;
block_commit();
}
else { // No old or new Inode block!
tw(tr(TR_END, TrFsck, "} %d\n", EFFS_NOFORMAT));
ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
return EFFS_NOFORMAT;
}
}
if ((fs.format >> 8) != (FFS_FORMAT_VERSION >> 8)) {
tw(tr(TR_END, TrFsck, "} %d\n", EFFS_BADFORMAT));
ttw(ttr(TTrInitLow, "} %d" NL, EFFS_BADFORMAT));
return EFFS_BADFORMAT;
}
// FIXME: Insert age sanity check; age distance must not be too big (> 2
// * FFS_AGE_DISTANCE)?
tw(tr(TR_END, TrFsck, "} %d\n", b_to_clean));
ttw(ttr(TTrInitLow, "} %d" NL, b_to_clean));
return b_to_clean;
}
// Set fs.inodes and fs.inodes_addr
void inodes_set(iref_t i)
{
fs.inodes = i;
fs.inodes_addr = (struct inode_s *)
(offset2addr(dev.binfo[fs.inodes].offset)
+ dev.atomsize - sizeof(struct inode_s));
}
/******************************************************************************
* inodes_fsck()
******************************************************************************/
// Now for each inode in the inodes block, update the bstat array
// information: free, used, objects. Also, locate the root inode. We could
// optimize this a little, because bstat[binodes].used gives an inidication
// of how many inodes are actually present in the system.
iref_t inodes_fsck(void)
{
iref_t i;
struct inode_s *ip;
char *addr;
bref_t block;
ttw(str(TTrInitLow, "inodes_fsck {" NL));
tw(tr(TR_BEGIN, TrFsck, "inodes_fsck() {\n"));
tw(tr(TR_FUNC, TrFsck, "inodes in block %d:\n", fs.inodes));
// the fields of the bstat entry for the inodes have the meaning:
// used = total number of used inodes (valid, erased, invalid)
// lost = total number of lost inodes (erased, invalid)
// objects = index of first free inode (used by inode_alloc())
fs.root = 0; // default to root inode not found
fs.ijournal = 0; // default to journal file inode not found
bstat[fs.inodes].objects = 1;
bstat[fs.inodes].used = 0;
bstat[fs.inodes].lost = 0;
fs.sequence = 0; // just for debug (fun)
// we must set some default value for this, so we set it to max possible!
fs.inodes_max = dev.blocksize / sizeof(struct inode_s);
ip = inode_addr(1);
tw(tr(TR_FUNC, TrFsck, " i addr cld sib seq upd flag size name\n"));
for (i = 1; i < fs.inodes_max; i++, ip++)
{
// just for debug (fun)
if (ip->sequence > fs.sequence)
fs.sequence = ip->sequence;
// compute block index and total data space occupied
block = offset2block(location2offset(ip->location));
// Only scan used inodes. blocks_fsck() accounted all used space as
// also being lost space, so now we subtract from the lost space,
// the space used by valid objects
if (ip->location != FLASH_NULL32)
{
bstat[fs.inodes].used++;
tw(tr(TR_FUNC, TrFsck, "%3d 0x%05X %3d %3d %4d %3d %s%s%s%s%s%s %6d %s\n",
i,
location2offset(ip->location),
ip->child, ip->sibling,
ip->sequence, ip->updates,
is_object(ip, OT_DIR) ? "d" : "",
is_object(ip, OT_LINK) ? "l" : "",
is_object(ip, OT_FILE) ? "f" : "",
is_object(ip, OT_SEGMENT) ? "s" : "",
is_object(ip, OT_ERASED) ? " " : "",
IS_BIT_SET(ip->flags, OF_READONLY) && !is_object(ip, OT_ERASED) ?
"r" : " ",
ip->size,
// Erased chunks do not have any name so we can not trace erased objects!
(ip->size && !is_object(ip, OT_SEGMENT) && !is_object(ip, OT_ERASED) ?
addr2name(offset2addr(location2offset(ip->location))) : "")
));
if (is_object_valid(ip)) {
// This inode is valid, so we account the data space as used
// and the inode as used too.
bstat[block].lost -= ip->size;
bstat[block].objects++;
// test if this is the root inode. store index if it is.
if (!is_object(ip, OT_SEGMENT)) {
addr = addr2name(offset2addr(location2offset(ip->location)));
if (*addr == '/')
fs.root = i;
else if (*addr == '.' &&
ffs_strcmp(addr, FFS_JOURNAL_NAME) == 0) {
fs.ijournal = i;
}
}
}
else if (is_object(ip, OT_ERASED)) {
// this inode's data is deleted, so we account the data
// space as used and lost and the inode as lost too.
bstat[fs.inodes].lost++;
}
else {
// This is an invalid object, so we account the data space
// as used and lost and the inode as lost too. NOTEME: error
// what should we do? Perhaps we should record semi-lost
// inodes? Can we safely account for it here if this is an
// object to be recovered because another inode.copied is
// referring to this? Will used/lost etc. be updated
// correctly then?
bstat[fs.inodes].lost++;
tw(tr(TR_NULL, TrFsck, "(invalid = %d)\n", ip->flags & OT_MASK));
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -