システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/classes/syspro-2003/2003-06-16
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
属性
Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。これを、
Unixでは、ファイルとプロセスに属性として、UID を持たせることで実現して
いる。
図? 実世界のユーザとUNIX中のプロセス・ファイル
1人のユーザが複数のグループに属することがある。
行の左から、型とモード、リンク数、ユーザ名(所有者)、グループ名、大き さ、更新時刻、名前が表示されている。 ls -l で、一番左の文字は、 ファイルの型 を表わしている。---------------------------------------------------------------------- % ls -l proc-uid-print.c-rw-r--r-- 1 yas lab 1769 Jun 16 22:37 proc-uid-print.c %
----------------------------------------------------------------------
-
の場合は普通のファイル、
d
の場合はディレクトリを意味する。
ls -l で、2文字目から9文字目までは、アクセスの可否を決めるための情報で
ある。この9文字を、モードという(型まで含めてモードと呼ぶこともある)。
9文字は、3文字の固まりが3組に分けられる。
1つのブロックの中の3文字はアクセス毎にその許可・拒否を表す。
r 読込み可 w 書込み可 x 実行可(ディレクトリの場合は探索可)9文字のうち、該当する部分が「-」の場合は、その種類のアクセスが許可さ れてないことを意味する。
「読込み可」とは、その内容を参照することができるという意味する。たとえ ば、読み出し可能なファイルは、cp コマンドでコピーしたり、less で内容を 表示することができる。読出し可能なディレクトリなら、ls コマンドでその ディレクトリ中のファイル名の一覧を表示することができる。
「書込み可」とは、その内容を変更することができるという意味です。たとえ ば、テキスト・ファイルなら、エディタで修正したものを書き込むことができ る。書込み可能なディレクトリなら、mv コマンドでそのディレクトリのなか にあるファイル名前を変更することができる。
「実行可」というのは、ファイルの内容がプログラムの場合は、 そのプログラムを実行することができることを意味する。
「検索可」というのは、その下にあるファイルやディレクトリを たぐることができることを意味する。 ディレクトリが「読込み可」でも、「検索可」でないと、 ディレクトリに「読込み可」のファイルがあっても、 ディレクトリ以下のファイルを読むことができない。 逆に、「検索可」でも、ディレクトリが「読込み可」でないと、 ディレクトリにあるファイル名やディレクトリ名を ls で表示させることが できないが、そのディレクトリにあるファイル名を知っていて、 そのファイルが「読み込み可」なら less などで表示させることはできる。
あるファイルを、特定の人にだけ特定のアクセスの方法をさせたいことがある。 このために、そのために、rwxの指定は、ファイルの所有者、ファイルの属す グループ、それ以外の人用に3セット用意されている。
たとえば、モードが「rw-r--r--」のファイルは、次のようなことを意味する。
普通のファイルは、多くの場合、リンク数が1になっている。ディレクトリは、 「.」や「..」でもアクセスできるので、リンク数が2以上になっている。
ls -l の表示の中で、左から3番目の固まり表示されるのが、ファイルやディ レクトリの 所有者 ( owner ) である。
ファイルの属性としては、UID で保存されているが、ls - l では、それをユー ザ名に変換して表示する。
UNIXでは複数のユーザが属す グループ ( group ) というものを設定できる。 あるファイルは、必ずどれか一つのグループに所属する。 ファイルの属性としては、GID で保存されているが、ls -l では、それをグルー プ名に変換して表示する。 ls -l の5番目の桁は、 ファイルの大きさ である。これは、 ファイルの内容をバイト数で数えた値である。 UNIXのファイルの属性には次の3種類の時刻が記録されている。ls -lu
で表示される。
ls -l
で表示される「時刻」。
単に「ファイルの時刻」といった場合にはこの時刻を指す。
ls -lc
で表示される「時刻」。
時刻も属性の1つなので、「最終更新時刻」を変更すると、
「最終変更時刻」も変更される。
プロセスの UID 属性を得るには、getuid() システムコールを用いる。以下の プログラムは、現在のプロセスの UID とユーザ名、および、UID 0 の UID と ユーザ名を表示するものである。
---------------------------------------------------------------------- 1: /* 2: proc-uid-print.c -- 現在のプロセスのUIDを表示するプログラム。 3: ~yas/syspro/proc/proc-uid-print.c 4: Start: 1998/05/18 23:20:16 5: */ 6: 7: #include <sys/types.h> /* getuid(2) */ 8: #include <unistd.h> /* getuid(2) */ 9: #include <pwd.h> /* getpwuid(3) */ 10: #include <grp.h> /* getgrgid(3) */ 11: 12: #if 0 13: 14: -------------------- /usr/include/bits/types.h: -------------------- 15: ... 16: typedef unsigned char __u_char; 17: typedef unsigned short __u_short; 18: typedef unsigned int __u_int; 19: typedef unsigned long __u_long; 20: ... 21: typedef __u_int __uid_t; /* Type of user identifications. */ 22: typedef __u_int __gid_t; /* Type of group identifications. */ 23: ... 24: 25: -------------------- /usr/include/sys/types.h: -------------------- 26: #include <bits/types.h> 27: ... 28: #ifndef __uid_t_defined 29: typedef __uid_t uid_t; 30: # define __uid_t_defined 31: #endif 32: ... 33: #ifndef __gid_t_defined 34: typedef __gid_t gid_t; 35: # define __gid_t_defined 36: #endif 37: 38: -------------------- /usr/include/unistd.h: -------------------- 39: /* Get the real user ID of the calling process. */ 40: extern __uid_t getuid (void) __THROW; 41: 42: #endif 43: 44: extern char *uid2uname(uid_t uid); 45: extern char *gid2gname(gid_t gid); 46: 47: main() 48: { 49: uid_t uid ; 50: uid = getuid(); 51: printf("%d: %s\n",uid,uid2uname(uid) ); 52: uid = 0 ; 53: printf("%d: %s\n",uid,uid2uname(uid) ); 54: } 55: 56: char *uid2uname(uid_t uid) 57: { 58: struct passwd *pwd ; 59: pwd = getpwuid( uid ); 60: if( pwd ) 61: return( pwd->pw_name ); 62: else 63: { 64: static char buf[100] ; /* must be static, bad for multithreading */ 65: sprintf(buf,"%d",uid ); 66: return( buf ); 67: } 68: } 69: 70: char *gid2gname(gid_t gid) 71: { 72: struct group *grp ; 73: grp = getgrgid( gid ); 74: if( grp ) 75: return( grp->gr_name ); 76: else 77: { 78: static char buf[100] ; /* must be static, bad for multithreading */ 79: sprintf(buf,"%d",gid ); 80: return( buf ); 81: } 82: } ----------------------------------------------------------------------uid_t は、最終的には、unsigned int として定義されている。unistd.h の __ THROW は、C++言語で使う時に有効なものであり、C言語のプログラムの場 合は、空の定義で置き換えられ、ソース・プログラムからは消えてる。
getuid() は、そのプロセス(現在実行中のプロセス)の UID を返すシステム コールである。
uid2uname() は、引数で与えられた UID を、標準ライブラリ関数getpwuid() を使って文字列に変換する。このライブラリ関数は、struct passwd へのポイ ンタを返す。この構造体は、次のようになっている。
struct passwd { char *pw_name; /* user name */ char *pw_passwd; /* user password */ uid_t pw_uid; /* user id */ gid_t pw_gid; /* group id */ char *pw_gecos; /* real name */ char *pw_dir; /* home directory */ char *pw_shell; /* shell program */ };pw_name に、文字列のユーザ名が入っている他に、パスワード(ハッシュ値) やユーザのホーム・ディレクトリやログイン・シェルも含まれている。
ライブラリ関数 getpwuid() は、/etc/passwd ファイルや NIS (Network Information Service) のパスワード・データベースを引いて、この構造体を 作り上げる。getpwuid() の他に、getpwnam() も、この構造体へのポインタを 返す。
struct passwd *getpwnam(const char * name); struct passwd *getpwuid(uid_t uid);uid2uname() は、UID を、対応したユーザ名(文字列)に変換する関数である。 uid2uname() は、結果を static で宣言したバッファに保存して返している。 この方法は、後で free() しなくてもよいという意味では便利な方法だが、マ ルチスレッドのプログラムではよくない。(マルチスレッドについては、3学 期のオペレーティング・システムIIに出てくる。)
gid2gname() は、uid2uname() と同様に、GID をグループ名に対応した文字列 に変換する関数である。内部では、標準のライブラリ関数 getgrgid() を使っ ている。これは、struct group を返す。詳しくは、man getgrgid を見なさい。
実行例:
最初の行は、各自異なるはずである。UID 0 は、rootと いう特別なユーザに対応している。---------------------------------------------------------------------- % cp ~yas/syspro/proc/proc-uid-print.c .% make proc-uid-print
cc proc-uid-print.c -o proc-uid-print % ./proc-uid-print
1013: yas 0: root %
----------------------------------------------------------------------
Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。 アクセス制御(access control) とは、ユーザ(プロセス) が、ファイルなどの資源をアクセスする時、どんな アクセスの仕方なら正しいということを定義して、それがきちんと守られてい ることをということを保証することである。
Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、UNIX では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。
これに対して、Personal Computer用のOS(Windows 95/98/ME, MacOS 9以下) では、このような複数のユーザを識別したアクセス制御は、できない。 Windows NT, Windows 2000, MacOS X は、可能である。
ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。
次の例は、モードが 000 ファイルをスーパーユーザの権限で読むことが出き ることを示している。
---------------------------------------------------------------------- % suPassword: [root@adonis9 tmp]# cd /tmp [root@adonis9 /tmp]# ls -l file1 ls: file1: そのようなファイルやディレクトリはありません [root@adonis9 /tmp]# echo "This is file1" > file1 [root@adonis9 /tmp]# chmod 000 file1 [root@adonis9 /tmp]# ls -l file1 ---------- 1 root root 14 6月 17 01:52 file1 [root@adonis9 /tmp]# cat file1 This is file1 [root@adonis9 /tmp]# ----------------------------------------------------------------------
ln -f
、rm -f
、link
、unlink
)
。
setuid(2)
)
。
reboot
,halt
, shutdown
)
date
, hostname
, domainname
)
acct
)
chroot
)
---------------------------------------------------------------------- 1: /* 2: ystat.c -- stat システム・コールのシェル・インタフェース 3: ~yas/syspro/file/ystat.c 4: Start: 1995/03/07 20:59:12 5: */ 6: 7: #include <sys/types.h> /* stat(2) */ 8: #include <sys/stat.h> /* stat(2) */ 9: #include <sys/sysmacros.h> /* major(), minor() */ 10: #include <stdio.h> 11: 12: extern void stat_print( char *path ); 13: 14: main( int argc, char *argv[] ) 15: { 16: if( argc != 2 ) 17: { 18: fprintf( stderr,"Usage:%% %s filename \n",argv[0] ); 19: exit( 1 ); 20: } 21: stat_print( argv[1] ); 22: } 23: 24: void stat_print( char *path ) 25: { 26: struct stat buf ; 27: if( stat( path,&buf ) == -1 ) 28: { 29: perror( path ); 30: exit( 1 ); 31: } 32: 33: printf("path: %s\n",path ); 34: printf("dev: %d,%d\n",major(buf.st_dev),minor(buf.st_dev) ); 35: printf("ino: %d\n",buf.st_ino ); 36: printf("mode: 0%o\n",buf.st_mode ); 37: printf("nlink: %d\n",buf.st_nlink ); 38: printf("uid: %d\n",buf.st_uid ); 39: printf("gid: %d\n",buf.st_gid ); 40: printf("rdev: %d,%d\n",major(buf.st_rdev),minor(buf.st_rdev) ); 41: printf("size: %d\n",buf.st_size ); 42: printf("blksize: %d\n",buf.st_blksize ); 43: printf("blocks: %d\n",buf.st_blocks ); 44: printf("atime: %s",ctime(&buf.st_atime) ); 45: printf("mtime: %s",ctime(&buf.st_mtime) ); 46: printf("ctime: %s",ctime(&buf.st_ctime) ); 47: } ----------------------------------------------------------------------
stat() システムコールは、引数として、ファイル名(ファイルやディレクト リの名前、パス名)と、struct stat のポインタを取る。この構造体は、マニュ アルには、次のようなものであると説明されている。
% man 2 stat ... struct stat { dev_t st_dev; /* device */ ino_t st_ino; /* inode */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device type (if inode device) */ off_t st_size; /* total size, in bytes */ unsigned long st_blksize; /* blocksize for filesystem I/O */ unsigned long st_blocks; /* number of blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last change */ };この構造体を使う限り、移植性がある(portable)プログラムが書ける。実際の システムでは、もう少し複雑な構造をしていることがある。たとえば、Linux では、/usr/include/bits/stat.h に定義がある。
この実行結果から次のようなことがわかる。---------------------------------------------------------------------- % cp ~yas/syspro/file/ystat.c .% make ystat
cc ystat.c -o ystat % ./ystat ystat.c
path: ystat.c dev: 0,8 ino: 1694566923 mode: 0100644 nlink: 1 uid: 1013 gid: 40 rdev: 0,0 size: 1178 blksize: 8192 blocks: 8 atime: Sun Jun 16 23:48:53 2002 mtime: Sun Jun 16 23:48:49 2002 ctime: Sun Jun 16 23:48:49 2002 % /usr/bin/stat ystat.c
File: "ystat.c" Size: 1178 Blocks: 8 Regular File Access: (0644/-rw-r--r--) Uid: ( 1013/ yas) Gid: ( 40/ lab) Device: 8 Inode: 1694566923 Links: 1 Access: Sun Jun 16 23:48:53 2002 Modify: Sun Jun 16 23:48:49 2002 Change: Sun Jun 16 23:48:49 2002 %
----------------------------------------------------------------------
ここで、モードが8進数で 0100644 (C言語の文法で、0から始まる数は、8進 数)であることから、ファイルの型(普通のファイルかディレクトリかという 情報)を調べることができる。
0100644の上位4ビット、つまり、0170000と AND (C言語のでは、 &演算子)をとった結果は 0100000 となる。この値は、普通のファ イル(regular file)を意味する。ディレクトリの場合、0040000 となる。こ れらの数は、<sys/stat.h> (/usr/include/sys/stat.h)で定義さ れている。(Linux では、/usr/include/linux/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) ----------------------------------------------------------------------プログラム中では、次のようにしてファイルの型を調べることができる。
---------------------------------------------------------------------- struct stat buf ; stat( path, &buf ); switch( buf.st_mode & S_IFMT ) { case S_IFREG: ファイルの時の処理; break; case S_IFDIR: ... ディレクトリの時の処理; break; .... } ----------------------------------------------------------------------あるいは、<sys/stat.h> に含まれている S_ISREG(), S_ISDIR() というマクロを用いて、次のように記述する方法もある。
---------------------------------------------------------------------- struct stat buf ; stat( path, &buf ); 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
コマンドコマンドを使う。
---------------------------------------------------------------------- % umask22 %
----------------------------------------------------------------------
umask
コマンドは、シェル(csh, tcsh)の内部コマンドである。マスク
は、プロセスの属性の1つで、子プロセス、孫プロセスと代々受け継がれてい
きく。現在のシェルのマスクを見れば、シェルから実行されるプロセスのマス
クがわかる。
umask を操作するシステム・コールは、umask(コマンドと同じ名前)である。
マスクは、モードと同じく8進数で考える。たとえば、022 は、次のように 読む。
---------------------------------------------------------------------- --- 000 新しいファイルは、自分自身は、読み書き自由。 -w- 020 同じグループの人は、ファイルの書込みを禁止する。 +) -w- 002 その他の人も、ファイルの書込みを禁止する。 ---------------- 022 ----------------------------------------------------------------------このように、モードの逆になる。 マスクを変えるには、umask コマンドに、引数を与えて実行する。
この例では、シェルのマスクを 022 から 066 へ変更している。---------------------------------------------------------------------- % umask22 % umask 066
% umask
66 %
----------------------------------------------------------------------
umask コマンドによるマスクの設定は、
普通、~/.cshrc
や ~/.login
に入れる。
ssh
でリモートログインした時にも実行さ
れるように、~/.cshrc
に入れて置くのが無難である。
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--
) になる。
マスクを 0 にして、同じことをすると、モードが 666 になる。---------------------------------------------------------------------- % ls -l file1ls: file1: No such file or directory % umask
22 % echo "This is file1" > file1
% ls -l file1
-rw-r--r-- 1 yas lab 14 Jun 17 00:06 file1 %
----------------------------------------------------------------------
---------------------------------------------------------------------- % rm file1% umask 000
% umask
0 % echo "This is file1" > file1
% ls -l file1
-rw-rw-rw- 1 yas lab 14 Jun 17 00:07 file1 %
----------------------------------------------------------------------
マスクは、新しくファイルを作る時にのみ有効である。すでに、ファイルが存 在する場合、ファイルに書込みをしても、モードは変わらない。たとえば、
新しくディレクトリを作る時、そのモードは、最大 777 (---------------------------------------------------------------------- % ls -l file1-rw-rw-rw- 1 yas lab 14 Jun 17 00:07 file1 % chmod 777 file1
% ls -l file1
-rwxrwxrwx 1 yas lab 14 Jun 17 00:07 file1 % umask 022
% echo a > file1
% ls -l file1
-rwxrwxrwx 1 yas lab 2 Jun 17 00:08 file1 %
----------------------------------------------------------------------
rwxrwxrwx
)になることが多い。これは、ディレクトリを作る
プログラムが、
mkdir("dir1",0777 );となっているからである。この結果、ファイルと同様に
mode = 0777 & ~maskというモードを持つディレクトリが作られる。
たとえば、マスクが 022 の時、作成されるディレクトリのモードは、777から
マスクの022を引くので、次のように 755 (rwxr-xr-x
) に
なる。
マスクを 0 にして、同じことをすると、---------------------------------------------------------------------- % umask22 % mkdir dir1
% ls -ld dir1
drwxr-xr-x 2 yas lab 6 Jun 17 00:08 dir1 %
----------------------------------------------------------------------
新しく---------------------------------------------------------------------- % ls -ld dir1ls: dir1: No such file or directory % umask
0 % mkdir dir1
% ls -ld dir1
drwxrwxrwx 2 yas lab 1024 Jun 17 00:10 dir1 %
----------------------------------------------------------------------
mkdir
コマンドで作ったディレクトリのモードが 777 になる。
上の結果は、/tmp で試したものである。 情報学類の計算機の場合、ホーム・ディレクトリ(実体は、ファイル・サーバ 上にある)で実験すると、モードが 777 にはならない。
---------------------------------------------------------------------- % rmdir dir1% ls -ld dir1
ls: dir1: No such file or directory % umask
0 % mkdir dir1
% ls -ld dir1
drwxr-xr-x 2 yas lab 6 Jun 17 00:14 dir1 %
----------------------------------------------------------------------
次は、3つの実リンクを持っているファイルの例である。
1つのファイルの実体を、3つの名前でアクセスできる。どれか1つの名前で も残っている限り、ファイルの実体は残る。全ての名前が消された時に ファイルの実体も消される。---------------------------------------------------------------------- % ls -li /usr/bin/{yppasswd,ypchsh,ypchfn}1089359 -r-xr-xr-x 3 root root 16988 Feb 27 2001 /usr/bin/ypchfn 1089359 -r-xr-xr-x 3 root root 16988 Feb 27 2001 /usr/bin/ypchsh 1089359 -r-xr-xr-x 3 root root 16988 Feb 27 2001 /usr/bin/yppasswd %
----------------------------------------------------------------------
次は、1つの実リンクと2つのシンボリック・リンクを持っているファイルの 例である。
---------------------------------------------------------------------- % ls -li /usr/bin/{mdir,mcopy,mtools}1088204 lrwxrwxrwx 1 root root 6 Feb 24 07:18 /usr/bin/mcopy -> mtools 1088209 lrwxrwxrwx 1 root root 6 Feb 24 07:18 /usr/bin/mdir -> mtools 1088317 -rwxr-xr-x 1 root root 139132 Jan 10 2001 /usr/bin/mtools %
----------------------------------------------------------------------
mtools
が消されると、ファイルの実体が消される。mdir
や
mcopy
が消されても、ファイルの実体はそのままである。
シンボリック・リンクは、内部的には別のファイルの名前(シンボル)を含ん でいる特殊なファイルである。open() などのシステムコールでは、自動的に リンクの先の名前に置き換えられる。
シンボリック・リンクは、symlink() システム・コールで作成することができ る。
本名は、open(), creat(), mkdir() の時に作られる。それ以外に、link() シ ステム・コールで増やすことができる。リンクを減らすには、unlink() シス テム・コールを使う。リンクが1つしかないファイルに unlink() を行うと、 ファイルが削除される。
UNIXのディレクトリは、ディスク中では、可変長の構造体になっている。C言 語では、直接的には可変長の構造体を扱うことはできない。
Linux では、getdents(2) システム・コールを使うと、ディスク中に保存され たディレクトリに近いデータを得ることができる。(システム・コールは、シ ステムにより異なる。システムによっては、ディレクトリについても、read() が使えるものがある。SunOS, Solaris, Irix でも、getdents()。HP-UX では、 getdirentries()。)
---------------------------------------------------------------------- 1: /* 2: dir-getdents.c -- ディレクトリの内容を表示するプログラム 3: ~yas/syspro/dir/dir-getdents.c 4: Start: 1995/03/07 21:44:51 5: */ 6: 7: #include <stdio.h> /* fprintf(), stderr */ 8: #include <sys/types.h> /* open(2) */ 9: #include <sys/stat.h> /* open(2) */ 10: #include <fcntl.h> /* open(2) */ 11: #include <unistd.h> /* getdents(2) */ 12: #include <linux/types.h> /* getdents(2) */ 13: #include <linux/dirent.h> /* getdents(2) */ 14: #include <linux/unistd.h> /* getdents(2) */ 15: 16: _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count); 17: 18: extern void dir_list( char *dirname ); 19: extern void xdump( unsigned char *buff, int n ); 20: extern void xdump16( unsigned char *buff, int n ); 21: 22: main( int argc, char *argv[] ) 23: { 24: if( argc != 2 ) 25: { 26: fprintf( stderr,"Usage:%% %s dirname \n",argv[0] ); 27: exit( 1 ); 28: } 29: dir_list( argv[1] ); 30: } 31: 32: #define BUFFERSIZE 8192 33: 34: void dir_list( char *dirname ) 35: { 36: int fd ; 37: struct dirent *p ; 38: char buff[BUFFERSIZE] ; 39: int rcount ; 40: 41: fd = open( dirname,O_RDONLY ); 42: if( fd == -1 ) 43: { 44: perror( dirname ); 45: exit( 1 ); 46: } 47: 48: while( (rcount=getdents(fd,(struct dirent *)buff,BUFFERSIZE)) >0 ) 49: { 50: xdump( buff, rcount ); 51: for( p = (struct dirent *)buff ; (char *)p < &buff[rcount] ; 52: p=(struct dirent *) ((int)p+(p->d_reclen)) ) 53: { 54: printf("p:%d, ", (char *)p - buff ); 55: printf("off:%u, ", p->d_off ); /* long */ 56: printf("ino:%u, ", p->d_ino ); /* unsigned long */ 57: printf("reclen:%d, ", p->d_reclen ); 58: printf("name:%s\n", p->d_name ); 59: } 60: } 61: close( fd ); 62: } 63: 64: void xdump( unsigned char *buff, int n ) 65: { 66: if( n<0 || n>100000 ) 67: return; 68: for( ; n>0 ; n-=16, buff+=16 ) 69: xdump16( buff,n>=16?16:n ); 70: } 71: 72: void xdump16( unsigned char *buff, int n ) 73: { 74: register int i ; 75: for( i=0 ; i<n ; i++ ) 76: printf("%02x ",buff[i] ); 77: for( i=n ; i<16 ; i++ ) 78: printf(" "); 79: for( i=0 ; i<n ; i++ ) 80: printf("%c",isprint(buff[i])?buff[i]:'#' ); 81: for( i=n ; i<16 ; i++ ) 82: printf(" "); 83: printf("\n"); 84: } ----------------------------------------------------------------------
_syscall3() は、Linux で3引数のシステムコールを使うためのマクロである。 getdents() システムコールは、普通のプログラムで使うものではないので、 単純にこのシステムコールを使うことはできない。(普通のプログラムは、移 植性がある opendir(3),readdir(3),closedir(3)を使う。)
dir_list() は、引数で会い得られた名前のディレクトリをopen() システムコー ルで開き、getdents() システムコールでその内容を読込み、画面に表示する。
getdents() システムコールは、read() システムコールと使い方が似ているが、 ディレクトリに対してしか有効ではない。引数は、マニュアルには、struct dirent * を取るように書かれているが、このように適当な大きさのバッファ を確保して、その番地を与える。
getdents() の結果、引数で指定された番地に、struct dirent の構造体で値 が保存される。ただし、この構造体は、固定長ではない。固定長ならば、次の ような形でアクセスできるであろう。
p = (struct dirent *)buff ; n = rcount/sizeof(struct dirent); for( i= 0 ; i< n ; i++, p++ ) { ... }C言語で 「
p++
」と書くと、内部的には、sizeof(struct dirent) バ
イト分だけ増える。
しかし、getdents() の結果のバッファは、固定長の構造体(struct dirent)が 並んでいるのではなく、可変長である。つまり、構造体の大きさは、それぞれ 異なり、実際の大きさは、sizeof(struct dirent) バイトではなく、 p->d_reclen バイトである。したがって、ループの最後(forの3 番目)で、このバイト数だけ増やしている。
なお、キャストを付けずに単純に次のようにするのは、意味が違う。
for( p = (struct dirent *)buff ; (char *)p < &buff[rcount] ; p=p+(p->d_reclen) )この場合、p は、p->d_reclen バイトずれるのではなくて、 p->d_reclen*sizeof(struct dirent)バイトずれてしまう。
実行例。
---------------------------------------------------------------------- % cp ~yas/syspro/dir/dir-getdents.c .% make dir-getdents
cc dir-getdents.c -o dir-getdents % ls -ld dir1
ls: dir1: No such file or directory % mkdir dir1
% ls -ld dir1
drwxr-xr-x 2 yas lab 6 Jun 17 00:30 dir1 % echo file1 > dir1/file1
% echo file2 > dir1/file2
% ./dir-getdents dir1
c6 1d 17 53 04 00 00 00 0c 00 2e 00 b5 0c 06 49 ###S######.####I 06 00 00 00 10 00 2e 2e 00 00 00 00 c7 1d 17 53 ######..#######S 08 00 00 00 10 00 66 69 6c 65 31 00 c8 1d 17 53 ######file1####S 00 02 00 00 10 00 66 69 6c 65 32 00 ######file2# p:0, off:4, ino:1394023878, reclen:12, name:. p:12, off:6, ino:1225133237, reclen:16, name:.. p:28, off:8, ino:1394023879, reclen:16, name:file1 p:44, off:512, ino:1394023880, reclen:16, name:file2 %
----------------------------------------------------------------------
ディレクトリを読むためのライブラリ関数として、次のようなものがある。
---------------------------------------------------------------------- #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); ----------------------------------------------------------------------これらのライブラリ関数は、システム・コールと比較して移植性が高い。
---------------------------------------------------------------------- 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![]()
---------------------------------------------------------------------- % cp ~yas/syspro/dir/dir-mkdir.c .% make dir-mkdir
cc dir-mkdir.c -o dir-mkdir % ./dir-mkdir
Usage:% ./dir-mkdir dirname % ./dir-mkdir dir100
% ls -ld dir100
drwxr-xr-x 2 yas lab 6 Jun 17 01:05 dir100 %
----------------------------------------------------------------------
注意:getgid() システムコールや getgroups() システムコールを使う。
ここで、uid は、getuid()、 gid は、getgid()、groups は、getgroups() シ ステムコールの結果である。getgroups() では、複数の GID が返される。---------------------------------------------------------------------- % iduid=1013(yas) gid=40(lab) groups=40(lab),510(softadm),500(jikken3) %
----------------------------------------------------------------------
ls -l では、3つの時刻のうち、どの時刻が表示されているのかを調べなさい。 また、他の2つの時刻を表示させる方法を調べなさい。
myls-l の結果の表示形式は、ls -l と完全に一致しなくてもよい。たとえば、 時刻の表示は、上の ystat.c の結果と同じでもよい。ファイル名を先に、 時刻を後に表示してもよい。
時刻の扱い で紹介した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) を使って、ディレクトリの内容を表示 するようにしてもよい。
ヒント:struct stat の st_mode から rwxrwxrwx の表示を得るには、いくつ もの方法がある。たとえば、3ビットずつ表示する関数を3回繰り返す方法が ある。
print_rwx( st_modeの8ビット目から6ビット目を取り出す ); print_rwx( st_modeの5ビット目から3ビット目を取り出す ); print_rwx( st_modeの2ビット目から0ビット目を取り出す ); ... print_rwx( int rwx ) { if( 2ビット目が1なら ) printf("r") else printf("-") ... }特定のビットを取り出すには、ビットごとの AND (& 演算子) を 用いる。ビットの位置をずらすには、左シフト<< や右シフト >> を用いる。
その外に、S_IRWXU, S_IRUSR, S_IWUSR などのマクロを使う方法がある。
if( mode & S_IWUSR ) printf("r"); else printf("-"); if( mode & S_IXUSR ) printf("w"); ...利用可能なマクロについては、2 章の stat (man 2 stat) を見なさい。
モードは、8進数で与えるものとする。引数として取ることができるファイル 名は、1つだけでよい。% mychmod 755 filename![]()
この結果、ファイルの最終更新時刻が現在の時刻になる。% mytouch filename![]()
ヒント:time(2) と utime(2) を使う。
ヒント:stat(2)システムコールで、from_name と to_name で指定された2つ のファイルの最終更新時刻を調べる。もし、前者が新しければ、コピーする。 後者が新しければ、なにもしない。
この課題では、次のような属性を保存しなさい。
コピーする時に、内容をコピーした後に、コピー元のファイルに stat() を実 行する。こうして得られた属性を、chmod() や utime() でコピー先のファイ ルに設定する。
opendir(3)、または、scandir(3) を用いて、次のような動きをするプログラ ムを作りなさい。
ls dirname
ls -ia dirname
dirname
が省略された時には、"." が指定されたものとし
て扱うようにしてもよい。
余裕があれば、ls-lプログラムの課題のように、 属性を扱いなさい。
この時、次の2つの名前を特別扱いして、探索しないようにする。
.
」
..
」
シンボリック・リンクかどうかの判定には、lstat() システムコールを使いな さい。