long getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count); #define _GNU_SOURCE /* feature_test_macros(7) 参照 */ #include <dirent.h> ssize_t getdents64(int fd, void *dirp, size_t count);
注: getdents() の glibc のラッパー関数は存在しない。「注意」の節を参照。
linux_dirent 構造体は以下のように宣言されている。
struct linux_dirent {
unsigned long d_ino; /* inode 番号 */
unsigned long d_off; /* 次の linux_dirent へのオフセット */
unsigned short d_reclen; /* この linux_dirent の長さ */
char d_name[]; /* (ヌル終端された) ファイル名 */
/* 実際の長さは (d_reclen - 2 -
offsetof(struct linux_dirent, d_name)) */
/*
char pad; // 値 0 のパディングバイト
char d_type; // ファイル種別 (Linux 2.6.4 以降のみ);
// オフセットは (d_reclen - 1)
*/
}
d_ino は inode 番号である。 d_off はディレクトリの先頭から次の linux_dirent の先頭までの距離である。 d_reclen はこの linux_dirent 全体のサイズである。 d_name はヌル文字で終わるファイル名である。
d_type は、構造体の最後のバイトであり、ファイルタイプを示す。 d_type は以下の値のいずれか一つを取る (<dirent.h> で定義されている)。
d_type フィールドは Linux 2.6.4 以降で実装されている。 このフィールドは、 linux_dirent 構造体の中で以前はゼロで埋められていた空間に配置されている。 したがって、2.6.3 以前のカーネルでは、このフィールドにアクセスしようとすると 常に値 0 (DT_UNKNOWN) が返される。
現在のところ、 d_type でファイルタイプを返す機能が完全にサポートされているのは、 いくつかのファイルシステムにおいてのみである (Btrfs, ext2, ext3, ext4 はサポートしている)。 どのアプリケーションも DT_UNKNOWN が返された際に適切に処理できなければならない。
getdents64() システムコールは getdents() と似ているが、 2 番目の引数が以下の構造体が入ったバッファへのポインターである点が異なる。
struct linux_dirent64 {
ino64_t d_ino; /* 64 ビットの inode 番号 */
off64_t d_off; /* 次の構造体への 64 ビットのオフセット */
unsigned short d_reclen; /* この dirent の大きさ */
unsigned char d_type; /* ファイル種別 */
char d_name[]; /* (ヌル終端された) ファイル名 */
};
おそらく、あなたが使いたいのは、これらのシステムコールではなく readdir(3) の方であろう。
これらのシステムコールは readdir(2) を置き換えるものである。
$ ./a.out /testfs/
--------------- nread=120 ---------------
inode# file type d_reclen d_off d_name
2 directory 16 12 .
2 directory 16 24 ..
11 directory 24 44 lost+found
12 regular 16 56 a
228929 directory 16 68 sub
16353 directory 16 80 sub2
130817 directory 16 4096 sub3
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
unsigned long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024
int
main(int argc, char *argv[])
{
int fd;
long nread;
char buf[BUF_SIZE];
struct linux_dirent *d;
char d_type;
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
if (fd == -1)
handle_error("open");
for (;;) {
nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
printf("--------------- nread=%d ---------------\n", nread);
printf("inode# file type d_reclen d_off d_name\n");
for (long bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
printf("%8ld ", d->d_ino);
d_type = *(buf + bpos + d->d_reclen - 1);
printf("%-10s ", (d_type == DT_REG) ? "regular" :
(d_type == DT_DIR) ? "directory" :
(d_type == DT_FIFO) ? "FIFO" :
(d_type == DT_SOCK) ? "socket" :
(d_type == DT_LNK) ? "symlink" :
(d_type == DT_BLK) ? "block dev" :
(d_type == DT_CHR) ? "char dev" : "???");
printf("%4d %10jd %s\n", d->d_reclen,
(intmax_t) d->d_off, d->d_name);
bpos += d->d_reclen;
}
}