yaffs2文件系统工具mkyaffs2image
yaffs2文件系统是针对nandflash的文件系统,其制作工具为mkyaffs2image。
一、差异
不同nand容量,工具不一样。
首先使用的是mkyaffs2image,编译生成根文件系统的镜像之后,下载到板子上,启动的时候报错,错误代码这里没有上传。
问题出在工具使用的不正确,查看工具目录下,发现有mkyaffs2image-128M工具,于是在网上查阅了首先采用的mkyaffs2image和mkyaffs2image-128M的区别,发现这对不同的板载nand flash容量,使用不同的工具制作根文件系统。
查看了自己使用的友善之臂板子上使用的nand flash是256M的,于是果断使用mkyaffs2image-128M工具制作根文件系统。因为mkyaffs2image-128M针对128M或者更大的容量。
二、编译
www.yaffs.net
If you want to use GIT directly from the command line then public read-only access is available, using the (bash) command:
git clone git://www.aleph1.co.uk/yaffs2
mkyaffs2image是在yaffs文件系统的utils目录下,只把其中的chunkSize spareSize 与 pagesPerBlock几个变量,按照nand_flash中的改一下就可以用。
// Adjust these to match your NAND LAYOUT: //#define chunkSize 8192 //#define spareSize 232 #define chunkSize 4096 //#define spareSize 218 #define spareSize 128 #define pagesPerBlock 128
三、使用
1、验证是否成功可直接在终端下输入:
mkyaffs2image
mkyaffs2image: image building tool for YAFFS2 built Jul 9 2009 usage: mkyaffs2image dir image_file [convert] dir the directory tree to be converted image_file the output file to hold the image 'convert' produce a big-endian image from a little-endian machine
注意核对convert的大小端。
2、制作镜像文件
mkyaffs2image dir image_file
3、烧写
通过uboot的nand write.yaffs命令烧写yaffs2到nand中。
nand erase;
tftp 20008000 app_install.yaffs2;nand write.yaffs 20008000 0 253080;
tftp 20008000 rmu.yaffs2;nand write.yaffs 20008000 4600000 307800;
四、源码分析
下面是mkyaffs2image的main, 其中, argc必须大于3。
argv[1] = dir //文件系统的path argv[2] = image_file //打包后生成yaffs.bin的路径 int main(int argc, char *argv[]) { struct stat stats; stat(argv[1],&stats); if(!S_ISDIR(stats.st_mode)) //保证argv[1]必须是一个目录 exit(1); outFile = open(argv[2],O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); //写入到镜像yaffs.bin的fd保存在一个全局变量outFile中 process_directory(YAFFS_OBJECTID_ROOT,argv[1]); //1. 依次读取目录中的文件,写到yaffs.bin中 pad_image(); //2. 将yaffs.bin扩充到block对齐 close(outFile); }
对目录的处理过程
static int process_directory(int parent, const char *path) { DIR *dir; char full_name[500]; struct stat stats; int equivalentObj; int newObj; struct dirent *entry; nDirectories++; dir = opendir(path); //打开目录 while((entry = readdir(dir)) != NULL) //遍历目录中的所有文件 { if(strcmp(entry->d_name,".") || strcmp(entry->d_name,"..")) //如果是 . 或者 .. 则直接跳过 continue; snprintf(full_name,sizeof(full_name),"%s/%s",path,entry->d_name); //构造文件的路径,存于full_name中 lstat(full_name,&stats); //获取目录下该文件的stat信息 if(!S_ISLNK(stats.st_mode) && !S_ISREG(stats.st_mode) && ! S_ISDIR(stats.st_mode) && !S_ISFIFO(stats.st_mode) && !S_ISBLK(stats.st_mode) && ! S_ISCHR(stats.st_mode) && !S_ISSOCK(stats.st_mode)) continue ; //不知道这TMD是什么类型的文件则跳过 newObj = obj_id++; n_obj++; if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) { write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL); } else { add_obj_to_list(stats.st_dev,stats.st_ino,newObj); if(S_ISLNK(stats.st_mode)) { char symname[500]; memset(symname,0, sizeof(symname)); readlink(full_name,symname,sizeof(symname) -1); write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname); } else if(S_ISREG(stats.st_mode)) //如果是普通文件 { if(write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL) == 0) //1.先写入文件头 { int h; u8 bytes[chunkSize]; int n_bytes; int chunk = 0; h = open(full_name,O_RDONLY); memset(bytes,0xff,sizeof(bytes)); while((n_bytes = read(h,bytes,sizeof(bytes))) > 0) { chunk++; write_chunk(bytes,newObj,chunk,n_bytes); //2.再写入文件内容 memset(bytes,0xff,sizeof(bytes)); } close(h); } } else if(S_ISSOCK(stats.st_mode)) //如果是socket文件 write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); //只写入文件头 else if(S_ISFIFO(stats.st_mode)) //如果是FIFO文件 write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); //只写入文件头 else if(S_ISCHR(stats.st_mode)) //如果是字符设备文件 write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); //只写入文件头 else if(S_ISBLK(stats.st_mode)) //如果是块设备文件 write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); //只写入文件头 else if(S_ISDIR(stats.st_mode)) //如果是目录文件 if (write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL) == 0) //只写入文件头 process_directory(newObj,full_name); //并把这次的newObj作为parent,继续 } } closedir(dir); return 0; }
static int write_object_header(int id, enum yaffs_obj_type t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias) { u8 bytes[chunkSize]; struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)bytes; memset(bytes,0xff,sizeof(bytes)); oh->type = t; oh->parent_obj_id = parent; if (strlen(name)+1 > sizeof(oh->name)) { errno = ENAMETOOLONG; return warn("object name"); } memset(oh->name,0,sizeof(oh->name)); strcpy(oh->name,name); if(t != YAFFS_OBJECT_TYPE_HARDLINK) { oh->yst_mode = s->st_mode; oh->yst_uid = s->st_uid; oh->yst_gid = s->st_gid; oh->yst_atime = s->st_atime; oh->yst_mtime = s->st_mtime; oh->yst_ctime = s->st_ctime; oh->yst_rdev = s->st_rdev; } if(t == YAFFS_OBJECT_TYPE_FILE) { oh->file_size = s->st_size; } if(t == YAFFS_OBJECT_TYPE_HARDLINK) { oh->equiv_id = equivalentObj; } if(t == YAFFS_OBJECT_TYPE_SYMLINK) { if (strlen(alias)+1 > sizeof(oh->alias)) { errno = ENAMETOOLONG; return warn("object alias"); } memset(oh->alias,0,sizeof(oh->alias)); strcpy(oh->alias,alias); } return write_chunk(bytes,id,0,0xffff); } static int write_chunk(u8 *data, u32 id, u32 chunk_id, u32 n_bytes) { struct yaffs_ext_tags t; struct yaffs_packed_tags2 pt; char spareData[spareSize]; if (write(outFile,data,chunkSize) != chunkSize) //写4K fatal("write"); memset(&t, 0, sizeof(t)); t.chunk_id = chunk_id; t.serial_number = 1; // **CHECK** t.n_bytes = n_bytes; t.obj_id = id; t.seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; t.chunk_used = 1; nPages++; memset(&pt, 0, sizeof(pt)); //函数将 yaffs_ext_tags转为yaffs_packed_tags2,并生成校验信息, yaffs_pack_tags2(&pt,&t,0); //但最后一个函数是0,所以只转化保存校验信息 memset(spareData, 0xff, sizeof(spareData)); shuffle_oob(spareData, &pt); if (write(outFile,spareData,sizeof(spareData)) != sizeof(spareData)) //写oob fatal("write"); return 0; }
struct yaffs_packed_tags2 { struct yaffs_packed_tags2_tags_only t; //数据 struct yaffs_ecc_other ecc; //校验信息 };
注:yaffs_packed_tags2是由数据和校难信息两部分组成的,但是这儿只用了数据不进行校验,所以内核里面也只是读出数据不校验。
扩充yaffs.bin到一个block
static void pad_image(void) { u8 data[chunkSize + spareSize]; int padPages = (nPages % pagesPerBlock); if (padPages) { memset(data, 0xff, sizeof(data)); for (padPages = pagesPerBlock-padPages; padPages; padPages--) { if (write(outFile, data, sizeof(data)) != sizeof(data)) fatal("write"); } } }
实验一下
a.新建目录fs_test,在fs_test新建一文件222.txt, 内空是"bbbcc"
sun@ubuntu:/tmp/mkyaffs/utils/test$ tree . └── fs_test └── 222.txt 1 directory, 1 file sun@ubuntu:/tmp/mkyaffs/utils/test$ cat fs_test/222.txt bbbccc
b. 利用mkyaffs2image生成yaffs的打包文件fs.yaffs
sun@ubuntu:/tmp/mkyaffs/utils/test$ ../mkyaffs2image fs_test/ fs.yaffs main[432]: mkyaffs2image: image building tool for YAFFS2 built Jul 3 2013 main[464]: Processing directory fs_test/ into image file fs.yaffs Object 257, fs_test//222.txt is a file, 1 data chunks written pad_image[255]: nPages=2 main[480]: Operation complete. 2 objects in 1 directories 2 NAND pages sun@ubuntu:/tmp/mkyaffs/utils/test$ ll total 540 drwxrwxr-x 3 sun sun 4096 Jul 3 16:36 ./ drwxrwxr-x 5 sun sun 4096 Jul 3 15:36 ../ drwxrwxr-x 2 sun sun 4096 Jul 3 15:06 fs_test/ -rw------- 1 sun sun 540672 Jul 3 16:36 fs.yaffs
可以看出fs.yaffs的大小是540672=128×(4096+128)
c. 分析一下fs.yaffs
按page的记录内容,可以分为两种:一个是oh_page另一个是data_page。
其中普通文件,需要一个oh_page,如果有内容还需要一个或多个data_page。其它的文件,则只需要一个oh_page就够了。
sizeof(yaffs_obj_hdr)=0x200=512B
28B的信息就是结构体 yaffs_packed_tags2
struct yaffs_packed_tags2_tags_only { unsigned seq_number; unsigned obj_id; unsigned chunk_id; unsigned n_bytes; }; struct yaffs_ecc_other { unsigned char col_parity; unsigned line_parity; unsigned line_parity_prime; }; struct yaffs_packed_tags2 { struct yaffs_packed_tags2_tags_only t; struct yaffs_ecc_other ecc; };
oh_page与data_page的根本区别
int write_chunk(u8 *data, u32 id, u32 chunk_id, u32 n_bytes); write_chunk(bytes,id,0,0xffff); //写oh_page chunk_id=0,写入oob write_chunk(bytes,newObj,chunk,n_bytes); //写data_page chunk_id!=0,写入oob
其中 write_chunk的第2个参数id, oh_page与data_page相等的话说明这是同一个文件的头与数据区
其中 write_chunk的第3个参数chunk_id, 区分oh_page与data_page, oh_page的chunk_id=0
参考:
1. http://blog.chinaunix.net/uid-26009923-id-3760474.html
2. http://blog.csdn.net/xingtian19880101/article/details/17504529