アクセス制御、ファイルの属性、ディレクトリの構造

システム・プログラム

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2001/2001-06-11
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html

■今日の重要な話

■アクセス制御とは

Unixは、マルチユーザのシステムである。1つのシステムを複数のユーザが同時 に利用する。システムに含まれているファイルやプロセスは、それぞれのユー ザと結び付いている。

Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。 アクセス制御(access control) とは、ユーザ(プロセス) が、ファイルなどの資源をアクセスする時、どんな アクセスの仕方なら正しいということを定義して、それがきちんと守られてい ることをということを保証することである。

ファイル
内容の読出し・書き込み・実行、属性の読出し・変更がで きるかどうかを調べる。
プロセス
シグナルを送れるかどうかを調べる。許されれば、その操作 は実行されて、許されなければ、エラーになる。

Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、UNIX では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。

これに対して、Personal Computer用のOS(Windows 95/98/ME, MacOS 9以下) では、このような複数のユーザを識別したアクセス制御は、できない。 Windows NT, Windows 2000, MacOS X は、可能である。

◆ファイルに対するアクセス制御

UNIXでは、ファイルの「内容」のアクセス制御を次の3段階で行う。
ユーザ
ファイルのUID(所有者)が、プロセスのUIDと同じ
グループ
ファイルのGIDが、プロセスのGIDのリストのどれかと同じ
その他
上の2つに当てはまらない時
これをつかって、モード属性の下位9ビットのうち、どの3ビットを使うかを 決める。そして、そのビットが1になっていれば、その操作が許される。

ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。

ユーザ
ファイルのUID(所有者)が、プロセスのUIDと同じ
それ以外
ファイルのUID(所有者)が、プロセスのUIDと異なる
ユーザの権限では、ファイルの属性(モード、グループ、時刻)を変更する ことができる。それ以外の権 限では、属性を読み出すことはできるが、変更は一切できない。 つまり、ファイルの内容がアクセスできなくても、ls -l, stat(2) で 属性を調べることはでる。

◆プロセスに対するアクセス制御

プロセスのアクセス制御は、次の2段階で行なう。
同一ユーザ
操作対象のプロセスが、操作するプロセスのUIDと一致している。
それ以外
操作対象のプロセスが、操作するプロセスのUIDと一致していない。
プロセスの操作としては、シグナルを送ることができるかどうか (kill() システムコール) と、デバッガで デバッグすることができるとか(ptrace() システムコール)、トレースを調べることができるかなどが ある。それらの操作は、同一ユーザの場合 許され、そうではない場合は、許されない。

スーパーユーザ

Unix では、UID が 0 のユーザは、 全てのアクセス制御の仕組みを無視して何でも実行することができる。 これを スーパー・ユーザ(super user)特権ユーザ(privileged user)、 あるいは、 ルート(root) と呼ばれる。

■ユーザとグループ

◆ユーザ

ユーザ(user, 利用者)とは、コンピュータの外 の世界では、コンピュータを使う人間のことである。 コンピュータの内部では、人間を ユーザ名(user name) という文字列 または、それとほぼ1対1に対応した16ビットの整数(Irixでは32ビット)で表す。 こ の数を UID(user ID, user identifier) という。

Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。これを、 Unixでは、ファイルとプロセスに属性として、UID を持たせることで実現して いる。

図? 実世界のユーザとUNIX中のプロセス・ファイル

図? 実世界のユーザとUNIX中のプロセス・ファイル

◆グループ

グループ(group)とは、コンピュータの世界では、 コンピュータ を使う人間の集合 のことである。コンピュータの内部では、ユーザ名/UIDと同様に、 グループ名(group name) と呼ばれる文字列、 または、それとほぼ1対1に対応した16ビッ トの整数で表す。この数を GID(group ID, group identifier) と言う。

1人のユーザが複数のグループに属することがある。

■ユーザの表現

IRIX では、ユーザを32ビット(伝統的なUNIXでは16ビット)の整数で表わす。 この整数を UID と呼ぶ。プロセスやファイルには、属性としてUID がある。
----------------------------------------------------------------------
   1:	/*
   2:	        proc-uid-print.c -- 現在のプロセスのUIDを表示するプログラム。
   3:	        ~yas/syspro-2001/proc/proc-uid-print.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/proc/RCS/proc-uid-print.c,v 1.3 2001/06/10 14:20:26 yas Exp $
   5:	        Start: 1998/05/18 23:20:16
   6:	*/
   7:	
   8:	#include <sys/types.h>  /* getuid(2) */
   9:	#include <unistd.h>     /* getuid(2) */
  10:	#include <pwd.h>        /* getpwuid(3) */
  11:	#include <grp.h>        /* getgrgid(3) */
  12:	
  13:	#if     0
  14:	typedef o_uid_t o_gid_t;                /* old GID type         */
  15:	typedef long            uid_t;
  16:	extern uid_t getuid(void);
  17:	#endif
  18:	
  19:	extern  char *uid2uname(uid_t uid);
  20:	extern  char *gid2gname(gid_t gid);
  21:	
  22:	main()
  23:	{
  24:	    uid_t uid ;
  25:	        uid = getuid();
  26:	        printf("%d: %s\n",uid,uid2uname(uid) );
  27:	        uid = 0 ;
  28:	        printf("%d: %s\n",uid,uid2uname(uid) );
  29:	}
  30:	
  31:	char *uid2uname(uid_t uid)
  32:	{
  33:	    struct passwd *pwd ;
  34:	        pwd = getpwuid( uid );
  35:	        if( pwd )
  36:	            return( pwd->pw_name );
  37:	        else
  38:	        {
  39:	             static char buf[100] ; /* must be static, bad for multithreading */
  40:	             sprintf(buf,"%d",uid );
  41:	             return( buf );
  42:	        }
  43:	}
  44:	
  45:	char *gid2gname(gid_t gid)
  46:	{
  47:	    struct group *grp ;
  48:	        grp = getgrgid( gid );
  49:	        if( grp )
  50:	            return( grp->gr_name );
  51:	        else
  52:	        {
  53:	             static char buf[100] ; /* must be static, bad for multithreading */
  54:	             sprintf(buf,"%d",gid );
  55:	             return( buf );
  56:	        }
  57:	}
----------------------------------------------------------------------

----------------------------------------------------------------------
% ./proc-uid-print [←]
1231: yas
0: root
% []
----------------------------------------------------------------------

■ファイルの属性

UNIXでは、stat() (あるいは、lstat(), fstat())システム・コールを 用いてファイルの属性を調べることができる。ファイルの属性を調べ、画面に 出力するプログラムを stat.c に示す。
----------------------------------------------------------------------
   1:	/*
   2:	        ystat.c -- stat システム・コールのシェル・インタフェース
   3:	        ~yas/syspro-2001/file/ystat.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/file/RCS/ystat.c,v 1.5 2001/06/10 14:32:31 yas Exp $
   5:	        Start: 1995/03/07 20:59:12
   6:	*/
   7:	
   8:	#include <sys/types.h>          /* stat(2) */
   9:	#include <sys/stat.h>           /* stat(2) */
  10:	#include <sys/sysmacros.h>      /* major(), minor() */
  11:	#include <stdio.h>
  12:	
  13:	#if     0
  14:	struct  stat {
  15:	        dev_t   st_dev;
  16:	        long    st_pad1[3];     /* reserved for network id */
  17:	        ino_t   st_ino;
  18:	        mode_t  st_mode;
  19:	        nlink_t st_nlink;
  20:	        uid_t   st_uid;
  21:	        gid_t   st_gid;
  22:	        dev_t   st_rdev;
  23:	        long    st_pad2[2];     /* dev and off_t expansion */
  24:	        off_t   st_size;
  25:	        long    st_pad3;        /* future off_t expansion */
  26:	        timestruc_t st_atim;    
  27:	        timestruc_t st_mtim;    
  28:	        timestruc_t st_ctim;    
  29:	        long    st_blksize;
  30:	        blkcnt_t st_blocks;
  31:	        char    st_fstype[_ST_FSTYPSZ];
  32:	        long    st_pad4[8];     /* expansion area */
  33:	};
  34:	#endif
  35:	
  36:	extern  void stat_print( char *path );
  37:	
  38:	main( int argc, char *argv[] )
  39:	{
  40:	        if( argc != 2 )
  41:	        {
  42:	            fprintf( stderr,"Usage:%% %s filename \n",argv[0] );
  43:	            exit( 1 );
  44:	        }
  45:	        stat_print( argv[1] );
  46:	}
  47:	
  48:	void stat_print( char *path )
  49:	{
  50:	    struct stat buf ;
  51:	        if( stat( path,&buf ) == -1 )
  52:	        {
  53:	            perror( path );
  54:	            exit( 1 );
  55:	        }
  56:	
  57:	        printf("path: %s\n",path );
  58:	        printf("dev: %d,%d\n",major(buf.st_dev),minor(buf.st_dev) );
  59:	        printf("ino: %d\n",buf.st_ino );
  60:	        printf("mode: 0%o\n",buf.st_mode );
  61:	        printf("nlink: %d\n",buf.st_nlink );
  62:	        printf("uid: %d\n",buf.st_uid );
  63:	        printf("gid: %d\n",buf.st_gid );
  64:	        printf("rdev: %d,%d\n",major(buf.st_rdev),minor(buf.st_rdev) );
  65:	        printf("size: %d\n",buf.st_size );
  66:	        printf("atime: %s",ctime(&buf.st_atime) );
  67:	        printf("mtime: %s",ctime(&buf.st_mtime) );
  68:	        printf("ctime: %s",ctime(&buf.st_ctime) );
  69:	        printf("blksize: %d\n",buf.st_blksize );
  70:	        printf("blocks: %d\n",buf.st_blocks );
  71:	}
----------------------------------------------------------------------

----------------------------------------------------------------------
% ./ystat ystat.c [←]
path: ystat.c
dev: 8,2
ino: 12585672
mode: 0100644
nlink: 1
uid: 1231
gid: 40
rdev: 0,0
size: 1754
atime: Sun Jun 10 23:32:32 2001
mtime: Sun Jun 10 23:32:32 2001
ctime: Sun Jun 10 23:32:32 2001
blksize: 8192
blocks: 4
% /sbin/stat ystat.c [←]
ystat.c:
        inode 12585672; dev 2097154; links 1; size 1754
        regular; mode is rw-r--r--; uid 1231 (yas); gid 40 (lab)
        st_fstype: nfs3
        change time - Sun Jun 10 23:32:32 2001 <992183552>
        access time - Sun Jun 10 23:32:32 2001 <992183552>
        modify time - Sun Jun 10 23:32:32 2001 <992183552>
% []
----------------------------------------------------------------------

この実行結果から次のようなことがわかる。
  1. ファイル名は、ystat.c である。
  2. 存在するデバイスは、メジャー番号8, マイナー番号3で識別される。
  3. アイノード番号は、12585672 である。
  4. モードは、0100644である。
  5. リンク数(本名の数)は、1である。
  6. ファイルの所有者のIDは、1231である。
  7. ファイルのグループは、40である。
  8. デバイスの識別子は、メジャー番号0, マイナー番号0である。 (この場合無効。デバイス・ファイルのみ有効。)
  9. ファイルの大きさは、1767バイトである。
  10. 最終アクセス時刻は、atime: Sun Jun 10 23:32:32 2001 である。
  11. 最終更新時刻は、mtime: Sun Jun 10 23:32:32 2001 である。
  12. 最終変更時刻は、ctime: Sun Jun 10 23:32:32 2001 である。
  13. 入出力に適したブロックサイズは、8192バイトである。
  14. ディスク中で実際に消費しているのは、4ブロックである。
ここで、モードが8進数で 0100644 (C言語の文法で、0から始まる数は、8進 数)であることから、ファイルの型(普通のファイルかディレクトリかという 情報)を調べることができる。

UNIXのモード

0100644の上位4ビット、つまり、0170000と AND (C言語のでは、 &演算子)をとった結果は 0100000 となる。この値は、普通のファ イル(regular file)を意味する。ディレクトリの場合、0040000 となる。こ れらの数は、 (/usr/include/sys/stat.h)で定義されている。

----------------------------------------------------------------------
#define S_IFMT          0xF000  /* type of file */
#define S_IFIFO         0x1000  /* fifo */
#define S_IFCHR         0x2000  /* character special */
#define S_IFDIR         0x4000  /* directory */
#define S_IFBLK         0x6000  /* block special */
#define S_IFREG         0x8000  /* regular */
#define S_IFLNK         0xA000  /* symbolic link */
#define S_IFSOCK        0xC000  /* socket */


#define S_ISFIFO(mode)  ((mode&S_IFMT) == S_IFIFO)
#define S_ISCHR(mode)   ((mode&S_IFMT) == S_IFCHR)
#define S_ISDIR(mode)   ((mode&S_IFMT) == S_IFDIR)
#define S_ISBLK(mode)   ((mode&S_IFMT) == S_IFBLK)
#define S_ISREG(mode)   ((mode&S_IFMT) == S_IFREG)
#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)
#define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)
----------------------------------------------------------------------
プログラム中では、次のようにしてファイルの型を調べることができる。
----------------------------------------------------------------------
	switch( buf.st_mode & S_IFMT )
	{
	case S_IFREG: 
	    ファイルの時の処理;
	    break;
	case S_IFDIR: ...
	    ディレクトリの時の処理;
	    break;
	....
	}
----------------------------------------------------------------------
あるいは、 に含まれている S_ISREG(), S_ISDIR() というマク ロを用いて、次のように記述する方法もある。
----------------------------------------------------------------------
	if( S_ISREG(buf.st_mode) )
	{
	    ファイルの時の処理;
	}
	else if( S_ISDIR(buf.st_mode) )
	{
	    ディレクトリの時の処理;
	}
----------------------------------------------------------------------

モードの下位9ビット(上の例では、8進数で 644 )は、許可されたアクセス 方法を表している。その9ビットは、3ビットづつに区切られおり、上位から 所有者(owner)、グループ(group)、その他(others)に許可(permission) されているアクセス方式を表している。所有者(owner)の代りに、利用者 (user)という言葉が使われることもある。

各3ビットは次の様なアクセス方法が許可されていることを意味する。

----------------------------------------------------------------------
ls -l	3ビット値	アクセス権
----------------------------------------------------------------------
r	4		読込み可能
w	2		書込み可能
x	1		実行可能(ディレクトリの場合は、検索可能)
----------------------------------------------------------------------

このように、普通のファイルとディレクトリで "x" の意味が異なる。

■umaskの影響

ファイルやディレクトリを新たに作る時にどのようなモードになるかは、次の 2つで決まる。
  1. ファイルやディレクトリを作るプログラムの中で指定したモード
  2. マスク
実際に作られるファイルのモードは、1. から 2. を引いた値になる。同じプ ログラムを使ったとしても、マスクの値によっては、作られるファイルのモー ドが違ってくる。

◆マスクの影響と操作

◆マスクの表示

マスクを見るには、umask コマンドコマンドを使う。
----------------------------------------------------------------------
% umask [←]
22
% []
----------------------------------------------------------------------
umask コマンドは、シェルの 内部コマンドである。マスクは、プロセスの属性の1つで、 子プロセス、孫プロセスと代々受け 継がれていきく。現在のシェルのマスクを見れば、 シェルから実行されるプロセスのマスクがわかる。

umask を操作するシステム・コールは、umask(コマンドと同じ名前)である。

マスクは、モードと同じく8進数で考える。たとえば、022 は、次のように 読む。

----------------------------------------------------------------------

   ---       000 新しいファイルは、自分自身は、読み書き自由。
      -w-    020 同じグループの人は、ファイルの書込みを禁止する。
+)       -w- 002 その他の人も、ファイルの書込みを禁止する。
----------------
             022

----------------------------------------------------------------------
このように、モードの逆になる。

◆マスクを変える

マスクを変えるには、umask コマンドに、引数を与えて実行します。
----------------------------------------------------------------------
% umask [←]
22
%
% umask 066 [←]
% umask [←]
66
% []
----------------------------------------------------------------------
この例では、シェルのマスクを 022 から 066 へ変更しています。

umask コマンドによるマスクの設定は、 普通、~/.cshrc~/.login に入れる。 rsh コマンド時も実行さ れるように、~/.cshrc に入れて置くのが無難である。

◆普通のファイルを作る時のマスクの影響

新しくファイルを作る時、そのモードは、最大 666 (rw-rw-rw-)になることが多い。これは、ファイルを作るプロ グラム(cp, mule などで)で、次のようにファイルを作っていることに由来す る。
	open("file1",O_CREAT|O_TRUNC,0666);
すると、UNIXオペレーティング・システムのカーネルは、システム・コールの 引数とマスクを保持している変数 mask を使って
	mode = 0666 & ~mask ;
というモードのファイルを作る。「&」 は、ビットごとのAND、「~」は、ビット反 転である。この場合、繰り下がりがないので、
	mode = 0666 - mask ;
と考えてもよい。結果として、マスクの部分のビットが落ちたモード を持つファイルが作られる。

たとえば、マスクが 022 の時、作 成されるファイルのモードは、666からマスクの022を引くので、次のように 644 (rw-r--r--) になる。


----------------------------------------------------------------------
% umask [←]
22
% echo "This is file1" > file1 [←]
% ls -l file1 [←]
-rw-r--r--  1 yas            14 Sep 14 17:25 file1
% []
----------------------------------------------------------------------
マスクを 0 にして、同じことをすると、モードが 666 になる。

----------------------------------------------------------------------
% umask 000 [←]
% umask [←]
0
% rm file1 [←]
% echo "This is file1" > file1 [←]
% ls -l file1 [←]
-rw-rw-rw-  1 yas            14 Sep 14 17:29 file1
% []
----------------------------------------------------------------------

マスクは、新しくファイルを作る時にのみ有効である。すでに、ファイルが存 在する場合、ファイルに書込みをしても、モードは変わらない。たとえば、


----------------------------------------------------------------------
% chmod 777 file1 [←]
% ls -l file1 [←]
-rwxrwxrwx  1 yas            14 Jan 23 16:28 file1
% umask 022 [←]
% echo "a" > file1 [←]
% ls -l file1 [←]
-rwxrwxrwx  1 yas             2 Jan 23 18:05 file1
% []
----------------------------------------------------------------------

◆ディレクトリを作る時のマスクの影響

新しくディレクトリを作る時、そのモードは、最大 777 (rwxrwxrwx)になることが多い。これは、ディレクトリを作る プログラムが、
	mkdir("dir1",0777 );
となっているからである。この結果、ファイルと同様に
	mode = 0777 & ~mask
というモードを持つディレクトリが作られる。

たとえば、マスクが 022 の時、作成されるディレクトリのモードは、777から マスクの022を引くので、次のように 755 (rwxr-xr-x) に なる。


----------------------------------------------------------------------
% umask [←]
22
% mkdir dir1 [←]
% ls -ld dir1 [←]
drwxr-xr-x  2 yas           512 Jan 23 18:09 dir1
% []
----------------------------------------------------------------------
マスクを 0 にして、同じことをすると、

----------------------------------------------------------------------
% umask 0 [←]
% rmdir dir1 [←]
% mkdir dir1 [←]
% ls -ld dir1 [←]
drwxrwxrwx  2 yas           512 Jan 23 18:09 dir1
% []
----------------------------------------------------------------------
新しく mkdir コマンドで作ったディレクトリの モードが 777 になる。

■リンクと名前

UNIXでは、ファイル名は、枝(リンク)に付いている。UNIXでは、ファイルの 名前には、次の2種類がある。 本名は、ファイルの寿命と関係している。普通のファイルは、本名の数 (リンク数)は、1である。複数の本名を持っているファ イルもある。全部の本名が消えるとファイルの実体が消える。

----------------------------------------------------------------------
% ls -li /usr/bsd/{compress,uncompress,zcat} [←]
1048720 -rwxr-xr-x    1 root     sys        33700   3月 28日 10時57分 /usr/bsd/compress
1048618 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/uncompress -> compress
1048620 lrwxr-xr-x    1 root     sys            8   2月  4日 05時50分 /usr/bsd/zcat -> compress
% []
----------------------------------------------------------------------

1つのファイルの実体を、3つの名前でアクセスできる。 compress が消されると、ファイルの実体が消される。 uncompresszcat が消されても、ファイルの実体はそのままである。

シンボリック・リンクは、内部的には別のファイルの名前(シンボル)を含ん でいる特殊なファイルである。open() などのシステムコールでは、自動的に リンクの先の名前に置き換えられる。シンボリック・リンクは、symlink() シ ステム・コールで作成することができる。

◆ディレクトリの実リンク

ディレクトリを stat(2) で調べると、リンク数が2以上になっている。 "." と 子ディレクトリの ".." の分だけ増えている。

本名は、open(), creat(), mkdir() の時に作られる。それ以外に、link() シ ステム・コールで増やすことができる。リンクを減らすには、unlink() シス テム・コールを使う。リンクが1つしかないファイルに unlink() を行うと、 ファイルが削除される。

■getdents(2)によるディレクトリの内容の取得

UNIXのディレクトリは、ディスク中では、可変長の構造体になっている。C言 語では、直接的には可変長の構造体を扱うことはできない。

IRIX では、getdents(2) システム・コールを使うと、ディスク中に保存され たディレクトリに近いデータを得ることができる。(システム・コールは、シ ステムにより異なる。システムによっては、ディレクトリについても、read() が使えるものがある。SunOS, Solaris でも、getdents()。HP-UX では、 getdirentries()


----------------------------------------------------------------------
   1:	/*
   2:	        dir-getdents.c -- ディレクトリの内容を表示するプログラム
   3:	        ~yas/syspro-2001/dir/dir-getdents.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/dir/RCS/dir-getdents.c,v 1.3 2001/06/10 15:27:12 yas Exp $
   5:	        Start: 1995/03/07 21:44:51
   6:	*/
   7:	
   8:	#include <stdio.h>      /* fprintf(), stderr */
   9:	#include <sys/types.h>  /* open(2) */
  10:	#include <sys/stat.h>   /* open(2) */
  11:	#include <fcntl.h>      /* open(2) */
  12:	#include <sys/dirent.h> /* getdents(2) */
  13:	
  14:	#if     0
  15:	typedef struct dirent {                 /* data from readdir() */
  16:	        ino_t           d_ino;          /* inode number of entry */
  17:	        off_t           d_off;          /* offset of disk directory entry */
  18:	        unsigned short  d_reclen;       /* length of this record */
  19:	        char            d_name[1];      /* name of file */
  20:	} dirent_t;
  21:	#endif
  22:	
  23:	
  24:	extern  void dir_list( char *dirname );
  25:	extern  void xdump( unsigned char *buff, int n );
  26:	extern  void xdump16( unsigned char *buff, int n );
  27:	
  28:	main( int argc, char *argv[] )
  29:	{
  30:	        if( argc != 2 )
  31:	        {
  32:	            fprintf( stderr,"Usage:%% %s dirname \n",argv[0] );
  33:	            exit( 1 );
  34:	        }
  35:	        dir_list( argv[1] );
  36:	}
  37:	
  38:	void dir_list( char *dirname )
  39:	{
  40:	    int fd ;
  41:	    struct dirent *p ;
  42:	    char buff[BUFSIZ] ;
  43:	    int rcount ;
  44:	
  45:	        fd = open( dirname,O_RDONLY );
  46:	        if( fd == -1 )
  47:	        {
  48:	            perror( dirname );
  49:	            exit( 1 );
  50:	        }
  51:	
  52:	        while( (rcount=getdents(fd,(dirent_t *)buff,BUFSIZ)) >0 )
  53:	        {
  54:	            xdump( buff, rcount );
  55:	            for( p = (struct dirent *)buff ; (char *)p < &buff[rcount] ;
  56:	                 p=(struct dirent *) ((int)p+(p->d_reclen)) )
  57:	            {
  58:	                printf("p:%d, ", (char *)p - buff );
  59:	                printf("off:%u, ", p->d_off );  /* long */
  60:	                printf("ino:%u, ", p->d_ino );  /* unsigned long */
  61:	                printf("reclen:%d, ", p->d_reclen );
  62:	                printf("name:%s\n", p->d_name );
  63:	            }
  64:	        }
  65:	        close( fd );
  66:	}
  67:	
  68:	void xdump( unsigned char *buff, int n )
  69:	{
  70:	        if( n<0 || n>100000 )
  71:	            return;
  72:	        for( ; n>0 ; n-=16, buff+=16 )
  73:	            xdump16( buff,n>=16?16:n );
  74:	}
  75:	
  76:	void xdump16( unsigned char *buff, int n )
  77:	{
  78:	    register int i ;
  79:	        for( i=0 ; i<n ; i++ )
  80:	            printf("%02x ",buff[i] );
  81:	        for( i=n ; i<16 ; i++ )
  82:	            printf("   ");
  83:	        for( i=0 ; i<n ; i++ )
  84:	            printf("%c",isprint(buff[i])?buff[i]:'#' );
  85:	        for( i=n ; i<16 ; i++ )
  86:	            printf(" ");
  87:	        printf("\n");
  88:	}
  89:	
----------------------------------------------------------------------

実行例。
----------------------------------------------------------------------
% ./dir-getdents dir1 [←]
00 07 a5 60 00 00 17 2e 00 0c 2e 00 01 8f 70 21 ###`###.##.###p!
6d 3b 32 b4 00 10 2e 2e 00 00 00 00 00 07 a5 63 m;2###..#######c
6d 3b 32 b7 00 10 66 69 6c 65 32 00 00 07 a5 61 m;2###file2####a
ff ff ff ff 00 10 66 69 6c 65 31 00             ######file1#    
p:0, off:5934, ino:501088, reclen:12, name:.
p:12, off:1832596148, ino:26177569, reclen:16, name:..
p:28, off:1832596151, ino:501091, reclen:16, name:file2
p:44, off:4294967295, ino:501089, reclen:16, name:file1
% []
----------------------------------------------------------------------

ディレクトリの構造

ディレクトリを読むためのライブラリ関数として、次のようなものがある。


----------------------------------------------------------------------
     #include <sys/types.h>
     #include <sys/dir.h>
     DIR *opendir(char *filename);
     struct direct *readdir(DIR *dirp);
     long telldir(DIR *dirp);
     void seekdir(DIR *dirp, long loc);
     void rewinddir(DIR *dirp);
     void closedir(DIR *dirp);
     int dirfd(DIR *dirp)
----------------------------------------------------------------------

これらのライブラリ関数は、システム・コールと比較して移植性が高い。

■ディレクトリの作成

ディレクトリを作成するには、mkdir(2) システム・コールを用いる。 mkdir(1) コマンドは、mkdir(2) システム・コールを使って作られている。

----------------------------------------------------------------------
   1:	/*
   2:	        dir-mkdir.c -- ディレクトリを作成するプログラム
   3:	        ~yas/syspro1/dir/dir-mkdir.c
   4:	        $Header: /home/lab2/OS/yas/syspro1/dir/RCS/dir-mkdir.c,v 1.2 1998/05/11 17:01:35 yas Exp $
   5:	        Start: 1997/05/12 21:26:10
   6:	*/
   7:	
   8:	#include <stdio.h>
   9:	#include <sys/stat.h>
  10:	
  11:	void main( int argc, char *argv[] )
  12:	{
  13:	        if( argc != 2 )
  14:	        {
  15:	            fprintf( stderr,"Usage:%% %s dirname \n",argv[0] );
  16:	            exit( 1 );
  17:	        }
  18:	        if( mkdir( argv[1],0777 ) == -1 )
  19:	        {
  20:	            perror( argv[1] );
  21:	        }
  22:	}
----------------------------------------------------------------------

単純に man mkdir と打つと、mkdir(1) コマンドのマニュアルが表示される。 mkdir(2) システム・コールを見るには、次のように打つ。
% man 2 mkdir [←]

■練習問題と課題

★練習問題 55 gidの表示

プロセスの GID属性を表示するプログラムを作りなさい。

注意:getgid() システムコールや getgroups() システムコールを使う。

★練習問題 56 ls-lプログラム

stat() システム・コールを用いて ls -l filename と似たような結果を出力 するプログラムを作りなさい。このプログラムの名前を myls-l とする。

ls -l では、3つの時刻のうち、どの時刻が表示されているのかを調べなさい。 また、他の2つの時刻を表示させる方法を調べなさい。

myls-l の結果の表示形式は、ls -l と完全に一致しなくてもよい。たとえば、 時刻の表示は、上の ystat.c の結果と同じでもよい。ファイル名を先に、 時刻を後に表示してもよい。

時刻の扱い 練習問題 24 localtime(3)、strftime(3) の利用 で紹介したlocaltime() や strftime() ライブラリ関数を利用すると、時刻の 表示をより簡単に ls -l の表示に近づけることができる。

uid (st_uid) については、ls -l では、ユーザ名(ログイン名)で表示され る。この課題では、ユーザやグループは、数字のまま表示してもよい。 proc-uid-print.c にある uid2uname(), gid2gname() を利用すれば、 数字ではなく文字列で表示することができる。

プログラムの引数となるファイルの数は、1個とする。複数のファイルについ て、ls -l と同様の表示をするように拡張してもよい。

普通のファイル(「-」)とディレクトリ(「d」)を必ず扱えるようにする。それ 以外の型のファイルについては、扱えなくてもよい。

引数としてディレクトリの名前が与えられた場合にも、ディレクトリの内容で はなくディレクトリ自身の属性を表示する。シンボリック・リンクには対応し なくてもよい。(よって正確には、ls -l filename ではなく、ls -ldL filename である。)

余裕があれば、lstat() と readlink() の、2つのシステム・コールを用いて、 ls -l と同じようにシンボリック・リンクの内容を表示しなさい。

さらに、opendir(3) や scandir(3) を使って、ディレクトリの内容を表示 するようにしてもよい。

★練習問題 57 chmod プログラム

ファイルのモードを変更するコマンド chmod に似たコマンドを作りなさい。
% mychmod 755 filename [←]
モードは、8進数で与えるものとする。ファイルは、1つだけでよい。

★練習問題 58 touch プログラム

ファイルの時刻を変更するコマンド touch に似たコマンドを作りなさい。
% mytouch filename [←]
この結果、ファイルの最終更新時刻が現在の時刻になる。

ヒント:times(2) と utime(2) を使う。

★練習問題 59 条件コピー

ファイルのコピー(file-copy.c) を修正して、ファイルが更新されていた時だけコピーするようなプログラムを 作りなさい。

ヒント:stat(2)システムコールで、from_name と to_name で指定された2つ のファイルの最終更新時刻を調べる。もし、前者が新しければ、コピーする。 後者が新しければ、なにもしない。

★練習問題 60 属性まで含んだコピー

cp -p (Preserve) と同じように、ファイルの内容に加えていくつかの属性を 保存しながらファイルをコピーするプログラムをつくりなさい。

この課題では、次のような属性を保存しなさい。

UID (owner), GID については、保存しなくてもよい。UID の保存は、 スーパーユーザしかできない。 スーパーユーザが実行した時には、保存できるようなプログラムを作成しても よい。一般ユーザで実行した時には、エラーを無視してよい。

★練習問題 61 リンクの作成

link(2) を使ってリンクを作成するプログラム(ln コマンドに似た動きをす るプログラム)を作りなさい。

★練習問題 62 リンクの削除

unlink(2) を使って、リンクを削除するプログラム(rm コマンドと似た動きを するプログラム)を作りなさい。

★練習問題 63 シンボリック・リンクの作成

symlink(2) を使って、シンボリック・リンクを作成するプログラム(ln -s と 似た動きをするプログラム)を作りなさい。

★練習問題 64 名前の変更

rename(2) を使って、ファイルの名前を変えるするプログラム(mv コマンドと 似た動きをするプログラム)を作りなさい。

★練習問題 65 単純なlsプログラム

opendir(3)、または、scandir(3) を用いて、次のような動きをするプログラ ムを作りなさい。

ls dirname
ファイル名の表示。ただし、「'.'」から始まる名前は表示しない。
ls -ia dirname
アイノード番号とファイル名の表示。
dirname が省略された時には、"." が指定されたものとし て扱うようにしてもよい。

余裕があれば、ls-lプログラムの課題のように、 属性を扱いなさい。

★練習問題 66 ディレクトリの木構造の探索(ls-R)

ls -R のように、ディレクトリの木構造を再帰的に探索して表示するプログラ ムを作りなさい。

この時、次の2つの名前を特別扱いして、探索しないようにする。

さらに、シンボリック・リンク を手繰らないようにしなさい。 シンボリック・リンクを手繰ると、無限ループに陥る可能性がある。

シンボリック・リンクかどうかの判定には、lstat() システムコールを使いな さい。

★練習問題 67 ディレクトリの削除

rmdir(2) を使ってディレクトリを削除するプログラムを作りなさい。