# HOWTO Clone Disk Images on Linux Booted from a Network

Guilherme
Tupynamba'

    						gtupy (at) uol.com.br

2002年 9月 9日
0.3
2002年 9月 24日
gct
査読の助言を組み込む
0.2
2002年 9月 23日
jyg
マイナー改訂
0.1
2002年 9月 9日
gct
ドラフト第一版
日本語訳：芳賀靖史

    yasufumi.haga@nifty.com

本文書では、Grub ブートローダーを用いて、 BOOTP/TFTP で Linux をブートできるようにする方法と、 TFTP サーバーとの間で、ディスクやパーティションのイメージ のセーブ／リストアをできるようにする方法を解説します。

## 法的告知事項

### 免責条項

ここで論じている事柄については、説明している方法を用いるにあたり、 その技術上の概念やコマンドを熟知していることが前提になっています。 本文書に掲載されているコマンドを実行に不安を感じたり、 クローンスクリプトを理解していない場合は、この指示は実行しないで下さい。

加えて、`/dev/hda` と `/dev/hda1` の違いが分からない場合は、本 HOWTO は使用しないで下さい。 これは、おそらくパーティションのリストアと、 全データの喪失の違いになります。 ここで論じている内容の誤りが原因のエラーであっても、 うまく行かなかった事に対する責任は、筆者はいっさい 負わないことを覚えておいて下さい。

本文書の目的上、サーバーとクライアントの両方で、 RedHat 7.3 ディストリビューションを使用しました。 大した違いは出ないと思いますが、 他のディストリビューションやカスタムセットアップの 環境では、テストは行ないませんでした。

### 著作権

著作権 (c) 2002 は、Guilherme Tupynamba' が有しています。

本文書の複製、配布、ないし変更は、 Free Software Foundationが公開している、 フリー文書利用許諾契約書 （ GNU Free Documentation License ） のバージョン 1.1 ないしはそれ以降の条件下で許可する。 ただし、変更不可能部分が無いこと、表表紙が無いこと、 および裏表紙が無いこと。 クローンスクリプトは、 GNU 一般公衆利用許諾契約書 (GPL) の条件でライセンスされている。

### フィードバック

本文書に関するご意見には特に感謝します。 ご意見は直接、gtupy (at) uol.com.br 宛てにお願いします。

## はじめに

### ディスクイメージのクローンを作る理由

ディスクイメージのクローンを作る主な理由は、 膨大な数のマシンに対する、 オペレーティングシステムや基本的なアプリケーションセットのインストールを 楽に行なうためです。 標準になる一台のマシンを用意し、 その最初のきれいなイメージを保存しておき、 他のマシンにリストアするようにすれば、 労力を節約し、手順を簡略化することになります。

### ネットワークからブートする理由

ハードディスクからブートしてしまうと、 イメージをコピーする見込みが少なくなってしまいます。 例を挙げれば、 ブートしたオペレーティングシステムがマウントしているパーティションとの間で、 コピーを安全に行なうことは不可能でしょう。 また、オペレーティングシステムが Linux ではない場合もあります。 この場合は、オプションを自由に使った、クローン作成ができなくなります。

### ネットワークブートの概略

クライアント機は Grub で、フロッピーディスクからブートします。 それから Grub の BOOTP サポートを用いて、 DHCP サーバーから IP アドレスを取得します。 次に、クライアント機は TFTP サーバーからカーネルと initrd イメージを取得します。 initrd イメージをメモリにマウントしてしまえば、 初期化スクリプトが動き、 このイメージに格納してあるプログラムとファイルを利用します。 このスクリプトを使えば、 TFTP サーバーにブロックデバイスの内容を保存したり、 TFTP サーバーから内容をブロックデバイスに書き込むことができるようになります。

## DHCP サーバーと TFTP サーバーの設定

DHCP サーバーは Grub (BOOTP) をブートし、 その後で Linux をブートする際、 クライアントに IP アドレスを提供するのに必要なものです。 TFTP サーバーは、Linux のブート用に、 ネットワークで利用でできるブートイメージを作成するのに必要になります。 ディスクイメージの保存／リストアを可能にする際にも、 TFTP サーバーは必要になります。

### DHCP の設定

DHCP の詳細は本文書の範疇を越えます。 Linux Networking HOWTO に DHCP の章があります。

DHCP の設定は非常に簡単です。 でも、他の人が管理しているネットワークにいる場合は、 動作中の DHCP サーバーを使う方が賢明です。 自分でネットワークを管理しているのなら、 この手順にしたがってもかまいません。

まだ DHCP をインストールしていない場合は、 rpm パッケージからインストールして下さい。 普通は Linux のディストリビューションの中に見つかります。

    # rpm -ihv dhcp-*.rpm

`/etc/dhcpd.conf` ファイルを編集して、 DHCP サービスを設定して下さい。 本文書中の設定は、 サーバーの IP アドレスが 10.0.0.1 で、 253 台までのクライアントに IP アドレスを提供します。 自分の環境に応じて、 `/etc/dhcpd.conf` を設定して下さい。

    #/etc/dhcpd.conf
    server-identifier dhcp.clonedomain.com;
    default-lease-time	172800;
    max-lease-time		604800;
    option domain-name	"clonedomain.com";
    subnet 10.0.0.0 netmask 255.255.255.0 {
    	range dynamic-bootp 10.0.0.2 10.0.0.254;
    }

dhcpd サーバーのスタート

`/etc/rc.d/init.d/dhcpd start`.

### TFTP の設定

TFTP の設定も、DHCP とほとんど同じくらい簡単です。

まず、rpm パッケージからインストールします。

    # rpm -ihv tftp-server-*.rpm

そして、ファイルを格納するディレクトリを作ります。

    # mkdir /tftpboot
    # chown nobody:nobody /tftpboot

`nobody` というのは、 ftfpd がファイルをアクセスする際に設定する、 ディフォルトのユーザー ID になるので、 `/tftpboot` というディレクトリの所有者は、 `nobody` にします。

`/etc/xinetd.d/tftp` ファイルを次のように編集します。

    service tftp
    {
    	socket_type	= dgram
    	protocol	= udp
    	wait		= yes
    	user		= root
    	server		= /usr/sbin/in.tftpd
    	server_args	= -c -s /tftpboot
    	disable		= no
    	per_source	= 11
    	cps		= 100 2
    }

ディフォルトのファイルから変更するのは、 `disable = no`（サービスを有効にする）パラメータと、 サーバーに与える引数 `-c` です。 後者の引数を与えると、ファイルの生成が可能になります。 これはブートイメージやディスクイメージを保存したい場合に必要になります。 通常の操作では、TFTP を読み取りのみにしておく必要があるかも知れません。

xinetd を再ロードします。

`/etc/rc.d/init.d/xinetd reload`

サーバーのテストには、`tftp` コマンドが使えます。 このコマンドは、 tftp rpm パッケージ（クライアント用）をインストールすれば利用できます。

### 別のサーバーを使用する

DHCP と TFTP に関しては、別のサーバーを使用することもできます。 これは動作中の DHCP サーバーを使用する場合に必要になるかもしれません。 DHCP の `next-server` パラメータを設定して、 TFTP サーバーを指すようにしたり、 Grub で `tftpserver` コマンドを使うことができます。

## ブートファイルの準備

これでサーバーの設定はできました。 次はクライアントをブートさせるファイルを準備する必要があります。 これには二つのファイルが必要になります。 それは、カーネルと初期 RAM ディスク(initrd) です。 後者は、カーネルがルートファイルシステムとしてマウントするものです。 本文書では、本節と次節で概説した手順は、 クライアント機で作成したものとしています。 ディスクイメージの保存／リストアの際は、 普通ローカルのハードディスクに Linux をインストールする必要はありません。 でも、数多くのマシンにディスクイメージを配置する場合は、 各マシンのモデルとなる、 一台のマシンに Linux をインストールすることから始めて下さい。 DHCP を使い、前節で設定したことを TFTP クライアントでテストして下さい。 特に指定しない限り、コマンドは 現在のディレクトリ内で、 ユーザー `root` が `bash` シェルで実行します。

### カーネル

圧縮されたカーネルのファイルを特定します。

    # cd /boot
    # ls vmlinuz-$(uname -r)
    vmlinuz-2.4.18-3

このバージョンは、システムによって様々です。 このファイルを TFTP サーバーにアップロードし、 名前を `vmlinuz` に変えます。

    # tftp 10.0.0.1
    tftp> binary
    tftp> put vmlinuz-2.4.18-3 vmlinuz
    Sent 1030147 bytes in 2.3 seconds
    tftp> quit

カーネルのファイル名は、ハードウェアが異なったり、 カーネルのバージョンによっては、 そのままにしておく必要があるかも知れないし、 別の名前にする必要があるかも知れません。 ここでは、カーネルと initrd に関して、 同じ名前を用います。 こうすれば、カーネルや initrd イメージを変更しても、 別のブートフロッピーディスクを作成する必要がなくなります。

### initrd 上のファイル

次に、クライアント用にルートファイルシステムのイメージを作成します。 このファイルの完全なリストは、 にあります。

このファイルは、強力なシェル(`bash`)を用意するための、 最低限の設定として、動作中のシステムから持ってきました。 クライアント用のネットワークユーティリティ (`dhcpcd` や `tftp`)、 やコピー・圧縮ユーティリティ (`dd`, `gzip`)、 管理用コマンド (`mknod` や `mount`, `fdisk` それに `insmod`) もあります。

現在のディレクトリで、 `initrd.lst` という名前のディレクトリを作成し、 これらのファイルをそこに置いて下さい。 自分のシステムでこれらのファイルが存在するかどうかを確かめるには、 次のコマンドを実行して下さい。

    # ls -d $(<initrd.lst) > /dev/null

以下のようなエラーが出力されるはずです。

    ls: /bin/clone: No such file or directory
    ls: /bin/tftp: No such file or directory
    ls: /lib/3c59x.o: No such file or directory

最初のエラーは、 現在のディレクトリに作ることになっているスクリプトです。 二番目のエラーは、 `/bin` ではなく、 `/usr/bin` ディレクトリで見つかった `tftp` プログラムです。 三番目はネットワークインタフェースカードのモジュール （たぶん自分のではないでしょう）で、 `/lib/modules/$(uname -r)/kernel/drivers/net` で見つかったものです。

この三つのファイルについては、以降の節で個別にすぐ論じます。 他にも無いファイルがあれば、 インストールのし忘れとか、バージョンの違い、 ディストリビューションやハードウェアの違いを調べて下さい。 そして、このリストは自分のシステムの合うように調整して下さい。

### initrd の作成

次はイメージを作成する段階です。 本文書での設定だと、 4 メガバイトの大きさがあれば、充分ファイルが保持できます。 必要ならサイズを大きくしてもかまいません。

    # dd if=/dev/zero of=initrd bs=1024 count=4096
    4096+0 records in
    4096+0 records out
    # yes | mkfs initrd
    mke2fs 1.27 (8-Mar-2002)
    initrd is not a block special device.
    Proceed anyway? (y,n) Filesystem label=
    blah blah blah...
    # mkdir mnt
    # mount -o loop initrd mnt/
    # egrep -v "clone|3c59x|tftp" initrd.lst | cpio -pdm mnt
    4876 blocks

さて、`egrep` コマンドで、 ファイルが三つ除外されました。 そこで、`tftp` プログラムを、 `/usr/bin` から イメージディレクトリにコピーします。

    # cp -p /usr/bin/tftp mnt/bin/

ネットワークインタフェースカード用には、 正しいモジュールを特定して下さい。 そのファイルを特定するには、 `lspci` コマンドと、 `lsmod` コマンドの出力を用います。 そのモジュールは、 `/lib/modules/$(uname -r)/kernel/drivers/net` にあります。

3c59x という名前を使っているところがあれば、 すべて自分のケースに合ったモジュール名に読み変えて下さい。

    # cp -p /lib/modules/$(uname -r)/kernel/drivers/net/3c59x.o mnt/lib/

にあるクローンスクリプトを編集して、 で説明した変数を変更します。 そのファイルを実行可能なパーミッションにして、 イメージディレクトリにコピーして下さい。

    # chmod +x clone
    # cp -p clone mnt/bin/

initrd イメージをアンマウントし、圧縮してから、 TFTP サーバーに送ります。

    # umount mnt/
    # gzip initrd
    # tftp 10.0.0.1
    tftp> binary
    tftp> put initrd.gz
    Sent 1155530 bytes in 2.8 seconds
    tftp> quit

## Grub を用いたフロッピーディスクからのブート

次の段階は、Grub を用いたブートフロッピーディスクの作成です。 Grub とは、GRand Unified Bootloader のことです。 これは BOOTP と TFTP が扱えるので、ネットワークからブートできます。

### Grub のメニューファイル

現在のディレクトリで、 `grub.conf` という名前で、以下のような内容のファイルを作ります。

    default=0
    timeout=1
    title Clone
    	bootp
    	root (nd)
    	kernel /vmlinuz rw root=/dev/ram ramdisk_size=4096 init=/bin/clone
    	initrd /initrd.gz

最後の 4 行は、ネットワークからブートするのに使う、 Grub のコマンドです。

- `bootp` は、 DHCP サーバーから IP アドレスを取得するのに使います。

- `root (nd)` は、 ネットワーク（TFTP サーバー）のルートを設定するのに使います。 `tftpserver` `<tftp server>` コマンドを使えば、このコマンドの前に、 別の TFTP サーバーを設定できます。

- `kernel` は、 カーネルファイルとそのパラメータを指定します。

  - `rw` は、 ルートファイルシステムを書き込み可能にしてマウントするよう指定します。

  - `root` は、 （RAM メモリ内の）ルートファイルシステムをどこにマウントするのかを指定します。

  - `ramdisk_size` は、 RAM ディスクのサイズを指定します。 4096 （kバイト）がディフォルトの大きさですが、 もっと大きなイメージが必要なら、 それに応じてこのパラメータを変更して下さい。

  - `init` は、 （`init` と `sh` が無い場合に）、 ユーザーモードで動く最初のプログラム （この場合は clone スクリプト） を指定します。

- `initrd` は、 ルートファイルシステムのイメージを保持しているファイルを指定します。

### ネットワークサポートをつけた Grub のコンパイル

Grub をコンパイルする場合は、 最初にソースの tar ボールを the Grub web site からダウンロードし、展開します。 そして、作成したばかりのメニューファイルと、 ネットワークインタフェースカードの型を引数に指定して、 `configure` を実行します。 それからいつものとおり、`make` を実行します。

    # tar xzf grub-0.92.tar.gz
    # cd grub-0.92
    # ./configure --enable-preset-menu=../grub.conf --enable-3c90x
    # make

繰り返しますが、 3c90x という型を見たら、 自分が使っているネットワークインタフェースカードの型に読み変えてください。 その際、最初に、Grub がその型をサポートしているかどうか調べて下さい。

### ブートフロッピーディスクの作成

一度 Grub をコンパイルしたら、 ブートフロッピーディスクのイメージは、 `stage1/stage1` ファイルと `stage2/stage2` ファイルを連結すればできます。 このフロッピーディスクを作成するには、 次のコマンドを実行して下さい。

    # cat stage1/stage1 stage2/stage2 | dd of=/dev/fd0

これで、ブートフロッピーディスクができたはずです。

## clone スクリプトの実行

で示している、 clone スクリプトは必須ではありません。 `init=/bin/bash` をカーネルパラメータにして、 利用可能なコマンドやプログラムが実行できるシェルで終ってもかまいません。

このスクリプトをここに表したのは、 正式なやり方のコマンドを示し、 タイプミスから被害が生じる可能性を減らすためです。 `tftp_server` や `nic_module`、 `major_a`、 `family_a` それに `image_a` といった変数は、 自分の環境やアプリケーションの合わせて変更する必要があります。

注意して欲しいのは、配列 `major_a` と配列 `family_a` は対応しているということです。 所定のファミリー名に誤ったメジャー番号を与えると、 ユーザーに誤解を招くことになります。 重要なデバイス（ディスクとパーティションの全体） のメジャー番号とマイナー番号は、 `/dev` ディレクトリのリストでわかります。 メジャー番号とマイナー番号は、 `ls -l` コマンドの出力にある、カンマで区切られた、 通常のファイルだとファイルサイズがある場所にあります。

    # ls -l /dev/fd0 /dev/hda /dev/hda1 /dev/hdc
    brw-rw----    1 root     disk       2,   0 Apr 11 11:25 /dev/fd0
    brw-rw----    1 root     disk       3,   0 Apr 11 11:25 /dev/hda
    brw-rw----    1 root     disk       3,   1 Apr 11 11:25 /dev/hda1
    brw-rw----    1 root     disk      22,   0 Apr 11 11:25 /dev/hdc

`set -e` コマンドは、 シェルにスクリプトをアボートするよう指示するもので、 ゼロ以外のコードが返るはずです。 通常の終り方のように、 Kernel panic: Attempted to kill init! というメッセージがそれに続きます。 でも、うろたえないで！この環境では、これが普通なのです。 コンピュータを落とすだけでかまいません。 スクリプトを終らせてこの見苦しいメッセージを避ける前に、 CtrlAltDel を押して、きちんとリブートして下さい。

`insmod` コマンドは、 ネットワークインタフェースモジュールをロードするコマンドで、 `dhcpcd` コマンドは、 DHCP クライアントをスタートさせるコマンドです。 Grub がそのブート中に DHCP を使用したということと、 Linux が同じことをしたということが、 無関係なことに注意して下さい。

このスクリプトでは、各々繰り返しを行なうために、 大きなループ処理を行なっています。 まず、次の三種類の操作の中のどれを行なうかを訊いてきます。 `ネットワークからデバイスへのコピー`、 `デバイスからネットワークへのコピー`、 `fdisk の実行`。 次に、使用するブロックデバイスがどれかを訊いてきます。 `major_a` 配列は、 使用できるブロックデバイスのメジャー番号を持っています。 また、`family_a` には、 デバイスファミリー個々の名称が入っています。 その後、使用するブロックデバイスのマイナー番号を訊いてきます。

### ディスクイメージの保存とリストア

コピー操作の内の一つを選ぶと、 スクリプトは保存ないしはリストアするイメージの名前を訊いてきます。 イメージ名称は、`image_a` 配列の要素に限定されています。 その名前が存在しなければ、イメージと同じ名前の、 名前付きパイプを作ります。 最後に、`dd` と `tftp` を同時に起動し、 イメージを転送します。 いつもの `ftp` とは違い、 `tftp` は名前付きパイプを、 ちょうど普通のファイルのように put したり get したりします。

### fdisk の使用

`fdisk` を選ぶと、ブロックデバイスに対して起動されます。 `fdisk` は普通、一つのパーティションではなく、 逆にディスク全体に対して実行されます。 注意して欲しいのは、 通常 `/dev/hda` と呼んでいるものが、 clone スクリプトでは `/dev/hda0` となることです。 お望みなら、スクリプト中に `fdisk` を置いて、 パーティション生成を自動化することも可能です。

## ソリューションの拡張

### ファイルシステムではなく、ファイルの保存とリストア

保存したいのがディスクイメージ全体ではなく、 ファイルシステムの中にあるファイルだけなら、 `dd` の代わりに、 `tar` や `cpio` で、似たようなソリューションが使えます。 ファイルシステムをマウントする必要はあります。 以下に示すように、 clone スクリプトにもっとコマンドを追加して下さい。

    # mkdir /mnt
    # mount ${device_name} /mnt
    # mknod ${image} p
    # tftp ${tftp_server} <<-EOT &
    binary
    put ${image}
    EOT
    tar czf ${image}

or

    # tftp ${tftp_server} <<-EOT &
    binary
    get ${image}
    EOT
    tar xzf ${image}

initrd イメージには、スクリプトが使えるように、 `mkdir` プログラムと `tar` プログラムを置いておく必要があります。

### マスターブートレコードの設定

本文書の内容を用いてパーティションの再編やサイズ変更を行なった状況では、 ディスクがブートせずに終るかもしれません。 その場合は、（イメージ中の `grub` に含まれている） Grub の `setup` コマンドを実行して下さい。 これで問題が解決するはずです。詳細は、 Grub のドキュメントを参照して下さい。

### 必要なモジュールのロード

カーネルによっては、 SCSI デバイスのようなブロックデバイスにアクセスするために、 余分なモジュールが必要になるかもしれません。 initrd イメージの `/lib` ディレクトリに必要なモジュールを置いて、 clone スクリプトで、 対応する `insmod` コマンドを実行して下さい。 同じことはファイルシステムにも言えます。 例えば、fat ファイルシステムのイメージではなく、 ファイルを保存したければ、 `fat.o` と `vfat.o` モジュールが必要になります。

### `grub.conf`で予め定義されている操作

Grub のメニューファイル `grub.conf` はカスタマイズして、 いくつかのコピーオプションを提示するようにしても良いし、 ディスクのパーティション再作成や、 ネットワークから指定したイメージを取得するような、 予め定義しておいた操作を実行するようにしてもかまいません。 繰り返しますが、 ここで示された考え方を用いて特定の作業に応用すれば、 その作業が達成できるようになるのです。

## 日本語版謝辞

校正にあたり、JF プロジェクトの皆様にご協力いただきました。 この場を借りて御礼申し上げます。

## initrd 上のファイル一覧

    /bin/
    /bin/bash
    /bin/clone
    /bin/dd
    /bin/gzip
    /bin/mknod
    /bin/mount
    /bin/tftp
    /dev/
    /dev/console
    /dev/null
    /etc/
    /etc/dhcpc/
    /etc/hosts
    /etc/nsswitch.conf
    /etc/protocols
    /etc/services
    /lib/
    /lib/3c59x.o
    /lib/i686/
    /lib/i686/libc-2.2.5.so
    /lib/i686/libc.so.6
    /lib/ld-2.2.5.so
    /lib/ld-linux.so.2
    /lib/libdl-2.2.5.so
    /lib/libdl.so.2
    /lib/libnss_files-2.2.5.so
    /lib/libnss_files.so.2
    /lib/libtermcap.so.2
    /lib/libtermcap.so.2.0.8
    /proc/
    /sbin/
    /sbin/dhcpcd
    /sbin/fdisk
    /sbin/insmod
    /tmp/
    /var/
    /var/run/

## Clone スクリプト

    #!/bin/bash

    set -e

    export PATH=/sbin:/bin

    tftp_server=10.0.0.1
    nic_module=3c59x.o
    major_a=(2 3 22)
    family_a=(fd hda hdc)
    image_a=(img0001 img0002 img0003 img0004)

    operation_a=(	"Copy from network to device" \
    		"Copy from device to network" \
    		"Run fdisk")

    mount -t proc proc /proc
    insmod /lib/${nic_module}
    /sbin/dhcpcd

    while true; do \
    	[ ! -z "${image}" ] && unset image
    	echo
    	echo "Clone Menu"
    	echo
    	echo "Operation"
    	echo
    	PS3="Choose operation (1-${#operation_a[*]}): "
    	select operation in "${operation_a[@]}"; do \
    		[ -z "${operation}" ] && continue
    		echo
    		echo $REPLY - $operation
    		echo
    		break
    	done

    	echo "Device Family"
    	echo
    	PS3="Choose device family (1-${#family_a[*]}): "
    	select family in "${family_a[@]}"; do \
    		[ -z "${family}" ] && continue
    		echo
    		echo $REPLY - $family
    		echo
    		break
    	done

    	major_i=$[REPLY-1]
    	major=${major_a[$major_i]}

    	echo "Minor Number"
    	echo
    	PS3="Choose minor number (0-255): "
    	echo -n "$PS3" >&2
    	read minor
    	minor=$[minor%256]
    	echo
    	echo $minor
    	echo

    	if [ "${operation}" != "${operation_a[2]}" ]; then \
    		echo "Image"
    		echo
    		PS3="Choose image (1-${#image_a[*]}): "
    		select image in "${image_a[@]}"; do \
    			[ -z "${image}" ] && continue
    			echo
    			echo $REPLY - $image
    			echo
    			break
    		done
    		image_i=$[REPLY-1]
    		image=${image_a[${image_i}]}
    	fi

    	echo
    	echo -e "Operation:\t$operation"
    	device_name=/dev/${family_a[${major_i}]}${minor}
    	echo -e "Device:\t\t${device_name} ($major, $minor)"
    	[ ! -z "${image}" ] && echo -e "Image:\t\t${image}"
    	echo

    	echo "Confirmation"
    	echo
    	PS3="Ok/Cancel (1-2): "
    	select ok in Ok Cancel; do \
    		[ -z "${ok}" ] && continue
    		echo
    		echo $REPLY - $ok
    		echo
    		break
    	done
    	if [ "${ok}" = "Ok" ]; then \
    		if [ ! -b ${device_name} ]; then \
    			echo "Creating ${device_name}"
    			mknod ${device_name} b ${major} ${minor}
    		fi
    		if [ ! -z "${image}" ]; then \
    			if [ ! -p ${image} ]; then \
    				echo "Creating pipe"
    				mknod ${image} p
    			fi
    		fi
    		if [ "${operation}" = "${operation_a[0]}" ]; then \
    			tftp ${tftp_server} <<-EOT &
    			binary
    			get ${image}
    			EOT
    			gzip -c -d < ${image} | dd of=${device_name}
    		elif [ "${operation}" = "${operation_a[1]}" ]; then \
    			tftp ${tftp_server} <<-EOT &
    			binary
    			put ${image}
    			EOT
    			dd if=${device_name} | gzip -c > ${image}
    		elif [ "${operation}" = "${operation_a[2]}" ]; then \
    			fdisk ${device_name}
    		fi
    		echo
    	fi

    	echo "Continuation"
    	echo
    	PS3="Continue/Exit (1-2): "
    	select new in Continue Exit; do \
    		[ -z "${new}" ] && continue
    		echo
    		echo $REPLY - $new
    		echo
    		break
    	done
    	[ "${new}" = "Exit" ] && break
    done
    exit 0

## References

### GRUB

    http://www.gnu.org/software/grub/