/* $NetBSD: installboot.c,v 1.3 2001/11/25 00:42:11 minoura Exp $ */ /* * Copyright (c) 2001 Minoura Makoto * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <paths.h> #include <err.h> #include <sys/param.h> #include <sys/disklabel.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <ufs/ufs/dinode.h> #include <ufs/ffs/fs.h> #include "dkcksum.h" #define MAXBBSIZE BBSIZE #define LABELBYTEOFFSET (LABELSECTOR*512+LABELOFFSET) int nflag = 0, vflag = 0, fflag = 0, merging = 0; int floppy = 0; char rawname[PATH_MAX]; off_t bboffset = 0; size_t bootprogsize; size_t blocksize; const char *progname; const char *bootprog; char *target; char template[] = _PATH_TMP "/installbootXXXXXX"; u_int8_t bootblock[MAXBBSIZE]; struct disklabel label; void usage(void) __attribute((__noreturn__)); int checkbootprog(const char *); int checktargetdev(const char *); int checkparttype(const char *, int); void usage(void) { fprintf(stderr, "usage: %s [-nvf] /usr/mdec/xxboot_ufs /dev/rxx?a\n", progname); exit(1); /* NOTREACHED */ } /* * name で示されるブートブロックイメージファイルが読み込み可能で * あることを確認し、そのサイズを大域変数 bootprogsize に入れる。 */ int checkbootprog(const char *name) { struct stat st; if (access(name, R_OK) < 0) err(1, "%s", name); if (stat(name, &st) < 0) err(1, "%s", name); bootprogsize = st.st_size; return 0; } /* * ターゲットデバイスのチェック。 * name は "/dev/rsd0a" とかいう文字列のはず。 */ int checktargetdev(const char *name) { struct stat st; /* 書き込み可能であることを確認 */ if (access(name, W_OK) < 0) err(1, "%s", name); /* キャラクタデバイスであることを確認 */ if (stat(name, &st) < 0) err(1, "%s", name); if ((st.st_mode & S_IFCHR) == 0) errx(1, "%s: not a character special device", name); /* パーティションが正しい範囲内にあることを確認 */ if (DISKPART(st.st_rdev) > MAXPARTITIONS) errx(1, "%s: invalid device", name); /* * ターゲットが FD であれば floppy = 1 にする。 * HD の場合はこの後でオープンするデバイス名のパーティションの * 部分を 'c' に書き換える。RAW_PART の値は 2 で、これは * sys/arch/x68k/include/disklabel.h で定義されている。 */ strcpy(rawname, name); if (strncmp(name + strlen(name) - 4, "fd", 2) == 0) floppy = 1; else rawname[strlen(name) - 1] = RAW_PART+'a'; /* * それはそれとして、HD の場合でパーティションが c なら * (この1つ前で 'c' にしたのはファイル名なので st はまだ * 引数として渡されたデバイス名に対するもの) エラーにする??? */ if (!floppy && DISKPART(st.st_rdev) == RAW_PART) errx(1, "%s is the raw device", name); return 0; } /* * パーティションのチェック。 * int checkparttype(const char *name, int force) { struct stat st; int fd, part; /* オープンしてローデバイスであることを確認 */ fd = open(rawname, O_RDONLY | O_EXLOCK); if (fd < 0) err(1, "opening %s", name); if (stat(name, &st) < 0) err(1, "%s", name); if ((st.st_mode & S_IFCHR) == 0) errx(1, "%s: not a character special device", name); /* * part にパーティション番号を入れる。disklabel を取得してみて * その中の最大パーティション番号よりも大きければエラーにする。 */ part = DISKPART(st.st_rdev); if (ioctl(fd, DIOCGDINFO, &label) < 0) err(1, "%s: reading disklabel", name); if (part >= label.d_npartitions) errx(1, "%s: invalid partition", name); /* * ブロックサイズのチェック。 */ blocksize = label.d_secsize; if (blocksize < 512) blocksize = 512; if (blocksize > MAXBBSIZE) errx(1, "%s: blocksize too large", name); /* * 先頭の1ブロックを大域変数 bootblock に読んでみて、一旦閉じる。 */ if (read(fd, bootblock, blocksize) != blocksize) errx(1, "%s: reading the mark", name); close(fd); /* * 先頭の8バイトが "X68SCSI1" でなければ FD だと思うことにする。 * XXX: フォーマットされてないものと区別がつかないってこと? */ if (strncmp(bootblock, "X68SCSI1", 8) != 0) floppy = 1; /* XXX: or unformated */ /* -f が指定されてなく HD の場合 */ if (!force && !floppy) { /* * ターゲットパーティションの fstype が FFS か LFS の * どちらでもなければエラーにする。 */ if (label.d_partitions[part].p_fstype != FS_BSDFFS && label.d_partitions[part].p_fstype != FS_BSDLFS) errx(1, "%s: invalid partition type", name); /* * パーティションの開始位置(?)が 0 ではなくて 32768 バイト以下 * だったら小さすぎなのでエラーにする?? */ if ((label.d_partitions[part].p_offset * blocksize < 32768) && label.d_partitions[part].p_offset != 0) errx(1, "%s: cannot make the partition bootable", name); } /* * HD の先頭のパーティションか、FD なら現在の disklabel 情報を * マージする。 */ if (floppy) merging = 1; else if (label.d_partitions[part].p_offset == 0) { merging = 1; bboffset = 1024; /* adjusted below */ } if (merging) { struct disklabel *lp; /* * disklabel は、さっき1ブロック読みこんだブートブロックに * 収まっているのでそれを大域変数 label にコピーしておく。 * で、チェックサムとって一致しなければクリアする。 */ lp = (struct disklabel *) &bootblock[LABELBYTEOFFSET]; memcpy(&label, lp, sizeof(struct disklabel)); if (dkcksum(lp) != 0) /* there is no valid label */ memset(&label, 0, sizeof(struct disklabel)); } return 0; } int main(int argc, char *argv[]) { int c; int fd; progname = argv[0]; while ((c = getopt(argc, argv, "nvf")) != -1) { switch (c) { case 'n': nflag = 1; break; case 'v': vflag = 1; break; case 'f': fflag = 1; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 2) usage(); /* * (オプションの後の) 1つ目の引数がブートローダファイル。 * 2つ目がターゲットデバイス名。 */ bootprog = argv[0]; target = argv[1]; /* ブートローダをチェックする */ if (checkbootprog(bootprog) < 0) errx(1, "aborting"); /* 書き込み先デバイスをチェックする */ if (checktargetdev(target) < 0) errx(1, "aborting"); /* 書き込み先デバイスのパーティションをチェックする */ if (checkparttype(target, fflag)) errx(1, "aborting"); /* * HD で disklabel をマージする場合で、ブロックサイズが bboffset * (この直前の checkparttype() でとりあえず代入したっぽい 1024) * より大きければ、bboffset をブロックサイズに合わせる。何これ? * ブロックサイズは通常 512 なのでここは偽になり bboffset は * 1024 のまま?? */ if (merging && blocksize > bboffset && !floppy) bboffset = blocksize; /* * ブートローダのサイズがブートブロック用領域より大きければエラー。 * MAXBBSIZE はブートストラップ用領域だと思う。8KB。 * そっから bboffset を引くのは何? */ if (bootprogsize > MAXBBSIZE - bboffset) errx(1, "%s: boot block too big", bootprog); /* Read the boot program */ /* * なんだかよく分からんけど、ブートローダを読み込む。 * 読み込む先は bootblock + bboffset。bootblock は大域変数で * checkparrtype() が読み込んだブートブロックの先頭番地。 * それに bboffset だけ足したところにおく。 * bboffset って何? */ fd = open(bootprog, O_RDONLY); if (fd < 0) err(1, "opening %s", bootprog); if (read(fd, bootblock + bboffset, bootprogsize) != bootprogsize) err(1, "reading %s", bootprog); close(fd); /* * マージが指定されてれば、ディスクラベルを上書きする。 */ if (merging) memcpy(bootblock + LABELBYTEOFFSET, &label, sizeof(label)); /* Write the boot block (+ disklabel if necessary) */ if (nflag) { target = template; fd = mkstemp(target); if (fd < 0) err(1, "opening the output file"); } else { int writable = 1; fd = open(target, O_WRONLY); if (fd < 0) err(1, "opening the disk"); if (merging && ioctl(fd, DIOCWLABEL, &writable) < 0) err(1, "opening the disk"); } bootprogsize = howmany(bootprogsize+bboffset, blocksize) * blocksize; if (write(fd, bootblock, bootprogsize) != bootprogsize) { warn("writing the label"); if (!nflag && merging) { int writable = 0; ioctl(fd, DIOCWLABEL, &writable); } exit(1); } if (!nflag && merging) { int writable = 0; ioctl(fd, DIOCWLABEL, &writable); } close(fd); if (nflag) fprintf(stderr, "The bootblock is kept in %s\n", target); else fprintf(stderr, "Do not forget to copy /usr/mdec/boot" " to the root directory of %s.\n", target); return 0; } |