📄 fsck.c
字号:
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 *bhp =
(struct block_header_s *) offset2addr(dev.binfo[b].offset);
tw(tr(TR_BEGIN, TrFormat, "fs_block_preformat(%d, %d)\n", b, age));
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));
}
}
ffsdrv.write_halfword(&bhp->age, age);
ffsdrv.write_halfword(&bhp->version, FFS_FORMAT_VERSION);
ffsdrv.write_halfword(&bhp->magic_low, BLOCK_MAGIC_LOW);
ffsdrv.write_halfword(&bhp->magic_high, BLOCK_MAGIC_HIGH);
bstat[b].flags = BF_IS_EMPTY;
bstat[b].used = 0;
bstat[b].lost = 0;
bstat[b].objects = 0;
block_flags_write(b, BF_FREE);
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 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 the next block a data block
if ((b = block_alloc(1, 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(OT_DIR);
if ((fs.ijournal = journal_create(0)) < 0) {
tw(tr(TR_END, TrFormat, "} %d\n", fs.ijournal));
return fs.ijournal;
}
fs.initerror = ffs_initialize();
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));
fs.journal.i = 0;
fs.journal.state = JOURNAL_IS_EMPTY;
fs.journal.repli = 0;
fs.link_child = 1; //Default link child in journal_commit()
if (oldi == 0) {
fs.journal.flags = 0xFF;
fs.journal.diri = 0;
fs.journal.oldi = 0;
fs.journal.location = 0;
fs.journal.size = 0;
}
else {
struct inode_s *oldip = inode_addr(oldi);
fs.journal.flags = oldip->flags;
fs.journal.diri = oldi;
fs.journal.oldi = oldi;
fs.journal.location = oldip->location;
fs.journal.size = oldip->size;
}
}
// NOTEME: We have compressed the macro code because it will NOT compile on
// Unix otherwise. So until we find out why, we use this as a work-around.
#if (FFS_TEST == 1)
#define JOURNAL_TEST(testcase, text) if (fs.testflags == testcase) { tw(tr(TR_END, TrJournal, "} (" text ")\n")); return; }
#else
#define JOURNAL_TEST(testcase, text)
#endif
// NOTEME: Should we empty journal file when we are anyway relocating it in
// data_reclaim()?
void journal_end(uint8 type)
{
struct inode_s *ip = inode_addr(fs.ijournal);
struct journal_s *addr = (struct journal_s *)
offset2addr(location2offset(ip->location) + fs.journal_pos);
tw(tr(TR_BEGIN, TrJournal, "journal_end(0x%x) {\n", type));
tw(tr(TR_FUNC, TrJournal, "journal_pos = 0x%04x (%d)\n", fs.journal_pos,
(fs.journal_pos - JOURNAL_POS_INITIAL) / sizeof(struct journal_s)));
// If this is a create, set the object type
if (type != 0 && fs.journal.oldi == 0)
fs.journal.flags = (fs.journal.flags & OF_MASK) | type;
// If there is no journal file, we can do without it, although we
// certainly don't like it!
if (fs.ijournal == 0) {
journal_commit(0);
tw(tr(TR_END, TrJournal, "} No jounal file\n"));
return;
}
JOURNAL_TEST(JOURNAL_TEST_EMPTY, "Oops in JOURNAL_IS_EMPTY");
// Write RAM journal to journal file.
if (fs.journal.state == (uint8) JOURNAL_IS_EMPTY) {
fs.journal.state = JOURNAL_IS_WRITING;
ffsdrv.write(addr, &fs.journal, sizeof(fs.journal));
}
JOURNAL_TEST(JOURNAL_TEST_WRITING, "Oops in JOURNAL_IS_WRITING");
// Advance journal file's state
if (fs.journal.state == (uint8) JOURNAL_IS_WRITING) {
fs.journal.state = JOURNAL_IS_READY;
ffsdrv_write_byte(&addr->state, fs.journal.state);
}
JOURNAL_TEST(JOURNAL_TEST_READY, "Oops in JOURNAL_IS_READY");
journal_commit(0);
JOURNAL_TEST(JOURNAL_TEST_COMMITTING, "Oops in JOURNAL_TEST_COMMITTING");
JOURNAL_TEST(JOURNAL_TEST_COMMITTED, "Oops in JOURNAL_COMMITTED");
// Advance journal file's state
ffsdrv_write_byte(&addr->state, JOURNAL_IS_DONE);
JOURNAL_TEST(JOURNAL_TEST_DONE, "Oops in JOURNAL_IS_DONE");
// Advance journal
fs.journal_pos += sizeof(struct journal_s);
// Unless we are currently relocating the journal file itself, check if
// journal file is near full and relocate it if it is.
if (fs.journal_pos >= fs.journal_size - FFS_JOURNAL_MARGIN *
sizeof(struct journal_s) && fs.journal.oldi != fs.ijournal) {
tw(tr(TR_FUNC, TrJournal, "Journal file (near) full!\n"));
journal_create(fs.ijournal);
}
// Check if we have just committed the journal file itself
if (fs.journal.oldi == fs.ijournal) {
fs.journal_pos = JOURNAL_POS_INITIAL;
fs.ijournal = fs.journal.i;
tw(tr(TR_FUNC, TrJournal, "Journal file re-created, fs.ijournal = %d\n",
fs.ijournal));
}
tw(tr(TR_END, TrJournal, "}\n"));
}
// Write contents of fs.journal to FFS meta data (inodes). Note that we do
// NOT traverse ip->copied as we used to do in the old
// object_update_commit(). Also, we do not check if object has been
// erased after traversing ip->copied. All this code has been removed
// because we will very soon have full callback functionality and thus the
// code is redundant.
void journal_commit(uint8 type)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -