Path: sran124!katsu From: katsu@sra.co.jp (WATANABE Katsuhiro) Message-ID: Date: 5 May 90 22:21:55 Organization: Software Research Associates, Inc.,Japan Newsgroups: sra.test, junk Subject: Today's junk article Distribution: sra GW特別企画 「本日のjunk記事」 この企画の趣旨: やりたいからやってるだけ。目指す領域は日記。 しかし今回は、私に泣きつかれた先輩方への顛末の報告も兼ねています。 「ファイルシステム修復はサルでもできる」 ある日のこと、毎日早朝に走らせている fsck から、いつもと違った 報告がきました。 ** /dev/rsd00f (NO WRITE) ** Last Mounted on /public DIRECTORY /usr/mmb/katsu/Mail/akemi/7: LENGTH 1897 NOT MULTIPLE OF 512 ADJUST? no DIRECTORY CORRUPTED I=31616 OWNER=katsu MODE=40600 SIZE=2048 MTIME=Apr 11 15:28 1990 DIR=/usr/mmb/katsu/Mail/akemi/7 SALVAGE? no MISSING '.' I=31616 OWNER=katsu MODE=40600 SIZE=2048 MTIME=Apr 11 15:28 1990 DIR=/usr/mmb/katsu/Mail/akemi/7 FIX? no MISSING '..' I=31616 OWNER=katsu MODE=40600 SIZE=2048 MTIME=Apr 11 15:28 1990 DIR=/usr/mmb/katsu/Mail/akemi/7 FIX? no LINK COUNT DIR I=25095 OWNER=katsu MODE=40755 SIZE=512 MTIME=Apr 17 04:01 1990 COUNT 2 SHOULD BE 3 ADJUST? no LINK COUNT DIR I=31616 OWNER=katsu MODE=40600 SIZE=1897 MTIME=Apr 11 15:28 1990 COUNT 1 SHOULD BE 2 ADJUST? no SUMMARY INFORMATION BAD SALVAGE? no 5195 files, 44301 used, 41569 free (401 frags, 5146 blocks, 0.5% fragmentation) Checking subsystem status: 「しまった!backup をとっていない!!」なんとかしてこのファイルシステムを 修復せねばなりません。 まずは落ち着いて ls -l /usr/mmb/katsu/Mail/akemi してみて、驚きました。 : : : -rw------- 1 katsu 1989 Apr 11 13:42 6 drw------- 1 katsu 1897 Apr 11 15:28 7 ! ! -rw------- 1 katsu 1308 Apr 11 15:22 8 : : : このファイルは本来、(mh で管理している)mail のはずなのにディレクトリに なってしまっています。cat 7 とかすると、ちゃんと中身を読むことができて… ============ To: katsu@sran125.sra.co.jp Cc: atdall@sra.co.jp Subject: Re: our SWEET-Akemi Date: Wed, 11 Apr 90 14:31:19 +0900 From: akemi-t@sran233.sra.co.jp [ 略 ] そもそも、渡辺さんが桐谷さんに言い出したのがいけないんです。 もし、これから、サインとか握手とか求められたり、 ファンクラブができちゃったら、 後処理は渡辺さんの役目ですよ。 [ 略 ] -- Akemi Tani -- ============ あけみちゃんからの mail だから大切に保存しようとしたのが間違いだったか? それとも私が桐谷さんに言い出したバチがあたったのだろうか? あるいはあけみちゃんの魔術にかかったのであろうか? ともかく、ただのファイルが何らかの原因でディレクトリに化けて しまったのです。こう考えると、上の fsck の出力もすべて理解できます。 念のため、stat(2) を試してみたところ、確かにファイルの種別が ディレクトリになっています。 これでは fsck で修復するわけにはいきません。もし手動で fsck をかけて 直そうとすると、そもそもディレクトリでないものを無理にディレクトリと 解釈して、その中の本来意味のない inode 情報などとファイルシステムの 矛盾を取り除こうとするでしょうから、余計にファイルシステムが混乱しそうな 気がします。 実は、以前に同じ機械で全く同じ現象が起きたそうなのですが、そのときの 措置を聞いたら「format して newfs して restore」だったそうです。しかし 私には restore すべき dump がない......。そもそも format もほんの10日前に したばかりだというのに。 しかし、ものは試しで泥縄式の dump をしてみることにします。dump 自体は 成功し、restore -i で問題のディレクトリを取り出してみます。add で ディレクトリを取り出すことを指定したところ、 Warning: `.' missing from directory ./usr/mmb/katsu/Mail/akemi/7 Warning: `..' missing from directory ./usr/mmb/katsu/Mail/akemi/7 という警告が出ましたが、無視して extract するとできてしまいました。 drw------- 2 katsu 1897 Apr 11 15:28 7 ^ ^ ls -l も cd も rmdir もできる立派なディレクトリです。これなら後から restore しても問題はなさそうです。 # ついでに、壊れたファイルだけを避けるようにして tar しておきました。 こうなるとかなり気楽です。せっかくの機会ですから少し探検をしてみました。 cat 7 > 7.bak としてまずはバックアップをとり、ls -l 7 とすると、 ls: finename too long といわれます。おそるおそる cd 7 とやってみると、 /public: bad dir ino 31616 at offset 0: mangled entry /public: bad dir ino 31616 at offset 512: mangled entry /public: bad dir ino 31616 at offset 1024: mangled entry /public: bad dir ino 31616 at offset 1536: mangled entry となって、pwd すると、 pwd: getwd: can't stat . と怒られます。(以降、相対パスでの cd がきかず、絶対パスでしか 抜けられません。) さて、どうせおかしいのはこのディレクトリだけですから、これをなんとか 削除してはどうでしょうか? しかし、rmdir 7 とやっても、 rmdir: 7: Directory not empty と断られてしまいます。rm -r a とすると、 rm: path name too long: 7 と怒られます。 コマンド類はどうやらあてになりません。ではシステムコールを直接 使ってやってみてはどうでしょう? if (rmdir("7")) {perror("rmdir(2)"); exit(1)} なんていうのを試すと、 rmdir(2): Directory not empty と perror に教えられます。 if (unlink("7")) {perror("unlink(2)"); exit(1)} なんていうのを root になってやってみても、 unlink(2): Not owner といわれるばかりです。 でも unlink が Not owner というのはちょっと変ですね。 試しに root で mkdir /tmp/ddd とでもしてきちんとしたディレクトリをつくり、 root のままで unlink("/tmp/ddd") をしたら、やっぱり unlink(2): Not owner と怒られます。ディレクトリの unlink は元々できないものなのでしょうか? man 2 unlink には、root であれば unlink できるようなことが書いてある のですが、やり方がちがうのでしょうか? ここで周囲に泣きつきました。 perl v3.0 には -U というスイッチがあって、これをつけると、危険なので 普段は禁止されている動作を perl に行なわせることができるようになります。 現在のところ root がディレクトリを unlink する行為だけが「危険」とされて 普段はできないことになっています。逆に言うと、perl -U -e 'unlink("...");' としたときに perl が行なう動作を追えば、ディレクトリを unlink する方法が わかるはずです。これを、泣きつかれた先輩が調べてくれたのですが、実はただ unlink(2) を呼び出しているだけです。一同ええー?!と思って実際に perl -U -e 'unlink("/tmp/ddd");' とやってみると、まさに /tmp/ddd は削除されません。 lwall さんは私と同様、 root であればディレクトリの unlink(2) が できるものと思っていたのかもしれません。(もしくは lwall さんの環境では unlink できたのでしょう。) 削除できないとなれば、これはもはや raw device をいじるしか手は ありません。clri の利用も考えましたが、そもそも(親の)ディレクトリの エントリに clri の対象となるファイルが存在してはいけないようで、 今回のような場合は意味がありません。 要するに、inode の中でファイルのモードを保持している場所の、 ディレクトリを表す bit を 0 にし、普通のファイルを表す bit を 1 にして やればいいはずです。これは clri を参考にして、というより clri の大部分を利用して、clri では bzero(INODE) しているところを、 INODE->MODE &= ‾DIRECTORY; INODE->MODE |= REGULAR_FILE; のように変えただけです。 そのようなコマンド dir2norm を早速試してみました。 dir2norm /dev/sd00f 31616 ^^^^^^^^^^ # 書式は clri そのままで、ファイルシステムに続き、対象の i-number を # 指定する。あまり深く考えずに、mount したままの block device を指定した では、普通のファイルに変わったでしょうか?ls -ld 7 してみると、 drw------- 1 katsu 1897 Apr 11 15:28 7 ごーん。だめです。ブロックデバイスに対してやったのがいけないのでしょうか? いやいや、ls (の中で呼ばれる stat ?)がわざわざ raw device を見るとは 考えにくいので、raw device に実行したら余計に結果が反映されないはずです。 sync; sync なんてことをやってみましたが変わりません。念のため stat(2) で 見てみてもやはりディレクトリのままです。 しかし、慌てないで一旦 umount して mount しなおすと、 -rw------- 1 katsu 1897 Apr 11 15:28 7 ちゃんと直りました。多分 Kernel のどこかに inode 等の buffer か cache が あるせいなのでしょう。(Unix Magazine 1990年5月号 p122 から推測)本当は unount してから dir2norm を試すべきでした。 ともあれこれで、もとどおりきれいなファイルシステムに戻りました。 fsck もちゃんと通るようになり、めでたしめでたしであります。 上の一連の作業は1日で行なわれたわけではなく、fsck が文句を言い出して から今日までの20日弱の間に少しづつ調べながらやったものです。ちなみに、 問題が起きたパーティションを「dump して newfs して restore」すると、 1時間ぐらいで終ります。 +-----------------------------+ |教訓:姑息な技より日頃の dump| +-----------------------------+ この企画の趣旨: やりたいからやってるだけ。目指す領域は「日記」。 オチ: 日記というのは3日ぐらいしか続かないものです。 -- ----____----____ 渡邊克宏 環境開発部 今月の標語:「生意気も意気のうち」