2021/05/06

/dev を復旧する

とある事情で Linux マシンの /devを殆ど全削除してしまったので復旧してみた。

chroot 環境で作業するスクリプトが chroot 環境を抜けるときに後始末として環境内の /dev等を削除しているのだが、 スクリプトの不具合で chroot 環境から抜けた後(むしろ不具合で chroot できなかった場合) にも /dev の削除処理が 動作してしまったので実環境の /dev が根こそぎ削除されてしまうという...何ともアレな BUG を踏み抜いた次第。

今回はたまたま同じ構成の Linux マシンがもう一台手元にあったので、 そちらの /dev 環境から情報を取得して mknod を実行するためのコマンドライン引数を取得する処理を作成した。

デバイス名やメジャー番号、マイナー番号は /dev 以下の全エントリの詳細情報を find (1) を利用して ls (1) を実行する事で取得し、 その出力を awk (1) を利用して加工し mknod (1) のコマンドライン形式として出力している。
作成するデバイスノードのオーナーとグループは ls (1) の出力から chown (1) コマンドを実行して設定している。 アクセス権限は ls (1)の出力は直接 chmod (1) コマンドで利用できないので、 stat (1) コマンドの出力書式に --format '%04a' を指定する事で 4桁の8進数として取得した値を利用して chmod (1) コマンドで設定している。

stat (1)--format オプションは GNU による独自拡張なので FreeBSD や macOS の純正 stat (1) では動作しないと思う。

/dev には他にもディレクトリ、シンボリックリンクが存在しているので ls (1) の出力からエントリーのタイプを判断し、 ディレクトリの場合は mkdir (1)、シンボリックリンクの場合は ln (1) を適宜実行している。

  1#!/bin/sh
  2
  3find /dev -exec ls -l {} \; |
  4    awk '
  5        # ディレクトリ作成
  6        function mkdir(name) 
  7        {
  8            "dirname " name | getline dirname
  9            if(dirname != "/dev")
 10                printf("mkdir -p %s\n", dirname)
 11            return dirname
 12        }
 13
 14        {
 15            if(NF > 3){
 16                # エントリーの種類取得
 17                type = substr($1, 1, 1)
 18                if(type == "c" || type == "b"){
 19                # デバイスノードの場合
 20                    # 親ディレクトリ作成
 21                    mkdir($NF)
 22                    # 権限取得
 23                    "stat --format '%04a' " $NF | getline mode
 24                    # コマンド出力
 25                    printf "if [ ! -%s %s ]; then  rm -f %s; mknod %s %s %d %d; fi; chown %s:%s %s; chmod %d %s\n",
 26                        type, $NF, $NF, $NF, type, $5, $6, $3, $4, $NF, mode, $NF 
 27                } else if(type == "d"){
 28                # ディレクトリの場合
 29                    # 権限取得
 30                    "stat --format '%04a' " $NF | getline mode
 31                    printf "if [ ! -%s %s ]; then rm -f %s; mkdir -p %s; fi; chown %s:%s %s; chmod %d %s\n",
 32                        type, $NF, $NF, $NF, $3, $4, $NF, mode, $NF 
 33                } else if(type == "l"){
 34                # シンボリックリンクの場合
 35                    # 親ディレクトリ作成
 36                    cwd = mkdir($(NF - 2))
 37                    printf "if [ ! -L %s ]; then rm -f %s; (cd %s; ln -s %s %s); fi\n",
 38                        $(NF-2), $(NF-2), cwd, $NF, $(NF-2)
 39                } else {
 40                    print type " " $0 > "/dev/stderr"
 41                }
 42            }
 43        }
 44    '
    

情報を取得するマシンでこのスクリプトを実行すると /dev を復元するためのコマンドが生成されるので、 /dev 環境がなくなってしまったマシンに転送して実行する事で /dev が復元できる。

必要に応じてその場で適当に作ったスクリプトなので完全無保証。
自分の場合は復旧できて今でもちゃんと動作している。


Copyright © 2008-2020 Mitzyuki IMAIZUMI. All rights reserved.