4.2 ファイルの属性
Menu MenuUNIXでは、stat() (あるいは、lstat(),fstat() )システム・コールを用いてファイルの属性(ファイルの管理情報)を調べることができる。ファイルの属性を調べ、画面に出力するプログラムをstat.c に示す。
/* stat.c -- stat システム・コールのシェル・インタフェース $Header: /home/h1/yas/slab-info1-os/4-file/RCS/stat.c,v 1.2 1995/05/31 11:43:32 yas Exp $ Start: 1995/03/07 20:59:12 */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> main( argc,argv ) int argc ; char *argv[] ; { if( argc != 2 ) { fprintf( stderr,"Usage:%% %s filename \n",argv[0] ); exit( 1 ); } stat_print( argv[1] ); /* 引数はファイル */ } stat_print( path ) char *path ; { struct stat buf ; if( stat( path,&buf ) == -1 ) { perror( path ); exit( 1 ); } printf("path: %s\n",path ); printf("dev: %d,%d\n",major(buf.st_dev),minor(buf.st_dev) ); printf("ino: %d\n",buf.st_ino ); printf("mode: 0%o\n",buf.st_mode ); printf("nlink: %d\n",buf.st_nlink ); printf("uid: %d\n",buf.st_uid ); printf("gid: %d\n",buf.st_gid ); printf("rdev: %d,%d\n",major(buf.st_rdev),minor(buf.st_rdev) ); printf("size: %d\n",buf.st_size ); printf("atime: %s",ctime(&buf.st_atime) ); printf("mtime: %s",ctime(&buf.st_mtime) ); printf("ctime: %s",ctime(&buf.st_ctime) ); printf("blksize: %d\n",buf.st_blksize ); printf("blocks: %d\n",buf.st_blocks ); }次のようにコンパイルして、実行して見なさい。
% make stat gcc -O2 -o stat stat.c % ./stat stat.c path: stat.c dev: 3,6 ino: 198491 mode: 0100644 nlink: 1 uid: 5020 gid: 0 rdev: 0,807163 size: 1171 atime: Thu Apr 30 23:16:56 1998 mtime: Sun Mar 22 20:13:03 1998 ctime: Sun Mar 22 20:21:52 1998 blksize: 8192 blocks: 4 %この結果から次のようなことがわかる。
(1) ファイル名は stat.c である。 (2) ファイルが存在するデバイスは、メジャー番号3,マイナー番号6である。 (ls -l /dev/wd0h としてみよう) (3) iノード番号は 198491 である。 (4) モードは 0100644 である。 (5) リンク数は 1 である。 (6) ファイルの所有者のuidは 5020である。 (7) ファイルのグループidのは 0である。 (8) デバイスの識別子は、0,807163 である。( ここではあまり意味がない) (9) ファイルの大きさは 1171 バイトである。 (10) 最終アクセス時刻は、Thu Apr 30 23:16:56 1998 である。 (11) 最終更新時刻は、Sun Mar 22 20:13:03 1998 である。 (12) ファイルの作成時刻は、Sun Mar 22 20:21:52 1998 である。 (13) 入出力に適したブロックサイズは、8192である。 (14) 実際に使われているブロックサイズは、4である。1ブロックは512バイト (ファイルの存在するファイル・システムによる)ここで、モードが8進数で0100644であることから、ファイルの型 (普通のファイルかディレクトリかという情報)を調べることができる。0100644の上位4bit、つまり0170000とのAND(論理積、Cでは&演算子)を取った結果は0100000となる。この値は、普通のファイルを意味する。ディレクトリの場合、0040000となる。これらの数は、<sys/stat.h>(/usr/include/sys/stat.h など)で定義されていりう。例えば、BSD/OSの場合、/usr/include/sys/stat.h で以下のように定義されている。
#ifndef _POSIX_SOURCE #define S_IFMT 0170000 /* type of file mask */ #define S_IFIFO 0010000 /* named pipe (fifo) */ #define S_IFCHR 0020000 /* character special */ #define S_IFDIR 0040000 /* directory */ #define S_IFBLK 0060000 /* block special */ #define S_IFREG 0100000 /* regular */ #define S_IFLNK 0120000 /* symbolic link */ #define S_IFSOCK 0140000 /* socket */ #define S_IFWHT 0160000 /* whiteout */ #define S_ISVTX 0001000 /* save swapped text even after use */ #endif #define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ #define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ #define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ #define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ #define S_ISFIFO(m) ((m & 0170000) == 0010000 || \ (m & 0170000) == 0140000) /* fifo or socket */したがってプログラム中では、次のようにしてファイルの型をチェックすることができる。
struct stat buf ; switch ( buf.st_mode & _IFMT ) { case S_IFREG: printf("普通のファイルです。\n"); braek; case S_IFDIR: printf("ディレクトリです。\n"); braek; default: printf("ファイルでもディレクトリでもありません。\n"); braek; }あるいは、<sys/stat.h>に含まれている S_ISREG(), S_ISDIR()というマクロを用いて、次のように記述する方法もある。
if ( S_ISREG(buf.st_mode) { printf("普通のファイルです。\n"); } else if ( S_ISDIR(buf.st_mode) { printf("ディレクトリです。\n"); } else { printf("ファイルでもディレクトリでもありません。\n"); }モードの下位9bit(上の例では、8進数で644)は、許可されたアクセス方法を表している。その9bitは、3bitづつに区切られており、上位から所有者(user)、グループ(group)、その他(others)に許可されているアクセス方法を示している。所有者はonwerとも呼ばれる。
各3bitは次の様なアクセス方法が許可されていることを意味する。
ls -l 3bit アクセス権 r 4 読み込み可 w 2 書き込み可 x 1 実行可または、ディレクトリのアクセス可
課題 3 ls -l コマンドの仕組み
stat()システム・コールを用いて、ls -l filename と似たような結果を出力するプログラムを作りなさい。このプログラムの骨格をmyls-l.c に示す。
/* myls-l.c -- ls -l filename とにた動きをするプログラム $Header: /home/h1/yas/slab-info1-os/4-file/RCS/myls-l.c,v 1.2 1995/06/08 08:23:45 yas Exp $ Start: 1995/03/07 20:59:12 */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <pwd.h> main( argc,argv ) int argc ; char *argv[] ; { if( argc != 2 ) { fprintf( stderr,"Usage:%% %s filename \n",argv[0] ); exit( 1 ); } ls_l( argv[1] ); /* 引数はファイル */ } ls_l( path ) char *path ; { /* 実験4.2の課題は、この関数を完成させることである。下請け関数として、以 下で定義されている uid_print() を使ってもよい。stat.c を参考にして、ファ イルの属性を取りだし、そのうちのいくつかを表示しなさい。 */ } /* struct stat の st_uid フィールドには、uid が入っている。これを数値とし てではなく、ログイン名として表示するためには、次の関数 uid_print() を 使うとよい。引数は、uid_t 型(unsigned short)である。 */ uid_print( uid ) uid_t uid ; /* unsigned short, in <sys/types.h > */ { struct passwd *pwd ; pwd = getpwuid( uid ); if( pwd == NULL ) printf("%d ",uid ); else printf("%s ", pwd->pw_name ); }ls -l では、3つの時刻のうち、どの時刻が表示されているかを調べなさい。また、他の二つの時刻を表示させる方法を調べなさない。また、その時刻を変更する方法について考察し実際に変更して見なさい。
myls-l の表示形式は、ls -l と完全に一致しなくてもよい。例えば、時刻の表示は、上のstat.c と同じでも良い。localtime(), strftime() ライブラリ関数を利用すると、時刻の表示をより簡単に、ls -l の表示に近づけることができる。
プログラムの引数となるファイルの数は一つとする。複数のファイルについてls -l と同様の表示をするように拡張しても良い。
引数とsてディレクトリの名前が与えられた場合にも、ディレクトリの内容ではなくディレクトリ自身の属性を表示する。シンボリックリンクには対応しなくて良い。(これは、ls -ldL の動作と似ている)
課題4 シンボリック・リンクの内容の表示
課題3で、lstat() と readlink() の2つのシステム・コールを用いて、ls -l と同じようにシンボリック・リンクの内容を表示しなさい。