ミラーリングポートに接続する機器のIP

かなりの期間、知らずにいたことですが、ミラーリングポートにパケットキャプチャの目的で接続する機器にそのネットワークのIPがふられている必要がないということ。

IPが何であろうがミラーリングポートにはパケットが流れてくるため、接続する機器は否応なしにパケットを見ることができます。
ですので、ネットワーク障害が発生したら、下記のようなミラーリングポート付きのハブとWiresharkのようなパケットをみることのできるソフトの入ったノートPCを持って行き、ネットワーク設定なしで即解析にはいることができます。

vectorの気遣い

vectorで(ポインタではなく)オブジェクト自体を取り扱う場合、push_backしたときに次のことが行われています。

  • push_backの対象となるオブジェクトをコピーコンストラクタでコピー
  • 既にpush_back済みのものがあれば、再びコピーコンストラクタでそのコピーを作り、元々コピーしてあったものをデストラクタで始末する。

実際にコードを書いて確認してみました。
まずは、クラス定義から。

class Hoge
{
public:
  int num;
  Hoge(int num);
  Hoge(const Hoge& me);
  ~Hoge();
  void printAbc(void);
};

Hoge::Hoge(int aNum)
{
  num = aNum;
  printf("constructer %d\n", num);
}

Hoge::Hoge(const Hoge& me) : num(me.num)
{
  printf("copy constructer %d\n", num);
}

Hoge::~Hoge()
{
  printf("destructer %d\n", num);
}

実際にvectorを使ってクラスHogeのオブジェクトを操作してみます。

int main(int argc, char** argv)
{
  Hoge sample1(1);
  Hoge sample2(2);
  std::vector<Hoge> iremono;

  iremono.push_back(sample1);
  iremono.push_back(sample2);

  return 0;
}

コンパイルして実行しますと、次のような結果が得られます。

constructer 1
constructer 2
copy constructer 1
copy constructer 2
copy constructer 1
destructer 1
destructer 1
destructer 2
destructer 2
destructer 1

最初のコンストラクタ二つは、ソースの3,4行目によるものです。このコンストラクタに対するデストラクタは、最後の二つで、mainを抜ける際に、ローカル変数であるsample1,sample2に対して発生しています。
最初のコピーコンストラクタは一回目のpush_back時に、つまりsample1のpush時に発生しています。そして次のコピーコンストラクタは二回目のpush_back時、つまりsample2のpush時に発生しています。直後、一回目のコピーコンストラクタでコピーされたもののコピーが発生しています。そして、次のデストラクタで一回目のpush_back時にコピーしたもののデストラクタが発生しています。
つまり、push_back時にはpushしたものがコピーされるだけではなく、既に入っているものも同時にコピーし直されます。

そして、次のデストラクタ二つはローカル変数iremonoがmainから抜けるとき、中のオブジェクトのデストラクタが発生することを示しています。このように明示しなくても中のオブジェクトをデストラクトしてくれるのは、vectorで(ポインタではなく)オブジェクト自体を持つことのメリットだと思います。ちなみに、このときのデストラクタの発生する順番はpush_backした順番です。

私はvectorでオブジェクト自体を持つのは避けるようにしています。上記のようなメリットはあるのですが、vectorの「気遣い」であるコピーコンストラクタ、デストラクタに、反って戸惑わされる恐れがあるからです。それよりも「後始末をしっかりする」ことさえ注意していればよい「ポインタを持つ」方が楽なのでは、と考えています。

Androidでスクリーンショット

以前iPhoneユーザがお手軽にスクリーンショットを撮っているのをみて、Androidでもできないかと調べてみたのですが、手順がややこしくとても実用的なものではなかったので諦めていました。
ところが今日、何気にAndroidのSDカードの直下を見てみると「screen shot」という見慣れないフォルダができているのを発見しました。中を見ると、2週間ほどまえの日付のホーム画面の画像がPNGで保存されていました。
さっそく再調査してみたところ、一部機種(私はGALAPAGOS SoftBank 003SHを使っています。)はホームボタンと電源ボタンの同時押しでスクリーンショットが撮れるとのこと。
試してみると見事に撮れました。

2週間ほど前に意図せず撮れていたスクリーンショットが功を奏したかたちとなりました。

MacBook Pro(15-Inch, Early 2011)のメモリ4GB→16GB増設

MacBook Pro(15-Inch, Early 2011)

を現在のSnow Leopard から Mountain Lionにしようと思ったのですが、どうも現在の4GBのメモリでは苦しそうだということが、いろいろな方のブログなどからわかりました。

そこで今回のメモリの増設となりました。このMacBook Proの公式な対応メモリ容量のMAXは8GBなのですが、いろいろな方の情報だと16GBも可能、とのことでしたので、8GBメモリの二枚挿し、16GBに増設することにしました。

肝心のメモリは、Transcend JetRam ノートPC用増設メモリ PC3-10600(DDR3-1333) 8GB 永久保証 JM1333KSH-8G

を2枚購入しました。Tポイントも使えましたので、合計5000円ほどで購入しました。
また、裏ぶたをあけるためのドライバを100円ショップで購入しました。

数日後、メモリが無事届きました。
届いたメモリ

さっそく裏ぶたをはずそうとしたのですが、どうしても緩められないネジがありました。100円ショップで購入した#00のプラスドライバーではグリップに力が入らないためのようで、しっかりしたドライバーを買う必要があると判断し、
VESSEL(ベッセル) マイクロドライバー No.9900 +00×75

を購入しました。

数日後、ドライバーが無事届きました。
届いたドライバー

半信半疑でしたが、届いたドライバーを使うとあっけなく裏ぶたを開けることができました。
蓋開け直後

中央部にメモリのスロットがあり、両サイドのフックを広げて外すと二枚重なっているのがわかります。
二枚重なっているメモリ

二枚を届いたメモリと交換し、裏ぶたを閉めて、動作確認をしました。
mac_16gb

BootCampに入れているWindows7でも当然16GBになっています。
win_16gb

メモリ増設成功です。

コマンドラインからmp4動画の任意場面をキャプチャする

キャプチャに使用するプログラムはフリーの動画プレイヤーとして有名なVLC Media Playerです。
インストールするとGUIで使用できるのはもちろんですが、CUIでも使用できます。
そして、VLCにはキャプチャ機能が備わっており、CUIからも使用できます。つまり、これはプログラムから起動(例えばfork&exec)させ、裏(GUI表示なし)でキャプチャができることを意味しています。

キャプチャは以下のコマンドで可能です。

cvlc hoge.mp4 --rate=1 --video-filter=scene --vout=dummy --start-time=0 --stop-time=0.5 --scene-format=jpg --scene-ratio=500 --scene-prefix=foo --scene-replace --scene-path=. vlc://quit

start-timeとstop-timeでキャプチャする対象となるシーンの位置を指定します。(単位は秒)
できるだけコマンド実行を早く終わらせるために、start-timeとstop-timeの間は小さくしたほうがいいです。(ちなみに同じ値にするとキャプチャできません。)
scene-ratioはキャプチャをする間隔(何フレーム毎にキャプチャするか)で、上記のように比較的大きな値にしておけばキャプチャは1度しか行われません。(上記の場合0.5秒間なので500フレームもあるはずないということです。最初のフレームをキャプチャした後、次のキャプチャまで500フレーム待ちますが、その前にstop-timeになって終了、、、という流れです。)

実行後、実行したディレクトリに
foo.jpg
というファイルができているはずです。

vectorに詰め込んだオブジェクト解放するための便利関数

久しぶりにテンプレート関数を使おうとしたときのいくつかの間違いを覚書として書いておきます。

Utilityクラスなるものを用意し、次のような静的メソッドを宣言、定義しました。

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v);

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  std::vector<T*>::iterator it;
  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

これをコンパイルすると、

.cpp: 静的メンバ関数 ‘static void Utility::releaser(std::vector<T*>&)’ 内:
.cpp:???:???: エラー: need ‘typename’ before ‘std::vector<T*>::iterator’ because ‘std::vector<T*>’ is a dependent scope
.cpp:???:???: エラー: expected ‘;’ before ‘it’
.cpp:???:???: エラー: ‘it’ was not declared in this scope

といったエラーがでます。

because ‘std::vector’ is a dependent scope
はstd::vectorという部分がTに依存するため、型なのか変数名なのか、何なのか分からない、ということ言っています。
(VC++の場合は、問題なくコンパイルが通るらしいです。)

need ‘typename’ before ‘std::vector::iterator’
型だと分かるように「typename」をつけてください、と言っています。
この時点でこのコンパイラ自身は型だとわかっているようですが、どんなコンパイラでも通るように厳しく戒めてくれている、という理解です。

Utility.cpp

template<typename T>
void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
}

言われた通りにつけてみましたが、今度は、

Hoge.o: In function `hogehoge(void)':
Hoge.cpp:???: undefined reference to `void Utility::releaser<Hoge>(std::vector<Hoge*, std::allocator<Hoge*> >&)'

と怒られてしまいました。
これは実際にHoge.cppでUtility::releaserを使用している個所でその実体が何か分からない、ということを言っています。
Utility.cppにおいて、クラスHogeを考慮したテンプレート関数の実体化を行っていない、かつ、Hoge.cppでも実体化を行っていないためです。
そこで、Hoge.cppで実体化が行われるように、Utility.h、つまりヘッダにテンプレートの実装を書きます。
Utility.cppの実装は消去し、

Utility.h

template<typename T>
static void Utility::releaser(std::vector<T*>& v)
{
  typename std::vector<T*>::iterator it;

  it = v.begin();
  while(it != v.end()){
    delete (*it);
    it = v.erase(it);
  }
};

これで、テンプレート関数が使えます。

どうしても定義と実装を分けたい場合は、
明示的実装というものがあり、分けることもできるそうです。
予めテンプレートの使用パターンを宣言しておくというやり方です。
また、exportという記述子があり、定義と実装を分け、予めのパターン列挙も必要ないそうです。
ただし、こちらは対応コンパイラが少ないそうです。

このあたりのお話は、こちらが参考になりました。
http://d.hatena.ne.jp/pknight/20090826/1251303641
http://d.hatena.ne.jp/pknight/20090826/1251307608

inotifyによるLinuxファイルシステムイベントの監視

Linuxファイルシステムに発生したイベントを監視するために、inotifyというAPIを使うことができます。
プログラム実行ディレクトリ以下を監視するプログラムのソースコードとその動作結果を掲載します。

まず、ソースコード(inotify_sample.c)です。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <limits.h>

#define WATCH_DIR "."

//イベントサイズは16バイト境界
#define INOTIFY_EVENT_MAX (((sizeof(struct inotify_event)+NAME_MAX+1)+16)&~16)

typedef struct _WD_INFO
{
  struct _WD_INFO* prev;
  struct _WD_INFO* next;
  int wd;
  char* path;
} WD_INFO;

static WD_INFO* topWdInfo = NULL;

static void getWdInfo(int fd, char* dirname)
{
  DIR* dir = NULL;
  struct dirent* entry;
  struct stat st;
  int wd;
  int dirname_len;
  int entname_len;
  char* fullpath = NULL;
  WD_INFO* newWdInfo;

  newWdInfo = (WD_INFO*)malloc(sizeof(WD_INFO));

  if(topWdInfo == NULL){
    // first
    topWdInfo = newWdInfo;
    topWdInfo->prev = topWdInfo;
    topWdInfo->next = topWdInfo;
  }else{
    newWdInfo->prev = topWdInfo->prev;
    topWdInfo->prev->next = newWdInfo;
    topWdInfo->prev = newWdInfo;
    newWdInfo->next = topWdInfo;
  }

  newWdInfo->wd = inotify_add_watch(fd, dirname, IN_ALL_EVENTS);
  newWdInfo->path = strdup(dirname);

  //Search Sub directry
  dir = opendir(dirname);

  dirname_len = strlen(dirname);

  while((entry = readdir(dir)) != NULL){

    if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0){
      continue;
    }

    //エントリのタイプ種別を非標準のd_typeを使わず、statで取得
    entname_len = strlen(entry->d_name);
    fullpath = (char*)malloc(dirname_len + 1 + entname_len + 1);
    strcpy(fullpath, dirname);
    strcat(fullpath, "/");
    strcat(fullpath, entry->d_name);
    stat(fullpath, &st);

    if(S_ISDIR(st.st_mode)){
      //再帰呼び出し
      getWdInfo(fd, fullpath);
    }

    free(fullpath);
  }

  closedir(dir);
}


static char* wd2path(int wd)
{
  WD_INFO* p;

  if(topWdInfo == NULL){
    return NULL;
  }

  p = topWdInfo;
  do{
    if(p->wd == wd){
      return p->path;
    }
    p = p->next;
  }while(p != topWdInfo);

  return NULL;
}

static void closeAllWdInfo(int fd)
{
  WD_INFO* p;

  if(topWdInfo == NULL){
    return;
  }

  p = topWdInfo;
  do{
    WD_INFO* del;
    del = p;
    p = p->next;
    free(del->path);
    inotify_rm_watch(fd, del->wd);
    free(del);
  }while(p != topWdInfo);
  topWdInfo = NULL;
}

static void deleteWdInfo(int wd)
{
  WD_INFO* p;

  if(topWdInfo == NULL){
    return;
  }

  p = topWdInfo;
  do{
    if(p->wd == wd){
      if(p->next == p->prev){
        topWdInfo = NULL;
      }else{
        if(p == topWdInfo){
          topWdInfo = p->next;
        }
        p->next->prev = p->prev;
        p->prev->next = p->next;
      }
      free(p->path);
      free(p);

      return;
    }
    p = p->next;
  }while(p != topWdInfo);

  return;
}

int main(int argc, char** argv)
{
  struct timeval waitval;
  int fd;
  int ret;
  fd_set readfds;

  fd = inotify_init();

  getWdInfo(fd, (char*)WATCH_DIR);

  while(1){
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);
    ret = select(fd+1, &readfds, NULL, NULL, NULL);
    if(0 < ret){
      if(FD_ISSET(fd, &readfds)){
        char* buf;
        int len;
        struct inotify_event* event;

        buf = (char*)malloc(INOTIFY_EVENT_MAX);

        //INOTIFY_EVENT_MAXを指定し、最低でも一つのイベントは読み込む。
        len = read(fd, buf, INOTIFY_EVENT_MAX);

        event = (struct inotify_event*)buf;

        //複数イベントがあるかもしれない。全部処理するまでループ。
        while(len > 0){
          char* target;
          if(event->len){
            target = event->name;
          }else{
            target = wd2path(event->wd); 
          }


          if(event->mask & IN_ACCESS){
            printf("[%s] was accessed.\n", target);
          }
          if(event->mask & IN_MODIFY){
            printf("[%s] was modified.\n", target);
          }
          if(event->mask & IN_ATTRIB){
            printf("Metadata of [%s] changed.\n", target);
          }
          if(event->mask & IN_CLOSE_WRITE){
            printf("Writtable [%s] was closed.\n", target);
          }
          if(event->mask & IN_CLOSE_NOWRITE){
            printf("Unwrittable [%s] closed.\n", target);
          }
          if(event->mask & IN_OPEN){
            printf("[%s] was opened.\n", target);
          }
          if(event->mask & IN_MOVED_FROM){
            printf("[%s] was moved from X.\n", target);
          }
          if(event->mask & IN_MOVED_TO){
            printf("[%s] was moved to Y.\n", target);
          }
          if(event->mask & IN_CREATE){
            printf("[%s] was created in [%s].\n", event->name, wd2path(event->wd));
            char* dirname;
            int dirname_len;
            int eventname_len;
            char* fullpath;
            struct stat st;

            dirname = wd2path(event->wd);
            eventname_len = strlen(event->name);
            fullpath = (char*)malloc(dirname_len + 1 + eventname_len + 1);
            strcpy(fullpath, dirname);
            strcat(fullpath, "/");
            strcat(fullpath, event->name);
            stat(fullpath, &st);

            if(S_ISDIR(st.st_mode)){
              //監視対象追加
              getWdInfo(fd, fullpath);
            }
          }
          if(event->mask & IN_DELETE){
            printf("[%s] was deleted in [%s].\n", event->name, wd2path(event->wd));
          }
          if(event->mask & IN_DELETE_SELF){
            printf("[%s] was deleted.\n", target);
          }
          if(event->mask & IN_MOVE_SELF){
            printf("[%s] was moved.\n", target);
          }

          if(event->mask & IN_IGNORED){
            printf("[%s] was ignored.\n", target);
            //監視対象削除
            deleteWdInfo(event->wd);
          }

          len -= (sizeof(struct inotify_event) + event->len);
          event = (struct inotify_event*)(((char*)event)+sizeof(struct inotify_event) + event->len);
        }
        free(buf);
      }
    }
  }

  closeAllWdInfo(fd);
  close(fd);

  return 0;
}

main()から見ていきます。
まず、inotify_init()にて監視対象の元締めみたいなものを作ります。その後、getWdInfo()を再帰的に呼び出して、カレントディレクトリ以下のディレクトリ全てを、inotify_add_watch()を使って、元締めに対して登録していきます。後はselect()を使って、元締めを監視します。監視対象のディレクトリ内でなんらかのイベントが発生すると、それを元締めが

/* Structure describing an inotify event.  */
struct inotify_event
{
  int wd;		/* Watch descriptor.  */
  uint32_t mask;	/* Watch mask.  */
  uint32_t cookie;	/* Cookie to synchronize two events.  */
  uint32_t len;		/* Length (including NULs) of name.  */
  char name __flexarr;	/* Name.  */
};

この構造体を通じて詳細に教えてくれます。教えてもらうには元締めからread()します。
構造体に記載の__flexarrは[0]のことであり、通知イベントが可変長であることを示しています。
read()時に気をつけなければならないのは、必ず一つのイベントを読みきるということです。read()時に指定するサイズが小さいとエラーを返します。
このことは構造体のwdからlenまでをまず読んで、lenにしたがって後のデータを読む、という誰もが考える効率の良い読み方ができないことを示しています。私はこれにハマってしまいました。気をつけてください。
とういうことで、read()に指定するサイズは考えられる最大イベントサイズを指定する必要があり、nameのMAXがlimits.hで定義されているNAME_MAX、かつ(実際実行してみたところ、)必ず16の倍数サイズにされるようなので、

#define INOTIFY_EVENT_MAX (((sizeof(struct inotify_event)+NAME_MAX+1)+16)&~16)

のようにread()サイズを指定しました。

あとはイベント内容を読み取り、表示しています。また、ディレクトリの追加イベント((IN_CREATE)発生時は、監視対象が増えたので、元締めに対してinotify_add_watch()を行ってます。監視対象削除イベント(IN_IGNORED)に対しては、保持している内部データWD_INFOから対象を削除しています。監視対象削除イベント発生時に自動的にinotify_rm_watch()相当が行われるため、inotify_rm_watch()は行わないで良いようです。

コンパイルしてみました。

$ gcc -g inotify_sample.c -o inotify_sample.c

動作を見るためにプログラム実行ディレクトリを、以下のような環境にしておきます。

$ find . -print
.
./foo
./foo/bar
./foo/bar/bar0
./inotify_sample.c
./inotify_sample
./hoge
./hoge/hoge0

環境を整え、実行してみました。

$ ./inotify_sample
[.] was opened.
[foo] was opened.
[./foo] was opened.
[bar] was opened.
[./foo/bar] was opened.
Unwrittable [bar] closed.
Unwrittable [./foo/bar] closed.
Unwrittable [foo] closed.
Unwrittable [./foo] closed.
[hoge] was opened.
[./hoge] was opened.
Unwrittable [hoge] closed.
Unwrittable [./hoge] closed.
Unwrittable [.] closed.

実行するとパラパラとイベントが発生しているのがわかります。
これらはinotify_sampleプログラムで監視対象を登録するためにopendir()等を行っているために発生しています。つまり、自分自身がイベントを発生させているわけです。

このまま、ファイルシステムに変更を与えた時の動作を見ていきました。
(結果を見ると動作は理解できると思いますので、説明は特にいれません。)
プログラム実行しているターミナルとは別ターミナルから

$ touch hoge/hoge1

とすると、プログラム実行側のターミナルでは、

[hoge1] was created in [./hoge].
[hoge1] was opened.
Metadata of [hoge1] changed.
Writtable [hoge1] was closed.

のようになります。その他、いろいろ操作してみました。

————————————————–

$ rm hoge/hoge1

とすると、

[hoge1] was deleted in [./hoge].

————————————————–

$mkdir foo/bar/fuga

とすると、

[fuga] was created in [./foo/bar].
[fuga] was opened.
[./foo/bar/fuga] was opened.
Unwrittable [fuga] closed.
Unwrittable [./foo/bar/fuga] closed.

————————————————–

$ mv foo piyo

とすると、

[foo] was moved from X.
[piyo] was moved to Y.
[./foo] was moved.

————————————————–

$ rm -rf piyo

とすると、

[piyo] was opened.
[./foo] was opened.
[bar] was opened.
[./foo/bar] was opened.
[bar0] was deleted in [./foo/bar].
[fuga] was opened.
[./foo/bar/fuga] was opened.
Unwrittable [fuga] closed.
Unwrittable [./foo/bar/fuga] closed.
[fuga] was deleted in [./foo/bar].
Unwrittable [bar] closed.
[./foo/bar/fuga] was deleted.
Unwrittable [./foo/bar] closed.
[bar] was deleted in [./foo].
[piyo] was deleted in [.].
Unwrittable [piyo] closed.
Unwrittable [./foo] closed.
[./foo/bar/fuga] was ignored.
[./foo/bar] was deleted.
[./foo/bar] was ignored.
[./foo] was deleted.
[./foo] was ignored.

————————————————–

正しく動作していそうです。

ソースコードはSyntaxHighlighter、ではコマンドラインは?

技術的な説明をするときに、ソースコードを載せて説明したりしますが、そのときにソースコードを体裁良く表示してくれるSyntaxHighlighterという便利なJavaScriptがあります。ここでもWordPressのプラグインとして有効化し、利用させてもらっています。

ソースコードの表示したければ、SyntaxHighlighterを使えばいい、というのは調べて簡単に分かりました。しかし、Linuxのコマンド入力などを説明するためにも同じようなものはないかと、いろいろと調べましたがなかなか見つかりませんでした。

やっと「コマンドラインをSyntaxHighlighterのように表示したい」という思いを「コマンドライン SyntaxHighligher」というキーワードにして検索したときに、所望の答えが見つかりました。実は、SyntaxHighlighterにすでに搭載されている機能とのことです。 例えば、以下のように書きます。

[bash] 
> cd /home/user
> 
[/bash] 

すると、

> cd /home/user
>

のように表示されます。

ちなみに前者は、

<pre class="brush:text";>
&#91;bash] 
> cd /home/user
> 
[/bash] 
</pre>

のように書いています。<pre class=”brush:~>,</pre>で囲み、「<」の文字実体参照「&#91;」を使うのがポイントです。

また、ドンピシャの答えを出してくれたところは、
http://ja.forums.wordpress.org/topic/9002
です。

extファイルシステムにおけるinode番号の無効値

ext2/3/4といったファイルシステムにあるinode番号について。inodeはファイルシステム内にあるファイルやディレクトリなどのオブジェクトを示すものであり、inode番号はそれらを識別するための番号であり、同じファイルシステム上でユニークな値、かつ、正の整数です。上限値は環境に依存しますが、LinuxのC言語ではino_tという型が用意されており、符号無し32ビット整数だったり符号無し64ビット整数だったりします。
このinode番号をプログラムで使用する場合、当然ino_t型の変数を用意して使用することになりますが、初期値や無効値には何を指定すればよいか悩みました。符号無しなので-1を使用することはできません。
いろいろ調べていて、0にするのが適切であるとの見解に至りました

/usr/include/linux/ext2_fs.h(あるいはext3_fs.h)を見てみるとinode番号に関して次のような定義があります。

 

/*
 * Special inode numbers
 */
#define	EXT2_BAD_INO		 1	/* Bad blocks inode */
#define EXT2_ROOT_INO		 2	/* Root inode */
#define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
#define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */

/* First non-reserved inode for old ext2 filesystems */
#define EXT2_GOOD_OLD_FIRST_INO	11

10までが予約されていて、通常は11以降の番号が振られる、ということを示しています。0については「Special inode numbers」でもないので、初期値や無効値として利用するにはちょうど良い、と考えました。手元のLinuxで0番が使われていないことを確認してみました。

 

> find / -inum 0 -print
>

使われていませんでした。 ということで、0をinode番号の初期値や無効値として利用することにしました。