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 と同じようにシンボリック・リンクの内容を表示しなさい。