📄 fsck.c
字号:
#if 1
// Check ERASE flag for intermediate states (10 or 01)
if ((ip->flags & OT_ERASED_MSb) !=
((ip->flags & (OT_ERASED_MSb >> 1)) << 1))
{
tw(tr(TR_FUNC, TrFsck,"Intermediate state detected in inode flag:\n"));
tw(tr(TR_FUNC, TrFsck, " inode %d (0x%x->0x%x)\n",
i, ip->flags, ip->flags & OT_ERASED));
ffsdrv_write_byte(&ip->flags, ip->flags & OT_ERASED);
}
#endif
tw(tr(TR_FUNC, TrFsck, "%3d 0x%05X %3d %3d %4d %3d %s%s%s%s%s%s%s %6d %s\n",
i,
location2offset(ip->location),
ip->child, ip->sibling,
ip->sequence, ip->updates,
is_object(ip, OTE_DIR) ? " d" : "",
is_object(ip, OTE_LINK) ? " l" : "",
is_object(ip, OTE_FILE) ? " f" : "",
is_object(ip, OTE_SEGMENT) ? " s" : "",
is_object(ip, OT_REP) ? "rp" : "",
is_object_erased(ip) ? " " : "",
IS_BIT_SET(ip->flags, OFE_READONLY) && !is_object_erased(ip) ?
"r" : " ",
ip->size,
// Erased chunks do not have any name so we cannot trace erased objects!
(ip->size && !is_object(ip, OTE_SEGMENT) && !is_object_erased(ip) ?
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. Only exception is the
// replacement inode, it is not counted as an object
if (!is_object(ip, OT_REP)) {
bstat[block].lost -= ip->size;
bstat[block].objects++;
}
// test if this is the root inode. store index if it is.
if (!is_object(ip, OTE_SEGMENT) && !is_object(ip, OT_REP)) {
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_erased(ip)) {
// 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 = 0x%x)\n", ip->flags));
}
if (is_object(ip, OT_REP)) {
// Add the replacement inode in the ram table
fs.repi_table[nrepi] = i;
nrepi++;
if (nrepi == FFS_REPLACEMENT_INODES_MAX)
ffs_panic(EFFS_2MANYREPI);
}
}
}
ttw(ttr(TTrInit, "fs.root=%d, journal=%d" NL, fs.root, fs.ijournal));
tw(tr(TR_END, TrFsck, "} used: %d, lost: %d, root: %d, journal: %d\n",
bstat[fs.inodes].used, bstat[fs.inodes].lost, fs.root, fs.ijournal));
fs.sequence++;
tw(tr_bstat());
if (fs.root == 0) {
ttw(ttr(TTrInitLow, "} %d" NL, EFFS_NOFORMAT));
return EFFS_NOFORMAT;
}
ttw(str(TTrInitLow, "} 0" NL));
return EFFS_OK;
}
/******************************************************************************
* Preformat and format
******************************************************************************/
// Prepare all blocks for fs_format(). Because ffs_is_formattable() has
// already been called prior to this function, we know that no sector erase
// is in progress! The blocks are prepared by putting them into the 'Free'
// state.
effs_t fs_preformat(void)
{
bref_t b;
ttw(str(TTrFormat, "preformat {" NL));
tw(tr(TR_BEGIN, TrFormat, "fs_preformat() {\n"));
// Mark ffs as being non-formatted from now on.
fs.root = 0;
// We must initialize bstat[fs.inodes].used and inodes_high, such that
// inodes_reclaim() isn't triggered in reclaim() on the following
// fs_format().
inodes_set(0);
bstat[fs.inodes].used = 0;
bstat[fs.inodes].lost = 0;
bstat[fs.inodes].objects = 0;
// While format is in progress, we make FFS inaccessible to other
// functions...
fs.initerror = EFFS_NOFORMAT;
if (dev.manufact == 0) {
b = EFFS_NODEVICE;
}
else {
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);
}
else if (!is_block(b, BF_IS_FREE)) {
block_free(b);
}
}
b = EFFS_OK;
}
tw(tr(TR_END, TrFormat, "} %d\n", b));
ttw(ttr(TTrFormat, "} %d" NL, b));
return b;
}
// Preformat a single block thus taking it from the 'Empty' state into
// 'Free' state.
void block_preformat(bref_t b, age_t age)
{
int set_age_max;
struct block_header_s bhb;
struct block_header_s *bhp =
(struct block_header_s *) offset2addr(dev.binfo[b].offset);
tw(tr(TR_BEGIN, TrFormat, "fs_block_preformat(%d, %d)\n", b, age));
POWERFAIL_DOMAIN_BEGIN(PFM_BLOCKHEADER);
POWERFAIL_SET_ADDR((int)bhp);
if (age == 0) {
age = fs.age_max;
}
else {
// We schedule an update of fs.age_max. Due to proper handling of
// age wrap-around, we can not actually set it now.
set_age_max = (age == fs.age_max);
age++;
if (age == 0)
age++;
if (set_age_max) {
fs.age_max = age;
tw(tr(TR_FUNC, TrFormat, "new fs.age_max = %d\n", fs.age_max));
}
}
bstat[b].flags = BF_IS_EMPTY;
bstat[b].used = 0;
bstat[b].lost = 0;
bstat[b].objects = 0;
block_flags_write(b, BF_WRITING);
// Write block header from local buffer
bhb.magic_low = BLOCK_MAGIC_LOW;
bhb.magic_high = BLOCK_MAGIC_HIGH;
bhb.version = FFS_FORMAT_VERSION;
bhb.age = age;
// Do not write flag and reserved
ffsdrv.write(bhp, &bhb, sizeof(bhb.magic_low) + sizeof(bhb.magic_high) +
sizeof(bhb.version) + sizeof(age_t));
block_flags_write(b, BF_FREE);
POWERFAIL_DOMAIN_END();
tw(tr(TR_END, TrFormat, ""));
}
// After preformat() has erased two blocks, this function can be called to
// initialize ffs by writing fs data and metadata. Note that ffs_begin() is
// *not* called before this function in ffs.c. Otherwise we would never
// enter this function because fs.root is zero. NOTEME: this is also a bug
// as this means we risk that this operation is started while an erase (or a
// write) is in progress! How the flash device reacts to this is currently
// unknown.
effs_t fs_format(const char *name)
{
bref_t i,b;
ttw(str(TTrFormat, "format {" NL));
tw(tr(TR_BEGIN, TrFormat, "fs_format('%s') {\n", name));
// Initialize file system parameters. It should be safe to change these
// now, as the format cannot fail at this point onwards.
fs_params_init(name);
// Make the first block be the inodes block
if ((fs.inodes = block_alloc(1, BF_COPYING)) < 0)
return EFFS_AGAIN;
block_flags_write(fs.inodes, BF_INODES);
inodes_set(fs.inodes);
// Make all block as data blocks except from the free_min and inode block
for (i = 0; i < dev.numblocks - fs.blocks_free_min - 1; i++)
if ((b = block_alloc(0, BF_DATA)) < 0)
return EFFS_AGAIN;
// Restart object sequencing (debug feature only)
fs.sequence = 0;
// Create root directory
journal_begin(0);
if ((fs.root = object_create(name, 0, 0, 0)) < 0) {
tw(tr(TR_END, TrFormat, "} %d\n", fs.root));
return fs.root;
}
journal_commit(OTE_DIR);
if ((fs.ijournal = journal_create(0)) < 0) {
tw(tr(TR_END, TrFormat, "} %d\n", fs.ijournal));
return fs.ijournal;
}
fs.initerror = ffs_initialize();
ffs_initerror[4] = fs.initerror;
ttw(ttr(TTrFormat, "} %d" NL, fs.initerror));
tw(tr(TR_END, TrFormat, "} %d\n", fs.initerror));
return fs.initerror;
}
// Check if we are ready to preformat (flag = 0) or format (flag = 1)
//
// For a format, we must first ensure no blocks are valid e.g. a preformat
// has already been run. Next, we must ensure we have preformatted all
// blocks e.g. all blocks are in the 'Free' state. This is actually the same
// thing but it sure helps the user because it yields a more precise error
// code when the format fails. In future we might be able to start a format
// when only two blocks have been preformatted, but this is harder because
// we have to make sure not to read from the physical sector that we are
// erasing, and this is exactly what ffs_ffs_initialize() currently does
// (when it is called at the end of format()).
//
// For a preformat, we must ensure an erase is not in progress (because we
// don't know how the device will react to a new erase when an erase is
// currently suspended).
effs_t is_formattable(int8 flag)
{
bref_t i, free, valid;
effs_t error = EFFS_OK;
tw(tr(TR_FUNC, TrFormat, "is_formattable() "));
// Count the number of valid and free blocks. These numbers will later
// be checked to see if we are really ready for a (pre)format(). Note
// that we *only* read block flags from the bstat[] array. We must not
// read directly from the flash sectors because an erase might be in
// progress!
for (i = 0, free = 0, valid = 0; i < dev.numblocks; i++) {
if (is_block(i, BF_IS_DATA) || is_block(i, BF_IS_INODES))
valid++;
if (is_block(i, BF_IS_FREE))
free++;
}
if (flag == 0) {
// In the case of a preformat, ensure an erase is not in
// progress (because we don't know how the device will react to a new
// erase when an erase is currently suspended).
if (dev.state == DEV_ERASE || dev.state == DEV_ERASE_SUSPEND) {
tw(tr(TR_NULL, TrFormat, "(%d)\n", EFFS_AGAIN));
return EFFS_AGAIN;
}
}
else {
if (valid > 0)
// Ensure we have preformatted prior to a format.
error = EFFS_NOPREFORMAT;
else if (free < dev.numblocks)
// Ensure all blocks are free before a format(). If not, a
// preformat() is currently in progress.
error = EFFS_AGAIN;
}
tw(tr(TR_NULL, TrFormat, "(%d)\n", error));
return error;
}
/******************************************************************************
* Journalling
******************************************************************************/
// The following matrix illustrates how the members of an inode change for
// the various (journalled) operations:
//
// | flags | size | loc | child | siblg | dir | oldi | updates
// ---------+-------+------+-----+-------+-------+-----+------+--------
// create | new | new | new | - | - | ins | n/a | 0
// fupdate | o | new | new | o | - | ins | del | old+1
// relocate | o | o | new | o | - | ins | del | old+1
// fctrl | new | o | o | o | - | ins | del | old+1
// remove | n/a | n/a | n/a | n/a | n/a | n/a | del | n/a
//
// - = leave empty (0xFFFF)
// ins = insert/append into directory
// o = old value
//
// We don't have to store child member in the journal entry because either
// it is EMPTY (fs.journal.oldi = 0) or it is retrieved from oldip->child.
// NOTEME: With journalling implemented, object_relocate might be able just
// to make a simple data copy!
// block_clean() is safe (without journalling), now that only ip->size is
// set to zero.
// Begin a new journal. Either a fresh object create (oldi == 0) or an
// update of an existing object (oldi == iref of old object)
void journal_begin(iref_t oldi)
{
tw(tr(TR_FUNC, TrJournal, "journal_begin(%d)\n", oldi));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -