[Exp2010]ファイルのモード

ファイルモードとリンク

本テキストの目的

  • ファイルモードを理解する (ls -l の出力結果が理解できるようになる).
  • ファイル保護ができるようになる (chmod を使いこなせる).
  • リンクの概念を覚える.

ファイルモード

Unix/Linux では、複数のユーザーで同一のマシンを使用することを前提に設計されています (マルチユーザの概念). そこで, Unix/Linux においてファイルへのアクセス権制御, 即ち他人に見せてもよいファイル・見せたくないファイルの制御が重要となります.

Unix/Linux では, 自分のホームディレクトリへ他人が勝手にファイルを書いたり, 自分のメールを他人に読まれたりされないようにすることが可能です. また特定のグループに所属するメンバーのみ読み書きが可能なファイルを作ることもできます. ここでは, このようなファイルのアクセス権の制御について見ていきます.

ファイルモード

自分のホームディレクトリで ls コマンドを幾つかのオプションを指定して実行すると, およそ以下のような出力が得られます. (以下はあくまで出力例です)

$ ls -laF
total 140
drwxr-xr-x 20 chikuwa2 chikuwa2  4096 Apr 24 17:05 ./
drwxr-xr-x  9 root     root      4096 Apr 20 03:34 ../
-rw-------  1 chikuwa2 chikuwa2     0 Apr  8 09:51 .ICEauthority
-rw-------  1 chikuwa2 chikuwa2    50 Apr 24 17:05 .Xauthority
drwx------  2 chikuwa2 chikuwa2  4096 Apr  1 14:11 .anthy/
-rw-------  1 chikuwa2 chikuwa2  7023 Apr 25 21:10 .bash_history
-rw-r--r--  1 chikuwa2 chikuwa2   220 Mar  7 21:19 .bash_logout
-rw-r--r--  1 chikuwa2 chikuwa2   414 Mar  7 21:19 .bash_profile
-rw-r--r--  1 chikuwa2 chikuwa2  2227 Mar  7 21:19 .bashrc
-rw-------  1 chikuwa2 chikuwa2    26 Mar 31 13:12 .dmrc
drwxr-xr-x  3 root     root      4096 Mar 21 10:52 .emacs.d/
drwx------  4 chikuwa2 chikuwa2  4096 Apr  8 09:51 .gconf/
drwxr-xr-x  2 chikuwa2 chikuwa2  4096 Mar 31 13:12 Desktop/
-rw-r--r--  1 chikuwa2 chikuwa2     5 Apr 25 21:21 hoge.txt         < このファイルに注目
drwxr-xr-x  2 root     root      4096 Mar 27 22:39 public_html/
drwxr-xr-x  2 chikuwa2 chikuwa2  4096 Apr 24 15:05 sdcrwchk/
drwxr-xr-x  2 chikuwa2 chikuwa2  4096 Apr 24 15:13 work/ 
........... (以下ファイル・ディレクトリが続く)

この出力の "hoge.txt" というファイルを例に取って, この読み方を解説します. 左から順に,

-rw-r--r--ファイルモード
1ファイルへの "リンク数"
chikuwa2ファイルの所有者
chikuwa2ファイルの属するグループ
5ファイルの大きさ(バイト単位)
Apr 25 21:21ファイルの最終更新時刻
hoge.txtファイルの名前

を表しています. これらをファイルの「属性」と呼びます. なお,

$ man ls

としてみるとより詳しいことが分かります.

一番左側の表示の文字列はファイルの「モード」, すなわちファイルの型 (type) と利用権限 (permission) を表しています. 例えば, hoge.txt のすぐ上の Desktop/ のモードを見ると,

drwxr-xr-x 

と書かれてあります. 左端の 1 文字はファイルの型を示します. 取りうる文字と型は後述します.

2 〜 10 文字目は, 利用権限を示しています. 利用権限の許可対象には,

  • ファイルの持ち主
  • ファイルの所有グループ
  • その他のユーザー

があり, それぞれに対する許可情報を,

  • ユーザーパーミッション (user permission)
  • グループパーミッション (group permission)
  • アザーズパーミッション (others permission)

と読んでいます.

また利用権限には,

  • 読み出し許可 (read permission)
  • 書き込み許可 (write permission)
  • 実行許可 (execute permission)

の 3 種類があります. ファイルモードの読み方は,

d rwx r-x r-x
^ ^^^ ^^^ ^^^
|  |   |   |
|  |   |   アザーズパーミッション
|  |   |
|  |   グループパーミッション
|  |
|  ユーザーパーミッション
|
ファイルの型(type)

となります. 各箇所で取りうる文字は,

- --- --- ---
d rwx rwx rwx
l   s   s   t
s   S   S
b
c
P

です.

左端の文字とファイルの型は, 次のように対応づけられています.

-通常のファイル
dディレクトリディレクトリファイル
lシンボリックリンクWindows の "ショートカット" に近い機能
sソケットファイルソケットという通信方法で使用される特殊ファイル
bブロックデバイスデータを一定の文字数で転送する際に使用される特殊ファイル
cキャラクタデバイスデータを 1 文字ずつ転送する際に使用される特殊ファイル
P名前つきパイプ複数のプロセス間で通信を行う際に使用される特殊ファイル

また, 利用権限は以下の通りです.

-その権限が出ていない
r読み込み許可
w書き込み許可
x実行許可
ss ビットが立っていて実行許可
Ss ビットが立っていて実行不可
tスティッキー (sticky)

s ビットについては後述します (スティッキー (sticky) については付録参照).

ファイルの所有者, ファイルの属するグループを変更するには chown, chgrp といったコマンドを使用します.

利用権限の設定 (ファイルモードの設定)

ファイルの利用権限を設定するには chmod コマンドを用います. 基本的な使い方は次の通りです. (詳しくは $ man chmod を参照してください).

$ chmod <mode> file

<mode> の部分には 0 から 7 までの数字を 3 桁並べたものが入ります. 左の桁から順にファイルの所有者, ファイルの所属グループ, その他のユーザーに対する利用権限を示します. 各々の読みだし, 書き込み, 実行権限は 4, 2, 1 の数字で表され, <mode> にはこの数字の和が代入されます.

許可記号数字
読込r4
書込w2
実行x1

例えば, モードを -rw-rw-r-- と変えたい場合,

-rw-rw-r--	
 42 42 4
 |  |  |
 6  6  4         < これが <mode> になる

となるので,

$ chmod 664 file

とすれば変更できます.

上では <mode> を数字で与えましたが, 記号で与えることもできます. <mode> の数字列の代わりに,

対象操作許可
[ugoa][+-=][rwxst]

という形式を "," で区切って並べます.

対象と操作の文字は以下の意味を表します.

※許可の所はファイルモードと同じです.

対象操作
u所有者
gグループ
oその他のユーザ
a全員 (ugo と同じ)
+許可を与える
-許可を取り消す
=許可を設定する

この場合, 先の例と同じ設定にするには次のようにします.

$ chmod u=rw,g=rw,o=r file

s ビット

s ビットとは, あるファイルの実行者を「成り変わる」機能のことを言います.

s ビットには, SUID ビットと SGID ビットの二種類あります.

具体例で見て行きましょう. SUID の例として /usr/bin/passwd を見てみます.

先週の実習で, passwd コマンドが出てきました (ログイン用パスワードの変更法). passwd は /usr/bin/ ディレクトリに存在します. passwd コマンドを実行してパスワードを変更すると, /etc/shadow が書き変わるのでしたね. そこで, /etc/shadow のパーミッションを見てみると,

$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1349 Apr 20 03:34 /etc/shadow

となっていますから, このファイルの書き込みができるのは, このファイルの持ち主, すなわち root だけです. しかし, 誰もがパスワードを変更したい (/etc/shadow が書き変わって欲しい) と思っています. どうやって /etc/shadow を書き変えることができるのでしょうか?

そこで /usr/bin/passwd のパーミッションを見てみます. すると,

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 28480 Feb 27  2007 /usr/bin/passwd 

となっています.

モードの左から 4 番目に s という文字が書かれています. これが SUID ビットで, このファイルが実行されると, プログラムの持ち主がこのファイルを実行したものと解釈されることを表します. つまり, passwd コマンドを一般ユーザが実行すると, root が実行したものと解釈されるのですから, /etc/shadow を書き換えることができるという訳です.

SGID は上のグループ版です. 似たようなものなので説明は割愛します.

ファイル保護を試してみる

では, 実際にファイルを作成してパーミッションの変更をしてみましょう.

下準備

以下の作業では, ログイン, ログアウトを繰り返すのが面倒なのでちょっとおまじないをします. まず, もう 1 つターミナルを立ち上げ, 今ログインしているアカウント (foo さん) の相方 (bar さん) になりかわります (仮に 2 人の班を想定して書いています. 3 人の班は適宜読み替えてください).

新しく立ち上げたターミナルを用いて以下のコマンドを打ち込んでみてください.

foo@joho**:~$ whoami         < 現在のログインアカウント名を尋ねる
foo

foo@joho**:~$ su bar         < bar になりかわる. このとき bar さんのパスワード入力が必要
Password:

bar@joho**:/home/foo$ whoami         < 無事 bar さんになっているか現在のアカウント名を尋ねる
bar

bar@joho**:/home/foo$ cd             < bar さんのホームディレクトリに移動しておく

以下, 元々あったターミナルを "A ターミナル (foo さんが作業する)", あたらしく起動し bar さんになり変わった状態のターミナルを "B ターミナル (bar さんが作業する)" と呼ぶことにします.

ファイルとディレクトリの作成

では, まず A, B 両ターミナルで (foo さん bar さんそれぞれが) 以下の作業をしてディレクトリとファイルを作成してください.

$ cd                                         < ホームディレクトリに移動
$ mkdir secret                               < secret ディレクトリを作成
$ echo 'secret-word' > ./secret/word.txt     < secret ディレクトリの中に 'secret-ward' が書き込まれた word.txt というファイルを作成
                                               'secret-word' の部分は各自の好きな単語に置き換えよう

次に, それぞれ "自分は見ることができ, かつ相手は見ることができない" ように, パーミッションを変更してください.

$ chmod ??? secret/word.txt                  < chmod を使って secret/word.txt ファイルのパーミッションを変える
                                               ファイルが相方から見えないよう ??? の部分を考える

さて, 上記の ??? には, いったい何の数字を入力すればよいでしょう?

ファイルを見てみましょう

foo さん, bar さん共に, 今作った相手のファイルを見てみましょう.

foo@joho**:~$ cat ~bar/secret/word.txt      < foo さんがターミナル A で実行

bar@joho**:~$ cat ~foo/secret/word.txt      < bar さんがターミナル B で実行

お互いに,

Permission denied

と注意され相手のファイルが見えなければ成功です. もし見られてしまった場合は, 先の chmod コマンドを先程とは違うモードで再度実行し, 見られない様にしてください.

見られないようにする方法が分かったら, 今度はいろいろなモードに変更してその違いを確かめてください. ディレクトリのモードを変えると, そのディレクトリに移動できなくなったりするはずです. cd コマンドなどを試してみてください.

ためしに, ディレクトリ「secret」に, "自分は中に入れ, かつ相手は中に入れない" ようなパーミッションを設定し, 互いに入れないか確認してください.

まず, 互いに "cd" で自分のホームディレクトリに戻ります.

$ cd /home/foo

$ cd /home/bar

お互いに, 自分のホームディレクトリに戻ったら, 上と同様にディレクトリのパーミッションを変更しましょう.

$ chmod ??? secret                           < chmod をつかって secret ディレクトリのパーミッションを変える
                                               相方がディレクトリに入れないように ??? の部分を考えよう 

foo@joho**:~$ cd ~bar/secret

bar@joho**:~$ cd ~foo/secret

実際に移動すると, お互いに,

Permission denied

と怒られましたか?

最後に自分の作った word.txt を消去してください. このとき, どのようなモードだとファイル消去できないかも是非試してください.

  • 発展問題:

    word.txt もしくはディレクトリ secret のモードを変更し, secret/word.txt ファイルが消去できないようにせよ.

    • この知識は自分の卒論などを誤って消してしまわないためにも重要です! つまり, 消去できないようにファイルのモードを変更しておけば, 誤って rm コマンドを実行しそうになったときも, 消去されずに済むわけです!

Unix/Linux ではファイルモードの設定によって, 他人のファイルを見ることが可能です. 大抵の場合, 普通に作成したファイルは他人が見れる状態にあるでしょう. もし, 本当に見られては困る内容のファイルはモードを正しい設定にするように心がけましょう. 他人がみても良い設定のファイルは基本的に "見ても良い" 訳ですが, "mail" といった文字列のつく名前のファイルやディレクトリの中, あるいは個人的な情報であることが推測できるファイルは, 仮に見れる状態にあっても絶対に見てはいけません. また, 同様に見られては困るファイルを見れる状態で置いておくことも避けましょう. こういったファイルは, 見た方も見られた方も嫌な思いをしてしまいます.

リンク

リンクとは, あるファイルやディレクトリを, 別のファイルやディレクトリに関連付ける仕組みをいいます.

リンクには,

  • ハードリンク
  • シンボリックリンク

の 2 種類があります. 以下はそれぞれの説明です.

ハードリンク

先に ls コマンドを用いた説明で, その実行結果の左から 2 番目に表示される数字は, リンク数を表すと述べました.

$ ls -l hoge.txt
-rw-r--r-- 1 chikuwa2 chikuwa2 5 Apr 25 21:21 hoge.txt

これは正しくは「ハードリンク」の数を表しています. ハードリンクの数は, 簡単に言うとファイルの「名前」の数を表しています.

人間は文字列 (上の場合 'hoge.txt') でファイルを識別しますが, システムはファイルを「番号」として管理しています. Unix では, この番号を「i-ノード」と呼びます. ファイルと i-ノード番号は 1 対 1 に対応 (リンク) しています.

  • 以下は, 最初は読み飛ばして良い. できれば後で読んでね.
    • i-ノードという概念は, そもそも Unix のカーネルが, それぞれのパーティションに, 装置番号 (device number) を割り当て, 「ディスク」ではなく「ファイルシステム」という論理的なレベルで処理を行うことに由来しています. ファイルシステムは, 論理的なブロックの集まりから構成され, 単位となるブロックの大きさはシステムによって異なります.
    • ファイルシステムは, 次のような 4 つの部分に分けられます.
      • ブートブロック: ファイルシステムの先頭には, マシンを立ち上げる bootstrap コードが格納されます. システムを立ち上げるには, 一つのブートブロックがあれば十分なのですが, 全てのファイルシステムがブートブロックを持っています.
      • スーパーブロック: ここには, ファイルシステムの大きさや, どこに空き領域があるかといった, ファイルシステム自身の管理情報が格納されます.
      • i-ノードリスト: スーパーブロックに続くのは iノードのリストです. i-ノード自身に含まれる情報は次に紹介します.
      • データブロック: この部分には, ファイルシステムの実体をなすファイルのデータやディレクトリの情報が格納されます.
    • i-ノードには, 次のような情報が含まれています. ここで注意して欲しいことは, i-ノードにはファイル名は含まれていないということです.
      • ファイルの所有者
      • ファイルの型
        1. 通常のファイル
        2. ディレクトリ
        3. スペシャルデバイスファイル (キャラクター, ブロック)
        4. FIFO
      • ファイルアクセスのパーミッション
      • タイムスタンプ
        1. 最終変更時間
        2. 最終アクセス時間
        3. 最終属性変更時間
      • リンク数
      • ファイルのデータのディスク上のアドレスの情報

        ユーザーは, ファイルをバイトストリームとして扱いますが, カーネルは, データをディスク上の不連続なブロックに格納しています. i-ノードは, その表を持っています.

      • ファイルの大きさ

ls -i とするとファイルの i-ノード番号を実際に見ることができます.

$ ls -i hoge.txt
11190497 hoge.txt

ハードリンクの数は, 実はそのファイルの対応している i-ノード番号を参照しているファイルの数を表しています.

したがって同じ i-ノード番号をもつ別名のファイルを作ること, 即ち, 別々の名前を持つがしかし内容や扱われ方が同じファイルをつくることができます. これは "ln" コマンドで実行できます.

touch コマンドで空のファイル file1 を作り, 試してみましょう. 通常のファイルのリンク数は 1 です.

$ touch file1
$ ls -l file1
-rw-r--r-- 1 chikuwa2 chikuwa2 0 Apr 25 22:30 file1

ここで,

$ ln file1 file2

とすると, file1 に file2 という新しい名前が追加されます (リンクを張る). file1 と file2 を ls してみると,

$ ls -l file1 file2
-rw-r--r-- 2 chikuwa2 chikuwa2 0 Apr 25 22:30 file1
-rw-r--r-- 2 chikuwa2 chikuwa2 0 Apr 25 22:30 file2 

リンク数が 2 に増えています. 一方, ディレクトリのリンク数は必ず 2 以上になります. (なぜなのか考えてみましょう)

さて,

$ cat file1
$ cat file2

としても何も表示されないように, file1 も file2 も空のファイルですが,

$ echo Hello! > file1

とすれば, file1 には Hello! と書き込まれます. 確認してみると,

$ cat file1
Hello!

と表示され, たしかに書き込まれています. では, file2 はどうでしょう. 先程までは空のファイルでしたが,

$ cat file2
Hello!

と表示されますね. つまり, file1 と file2 は名前が違えど同じファイルなのです. これがリンクというものです.

また, リンクを消すには rm コマンドを使って file1 か file2 を消せばいいです.

シンボリックリンク

上の ln コマンドにオプション -s をつけて実行してみます. これは「シンボリックリンク」と呼ばれ, 簡単にいうとファイルの「別名」をつけるようなものです. 類似のものに, Windows では「ショートカット」, Mac では「エイリアス」があります.

$ ln -s file1 file3
$ ls -l file1 file3
-rw-r--r-- 2 chikuwa2 chikuwa2 0 Apr 25 22:37 file1
lrwxrwxrwx 1 chikuwa2 chikuwa2 5 Apr 25 22:38 file3 -> file1

ハードリンクの場合は i-ノードを直接参照しているのに対し, シンボリックリンクは「ファイル名」を参照しています. 従って、上の例でも file1 のリンク数も増えませんし, file3 のリンク数も 1 のままです.

シンボリックリンクの特徴をみるため, 次のようにコマンドを実行してみましょう.

$ rm file1
$ cat file3
cat: file3: No such file or directory

このようにエラーが返されます. しかし,

$ ls file3
file3

となり file3 そのものは存在します. これはシンボリックリンクでは「ファイル名」を参照しているためです. 参照先のファイル名 (file1) が消去されてしまうと, 参照していたファイルの i-ノードを見ることができなくなり, 上の例では file3 を表示しようとするとエラーが返るのです.

ハードリンクとは異なり, ディレクトリに対するシンボリックリンクは可能です. シンボリックリンクを消すにはやはり rm コマンドを使います.

付録 (時間があればやってください)

スティッキー(sticky)ビット

スティッキービットとは, ファイルまたはディレクトリの所有者だけが目的のファイルの削除や名前の変更を行えるようにする機能のことです.

s ビットの時と同様に具体例で見て行きましょう. ルートディレクトリ(/)の下には tmp というディレクトリがあります. このディレクトリのパーミッションを見ると,

$ ls -l /
drwxrwxrwt   8 root root  4096 Apr 25 21:09 tmp 

となっています.

これによると, このディレクトリは誰でも読み書きができるようになっていますが, 「アザーズ」の実行許可のところに t と書かれています. つまり, 誰でもこのディレクトリの中に新しいファイルやディレクトリを作ることはできますが, その作ったファイルを消去したりすることができるのは, そのファイルの所有者だけということになります. これがスティッキービットというものです.

実際, "tmp" ディレクトリの中を "ls -l" で見てみますと, 中にあるファイルやディレクトリの変更権限は, そのファイルの所有者しか許されていないはずです.

デバイスファイル

ls -l /dev/ としてみると以下のような出力が得られます.

$ ls -l /dev/
total 0
lrwxrwxrwx 1 root root            13 Apr 24 16:54 MAKEDEV -> /sbin/MAKEDEV
crw-rw---- 1 root audio    14,    12 Apr 24 16:54 adsp
crw-rw---- 1 root audio    14,     4 Apr 24 16:54 audio
drwxr-xr-x 3 root root            60 Apr 24 16:54 bus
lrwxrwxrwx 1 root root             3 Apr 24 16:54 cdrom -> hda 
   :
   :
brw-rw---- 1 root disk      8,     0 Apr 24 16:54 sda
brw-rw---- 1 root disk      8,     1 Apr 24 16:54 sda1
brw-rw---- 1 root disk      8,     2 Apr 24 16:54 sda2
brw-rw---- 1 root disk      8,     5 Apr 24 16:54 sda5
brw-rw---- 1 root disk      8,     6 Apr 24 16:54 sda6
brw-rw---- 1 root disk      8,    16 Apr 24 16:54 sdb
   :
   :

ファイルモードを見ると, 一番左が見慣れない c や b になっています. このようなファイルは通常のファイルではありません.

UNIX では基本的にあらゆる物を抽象化して「ファイル」として扱います. たとえばマウス(/dev/mouse)や画面等もファイルに抽象化して取り扱ってしまいます.

このようなファイルは「デバイスファイル(device file)」と呼ばれます. デバイスファイルには, c で表されるキャラクタデバイスと, b で表されるブロックデバイスがあります.

/dev/null

次を実行してみましょう.

$ ls > /dev/null        < ls を実行したときの出力を /dev/null に書き込む

何も出力されません. では, これはどうでしょう?

$ cat /dev/null

やはり何も表示されません.

/dev/null というファイルは, いかなる入力も捨ててしまう性質を持つデバイスファイルです. コマンドの出力がうるさい時などに, リダイレクトと組み合わせて使います.

リダイレクトについては次週, 最低限 Unix (Linux) 3 で説明します. とはいうものの, 今週のこの実習でも何回も出てきましたが…