UNIX 7th code reading - ブロックデバイスのリソース管理

はじめに

今回はsuperblockに関する処理や、alloc( ), ialloc( )などブロックデバイスのリソース管理を見ていきます。

iinit( )

起動時のmain( )からiinit( )が呼ばれ、root deviceの初期化が行われます。

root deviceの、0から数えた1番目のblock(=superblock)を読み出し、mount[0]にroot devを設定します。mount[0]からroot devのsuperblock(filsys)情報にアクセスできます。

v6と処理内容は変わっていません。

ialloc( )

ialloc( )はblock deviceのinodeフリーリストから空きinode numberを取得し、iget( )を使ってコア中のinodeを呼び出し元に返します。

フリーリストから取得したinode numberがroot inode(=2)未満ならば取得のやり直しをするというロジックが追加されています。

 161                 ino = fp->s_inode[--fp->s_ninode];
 162                 if (ino < ROOTINO)
 163                         goto loop;

v6ではroot inodeは1だったのですが、v7では2になったがために追加されたロジックでしょうか。inode number 1のinodeはialloc( )では取得できないということだと思われます。

ifree( )

ifree( )は必要なくなったinodeのinode numberをフリーリストに戻します。フリーリストが満杯だったら、そのinode numberはフリーリストに戻らず捨てられます。捨てられたinode numberはialloc( )でのフリーリストへの格納処理時に拾われます。

v6と処理内容は変わっていません。

alloc( )

alloc( )はblock deviceのdata blockフリーリストから空きdata block numberを取得し、getblock( )を使ってブロックバッファを呼び出し元に返します。

フリーリストの要素数がMAX100より多くなっていたらno spaceエラーとなるロジックが追加されています。100より多くなるのはどういうときなんでしょう。

  39                 if (fp->s_nfree > NICFREE) {
  40                         prdev("Bad free count", dev);
  41                         goto nospace;
  42                 }

また、フリーリストが空になったときに、フリーリストに空きblock numberを格納する際、bread( )後にエラーチェックが入っています。どのようなエラーを想定しているのでしょうか。

  49                 bp = bread(dev, bno);
  50                 if ((bp->b_flags&B_ERROR) == 0) {
  51                         fp->s_nfree = ((FBLKP)(bp->b_un.b_addr))->df_nfree;
  52                         bcopy((caddr_t)((FBLKP)(bp->b_un.b_addr))->df_free,
  53                             (caddr_t)fp->s_free, sizeof(fp->s_free));
  54                 }

block number格納処理後、フリーリストの要素数が0以下(つまり資源枯渇)ならばエラーとなるようになりました。

  58                 if (fp->s_nfree <=0)
  59                         goto nospace;

free( )

free( )は必要なくなったdata blockのblock numberをフリーリストに戻します。フリーリストが満杯になったら、そのフリーリストの内容はブロックデバイスに書き戻し、次のリストをブロックデバイスから読み込みます。

v6と処理は変わっていません。

badblock( )

badblock( )はblock numberの妥当性チェックをします。

v6と処理内容は変わりません。

getfs( )

getfs( )はdevice numberからmountされたブロックデバイスのsuperblock(=filsys)を取得します。

v6と処理は変わっていません。

update( )

update( )はコア中のmount[ ], inode[ ], block bufferをブロックデバイスにフラッシュします。

mount[ ]の内容をフラッシュするときに、getblk( )でブロックバッファを取得した後にエラーチェックをするロジックが追加されました。

 302                         bp = getblk(mp->m_dev, SUPERB);
 303                         if (bp->b_flags & B_ERROR)
 304                                 continue;

inode[ ]の内容をフラッシュするときに、inodeの参照カウンタが0でないという条件が追加されました。v6ではこれがなかったので、参照カウンタが0のものも更新を試みてしまっていたようです。

また、iupdat( )の前後で、参照カウンタのインクリメント、iput( )によるデクリメントが追加されました。これが何を意味しているのか、まだよくわかっていません。

 310         for(ip = &inode[0]; ip < &inode[NINODE]; ip++)
 311                 if((ip->i_flag&ILOCK)==0 && ip->i_count) {
 312                         ip->i_flag |= ILOCK;
 313                         ip->i_count++;
 314                         iupdat(ip, &time, &time);
 315                         iput(ip);
 316                 }

終わりに

全体的にv6から変更の少ない箇所でした。

それでもチェック強化のロジックはちらほら入っており、それらがどういう経緯で入ったのか、もしくは、そのロジックのないv6ではどのような脆弱性があったのか、などをもっと考えたいのですが、とりあえずはどんどん先に進もうと思います。

ちょいと諸事情でblogの更新頻度が落ちそうな感じです。