システム・プログラム
電子・情報工学系
新城 靖
<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人ひとりを認識するような アクセス制御の仕組みを持っている。 アクセス制御(access control) とは、ユーザ(プロセス) が、ファイルなどの資源をアクセスする時、どんな アクセスの仕方なら正しいということを定義して、それがきちんと守られてい ることをということを保証することである。
Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、UNIX では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。
これに対して、Personal Computer用のOS(Windows 95/98/ME, MacOS 9以下) では、このような複数のユーザを識別したアクセス制御は、できない。 Windows NT, Windows 2000, MacOS X は、可能である。
ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。
Unixでは、全てのファイルやプロセスは、あるユーザの所有物である。これを、
Unixでは、ファイルとプロセスに属性として、UID を持たせることで実現して
いる。
図? 実世界のユーザとUNIX中のプロセス・ファイル
1人のユーザが複数のグループに属することがある。
----------------------------------------------------------------------
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-print1231: yas 0: root %
----------------------------------------------------------------------
----------------------------------------------------------------------
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.cpath: 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> %
----------------------------------------------------------------------
0100644の上位4ビット、つまり、0170000と AND (C言語のでは、
&演算子)をとった結果は 0100000 となる。この値は、普通のファ
イル(regular file)を意味する。ディレクトリの場合、0040000 となる。こ
れらの数は、
----------------------------------------------------------------------
#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;
....
}
----------------------------------------------------------------------
あるいは、
----------------------------------------------------------------------
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 コマンドは、シェルの
内部コマンドである。マスクは、プロセスの属性の1つで、
子プロセス、孫プロセスと代々受け
継がれていきく。現在のシェルのマスクを見れば、
シェルから実行されるプロセスのマスクがわかる。
umask を操作するシステム・コールは、umask(コマンドと同じ名前)である。
マスクは、モードと同じく8進数で考える。たとえば、022 は、次のように 読む。
----------------------------------------------------------------------
--- 000 新しいファイルは、自分自身は、読み書き自由。
-w- 020 同じグループの人は、ファイルの書込みを禁止する。
+) -w- 002 その他の人も、ファイルの書込みを禁止する。
----------------
022
----------------------------------------------------------------------
このように、モードの逆になる。
マスクを変えるには、umask コマンドに、引数を与えて実行します。
この例では、シェルのマスクを 022 から 066 へ変更しています。---------------------------------------------------------------------- % umask22 % % umask 066
% umask
66 %
----------------------------------------------------------------------
umask コマンドによるマスクの設定は、
普通、~/.cshrc や ~/.login に入れる。
rsh コマンド時も実行さ
れるように、~/.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 になる。---------------------------------------------------------------------- % umask22 % echo "This is file1" > file1
% ls -l file1
-rw-r--r-- 1 yas 14 Sep 14 17:25 file1 %
----------------------------------------------------------------------
---------------------------------------------------------------------- % 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 %
----------------------------------------------------------------------
マスクは、新しくファイルを作る時にのみ有効である。すでに、ファイルが存 在する場合、ファイルに書込みをしても、モードは変わらない。たとえば、
新しくディレクトリを作る時、そのモードは、最大 777 (---------------------------------------------------------------------- % 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 %
----------------------------------------------------------------------
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 512 Jan 23 18:09 dir1 %
----------------------------------------------------------------------
新しく---------------------------------------------------------------------- % umask 0% rmdir dir1
% mkdir dir1
% ls -ld dir1
drwxrwxrwx 2 yas 512 Jan 23 18:09 dir1 %
----------------------------------------------------------------------
mkdir コマンドで作ったディレクトリの
モードが 777 になる。
----------------------------------------------------------------------
% 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 が消されると、ファイルの実体が消される。
uncompress や zcat が消されても、ファイルの実体はそのままである。
シンボリック・リンクは、内部的には別のファイルの名前(シンボル)を含ん でいる特殊なファイルである。open() などのシステムコールでは、自動的に リンクの先の名前に置き換えられる。シンボリック・リンクは、symlink() シ ステム・コールで作成することができる。
本名は、open(), creat(), mkdir() の時に作られる。それ以外に、link() シ ステム・コールで増やすことができる。リンクを減らすには、unlink() シス テム・コールを使う。リンクが1つしかないファイルに unlink() を行うと、 ファイルが削除される。
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 dir100 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)
----------------------------------------------------------------------
これらのライブラリ関数は、システム・コールと比較して移植性が高い。
----------------------------------------------------------------------
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![]()
注意:getgid() システムコールや getgroups() システムコールを使う。
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) を使って、ディレクトリの内容を表示 するようにしてもよい。
モードは、8進数で与えるものとする。ファイルは、1つだけでよい。% mychmod 755 filename![]()
この結果、ファイルの最終更新時刻が現在の時刻になる。% mytouch filename![]()
ヒント:times(2) と utime(2) を使う。
ヒント:stat(2)システムコールで、from_name と to_name で指定された2つ のファイルの最終更新時刻を調べる。もし、前者が新しければ、コピーする。 後者が新しければ、なにもしない。
この課題では、次のような属性を保存しなさい。
opendir(3)、または、scandir(3) を用いて、次のような動きをするプログラ ムを作りなさい。
ls dirname
ls -ia dirname
dirname が省略された時には、"." が指定されたものとし
て扱うようにしてもよい。
余裕があれば、ls-lプログラムの課題のように、 属性を扱いなさい。
この時、次の2つの名前を特別扱いして、探索しないようにする。
.」
..」
シンボリック・リンクかどうかの判定には、lstat() システムコールを使いな さい。