ファイルチェックに関する調査
SPECIAL
はじめに
ただ慣れていないせいなのか、どうもファイル操作というのは簡単には行えないようで…。なのでちょっと調べたついでに記録に残しておくことにしました。
内容は、実際に試しているとは限らないので、そのあたりを注意して利用してください。
ディレクトリ一覧を取得する
ディレクトリの中のファイルエントリを取得したい場合には、 opendir システムコールを利用するのがいいようです。
このシステムコールは戻り値として DIR 型のディレクトリエントリの先頭を示す値を返します。これが NULL として戻ってきた場合はエラーです。
エラーは errno の値を参照することによって区別でき、その内容は次のようなものだそうです。
| EACCES | 13 | アクセス権がない。 |
|---|---|---|
| EMFILE | 24 | プロセスで使われているファイルディスクリプタが多すぎる。 |
| ENFILE | 23 | システムで開かれているファイルが多すぎる。 |
| ENOENT | 2 | ディレクトリエントリが存在しない。 |
| ENOMEM | 12 | 十分なメモリがない。 |
| ENOTDIR | 20 | ディレクトリではない。 |
なお、これらのエラー定数は <errno.h> にて定義されているそうです。
これを利用してディレクトリエントリを取得するには、戻ってきた DIR 型の値を、readdir() を用いて順次取り出して、最後に closedir() にて手続きを終了する、という形になります。
readdir() は dirent 型へのポインタを返します。エントリを読みきったり、またはエラーが発生した場合は NULL を返します。
dirent 構造体には次のようなメンバが存在します。
| d_ino | inode number of entry |
|---|---|
| d_off | offset of disk directory entry |
| d_reclen | length of this record |
| d_name | name of file |
なにやらいくつかあるようですけど、とりあえず、d_name を参照することによってエントリ名の文字列を取得することが出来ます。
これらを利用して、実際にエントリを取得するプログラムは次のような感じになります。
#include <sys/types.h>
#include <dirent.h>
DIR *dp = opendir(dir);
struct dirent *ent;
if (dp != NULL)
{
while ((ent = readdir(dp)) != NULL)
{
// ここで ent->d_name を利用して何かをする…
}
}
closedir(dp);
取得できるエントリには、一般的なものの他、"." から始まる隠しファイルや、カレントディレクトリを示す "." と、親ディレクトリを示す ".." も含まれるようです。
ファイルが存在するかを調べる [opendir]
ファイルの存在チェックなのですけど、どうもいまいちこれだっ、という情報が得られませんでした^^;;
なので上の 「ディレクトリ一覧を取得する」 で調べた内容をつかってやる方法を書いてみることにします。
まあ、ディレクトリエントリを取得して名前と比較すればとりあえずそのエントリがあるかはわかるのですけど、ループさせるのも、文字列比較するのもなんだし。
幸い、opendir がいろいろとエラーを返してくれるようなのでこちらが使えそうです。
ファイルが存在するかを調べるのですから、ファイル名はわかるでしょう…。
そのファイル名を直接、opendir に渡します。そうすると、ディレクトリならば NULL 以外が返ってきますし、エラーが発生すれば NULL とともに、errno 変数にエラー内容が保存されます。
このエラー内容には、権限とかそもそも存在しないとかいったシステム的なエラーのほかに、ENOENT としてエントリが存在しない、ENOTDIR としてエントリはディレクトリではない、といった判別が可能のようです。
なのでこれらをうまく判定すれば、単純にエントリが存在していることをしらべるとか、またはそのエントリがディレクトリであるのかいないのか、といった程度の判別が可能です。
ファイルが存在するかを調べる [stat]
もう少し調べてみたところ、stat という関数を利用してファイルの状態を取得することができるようでした。これを利用するには sys/types.h, sys/stat.h, unistd.h を組み込む必要があるようです。
stat 関数は次のような値を持つ stat 構造体を返すとのことなので、基本的にはこれらの情報を取得することができます。
| st_dev | デバイス |
|---|---|
| st_ino | i node |
| st_mode | プロテクション |
| st_nlink | ハードリンクの数 |
| st_uid | 所有者の UID |
| st_gid | 所有者の GID |
| st_rdev | デバイスタイプ (i node デバイスのみ) |
| st_size | 合計サイズ (バイト単位) / シンボリックリンク自身の大きさは、含まれるパス名の長さらしい。 |
| st_blksize | ブロックサイズ (ファイルシステム I/O 用?) |
| st_blocks | 割り当てられているブロック数 / 1 ブロックは 512 バイトらしい。 |
| st_atime | 最終アクセス日 |
| st_mtime | 最終更新日(内容の修正で更新されるらしい) [modification] |
| st_ctime | 最終更新日(内容の修正や所有権の変更等で更新されるらしい) [change] |
このような感じのようです。
なお、stat 系の関数には3タイプがあり、どれも2番目の引数に stat 型の構造体へのポインタを渡すのですけど、1番目の引数は、stat と lstat はファイル名を文字列で、fstat は open が返すファイルハンドルで指定します。
stat と lstat の違いはシンボリックリンクに対する振る舞いの違いで、stat の方はリンク先の情報を取得するのに対して、lstat の方はシンボリックリンク自身の情報を取得するそうです。
戻り値は int 型で、0 ならば成功、-1 ならば失敗となります。またエラーの場合、errno に次の情報が設定されます。
| EBADF | fstat の第一引数 (ファイルハンドル) が無効 |
|---|---|
| ENOTENT | stat または lstat の第一引数 (パス) が存在しない |
| ENOTDIR | パスの成分がディレクトリではない(?) |
| ELOOP | 指定されたパスに含まれるシンボリックリンクが多すぎる |
| EFAULT | アドレスが間違っている(?) |
| EACCES | アクセス許可がない |
| ENOMEM | カーネルのメモリが足りない |
| ENAMETOOLONG | ファイル名が長すぎる |
さて今回使いたいと思われる肝心の st_rdev に関する情報が検索不足なのか得られませんでしたが、なにやら st_mode の方がファイルであるかどうかなどを判定するのに使う情報のような感じがしたので、とりあえず、そのあたりをまとめてみます。
| S_IFMT | 0x0170000 | ビットマスク (ファイル型の判定用) |
|---|---|---|
| S_IFSOCK | 0x0140000 | ソケット |
| S_IFLNK | 0x0120000 | シンボリックリンク |
| S_IFREG | 0x0100000 | 通常のファイル |
| S_IFBLK | 0x0060000 | ブロックデバイス |
| S_IFDIR | 0x0040000 | ディレクトリ |
| S_IFCHR | 0x0020000 | キャラクターデバイス |
| S_IFIFO | 0x0010000 | パイプ (FIFO) |
| S_ISUID | 0x0004000 | set UID ビット |
| S_ISGID | 0x0002000 | set GID ビット |
| S_ISVTX | 0x0001000 | スティッキービット |
| S_IRWXU | 0x0000700 | ビットマスク (所有者の権限判定用) |
| S_IRUSR | 0x0000400 | 所有者の読み込み許可 |
| S_IWUSR | 0x0000200 | 所有者の書き込み許可 |
| S_IXUSR | 0x0000100 | 所有者の実行許可 |
| S_IRWXG | 0x0000070 | ビットマスク (グループの権限判定用) |
| S_IRGRP | 0x0000040 | グループの読み込み許可 |
| S_IWGRP | 0x0000020 | グループの書き込み許可 |
| S_IXGRP | 0x0000010 | グループの実行許可 |
| S_IRWXO | 0x0000007 | ビットマスク (他人の権限判定用) |
| S_IROTH | 0x0000004 | 他人の読み込み許可 |
| S_IWOTH | 0x0000002 | 他人の書き込み許可 |
| S_IXOTH | 0x0000001 | 他人の実行許可 |
これらを使って論理演算すれば、普通のファイルなのかディレクトリなのかとかが判別できそうな感じですね。
そして、この判定を行ってくれるマクロがある、とのことなのでそれを挙げてみます。
| S_ISREG(m) | 通常のファイルかどうかを調べる。 |
|---|---|
| S_ISDIR(m) | ディレクトリかどうかを調べる。 |
| S_ISCHR(m) | キャラクターデバイスであるかを調べる。 |
| S_ISBLK(m) | ブロックデバイスであるかを調べる。 |
| S_ISFIFO(m) | FIFO (名前付きパイプ) であるかを調べる。 |
| S_ISLINK(m) | シンボリックリンクであるかを調べる。 |
| S_ISSOCK(m) | ソケットであるかを調べる。 |
よって、取得した st_mode の値をこれらのマクロで処理すれば、けっこう手軽にファイルの種類を特定できそうですね。