romfs
romfsはromfs_super_blockをスーパブロックとし、以降にromfs_inode、そしてその実態というレイアウトのファイル階層的に繰り返されたベタのイメージを持つファイルフォーマトです。これはgenromfsコマンドで作成できます。
romfsと名から分かるように、ファイルの更新等はできません。これはinodeを管理する必要が無いという事で、従って物理ストレージ内のinodeブロックが必要なく、ストレージ全てを実エリアとして使うことができるわけです。しかもブロックで管理する必要もないため、小さいファイルであっても、ストレーッジを無駄なく使うことができます。ただしアライメントとして16バイト単位とします。
従って、参照としてのファイル群をUSBメモリに作成する場合、ext3等で作成するより、romfsで作成した方が、実容量を最大限に使用することができると言う事です。
romfs_inodeのnextの設定値から、ディレクトリ/通常ファイル/リンク/スペシャルファイルを決定し、対応するinode,fileコールバック関数を設定し、また、実データ位置はinode->i_dataoffsetに設定しています。
上で見てきたようにエリアを有効に使うことができるのですが、問題はinodeを管理するブロックがないため、ファイル検索でのパフォーマンスが気になるところです。宅内ネットワークのmyサーバですが、youtubeからダウンロードした音源/動画ファイルがいうに3000を超えていて、音源ファイルを内部HDからusbメモリにromfsとして検証がてら試してみようかと思っています。
romfsと名から分かるように、ファイルの更新等はできません。これはinodeを管理する必要が無いという事で、従って物理ストレージ内のinodeブロックが必要なく、ストレージ全てを実エリアとして使うことができるわけです。しかもブロックで管理する必要もないため、小さいファイルであっても、ストレーッジを無駄なく使うことができます。ただしアライメントとして16バイト単位とします。
従って、参照としてのファイル群をUSBメモリに作成する場合、ext3等で作成するより、romfsで作成した方が、実容量を最大限に使用することができると言う事です。
struct romfs_super_block { __be32 word0; __be32 word1; __be32 size; __be32 checksum; char name[0]; }; struct romfs_inode { __be32 next; __be32 spec; __be32 size; __be32 checksum; char name[0]; };romfs作成サンプル
[root@localhost romfs]# ls 111 aaa [root@localhost romfs]# cat aaa this is aaa [root@localhost romfs]# ls 111 bbb [root@localhost romfs]# cat 111/bbb this is bbb上記内容をgenromfsコマンドで、romfsのイメージファイルromfs.imgを作成します。
[root@localhost romfs]# genromfs -f romfs.img [root@localhost romfs]# od -tx1z romfs.img スーパブロック 0000000 2d 72 6f 6d 31 66 73 2d 00 00 01 20 a6 eb 97 7e -rom1fs-... ...~ 0000020 72 6f 6d 20 35 32 64 39 65 32 37 35 00 00 00 00 rom 52d9e275.... rootディレクトリ 0000040 00 00 00 49 00 00 00 20 00 00 00 00 d1 ff ff 97 ...I... ........ 0000060 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ currentディレクトリ 0000100 00 00 00 60 00 00 00 20 00 00 00 00 d1 d1 ff 80 ...`... ........ 0000120 2e 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 111ディレクトリ 0000140 00 00 00 f9 00 00 00 80 00 00 00 00 ce ce cd 87 ................ 0000160 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 111............. rootディレクトリ 0000200 00 00 00 a0 00 00 00 60 00 00 00 00 d1 ff ff 00 .......`........ 0000220 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ currentディレクトリ 0000240 00 00 00 c0 00 00 00 20 00 00 00 00 d1 d1 ff 20 ....... ....... 0000260 2e 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ bbbファイル 0000300 00 00 00 02 00 00 00 00 00 00 00 0c 9d 9d 9d f2 ................ 0000320 62 62 62 00 00 00 00 00 00 00 00 00 00 00 00 00 bbb............. bbbの中身 0000340 74 68 69 73 20 69 73 20 62 62 62 0a 00 00 00 00 this is bbb..... aaaファイル 0000360 00 00 00 02 00 00 00 00 00 00 00 0c 9e 9e 9e f2 ................ 0000400 61 61 61 00 00 00 00 00 00 00 00 00 00 00 00 00 aaa............. aaaの中身 0000420 74 68 69 73 20 69 73 20 61 61 61 0a 00 00 00 00 this is aaa..... 0000440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................/dev/sddはusbメモリです。romfs.imgをusbメモリに書き込み、usbメモリをmountします。
[root@localhost romfs]# dd if=romfs.img of=/dev/sdd 2+0 レコード入力 2+0 レコード出力 1024 バイト (1.0 kB) コピーされました、 0.000275787 秒、 3.7 MB/秒 [root@localhost romfs]# mount -t romfs /dev/sdd /mnt3 mount: 警告: /mnt3 はリードオンリーとしてマウントされているようです。 [root@localhost romfs]# ls /mnt3 111 aaaromfsをmountした時のカーネルメッセージです。52d9e275はromfs_super_block->nameです。なお、物理デバイスがブロックデバイスと実メモリーの2つのケースがあります。サンプルはUSBメモリですからブロックデバイスとなります。
[root@localhost romfs]# dmesg : [70156.228123] ROMFS: Mounting image 'rom 52d9e275' through the block layerromfs_fill_super()はromfs_mount()からコールされ、struct super_block *sbを設定し、rootエントリを取得します。ROMFH_SIZEは __be32 word0/__be32 word1/__be32 size/__be32 checksumの16バイト、lenはエントリname長さで、16アライメントとすべくROMFH_PADを加算して、 & ROMFH_MASKとした値が、romfsイメージの最初のエントリ(root)となり、romfs_iget(sb, pos);でrootのinodeを取得します。
static struct file_system_type romfs_fs_type = { .owner = THIS_MODULE, .name = "romfs", .mount = romfs_mount, .kill_sb = romfs_kill_sb, .fs_flags = FS_REQUIRES_DEV, }; #define ROMSB_WORD0 __mk4('-','r','o','m') #define ROMSB_WORD1 __mk4('1','f','s','-') #define ROMFH_SIZE 16 #define ROMFH_PAD (ROMFH_SIZE-1) #define ROMFH_MASK (~ROMFH_PAD) static int romfs_fill_super(struct super_block *sb, void *data, int silent) { struct romfs_super_block *rsb; struct inode *root; unsigned long pos, img_size; const char *storage; size_t len; int ret; #ifdef CONFIG_BLOCK if (!sb->s_mtd) { sb_set_blocksize(sb, ROMBSIZE); } else { sb->s_blocksize = ROMBSIZE; sb->s_blocksize_bits = blksize_bits(ROMBSIZE); } #endif sb->s_maxbytes = 0xFFFFFFFF; sb->s_magic = ROMFS_MAGIC; sb->s_flags |= MS_RDONLY | MS_NOATIME; sb->s_op = &romfs_super_ops; rsb = kmalloc(512, GFP_KERNEL); if (!rsb) return -ENOMEM; sb->s_fs_info = (void *) 512; ret = romfs_dev_read(sb, 0, rsb, 512); if (ret < 0) goto error_rsb; img_size = be32_to_cpu(rsb->size); if (sb->s_mtd && img_size > sb->s_mtd->size) goto error_rsb_inval; sb->s_fs_info = (void *) img_size; if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 || img_size < ROMFH_SIZE) { if (!silent) printk(KERN_WARNING "VFS:" " Can't find a romfs filesystem on dev %s.\n", sb->s_id); goto error_rsb_inval; } if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) { printk(KERN_ERR "ROMFS: bad initial checksum on dev %s.\n", sb->s_id); goto error_rsb_inval; } storage = sb->s_mtd ? "MTD" : "the block layer"; len = strnlen(rsb->name, ROMFS_MAXFN); if (!silent) printk(KERN_NOTICE "ROMFS: Mounting image '%*.*s' through %s\n", (unsigned) len, (unsigned) len, rsb->name, storage); kfree(rsb); rsb = NULL; pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK; root = romfs_iget(sb, pos); if (IS_ERR(root)) goto error; sb->s_root = d_alloc_root(root); if (!sb->s_root) goto error_i; return 0; error_i: iput(root); error: return -EINVAL; error_rsb_inval: ret = -EINVAL; error_rsb: kfree(rsb); return ret; }最初のfor()は、対象のエントリがハードリンクの場合の処理です。エントリがディレクトリないし、ハードリンクの場合、次のエントリはromfs_inodeのspecに設定されるようです。
romfs_inodeのnextの設定値から、ディレクトリ/通常ファイル/リンク/スペシャルファイルを決定し、対応するinode,fileコールバック関数を設定し、また、実データ位置はinode->i_dataoffsetに設定しています。
static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) { struct romfs_inode_info *inode; struct romfs_inode ri; struct inode *i; unsigned long nlen; unsigned nextfh; int ret; umode_t mode; for (;;) { ret = romfs_dev_read(sb, pos, &ri, sizeof(ri)); if (ret < 0) goto error; /* XXX: do romfs_checksum here too (with name) */ nextfh = be32_to_cpu(ri.next); if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) break; pos = be32_to_cpu(ri.spec) & ROMFH_MASK; } nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN); if (IS_ERR_VALUE(nlen)) goto eio; /* get an inode for this image position */ i = iget_locked(sb, pos); if (!i) return ERR_PTR(-ENOMEM); if (!(i->i_state & I_NEW)) return i; /* precalculate the data offset */ inode = ROMFS_I(i); inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK; inode->i_dataoffset = pos + inode->i_metasize; set_nlink(i, 1); /* Hard to decide.. */ i->i_size = be32_to_cpu(ri.size); i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; /* set up mode and ops */ mode = romfs_modemap[nextfh & ROMFH_TYPE]; switch (nextfh & ROMFH_TYPE) { case ROMFH_DIR: i->i_size = ROMFS_I(i)->i_metasize; i->i_op = &romfs_dir_inode_operations; i->i_fop = &romfs_dir_operations; if (nextfh & ROMFH_EXEC) mode |= S_IXUGO; break; case ROMFH_REG: i->i_fop = &romfs_ro_fops; i->i_data.a_ops = &romfs_aops; if (i->i_sb->s_mtd) i->i_data.backing_dev_info = i->i_sb->s_mtd->backing_dev_info; if (nextfh & ROMFH_EXEC) mode |= S_IXUGO; break; case ROMFH_SYM: i->i_op = &page_symlink_inode_operations; i->i_data.a_ops = &romfs_aops; mode |= S_IRWXUGO; break; default: /* depending on MBZ for sock/fifos */ nextfh = be32_to_cpu(ri.spec); init_special_inode(i, mode, MKDEV(nextfh >> 16, nextfh & 0xffff)); break; } i->i_mode = mode; unlock_new_inode(i); return i; eio: ret = -EIO; error: printk(KERN_ERR "ROMFS: read error for inode 0x%lx\n", pos); return ERR_PTR(ret); }
追記
上記サンプルはUSBメモリに複写しての操作ですが、ループバックデバイスにromfss.imgをマウントすることもできます。システムの容量が厳しくなった時、とりわけiノード数が厳しくなった時の回避策として使えそうです。とりわけiノードについては、効果大と言うのは、言うまでもありません。上で見てきたようにエリアを有効に使うことができるのですが、問題はinodeを管理するブロックがないため、ファイル検索でのパフォーマンスが気になるところです。宅内ネットワークのmyサーバですが、youtubeからダウンロードした音源/動画ファイルがいうに3000を超えていて、音源ファイルを内部HDからusbメモリにromfsとして検証がてら試してみようかと思っています。