接上一篇博客,这篇博客继续讲解jffs2的挂载过程
现在到了jffs2_build_filesystem函数,该函数进行对flash的扫描以及目录树的建立操作,分为三个阶段(以下来源于内核代码注释):
/* Scan plan:
- Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
- Scan directory tree from top down, setting nlink in inocaches
- Scan inocaches for inodes with nlink==0
*/
阶段一:扫描flash,为flash上的节点创建相应的内存描述符,并分配inocache;
阶段二:根据目录情况,增加目录下文件的硬链接数
阶段三,删除硬链接数为0的inocache节点
static int jffs2_build_filesystem(struct jffs2_sb_info *c)
{
int ret, i, dir_hardlinks = 0;
struct jffs2_inode_cache *ic;
struct jffs2_full_dirent *fd;
struct jffs2_full_dirent *dead_fds = NULL;
dbg_fsbuild("build FS data structures\n");
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
c->flags |= JFFS2_SB_FLAG_SCANNING;
ret = jffs2_scan_medium(c); //进行flash的扫描操作,这是阶段一
c->flags &= ~JFFS2_SB_FLAG_SCANNING;
if (ret) //如果返回错误码,则退出
goto exit;
dbg_fsbuild("scanned flash completely\n");
jffs2_dbg_dump_block_lists_nolock(c);
dbg_fsbuild("pass 1 starting\n");
c->flags |= JFFS2_SB_FLAG_BUILDING;
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) { //阶段二开始,遍历所有inocache
if (ic->scan_dents) { //如果该inocache代表的是一个目录,则对其进行处理
jffs2_build_inode_pass1(c, ic, &dir_hardlinks);//增加其目录项的硬链接数,该函数在下文中分析
cond_resched(); //适当的让出CPU
}
}
//阶段二完毕
dbg_fsbuild("pass 1 complete\n");
/* Next, scan for inodes with nlink == 0 and remove them. If
they were directories, then decrement the nlink of their
children too, and repeat the scan. As that's going to be
a fairly uncommon occurrence, it's not so evil to do it this
way. Recursion bad. */
dbg_fsbuild("pass 2 starting\n");
//阶段三开始
for_each_inode(i, c, ic) { //遍历所有inocache
if (ic->pino_nlink) //如果硬链接数不为0则跳过
continue;
//如果硬链接数为0,对齐进行删除操作,该函数在下文中分析
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
cond_resched();
}
dbg_fsbuild("pass 2a starting\n");
while (dead_fds) { //这里是为了处理某些没有被用到的目录(即该目录没有连接到从根目录长出来的目录树)的情况
fd = dead_fds;
dead_fds = fd->next;
ic = jffs2_get_ino_cache(c, fd->ino);
if (ic) //对于每个死目录,调用jffs2_build_remove_unlinked_inode
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
jffs2_free_full_dirent(fd);
}
dbg_fsbuild("pass 2a complete\n");
if (dir_hardlinks) { //这里是为了处理某些特殊情况下出现的目录硬链接的情况
/* If we detected directory hardlinks earlier, *hopefully*
* they are gone now because some of the links were from
* dead directories which still had some old dirents lying
* around and not yet garbage-collected, but which have
* been discarded above. So clear the pino_nlink field
* in each directory, so that the final scan below can
* print appropriate warnings. */
for_each_inode(i, c, ic) {
if (ic->flags & INO_FLAGS_IS_DIR)
ic->pino_nlink = 0; //强行将目录项的pino_nlink置为0
}
}
dbg_fsbuild("freeing temporary data structures\n");
//好,终于到了最后一步,释放挂载过程中临时使用的jffs2_full_dirent结构体所占用的内存
/* Finally, we can scan again and free the dirent structs */
for_each_inode(i, c, ic) {
while(ic->scan_dents) {
fd = ic->scan_dents;
ic->scan_dents = fd->next;
/* We do use the pino_nlink field to count nlink of
* directories during fs build, so set it to the
* parent ino# now. Now that there's hopefully only
* one. */
if (fd->type == DT_DIR) {
if (!fd->ic) {
/* We'll have complained about it and marked the coresponding
raw node obsolete already. Just skip it. */
continue;
}
/* We *have* to have set this in jffs2_build_inode_pass1() */
BUG_ON(!(fd->ic->flags & INO_FLAGS_IS_DIR));
/* We clear ic->pino_nlink ∀ directories' ic *only* if dir_hardlinks
* is set. Otherwise, we know this should never trigger anyway, so
* we don't do the check. And ic->pino_nlink still contains the nlink
* value (which is 1). */
if (dir_hardlinks && fd->ic->pino_nlink) {
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u is also hard linked from dir ino #%u\n",
fd->name, fd->ino, ic->ino, fd->ic->pino_nlink);
/* Should we unlink it from its previous parent? */
}
/* For directories, ic->pino_nlink holds that parent inode # */
fd->ic->pino_nlink = ic->ino; //将对应inocache的pino_nlink置为其父目录的ino号
}
jffs2_free_full_dirent(fd); //释放内存空间
}
ic->scan_dents = NULL;
cond_resched(); //适当的让出CPU
}
jffs2_build_xattr_subsystem(c);
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
dbg_fsbuild("FS build complete\n");
/* Rotate the lists by some number to ensure wear levelling */
jffs2_rotate_lists(c); //这个操作是为了磨损均衡而做的
ret = 0;
exit:
if (ret) {
for_each_inode(i, c, ic) {
while(ic->scan_dents) {
fd = ic->scan_dents;
ic->scan_dents = fd->next;
jffs2_free_full_dirent(fd);
}
}
jffs2_clear_xattr_subsystem(c);
}
return ret;
}
下面详细分析一下阶段一,也就是扫描flash,为flash上的节点创建相应的内存描述符,并分配inocache,具体实现该操作的函数是jffs2_scan_medium。
jffs2_scan_medium函数的逻辑是,通过一个for循环,循环扫描flash上所有的块,根据每个块扫描的结果,将块加到对应的链表里去(比如free链表、dirty链表等),最后更新超级块的统计信息。
jffs2挂载要求flash上至少有一个有效的结点(raw inode不算,这类结点要在mount后由gc验证为有效)或者flash由刚擦除完的块以及坏块(至少有一个能用的块)组成
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
int i, ret;
uint32_t empty_blocks = 0, bad_blocks = 0;
unsigned char *flashbuf = NULL;
uint32_t buf_size = 0;
struct jffs2_summary *s = NULL; /* summary info collected by the scan process */
#ifndef __ECOS
size_t pointlen, try_size;
ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen,
(void **)&flashbuf, NULL);
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
pointlen);
mtd_unpoint(c->mtd, 0, pointlen);
flashbuf = NULL;
}
if (ret && ret != -EOPNOTSUPP)
jffs2_dbg(1, "MTD point failed %d\n", ret);
#endif
if (!flashbuf) {
/* For NAND it's quicker to read a whole eraseblock at a time,
apparently */
if (jffs2_cleanmarker_oob(c)) //对于nand flash,每次读取的是一个页
try_size = c->sector_size;
else
try_size = PAGE_SIZE;
jffs2_dbg(1, "Trying to allocate readbuf of %zu "
"bytes\n", try_size);
flashbuf = mtd_kmalloc_up_to(c->mtd, &try_size);
if (!flashbuf)
return -ENOMEM;
jffs2_dbg(1, "Allocated readbuf of %zu bytes\n",
try_size);
buf_size = (uint32_t)try_size;
}
if (jffs2_sum_active()) { //如果使能了summary功能
s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL); //这里只是分配了相应的内存描述符
if (!s) {
JFFS2_WARNING("Can't allocate memory for summary\n");
ret = -ENOMEM;
goto out;
}
}
for (i=0; i<c->nr_blocks; i++) { //进入for循环,遍历扫描每一个block
struct jffs2_eraseblock *jeb = &c->blocks[i]; //获取擦除块描述符
cond_resched();
/* reset summary info for next eraseblock scan */
jffs2_sum_reset_collected(s);
ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
buf_size, s); //扫描擦除块并进行分析的动作在这个函数里面进行
if (ret < 0) //如果返回错误则退出
goto out;
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
/* Now decide which list to put it on */
switch(ret) { //根据jffs2_scan_eraseblock返回的结果,将擦除块加入对应的链表里
case BLK_STATE_ALLFF: //如果擦除块是全F
/*
* Empty block. Since we can't be sure it
* was entirely erased, we just queue it for erase
* again. It will be marked as such when the erase
* is complete. Meanwhile we still count it as empty
* for later checks.
*/
empty_blocks++;
list_add(&jeb->list, &c->erase_pending_list); //那就加到erase_pending_list,等待cleanmarker的写入
c->nr_erasing_blocks++;
break;
case BLK_STATE_CLEANMARKER: //只有一个cleanmarker
/* Only a CLEANMARKER node is valid */
if (!jeb->dirty_size) { //如果没有无效数据
/* It's actually free */
list_add(&jeb->list, &c->free_list); //加入free_list
c->nr_free_blocks++;
} else { //否则重新进行擦除操作
/* Dirt */
jffs2_dbg(1, "Adding all-dirty block at 0x%08x to erase_pending_list\n",
jeb->offset);
list_add(&jeb->list, &c->erase_pending_list); //加入erase_pending_list
c->nr_erasing_blocks++;
}
break;
case BLK_STATE_CLEAN: //如果数据全是非过时的
/* Full (or almost full) of clean data. Clean list */
list_add(&jeb->list, &c->clean_list);
break;
case BLK_STATE_PARTDIRTY: //至少有一个数据节点是过时的
/* Some data, but not full. Dirty list. */
/* We want to remember the block with most free space
and stick it in the 'nextblock' position to start writing to it. */
if (jeb->free_size > min_free(c) &&
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
//nextblock是写入操作应该作用的块,扫描阶段会根据每个块的情况,选择free_size最大的块作为下次写入操作应该作用的块
//如果程序走到这里,说明要对变量c->nextblock进行改变
/* Better candidate for the next writes to go to */
if (c->nextblock) { //如果此时c->nextblock不为空,则将原先的c->nextblock加入到dirty_list或者very_dirty_list
ret = file_dirty(c, c->nextblock);
if (ret)
goto out;
/* deleting summary information of the old nextblock */
jffs2_sum_reset_collected(c->summary);
}
/* update collected summary information for the current nextblock */
jffs2_sum_move_collected(c, s);
jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
__func__, jeb->offset);
c->nextblock = jeb; //更新c->nextblock
} else {
ret = file_dirty(c, jeb); //否则把这个块加入到dirty_list或者very_dirty_list
if (ret)
goto out;
}
break;
case BLK_STATE_ALLDIRTY: //全是脏数据的话,加入erase_pending_list准备进行擦除操作
/* Nothing valid - not even a clean marker. Needs erasing. */
/* For now we just put it on the erasing list. We'll start the erases later */
jffs2_dbg(1, "Erase block at 0x%08x is not formatted. It will be erased\n",
jeb->offset);
list_add(&jeb->list, &c->erase_pending_list);
c->nr_erasing_blocks++;
break;
case BLK_STATE_BADBLOCK://这个快有问题,加入坏块链表bad_list
jffs2_dbg(1, "Block at 0x%08x is bad\n", jeb->offset);
list_add(&jeb->list, &c->bad_list);
c->bad_size += c->sector_size;
c->free_size -= c->sector_size;
bad_blocks++;
break;
default:
pr_warn("%s(): unknown block state\n", __func__);
BUG();
}
}
/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
//更新统计信息
if (c->nextblock && (c->nextblock->dirty_size)) {
c->nextblock->wasted_size += c->nextblock->dirty_size;
c->wasted_size += c->nextblock->dirty_size;
c->dirty_size -= c->nextblock->dirty_size;
c->nextblock->dirty_size = 0;
}
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) {
/* If we're going to start writing into a block which already
contains data, and the end of the data isn't page-aligned,
skip a little and align it. */
uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize;
jffs2_dbg(1, "%s(): Skipping %d bytes in nextblock to ensure page alignment\n",
__func__, skip);
jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
jffs2_scan_dirty_space(c, c->nextblock, skip);
}
#endif
if (c->nr_erasing_blocks) { //如果有擦除块需要擦除
if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",
empty_blocks, bad_blocks, c->nr_blocks);
ret = -EIO;
goto out;
}
spin_lock(&c->erase_completion_lock);
jffs2_garbage_collect_trigger(c); //则唤醒GC线程进行擦除操作
spin_unlock(&c->erase_completion_lock);
}
ret = 0;
out:
if (buf_size)
kfree(flashbuf);
#ifndef __ECOS
else
mtd_unpoint(c->mtd, 0, c->mtd->size);
#endif
kfree(s);
return ret;
}
可以看到,jffs2_scan_medium函数将擦除块扫描的任务交给了jffs2_scan_eraseblock函数去完成,jffs2_scan_eraseblock函数会根据扫描出来的结果返回适当的返回值给jffs2_scan_medium函数,从而将对应的块加入到适当的链表中,下面分析真正完成扫描操作的函数jffs2_scan_eraseblock。这个函数很长,但逻辑很清晰:如果summary功能使能了,那么首先会去扫描summary信息(对于nor flash,summary信息存放在每个块的结尾)以加速挂载,若summary信息扫描没有问题,则根据summary信息进行分析,函数便可返回;若没有使能summary或者summary信息有问题,那么只能进行全扫描操作。
全扫描操作的逻辑是:先把flash上的数据读入内存中,然后从头到尾进行遍历分析,根据之前的介绍,根据节点头部node->nodetype成员我们就能知道该节点的类型,根据不同的类型,jffs2会生成不同的内核描述符并进行处理。
/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
the flash, XIP-style */
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
struct jffs2_unknown_node *node;
struct jffs2_unknown_node crcnode;
uint32_t ofs, prevofs, max_ofs;
uint32_t hdr_crc, buf_ofs, buf_len;
int err;
int noise = 0;
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
int cleanmarkerfound = 0;
#endif
ofs = jeb->offset;
prevofs = jeb->offset - 1;
jffs2_dbg(1, "%s(): Scanning block at 0x%x\n", __func__, ofs);
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) {
int ret;
if (mtd_block_isbad(c->mtd, jeb->offset))
return BLK_STATE_BADBLOCK;
ret = jffs2_check_nand_cleanmarker(c, jeb);
jffs2_dbg(2, "jffs_check_nand_cleanmarker returned %d\n", ret);
/* Even if it's not found, we still scan to see
if the block is empty. We use this information
to decide whether to erase it or not. */
switch (ret) {
case 0: cleanmarkerfound = 1; break;
case 1: break;
default: return ret;
}
}
#endif
if (jffs2_sum_active()) { //如果使能了summary功能
struct jffs2_sum_marker *sm;
void *sumptr = NULL;
uint32_t sumlen;
if (!buf_size) {
/* XIP case. Just look, point at the summary if it's there */
sm = (void *)buf + c->sector_size - sizeof(*sm);
if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
sumptr = buf + je32_to_cpu(sm->offset);
sumlen = c->sector_size - je32_to_cpu(sm->offset);
}
} else {
/* If NAND flash, read a whole page of it. Else just the end */
if (c->wbuf_pagesize)
buf_len = c->wbuf_pagesize;
else
buf_len = sizeof(*sm);
/* Read as much as we want into the _end_ of the preallocated buffer */
err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len,
jeb->offset + c->sector_size - buf_len,
buf_len);//调用驱动把数据从flash上读出来
if (err)
return err;
sm = (void *)buf + buf_size - sizeof(*sm);
if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {//summary的魔术码
sumlen = c->sector_size - je32_to_cpu(sm->offset);
sumptr = buf + buf_size - sumlen; //summary信息对应的位置
/* sm->offset maybe wrong but MAGIC maybe right */
if (sumlen > c->sector_size)
goto full_scan;
/* Now, make sure the summary itself is available */
if (sumlen > buf_size) {
/* Need to kmalloc for this. */
sumptr = kmalloc(sumlen, GFP_KERNEL);
if (!sumptr)
return -ENOMEM;
memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len);
}
if (buf_len < sumlen) {
/* Need to read more so that the entire summary node is present */
err = jffs2_fill_scan_buf(c, sumptr,
jeb->offset + c->sector_size - sumlen,
sumlen - buf_len);
if (err)
return err;
}
}
}
if (sumptr) {
err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random); //这个函数处理summary信息
if (buf_size && sumlen > buf_size)
kfree(sumptr);
/* If it returns with a real error, bail.
If it returns positive, that's a block classification
(i.e. BLK_STATE_xxx) so return that too.
If it returns zero, fall through to full scan. */
if (err) //如果返回,则summary信息有问题,需要对整个块进行扫描工作
return err; //如果summary信息正确,则函数就在这里返回了
}
}
full_scan: //如果函数走到了这里,说明需要对块进行全扫描操作
buf_ofs = jeb->offset;
if (!buf_size) {
/* This is the XIP case -- we're reading _directly_ from the flash chip */
buf_len = c->sector_size;
} else {
buf_len = EMPTY_SCAN_SIZE(c->sector_size);
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
if (err)
return err;
}
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
ofs = 0;
max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
/* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */
while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) //发现全F的数据
ofs += 4;
if (ofs == max_ofs) { //如果一直到块结尾都是全F
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) { //nand flash需要检查cleanmarker标记是否有写入
/* scan oob, take care of cleanmarker */
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
jffs2_dbg(2, "jffs2_check_oob_empty returned %d\n",
ret);
switch (ret) {
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
case 1: return BLK_STATE_ALLDIRTY;
default: return ret;
}
}
#endif
jffs2_dbg(1, "Block at 0x%08x is empty (erased)\n",
jeb->offset);
if (c->cleanmarker_size == 0)
return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */
else
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
}
if (ofs) {
jffs2_dbg(1, "Free space at %08x ends at %08x\n", jeb->offset,
jeb->offset + ofs);
if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
return err;
if ((err = jffs2_scan_dirty_space(c, jeb, ofs)))
return err;
}
/* Now ofs is a complete physical flash offset as it always was... */
ofs += jeb->offset;
noise = 10;
dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset);
scan_more: //如果扫描到不是全F的数据
while(ofs < jeb->offset + c->sector_size) {
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
/* Make sure there are node refs available for use */
err = jffs2_prealloc_raw_node_refs(c, jeb, 2); //先预分配内存描述符
if (err)
return err;
cond_resched();
//下面这里很大一段代码都是为了数据读取的完整和正确性做的准备工作
if (ofs & 3) {
pr_warn("Eep. ofs 0x%08x not word-aligned!\n", ofs);
ofs = PAD(ofs);
continue;
}
if (ofs == prevofs) {
pr_warn("ofs 0x%08x has already been seen. Skipping\n",
ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
prevofs = ofs;
if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
jffs2_dbg(1, "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n",
sizeof(struct jffs2_unknown_node),
jeb->offset, c->sector_size, ofs,
sizeof(*node));
if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs)))
return err;
break;
}
if (buf_ofs + buf_len < ofs + sizeof(*node)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
jffs2_dbg(1, "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_unknown_node),
buf_len, ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
}
node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {//扫描过程中又发现全F的数据
uint32_t inbuf_ofs;
uint32_t empty_start, scan_end;
empty_start = ofs;
ofs += 4;
scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len);
jffs2_dbg(1, "Found empty flash at 0x%08x\n", ofs);
more_empty:
inbuf_ofs = ofs - buf_ofs;
while (inbuf_ofs < scan_end) {
if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) {
pr_warn("Empty flash at 0x%08x ends at 0x%08x\n",
empty_start, ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
return err;
goto scan_more;
}
inbuf_ofs+=4;
ofs += 4;
}
/* Ran off end. */
jffs2_dbg(1, "Empty flash to end of buffer at 0x%08x\n",
ofs);
/* If we're only checking the beginning of a block with a cleanmarker,
bail now */
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) {
jffs2_dbg(1, "%d bytes at start of block seems clean... assuming all clean\n",
EMPTY_SCAN_SIZE(c->sector_size));
return BLK_STATE_CLEANMARKER;
}
if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */
scan_end = buf_len;
goto more_empty;
}
/* See how much more there is to read in this eraseblock... */
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
if (!buf_len) {
/* No more to read. Break out of main loop without marking
this range of empty space as dirty (because it's not) */
jffs2_dbg(1, "Empty flash at %08x runs to end of block. Treating as free_space\n",
empty_start);
break;
}
/* point never reaches here */
scan_end = buf_len;
jffs2_dbg(1, "Reading another 0x%x at 0x%08x\n",
buf_len, ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
goto more_empty;
}
//到这里铺垫操作结束,终于要进行节点头的判断操作了
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { //首先判断魔术码
pr_warn("Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n",
ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {//继续判断魔术码
jffs2_dbg(1, "Dirty bitmask at 0x%08x\n", ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {//继续判断魔术码
pr_warn("Old JFFS2 bitmask found at 0x%08x\n", ofs);
pr_warn("You cannot use older JFFS2 filesystems with newer kernels\n");
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {//魔术码不对
/* OK. We're out of possibilities. Whinge and move on */
noisy_printk(&noise, "%s(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
__func__,
JFFS2_MAGIC_BITMASK, ofs,
je16_to_cpu(node->magic));
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4; //那就往后移4字节继续扫描
continue;
}
/* We seem to have a node of sorts. Check the CRC */
crcnode.magic = node->magic;
crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
crcnode.totlen = node->totlen;
hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);//进行数据头部的CRC值校验
if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
noisy_printk(&noise, "%s(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
__func__,
ofs, je16_to_cpu(node->magic),
je16_to_cpu(node->nodetype),
je32_to_cpu(node->totlen),
je32_to_cpu(node->hdr_crc),
hdr_crc);
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) {
/* Eep. Node goes over the end of the erase block. */
pr_warn("Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
ofs, je32_to_cpu(node->totlen));
pr_warn("Perhaps the file system was created with the wrong erase size?\n");
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
return err;
ofs += 4;
continue;
}
if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
/* Wheee. This is an obsoleted node */
jffs2_dbg(2, "Node at 0x%08x is obsolete. Skipping\n",
ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
return err;
ofs += PAD(je32_to_cpu(node->totlen));
continue;
}
//到这里,一些不正确的节点类型都被过滤了
switch(je16_to_cpu(node->nodetype)) { //判断节点类型
case JFFS2_NODETYPE_INODE: //inode节点
if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
jffs2_dbg(1, "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
sizeof(struct jffs2_raw_inode),
buf_len, ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s); //调用这个函数处理inode节点,会分配相应的内核描述符以及加入链表等操作
if (err) return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_NODETYPE_DIRENT: //处理dirent节点
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
jffs2_dbg(1, "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len,
ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s);//调用这个函数处理dirent节点,会分配相应的内核描述符以及加入链表等操作
if (err) return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: //xattr节点
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
jffs2_dbg(1, "Fewer than %d bytes (xattr node) left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len,
ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s);
if (err)
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_NODETYPE_XREF:
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
jffs2_dbg(1, "Fewer than %d bytes (xref node) left to end of buf. Reading 0x%x at 0x%08x\n",
je32_to_cpu(node->totlen), buf_len,
ofs);
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
if (err)
return err;
buf_ofs = ofs;
node = (void *)buf;
}
err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s);
if (err)
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
#endif /* CONFIG_JFFS2_FS_XATTR */
case JFFS2_NODETYPE_CLEANMARKER: //cleanmarker节点
jffs2_dbg(1, "CLEANMARKER node found at 0x%08x\n", ofs);
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
pr_notice("CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
ofs, je32_to_cpu(node->totlen),
c->cleanmarker_size);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
return err;
ofs += PAD(sizeof(struct jffs2_unknown_node));
} else if (jeb->first_node) {
pr_notice("CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n",
ofs, jeb->offset);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
return err;
ofs += PAD(sizeof(struct jffs2_unknown_node));
} else {
jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);
ofs += PAD(c->cleanmarker_size);
}
break;
case JFFS2_NODETYPE_PADDING:
if (jffs2_sum_active())
jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
default:
switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
case JFFS2_FEATURE_ROCOMPAT:
pr_notice("Read-only compatible feature node (0x%04x) found at offset 0x%08x\n",
je16_to_cpu(node->nodetype), ofs);
c->flags |= JFFS2_SB_FLAG_RO;
if (!(jffs2_is_readonly(c)))
return -EROFS;
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_FEATURE_INCOMPAT:
pr_notice("Incompatible feature node (0x%04x) found at offset 0x%08x\n",
je16_to_cpu(node->nodetype), ofs);
return -EINVAL;
case JFFS2_FEATURE_RWCOMPAT_DELETE:
jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
je16_to_cpu(node->nodetype), ofs);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
case JFFS2_FEATURE_RWCOMPAT_COPY: {
jffs2_dbg(1, "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n",
je16_to_cpu(node->nodetype), ofs);
jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL);
/* We can't summarise nodes we don't grok */
jffs2_sum_disable_collecting(s);
ofs += PAD(je32_to_cpu(node->totlen));
break;
}
}
}
}
if (jffs2_sum_active()) { //如果使能了summary功能,就需要记录这些节点的信息
if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) {
dbg_summary("There is not enough space for "
"summary information, disabling for this jeb!\n");
jffs2_sum_disable_collecting(s);
}
}
jffs2_dbg(1, "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size,
jeb->unchecked_size, jeb->used_size, jeb->wasted_size);
/* mark_node_obsolete can add to wasted !! */
if (jeb->wasted_size) { //更新统计信息
jeb->dirty_size += jeb->wasted_size;
c->dirty_size += jeb->wasted_size;
c->wasted_size -= jeb->wasted_size;
jeb->wasted_size = 0;
}
return jffs2_scan_classify_jeb(c, jeb); //根据统计信息,确定这个块所属类型
}
值得注意的是,在全盘扫描过程中,mount流程会检查除数据的crc之外所有的crc值;但是,我们查看flash上summary信息的结构体发现,整块summary数据只有两个CRC值,mount也确实检查了这两个CRC值,这样一来,那非summary区域的那些原始数据的CRC值根本没有被检验了?
答案是否定的,在读取summary数据里各个节点的信息时,会将REF_UNCHECKED标识传给函数sum_link_node_ref函数,标识这个节点是未被校验的,等到mount流程结束后、真正读取数据之前,会根据REF_UNCHECKED表示决定是否对这些CRC值进行校验。