shibomb

Linuxの権限エラーを直す!所有者とchmodで原因を切り分ける

ファイルを編集しようとしたら「Permission denied」。スクリプトを実行しても同じ表示。どの権限を直せばよいのでしょうか。焦って sudo を付けたり chmod 777 を打ったりすると、必要以上の権限を与えてしまうおそれがあります。この記事では、id と ls -l で原因を切り分け、必要な権限だけを付ける手順を、Linux の練習用フォルダで再現しながら説明します。

まずは実行ユーザーと所有者を確認:idとls -lの読み方

Permission denied が出たら、まず「誰が操作しているか」と「対象の所有者・グループ・権限」を確認します。所有者が違うだけで拒否されるとは限らず、同じ所有者でも必要な権限がなければ操作できません。

id
ls -l memo.txt

出力例は次のとおりです。

uid=1000(taro) gid=1000(taro) groups=1000(taro),1001(dev)
-rw-r----- 1 alice dev 120 Nov  1 10:00 memo.txt

id から、自分が taro で、taro と dev のグループに所属しているとわかります。ls -l の alice dev は、Linux の所有者(ユーザー)とグループです。この例では所有者が alice、グループが dev です。

先頭の -rw-r----- は10文字で、1文字目がファイル種別、続く9文字が所有者・グループ・その他の3組です。通常の権限ビットで判断するこの例では、taro は所有者ではなく dev グループに入っているので、グループ欄の r-- が適用されます。読めますが書けません。この「どの欄が自分に当たるか」を確認する習慣が、Linux パーミッションを読む出発点です。

r・w・xの意味を整理:ファイルとディレクトリで異なる権限の働き

r は読み取り、w は書き込み、x は実行です。ここまでは多くの入門書にある通りです。つまずきやすいのは、同じ文字でもファイルとディレクトリで意味が変わる点です。

ファイルでは、r で中身を読め、w で内容を変更でき、x はプログラムとして実行するための条件の1つです。ディレクトリでは、r は中のファイル名の一覧を取得する権限、w はファイルの作成・削除・名前変更に関わる権限、x はそのディレクトリに入ったり中のファイルへ辿り着いたりする権限を表します。作成・削除・名前変更には、通常、そのディレクトリの w と x の両方が必要です。

ディレクトリの実行権限(x)は、初心者の方に一番伝えたい知識です。名前から「実行するものではないから関係ない」と誤解しやすいのですが、x がないと中のファイルへアクセスできません。ただし、r があれば名前の一覧だけは取得できます。ファイルの削除に必要なのも、ファイル自身の w ではなく、置かれているディレクトリの w と x です。なお、sticky bit という削除制限が設定されていると、これらの権限があっても他人のファイルを削除できない場合があります。権限の仕様、削除制限の仕様

練習用フォルダで再現:読めない・書けない・入れない原因を探す

実際に壊して確かめるのが早いので、練習用の場所を作ります。root では通常の読み書きの権限チェックを回避でき、結果が変わるため、普通のユーザーで実行してください。以下は ~/perm-lab がまだ存在しない状態で試す例です。

mkdir ~/perm-lab && cd ~/perm-lab
echo 'hello' > secret.txt
chmod 000 secret.txt
cat secret.txt

英語表示の環境では、cat: secret.txt: Permission denied と出ます。ls -l で ---------- となっているので、自分に r がないのが原因です。

次はスクリプトです。

printf '#!/bin/bash\necho hi\n' > run.sh
chmod u=rw run.sh
./run.sh

x がないため、これも Permission denied です。ちなみに、Bash がインストールされていれば bash run.sh なら動きます。スクリプト自身の x は不要で、bash がその内容を読み取って実行するからです。

最後にディレクトリです。

mkdir box && touch box/a.txt
chmod u=rw box
cd box
ls -l box

cd は拒否され、ls -l も中のファイルの属性を取得できずにエラーを出します。名前の一覧を読む r と、中のファイルへ辿り着く x の違いが確認できます。確認が済んだら chmod u+x box で戻しておきましょう。

chmod u+xと640・750を読み解き、必要な権限だけを付ける

chmod 使い方は2種類あります。記号で「足す・引く」書き方と、数字で「全体を指定する」書き方です。

記号式の chmod u+x run.sh は、所有者(u)に実行権限(x)だけを足します。ほかの欄には触れないので、今回のように所有者の x 不足が原因なら、この変更で足ります。g はグループ、o はその他、a は全員を指します。

数字式は r=4、w=2、x=1 の合計で1桁を作ります。

  • 640: 所有者は rw-(6)、グループは r—(4)、その他は ---(0)
  • 750: 所有者は rwx(7)、グループは r-x(5)、その他は ---(0)

設定ファイルなら 640、共有するスクリプトやディレクトリなら 750 あたりが出発点として扱いやすいと思います。

再帰的に変える -R には注意が必要です。chmod -R 644 dir とすると、ディレクトリから x が消えて中に入れなくなります。そういう場面では chmod -R u=rwX,g=rX,o= dir のように大文字の X を使います。X は、ディレクトリか、すでに誰かに x があるファイルにだけ実行権限を付けます。

チームで共有するときの注意点:グループ権限と変更範囲を確認する

複数人で同じディレクトリを触るなら、「全員に開く」のではなく共通のグループを作るのが筋です。たとえば dev グループを用意し、メンバーを追加します。以下は dev グループと /srv/project が存在する前提です。

sudo usermod -aG dev taro
sudo chgrp -R dev /srv/project
sudo chmod -R g+rwX /srv/project

-aG の -a を忘れると、指定しなかった既存の補助グループから外れます。主グループはこの指定では変わりません。グループへの追加を現在の作業環境に反映させるには、通常、ログアウトしてログインし直します。id で dev が見えない間は、権限を疑う前にセッションを確認してください。usermod の仕様

もう1点、ディレクトリに chmod g+s /srv/project で setgid を付けると、その直下で新しく作られるファイルやディレクトリが親のグループを引き継ぎます。毎回 chgrp する手間が減ります。ただし、既存のサブディレクトリには別途設定が必要です。また、グループの書き込み権限まで保証するものではなく、作成時の権限を制限する umask などにも左右されます。Red Hat の共有ディレクトリの説明

変更前に必ず ls -ld /srv/project で現状を確認し、-R を使うなら対象パスを声に出して読むくらい慎重にいきたいところです。システムの重要なディレクトリへ誤って適用すると、動作や安全性を損なうおそれがあります。

権限を直しても動かないとき:親ディレクトリ、ACL、読み取り専用マウントを切り分ける

ファイル自体の権限が正しいのにエラーが続くなら、まず次の3つを確認します。

1つ目は親ディレクトリです。通常の権限チェックでは、パスを辿る途中のどれか1つに x がなければ失敗します。namei -l /srv/project/app/run.sh を使うと、経路上の各階層の所有者と権限が一覧で見えます。パス探索の仕様

2つ目は ACL です。GNU の ls -l では、権限欄の末尾の + は ACL などの追加のアクセス制御がある目印です。getfacl ファイル名 で内容を確認してください。mask という項目が、名前付きユーザーやグループなどに与える権限の上限を絞っていることもあります。ls の表示仕様

3つ目はマウントです。findmnt -T /srv/project で対象がどのファイルシステムにあるかを調べます。ro なら読み取り専用で、書き込み時には通常 Read-only file system と表示されます。noexec が付いていると、x を付けても直接実行した際に Permission denied になります。マウントオプションの仕様

環境によっては SELinux などの仕組みも拒否の原因になります。上の3つで説明がつかなければ、こうした追加の制御も確認します。なお、namei、getfacl、findmnt は環境によって追加インストールが必要です。

次に Permission denied を見たら、まず id と ls -l 対象パス で操作するユーザーと対象の権限を確認しましょう。必要に応じて namei -l 対象パス で親ディレクトリまで辿ると、変更すべき場所を絞り込めます。

関連記事