4.2 ファイルの属性

Menu Menu

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


課題5 ディレクトリの内容

課題3で、opendir(), scandir() ライブラリ関数を利用したり、あるいは、次の実験のプログラム dir-list.c と組み合わせて、より ls -l の表示に近づけなさい。余裕があれば qsort() も使って見ること。

Shinji KONO / Fri May 1 00:39:29 1998