ディレクトリストリームの型と構造体
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関数
sysdeps/unix/sysv/linux/opendir.c
[POSIX:opendir] [glibc:Opening a Directory Stream] [Manpages:OPENDIR(3)]
▹opendir関数は,
ディレクトリファイルをオープンして,
ディレクトリストリームのメモリブロックを確保しています.opendir関数が行っているのはここまでです.
ディレクトリファイルからディレクトリエントリー群を取得してメモリブロックに設定する処理は,readdir関数が行っています.
[POSIX:opendir] [glibc:Opening a Directory Stream] [Manpages:OPENDIR(3)]
readdir関数
sysdeps/unix/sysv/linux/readdir.c
[POSIX:readdir] [glibc:Reading and Closing a Directory Stream] [Manpages:READDIR(3)]
readdir関数は,
[POSIX:readdir] [glibc:Reading and Closing a Directory Stream] [Manpages:READDIR(3)]
- メモリブロック内のディレクトリエントリーをすべて消費していたら(正確には,dirstraem構造体のsizeとoffsetに対して,offset $\geq$ size となっていたら), メモリブロックに収まる量のディレクトリエントリー群をディレクトリファイルから新たに読み込んで,
- メモリブロック内のエントリーを呼び出されるたびに順々に返す
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構造体の配列ではなく, バイナリーデータ(バイト列)として扱っていることが分かります.