ディレクトリストリーム

変更履歴

概 要

目 次

参考資料

ディレクトリストリームの型と構造体

DIR型

▹ディレクトリストリームのデータ型として, 次の DIR 型が定義されています. なお,上記の dirent/dirent.h は,DIR型を定義しているファイルの相対パスを示しています(以下同様).その起点はglibcのソースコードを展開したディレクトリです.
/* This is the data type of directory stream objects.
   The actual structure is opaque to users.  */
typedef struct __dirstream DIR;

dirstream構造体

▹struct __dirstream(以下,dirstream構造体)は, 次のように定義されています.
/* Directory stream type.

   The miscellaneous Unix `readdir' implementations read directory data
   into a buffer and return `struct dirent *' pointers into it.  */

struct __dirstream
  {
    int fd;                     /* File descriptor.  */

    __libc_lock_define (, lock) /* Mutex lock for this structure.  */

    size_t allocation;          /* Space allocated for the block.  */
    size_t size;                /* Total valid data in the block.  */
    size_t offset;              /* Current offset into the block.  */

    off_t filepos;              /* Position of next entry to read.  */

    int errcode;                /* Delayed error code.  */

    /* Directory block.  We must make sure that this block starts
       at an address that is aligned adequately enough to store
       dirent entries.  Using the alignment of "void *" is not
       sufficient because dirents on 32-bit platforms can require
       64-bit alignment.  We use "long double" here to be consistent
       with what malloc uses.  */
    char data[0] __attribute__ ((aligned (__alignof__ (long double))));
  };
ディレクトリストリームの実体(ディレクトリエントリーの列)は, 上記の data 配列にバイナリデータとして格納されていると思われます. この点を正確に理解するためには getdents.c の中で定義されている __getdents 関数の処理内容を理解しなければならないのですが, 残念ながら筆者にはその力量がありません. ただ,readdir関数の処理(後述)を見ると, このように推測するのが妥当だと思います.

dirent構造体

▹ディレクトリエントリーは, 次のような構造体(以下,dirent構造体)として定義されています. なお,日本語の行コメントは readdir(3)(readdirライブラリ関数のmanpage)を参考に, 筆者が追加したものです.
struct dirent
  {
#ifndef __USE_FILE_OFFSET64
    __ino_t d_ino;                  // inode番号
    __off_t d_off;                  // 理解不能(下記参照)
#else
    __ino64_t d_ino;
    __off64_t d_off;
#endif
    unsigned short int d_reclen;    // このエントリーの長さ
    unsigned char d_type;           // ファイルの種別
    char d_name[256];               // ファイル名・ディレクトリ名
  };
▹上記の d_off について readdir(3) は次のように説明しています.
The value returned in d_off is the same as would be returned by calling telldir(3) at the current position in the directory stream. Be aware that despite its type and name, the d_off field is seldom any kind of directory offset on modern filesystems. Applications should treat this field as an opaque value, making no assumptions about its contents; see also telldir(3).
さらに, telldir(3)のNOTESは次のように説明しています.
In early filesystems, the value returned by telldir() was a simple file offset within a directory. Modern filesystems use tree or hash structures, rather thanflat tables, to represent directories. On such filesystems, the value returned by telldir() (and used internally by readdir(3)) is a "cookie" that is used by the implementation to derive a position within a directory. Application programs should treat this strictly as an opaque value, making no assumptions about its contents.
上記の「used internally by readdir(3)」は,readdirライブラリ関数がd_offを使用しているように推測させますが,実質的にまったく使用していないと思います(後述). 従って,d_offの意味(役割)は, ディレクトリストリームの概要を理解する上では気にする必要はないように思います.

d_reclenは,dirent構造体をバイナリーデータ(バイト列)として見たときのバイト数です.

readdir(3)d_typeを次のように説明しています.
This field contains a value indicating the file type, making it possible to avoid the expense of calling lstat(2) if further actions depend on the type of the file.

ライブラリ関数

opendir関数

▹opendir関数は, ディレクトリファイルをオープンして, ディレクトリストリームのメモリブロックを確保しています.opendir関数が行っているのはここまでです. ディレクトリファイルからディレクトリエントリー群を取得してメモリブロックに設定する処理は,readdir関数が行っています.

readdir関数

readdir関数は, といった処理を行います.

ディレクトリエントリー群を新たに読み込む処理を無視して,エントリーを返す処理だけを抽出すると次のような処理を行っています. なお,readdir関数は下記の関数(__readdir_unlocked)を呼び出しているだけです.
struct dirent *
__readdir_unlocked (DIR *dirp)
{
  struct dirent *dp;

  do
    {
      size_t reclen;

      dp = (struct dirent *) &dirp->data[dirp->offset];
      reclen = dp->d_reclen;
      dirp->offset += reclen;
      dirp->filepos = dp->d_off;

      /* Skip deleted files.  */
    } while (dp->d_ino == 0);

  return dp;
}
readdir関数が呼び出されるたびに上記の処理を繰り返し行っていることを想像してみると,fileposメンバはまったく利用せず, ディレクトリエントリーはoffsetメンバを使って取得していることが分かります. 上記のコード以外の部分(ディレクトリエントリー群を新たに読み込む処理)でもfileposを使っている実行文が1つもありません. 従って,上記のfileposメンバへの代入は必要ないように思えます. さらに,dirent構造体のd_offメンバも上の代入文にしか現れないので, これも利用していないように見えます. ただし,ここで述べていることは,readdir関数だけを見ている場合の話です.

さらに,上の処理から, ディレクトリエントリーの列を,dirent構造体の配列ではなく, バイナリーデータ(バイト列)として扱っていることが分かります.

rewinddir関数

▹rewinddir関数は, ディレクトリファイルのオフセットを先頭に戻して, ディレクトリストリーム(dirstream構造体)のsizeとoffsetを0に設定します. このようにするとディレクトリストリームをリセットしたことになります. なぜなら,readdir関数は offset $\geq$ size の場合, ディレクトリファイルからディレクトリエントリー群を新たに読み込むからです.

closedir関数

▹closedir関数は,メモリブロックを解放して, ディレクトリファイルをクローズします.
(おしまい)