jffs2文件系统-------挂载 .

按照文件系统的惯例,如果是非rootfs的话,就需要手动挂载。如: mount -t jffs2 /dev/mtdjffs2 jffs2  (这行shell的大体意思就是将mtdjffs2这个mtd块设备,按照jffs2文件系统的格式,挂载到jffs2文件夹下)。支持这行shell的前提有两个,其一内核要支持jffs2文件系统;其二mtdjffs2这个块设备上的数据,要符合jffs2文件系统的要求。内核支持很简单,在config文件中,添加jffs2即可;第二个要求则需要工具,谷歌jffs2的移植。

       本文重要的是看源码,配置、移植之类的,不再累赘。

     挂载代码可以简略成三部分,其中第三部分是最关键,也是代码量最大的,第一第二步很简单,也不再累赘,主要是匹配mtd块设备。

1、jffs2_get_sb
得到jffs2是第几个mtd分区后,进入第二步
2、jffs2_get_sb_mtdnr
从mtd_table得到分区的信息后,进入第三步
3、jffs2_get_sb_mtd
对超级块的设置,系统挂载

       关于第三部分,可看代码(只截取重点部分)

  1. static int jffs2_get_sb_mtd(struct file_system_type *fs_type,  
  2.                 int flags, const char *dev_name,  
  3.                 void *data, struct mtd_info *mtd,  
  4.                 struct vfsmount *mnt)  
  5. {  
  6.     struct super_block *sb;  
  7.     struct jffs2_sb_info *c;  
  8.     sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);  
  9.         //jffs2_sb_set  :将sb->s_fs_info指向c,这个在fill_super会用到   
  10.        ……  
  11.     sb->s_op = &jffs2_super_operations;  //这个也很重要   
  12.         ……  
  13.     ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); //最重要的一个函数   
  14.       ……  
  15. }  
static int jffs2_get_sb_mtd(struct file_system_type *fs_type,
			    int flags, const char *dev_name,
			    void *data, struct mtd_info *mtd,
			    struct vfsmount *mnt)
{
	struct super_block *sb;
	struct jffs2_sb_info *c;
	sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
		//jffs2_sb_set  :将sb->s_fs_info指向c,这个在fill_super会用到
       ……
	sb->s_op = &jffs2_super_operations;	//这个也很重要
        ……
	ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);	//最重要的一个函数
      ……
}
       其中sget是去获取文件系统的超级块,而它的s_fs_info(Filesystem private info)指向jffs2的超级块。

      而jffs2_do_fill_super,从字面上看是堆超级块的填充,这个也是挂载部分最终的部分(一般文件系统挂载,无非是搞一个超级块,再搞个dirent,inode)。

  1. int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)  
  2. {  
  3.     struct jffs2_sb_info *c;  
  4.     struct inode *root_i;  
  5.     int ret;  
  6.     size_t blocks;  
  7.   
  8.     c = JFFS2_SB_INFO(sb);  //sb->s_fs_info   
  9.       
  10.     c->cleanmarker_size = sizeof(struct jffs2_unknown_node); //节点的大小   
  11.       
  12.     jffs2_init_xattr_subsystem(c);// is used to initialize semaphore and list_head, and some variables.   
  13.   
  14.     if ((ret = jffs2_do_mount_fs(c)))  
  15.         goto out_inohash;  
  16.   
  17.     root_i = iget(sb, 1);   //根节点的inode   
  18.     sb->s_root = d_alloc_root(root_i);   //根目录   
  19.     if (!(sb->s_flags & MS_RDONLY))  
  20.         jffs2_start_garbage_collect_thread(c);  //回收线程开启   
  21.     return 0;  
  22.  }  
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
{
	struct jffs2_sb_info *c;
	struct inode *root_i;
	int ret;
	size_t blocks;

	c = JFFS2_SB_INFO(sb);	//sb->s_fs_info
	
	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);	//节点的大小
	
	jffs2_init_xattr_subsystem(c);// is used to initialize semaphore and list_head, and some variables.

	if ((ret = jffs2_do_mount_fs(c)))
		goto out_inohash;

	root_i = iget(sb, 1);	//根节点的inode
	sb->s_root = d_alloc_root(root_i);	//根目录
	if (!(sb->s_flags & MS_RDONLY))
		jffs2_start_garbage_collect_thread(c);	//回收线程开启
	return 0;
 }
最关键的三部:jffs2_do_mount_fs,iget,jffs2_start_garbage_collect_thread。

jffs2_do_mount_fs:扫描整个flash,并对flash数据进行分析挂载

iget:获得第一个inode

jffs2_start_garbage_collect_thread:开启垃圾回收线程

  1. int jffs2_do_mount_fs(struct jffs2_sb_info *c)  
  2. {  
  3.     ret = jffs2_sum_init(c);  
  4.   
  5.     if (jffs2_build_filesystem(c)) {  
  6.         goto out_free;  
  7.     }  
  8.   
  9.     jffs2_calc_trigger_levels(c);  
  10.   
  11.     return 0;  
  12.   
  13.  }  
int jffs2_do_mount_fs(struct jffs2_sb_info *c)
{
	ret = jffs2_sum_init(c);

	if (jffs2_build_filesystem(c)) {
		goto out_free;
	}

	jffs2_calc_trigger_levels(c);

	return 0;

 }
重点是jffs2_build_filesystem
  1. /* Scan plan: 
  2.  - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go 
  3.  - Scan directory tree from top down, setting nlink in inocaches 
  4.  - Scan inocaches for inodes with nlink==0 
  5. */  
  6. static int jffs2_build_filesystem(struct jffs2_sb_info *c)  
  7. {  
  8.     /* First, scan the medium and build all the inode caches with 
  9.        lists of physical nodes */  
  10.     ret = jffs2_scan_medium(c);     //正如上面注释上所说扫描介质,   
  11.                                 //并且建立所有的节点缓存通过list of physical nodes   
  12.                                 //节点信息挂载在c->inocachelist中   
  13.   
  14.       
  15.     /* Now scan the directory tree, increasing nlink according to every dirent found. */  
  16.     for_each_inode(i, c, ic) {//c->inocache_list中的元素一个个扫描过去   
  17.         if (ic->scan_dents) {    //如果inocache中有目录项元素   
  18.             jffs2_build_inode_pass1(c, ic);//给有目录项指向的数据节点的nlink++   
  19.             cond_resched();  
  20.         }  
  21.     }  
  22.   
  23.     /* Next, scan for inodes with nlink == 0 and remove them. If 
  24.        they were directories, then decrement the nlink of their 
  25.        children too, and repeat the scan. As that's going to be 
  26.        a fairly uncommon occurrence, it's not so evil to do it this 
  27.        way. Recursion bad. */  
  28.        //nlink==0的那种属于没有目录项来指向的   
  29.     for_each_inode(i, c, ic) {  
  30.         if (ic->nlink)  
  31.             continue;  
  32.         jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);  
  33.     }  
  34.   
  35.     while (dead_fds) {  
  36.         fd = dead_fds;  
  37.         dead_fds = fd->next;  
  38.   
  39.         ic = jffs2_get_ino_cache(c, fd->ino);  
  40.   
  41.         if (ic)  
  42.             jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);  
  43.         jffs2_free_full_dirent(fd);  
  44.     }  
  45.   
  46.     jffs2_build_xattr_subsystem(c);  
  47. }  
/* 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
*/
static int jffs2_build_filesystem(struct jffs2_sb_info *c)
{
	/* First, scan the medium and build all the inode caches with
	   lists of physical nodes */
	ret = jffs2_scan_medium(c); 	//正如上面注释上所说扫描介质,
								//并且建立所有的节点缓存通过list of physical nodes
								//节点信息挂载在c->inocachelist中

	
	/* Now scan the directory tree, increasing nlink according to every dirent found. */
	for_each_inode(i, c, ic) {//c->inocache_list中的元素一个个扫描过去
		if (ic->scan_dents) {	//如果inocache中有目录项元素
			jffs2_build_inode_pass1(c, ic);//给有目录项指向的数据节点的nlink++
			cond_resched();
		}
	}

	/* 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. */
	   //nlink==0的那种属于没有目录项来指向的
	for_each_inode(i, c, ic) {
		if (ic->nlink)
			continue;
		jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
	}

	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(c, ic, &dead_fds);
		jffs2_free_full_dirent(fd);
	}

	jffs2_build_xattr_subsystem(c);
}
按照注释所言,这个函数,首先是要扫描物理节点,建立dirent、inode的map,再设置dirent的nlink,最后扫面nlink==o的inocache

扫描物理节点,建立dirent、inode的map

  1. /* 
  2. 扫描一个个物理扇区,并将它们的信息挂载到超级块(c)上 
  3. 总体而言更新了c->inocache_list,c->wasted_size等信息,最主要的是inocache_list 
  4. 还有c->free_list之类的链表中保存扇区信息 
  5. 个人觉着这个函数的核心是jffs2_scan_eraseblock 
  6. */  
  7. int jffs2_scan_medium(struct jffs2_sb_info *c)  
  8. {  
  9.   
  10.     for (i=0; i<c->nr_blocks; i++) {  //一个扇区一个扇区扫描   
  11.         ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),  
  12.                         buf_size, s);   //扫描一个扇区,在超级块c的ionde_chache中挂载各种节   
  13.     //下面代码中有个switch,是对这个区挂载到sb的链表中进行一个分析,其实挺重要的,but代码太多,暂时省略   
  14.                           
  15.   
  16. }  
/*
扫描一个个物理扇区,并将它们的信息挂载到超级块(c)上
总体而言更新了c->inocache_list,c->wasted_size等信息,最主要的是inocache_list
还有c->free_list之类的链表中保存扇区信息
个人觉着这个函数的核心是jffs2_scan_eraseblock
*/
int jffs2_scan_medium(struct jffs2_sb_info *c)
{

	for (i=0; i<c->nr_blocks; i++) {	//一个扇区一个扇区扫描
		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
						buf_size, s);	//扫描一个扇区,在超级块c的ionde_chache中挂载各种节
	//下面代码中有个switch,是对这个区挂载到sb的链表中进行一个分析,其实挺重要的,but代码太多,暂时省略
						

}
jffs2_scan_eraseblock的入口参数:超级块,擦出块,数据缓存,数据缓存长度,摘要信息。
  1. /* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into 
  2.    the flash, XIP-style  
  3.    c是超级块,jeb是要扫描的扇区,buf是数据缓存,buf_size是数据最大的大小 
  4.    s是概要,一般在最后面 
  5.     
  6.     这个函数的大体作用就是扫描一个扇区,然后将目录项,数据节点之类的 
  7.     都挂载到超级块的链表里面(inocache_list),可以通过ino来查询,如果是dirent的话 
  8.     可以用pino先找到inode_cache,再用name找到dirent,而它的ino可以相对应的inode。 
  9.     inode_cache中有flash的信息 
  10.    */  
  11. static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,  
  12.                   unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {  
  13.     if (jffs2_sum_active()) {  
  14.         ……//如果有摘要的话,执行这部分代码   
  15.     }  
  16.     /* Scan only 4KiB of 0xFF before declaring it's empty */  
  17.     while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)  
  18.         ofs += 4;       //如果分区头4k字节全为空,这时候ofs =1024   
  19.     //如果前面有空,后面还有一堆代码对这种情况进行分析,篇幅有限,省去代码跟分析   
  20.   
  21. scan_more:  
  22.     while(ofs < jeb->offset + c->sector_size) {//开始扫描这个扇区里的数据,一个数据段一个数据段扫描   
  23.                     //最糟糕的情况是扫描整页   
  24.   
  25.         /* Make sure there are node refs available for use */  
  26.         err = jffs2_prealloc_raw_node_refs(c, jeb, 2);  //jeb->last_node生成,最后一个非空,参数c无视掉   
  27.                             //其实就是生成一个jffs2_raw_node_ref然后jeb->last_node指向它   
  28.   
  29.   
  30.         node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];  //头部结构,一开始要假装这个头是什么都不知道的   
  31.   
  32.         if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {   //如果是空   
  33.             ……//一堆代码,扫面后面的是不是也都是空的   
  34.         }  
  35.   
  36.         switch(je16_to_cpu(node->nodetype)) {//根据节点类型,省略篇幅,截取最常见的三个   
  37.         case JFFS2_NODETYPE_INODE:  
  38.             err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s);//生成一个inode,挂载在c上面,可以通过info查询的到   
  39.                         //暂时这么理解,将inode挂到sb(超级块)中   
  40.             break;  
  41.   
  42.         case JFFS2_NODETYPE_DIRENT: //这是个目录项   
  43.             err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s);     //扫描目录节点   
  44.                                                                 //在c中挂载inode,在inode中挂载fd   
  45.             break;  
  46.   
  47.         case JFFS2_NODETYPE_CLEANMARKER:  
  48.                 jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);  
  49.                 //jeb->first_node,c->free_size,jeb->free_size这些值在上面这个过程中有了变化   
  50.                 //jeb->last_node->flash_offset也变成了REF_NORMAL   
  51.                 ofs += PAD(c->cleanmarker_size);  
  52.                 //4字节对齐,还有就是c->cleanmarker_size就是节点头占用的大小   
  53.             break;  
  54.         }  
  55.     }  
  56.   
  57.     D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",  
  58.           jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size));  
  59.     //总结这一块里的信息,总结这一个扇区里的信息,多少字节空闲,多少被用,多少脏,多少浪费   
  60.     //clearmask跟没过时的目录项是used ,inode数据是unchecked,过时的目录项是waste   
  61.       
  62.     return jffs2_scan_classify_jeb(c, jeb);  //返回这个擦出块的使用状况   
  63. }  
/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
   the flash, XIP-style 
   c是超级块,jeb是要扫描的扇区,buf是数据缓存,buf_size是数据最大的大小
   s是概要,一般在最后面
   
   	这个函数的大体作用就是扫描一个扇区,然后将目录项,数据节点之类的
	都挂载到超级块的链表里面(inocache_list),可以通过ino来查询,如果是dirent的话
	可以用pino先找到inode_cache,再用name找到dirent,而它的ino可以相对应的inode。
	inode_cache中有flash的信息
   */
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) {
	if (jffs2_sum_active()) {
		……//如果有摘要的话,执行这部分代码
	}
	/* Scan only 4KiB of 0xFF before declaring it's empty */
	while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
		ofs += 4;		//如果分区头4k字节全为空,这时候ofs =1024
	//如果前面有空,后面还有一堆代码对这种情况进行分析,篇幅有限,省去代码跟分析

scan_more:
	while(ofs < jeb->offset + c->sector_size) {//开始扫描这个扇区里的数据,一个数据段一个数据段扫描
					//最糟糕的情况是扫描整页

		/* Make sure there are node refs available for use */
		err = jffs2_prealloc_raw_node_refs(c, jeb, 2);	//jeb->last_node生成,最后一个非空,参数c无视掉
							//其实就是生成一个jffs2_raw_node_ref然后jeb->last_node指向它


		node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];	//头部结构,一开始要假装这个头是什么都不知道的

		if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {	//如果是空
			……//一堆代码,扫面后面的是不是也都是空的
		}

		switch(je16_to_cpu(node->nodetype)) {//根据节点类型,省略篇幅,截取最常见的三个
		case JFFS2_NODETYPE_INODE:
			err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s);//生成一个inode,挂载在c上面,可以通过info查询的到
						//暂时这么理解,将inode挂到sb(超级块)中
			break;

		case JFFS2_NODETYPE_DIRENT:	//这是个目录项
			err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s); 	//扫描目录节点
																//在c中挂载inode,在inode中挂载fd
			break;

		case JFFS2_NODETYPE_CLEANMARKER:
				jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);
				//jeb->first_node,c->free_size,jeb->free_size这些值在上面这个过程中有了变化
				//jeb->last_node->flash_offset也变成了REF_NORMAL
				ofs += PAD(c->cleanmarker_size);
				//4字节对齐,还有就是c->cleanmarker_size就是节点头占用的大小
			break;
		}
	}

	D1(printk(KERN_DEBUG "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));
	//总结这一块里的信息,总结这一个扇区里的信息,多少字节空闲,多少被用,多少脏,多少浪费
	//clearmask跟没过时的目录项是used ,inode数据是unchecked,过时的目录项是waste
	
	return jffs2_scan_classify_jeb(c, jeb);  //返回这个擦出块的使用状况
}
上面代码中有个重要的函数,没有截取出来jffs2_fill_scan_buf这个函数的作用是从物理介质中去取得相应的数据。

上面的注释已经说明了这个函数的大体作用,扫描物理介质,一个个node的读过来。一开始假装是一个unknow的node,然后根据nodetype来分析这个node的类型。

如果是inode的话:生成一个inocache,挂载到超级块的inocache_list中;nlink=0

如果是dirent的话:生成一个inocache(如果找不到相应的pino的ic的话,就要生成),挂载到超级块的inocache_list中,如果是根目录下的dirent,nlink=1。生成一个临时变量fd,这个fd生成在这里,but释放在其他地方,且fd中存放dirent的各种信息,然后将这个fd挂载到ic->scan_dents链表中

如果是cleanmask:一般情况下,就是个头,这种节点,直接偏移,对齐,over。


程序将返回到jffs2_build_filesystem,执行jffs2_build_inode_pass1,这个函数的作用是将dirent的节点所指向的inode节点的nlink++。因为dirent会指向一个inode,在jffs2中dirent的ino(如果ino=0,则表示这个dirent已经是被删除了的)指向inode在sb->inocache_list中的ino,此函数就是将inode的ino_cache->nlink++。

jffs2_build_remove_unlinked_inode这个函数的作用,是将没有nlink(没有nlink是个没人要的怨妇,浪费空间)的ic给标记过时,修改freesize,dirtysize之类的信息。


程序返回到jffs2_do_fill_super,接下来执行iget。这个函数的作用是获得第一个inode,也就是根目录的inode。抽丝剥茧,这个函数最后调用的是jffs2_read_inode(因为sb->s_op->read_inode(inode)  === jffs2_read_inode)。

  1. void jffs2_read_inode (struct inode *inode)     //这个函数里有inode初始化   
  2. {  
  3.       
  4.     f = JFFS2_INODE_INFO(inode);    //这个inode是系统刚刚给分配的inode   
  5.     c = JFFS2_SB_INFO(inode->i_sb);  //根据sb->s_fs_info来得到jffs2的超级块   
  6.       
  7.     jffs2_init_inode_info(f);   //红黑树是NULL,metadata,dents都是NULL   
  8.     ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);  
  9.   
  10.     switch (inode->i_mode & S_IFMT) {  //各种节点类型   
  11.         ……//一大堆代码,主要是对inode节点操作的赋值   
  12.     }  
  13.   
  14. }  
void jffs2_read_inode (struct inode *inode)		//这个函数里有inode初始化
{
	
	f = JFFS2_INODE_INFO(inode);	//这个inode是系统刚刚给分配的inode
	c = JFFS2_SB_INFO(inode->i_sb);	//根据sb->s_fs_info来得到jffs2的超级块
	
	jffs2_init_inode_info(f);	//红黑树是NULL,metadata,dents都是NULL
	ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);

	switch (inode->i_mode & S_IFMT) {  //各种节点类型
		……//一大堆代码,主要是对inode节点操作的赋值
	}

}
重点也是难点是jffs2_do_read_inode,这个函数我也看得迷迷糊糊,应该有很大的漏洞。入口参数:jffs2的超级块,jffs2的inode,inode的ino(挂载的时候,这个是1),回调参数。
  1. /* Scan the list of all nodes present for this ino, build map of versions, etc. */  
  2. int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,  
  3.             uint32_t ino, struct jffs2_raw_inode *latest_node)  
  4. {  
  5.  retry_inocache:  
  6.     f->inocache = jffs2_get_ino_cache(c, ino);  
  7.   
  8.     if (f->inocache) {  
  9.     /* Check its state. We may need to wait before we can use it */  
  10.     //一堆代码来检测这个inocache的状态,可能需要等待   
  11.     }  
  12.       
  13.     if (!f->inocache && ino == 1) {  //如果inocache不在,且ino=1,初始化的时候可能会进入,因为根节点必须存在inode,没有也得搞一个出来   
  14.     ……  
  15.     }  
  16.   
  17.     return jffs2_do_read_inode_internal(c, f, latest_node);  
  18. }  
/* Scan the list of all nodes present for this ino, build map of versions, etc. */
int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
			uint32_t ino, struct jffs2_raw_inode *latest_node)
{
 retry_inocache:
	f->inocache = jffs2_get_ino_cache(c, ino);

	if (f->inocache) {
	/* Check its state. We may need to wait before we can use it */
	//一堆代码来检测这个inocache的状态,可能需要等待
	}
	
	if (!f->inocache && ino == 1) {	//如果inocache不在,且ino=1,初始化的时候可能会进入,因为根节点必须存在inode,没有也得搞一个出来
	……
	}

	return jffs2_do_read_inode_internal(c, f, latest_node);
}
最蛋疼的jffs2_do_read_inode_internal。入口参数:jffs2的超级块,jffs2的inode,回调参数。由于不太懂,所以这个函数只大致说一下作用,将有数据的node挂到f的红黑树上面。没有数据,即使version再高也没用。入口参数中的f->fragtree这颗树上挂有数据,按照ofs+size来挂载的
  1. /* 
  2. 将有数据的node插到f->freg这颗红黑树上,latest_node是version最高的那个,不管有没有数据 
  3. */  
  4. static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,  
  5.                     struct jffs2_inode_info *f,  
  6.                     struct jffs2_raw_inode *latest_node)  
  7. {  
  8.     /* Grab all nodes relevant to this ino */  
  9.     ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);  
  10.         //所有的node,然后返回最高版本,以及一颗红黑树tn_list,这颗红黑树是按照version插入的   
  11.   
  12.     f->dents = fd_list;  
  13.   
  14.     rb = rb_first(&tn_list);  
  15.   
  16.     while (rb) {    //查找第一个有有效数据的fn,rb这颗红黑树是根据version来排列的   
  17.         if (fn->size) {  //有数据   
  18.             ret = jffs2_add_older_frag_to_fragtree(c, f, tn);   //红黑树f->frag,将tn->fn插进去,按照fn->ofs跟size来插   
  19.                                                         //可以理解成数据最后的结束位置,来插入红黑树   
  20.         }  
  21.         //下面一堆红黑树的操作,删除rb的操作,且如果节点过期就删除掉它   
  22.         rb = rb_next(rb);   //rb指向下一个   
  23.         /* Remove the spent tn from the tree; don't bother rebalancing 
  24.          * but put our right-hand child in our own place. */  
  25.     }  
  26.     jffs2_dbg_fragtree_paranoia_check_nolock(f);  
  27.   
  28.   
  29.     ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);  
  30.     //去读这个节点的信息,这个lastnode还是version最高的那个   
  31.     switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {  
  32.     //暂时指向分析一个   
  33.     case S_IFREG:  
  34.         /* If it was a regular file, truncate it to the latest node's isize */  
  35.         //isize是实际长度   
  36.         jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));//在f->frag红黑树上删除那些长度+偏移>isize的节点。   
  37.         break;  
  38.     }  
  39. }  
/*
将有数据的node插到f->freg这颗红黑树上,latest_node是version最高的那个,不管有没有数据
*/
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
					struct jffs2_inode_info *f,
					struct jffs2_raw_inode *latest_node)
{
	/* Grab all nodes relevant to this ino */
	ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
		//所有的node,然后返回最高版本,以及一颗红黑树tn_list,这颗红黑树是按照version插入的

	f->dents = fd_list;

	rb = rb_first(&tn_list);

	while (rb) {	//查找第一个有有效数据的fn,rb这颗红黑树是根据version来排列的
		if (fn->size) {	//有数据
			ret = jffs2_add_older_frag_to_fragtree(c, f, tn);	//红黑树f->frag,将tn->fn插进去,按照fn->ofs跟size来插
														//可以理解成数据最后的结束位置,来插入红黑树
		}
		//下面一堆红黑树的操作,删除rb的操作,且如果节点过期就删除掉它
		rb = rb_next(rb);	//rb指向下一个
		/* Remove the spent tn from the tree; don't bother rebalancing
		 * but put our right-hand child in our own place. */
	}
	jffs2_dbg_fragtree_paranoia_check_nolock(f);


	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
	//去读这个节点的信息,这个lastnode还是version最高的那个
	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
	//暂时指向分析一个
	case S_IFREG:
		/* If it was a regular file, truncate it to the latest node's isize */
		//isize是实际长度
		jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));//在f->frag红黑树上删除那些长度+偏移>isize的节点。
		break;
	}
}


程序返回到jffs2_do_fill_super,开启垃圾回收线程。

挂载到此结束。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值