9000   /*      installboot 2.0 - Make a device bootable        Author: Kees J. Bot
9001    *                                                              21 Dec 1991
9002    *
9003    * Either make a device bootable or make an image from kernel, mm, fs, etc.
9004    */
9005   #define nil 0
9006   #define _POSIX_SOURCE   1
9007   #define _MINIX          1
9008   #include <stdio.h>
9009   #include <stddef.h>
9010   #include <sys/types.h>
9011   #include <sys/stat.h>
9012   #include <sys/ioctl.h>
9013   #include <stdlib.h>
9014   #include <unistd.h>
9015   #include <fcntl.h>
9016   #include <string.h>
9017   #include <errno.h>
9018   #include <a.out.h>
9019   #include <minix/config.h>
9020   #include <minix/const.h>
9021   #include <minix/partition.h>
9022   #include "rawfs.h"
9023   #include "image.h"
9024
9025   #define BOOTBLOCK       0       /* Of course */
9026   #define SECTOR_SIZE     512     /* Disk sector size. */
9027   #define RATIO           (BLOCK_SIZE/SECTOR_SIZE)
9028   #define SIGNATURE       0xAA55  /* Boot block signature. */
9029   #define BOOT_MAX        64      /* Absolute maximum size of secondary boot */
9030   #define JMP             0xEB    /* Opcode of a short jump */
9031   #define JMPOFFM         0x01    /* Jump offset in normal master bootstrap */
9032   #define JMPOFFE         0x02    /* Jump offset in extended bootstrap */
9033   #define XOR             0x31    /* Jumping to an XOR instruction? */
9034   #define SIGPOS          510     /* Where to put signature word. */
9035   #define PARTPOS         446     /* Offset to the partition table in a master
9036                                    * boot block.
9037                                    */
9038
9039   #define between(a, c, z)        ((unsigned) ((c) - (a)) <= ((z) - (a)))
9040   #define control(c)              between('\0', (c), '\37')
9041
9042   #if !__minix_vmd
9043   #define cv64ul(i)       (i)
9044   #endif
9045
9046   void report(char *label)
9047   /* installboot: label: No such file or directory */
9048   {
9049           fprintf(stderr, "installboot: %s: %s\n", label, strerror(errno));
9050   }
9051
9052   void fatal(char *label)
9053   {
9054           report(label);
9055           exit(1);
9056   }
9057
9058   char *basename(char *name)
9059   /* Return the last component of name, stripping trailing slashes from name.
9060    * Precondition: name != "/".  If name is prefixed by a label, then the
9061    * label is copied to the basename too.
9062    */
9063   {
9064           static char base[IM_NAME_MAX];
9065           char *p, *bp= base;
9066
9067           if ((p= strchr(name, ':')) != nil) {
9068                   while (name <= p && bp < base + IM_NAME_MAX - 1)
9069                           *bp++ = *name++;
9070          }
9071           for (;;) {
9072                   if ((p= strrchr(name, '/')) == nil) { p= name; break; }
9073                   if (*++p != 0) break;
9074                   *--p= 0;
9075          }
9076           while (*p != 0 && bp < base + IM_NAME_MAX - 1) *bp++ = *p++;
9077           *bp= 0;
9078           return base;
9079   }
9080
9081   void bread(FILE *f, char *name, void *buf, size_t len)
9082   /* Read len bytes.  Don't dare return without them. */
9083   {
9084           if (len > 0 && fread(buf, len, 1, f) != 1) {
9085                   if (ferror(f)) fatal(name);
9086                   fprintf(stderr, "installboot: Unsuspected EOF on %s\n", name);
9087                   exit(1);
9088          }
9089   }
9090
9091   void bwrite(FILE *f, char *name, void *buf, size_t len)
9092   {
9093           if (len > 0 && fwrite(buf, len, 1, f) != 1) fatal(name);
9094   }
9095
9096   long total_text= 0, total_data= 0, total_bss= 0;
9097   int making_image= 0;
9098
9099   void read_header(int talk, char *proc, FILE *procf, struct image_header *ihdr)
9100   /* Read the a.out header of a program and check it.  If procf happens to be
9101    * nil then the header is already in *image_hdr and need only be checked.
9102    */
9103   {
9104           int n, big= 0;
9105           static int banner= 0;
9106           struct exec *phdr= &ihdr->process;
9107
9108           if (procf == nil) {
9109                   /* Header already present. */
9110                   n= phdr->a_hdrlen;
9111          } else {
9112                   memset(ihdr, 0, sizeof(*ihdr));
9113
9114                   /* Put the basename of proc in the header. */
9115                   strncpy(ihdr->name, basename(proc), IM_NAME_MAX);
9116
9117                   /* Read the header. */
9118                   n= fread(phdr, sizeof(char), A_MINHDR, procf);
9119                   if (ferror(procf)) fatal(proc);
9120          }
9121
9122           if (n < A_MINHDR || BADMAG(*phdr)) {
9123                   fprintf(stderr, "installboot: %s is not an executable\n", proc);
9124                   exit(1);
9125          }
9126
9127           /* Get the rest of the exec header. */
9128           if (procf != nil) {
9129                   bread(procf, proc, ((char *) phdr) + A_MINHDR,
9130                                                   phdr->a_hdrlen - A_MINHDR);
9131          }
9132
9133           if (talk && !banner) {
9134                   printf("    text    data     bss     size\n");
9135                   banner= 1;
9136          }
9137
9138           if (talk) {
9139                   printf("%8ld%8ld%8ld%9ld  %s\n",
9140                           phdr->a_text, phdr->a_data, phdr->a_bss,
9141                           phdr->a_text + phdr->a_data + phdr->a_bss, proc);
9142          }
9143           total_text+= phdr->a_text;
9144           total_data+= phdr->a_data;
9145           total_bss+= phdr->a_bss;
9146
9147           if (phdr->a_cpu == A_I8086) {
9148                   long data= phdr->a_data + phdr->a_bss;
9149
9150                   if (!(phdr->a_flags & A_SEP)) data+= phdr->a_text;
9151
9152                   if (phdr->a_text >= 65536) big|= 1;
9153                   if (data >= 65536) big|= 2;
9154          }
9155           if (big) {
9156                   fprintf(stderr,
9157                           "%s will crash, %s%s%s segment%s larger then 64K\n",
9158                           proc,
9159                           big & 1 ? "text" : "",
9160                           big == 3 ? " and " : "",
9161                           big & 2 ? "data" : "",
9162                           big == 3 ? "s are" : " is");
9163          }
9164   }
9165
9166   void padimage(char *image, FILE *imagef, int n)
9167   /* Add n zeros to image to pad it to a sector boundary. */
9168   {
9169           while (n > 0) {
9170                   if (putc(0, imagef) == EOF) fatal(image);
9171                   n--;
9172          }
9173   }
9174
9175   #define align(n)        (((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1))
9176
9177   void copyexec(char *proc, FILE *procf, char *image, FILE *imagef, long n)
9178   /* Copy n bytes from proc to image padded to fill a sector. */
9179   {
9180           int pad, c;
9181
9182           /* Compute number of padding bytes. */
9183           pad= align(n) - n;
9184
9185           while (n > 0) {
9186                   if ((c= getc(procf)) == EOF) {
9187                           if (ferror(procf)) fatal(proc);
9188                           fprintf(stderr, "installboot: premature EOF on %s\n",
9189                                                                           proc);
9190                           exit(1);
9191                  }
9192                   if (putc(c, imagef) == EOF) fatal(image);
9193                   n--;
9194          }
9195           padimage(image, imagef, pad);
9196   }
9197
9198   void make_image(char *image, char **procv)
9199   /* Collect a set of files in an image, each "segment" is nicely padded out
9200    * to SECTOR_SIZE, so it may be read from disk into memory without trickery.
9201    */
9202   {
9203           FILE *imagef, *procf;
9204           char *proc, *file;
9205           int procn;
9206           struct image_header ihdr;
9207           struct exec phdr;
9208           struct stat st;
9209
9210           making_image= 1;
9211
9212           if ((imagef= fopen(image, "w")) == nil) fatal(image);
9213
9214           for (procn= 0; (proc= *procv++) != nil; procn++) {
9215                   /* Remove the label from the file name. */
9216                   if ((file= strchr(proc, ':')) != nil) file++; else file= proc;
9217
9218                   /* Real files please, may need to seek. */
9219                   if (stat(file, &st) < 0
9220                           || (errno= EISDIR, !S_ISREG(st.st_mode))
9221                           || (procf= fopen(file, "r")) == nil
9222                   ) fatal(proc);
9223
9224                   /* Read a.out header. */
9225                   read_header(1, proc, procf, &ihdr);
9226
9227                   /* Scratch. */
9228                   phdr= ihdr.process;
9229
9230                   /* The symbol table is always stripped off. */
9231                   ihdr.process.a_syms= 0;
9232                   ihdr.process.a_flags &= ~A_NSYM;
9233
9234                   /* Write header padded to fill a sector */
9235                   bwrite(imagef, image, &ihdr, sizeof(ihdr));
9236
9237                   padimage(image, imagef, SECTOR_SIZE - sizeof(ihdr));
9238
9239                   /* A page aligned executable needs the header in text. */
9240                   if (phdr.a_flags & A_PAL) {
9241                           rewind(procf);
9242                           phdr.a_text+= phdr.a_hdrlen;
9243                  }
9244
9245                   /* Copy text and data of proc to image. */
9246                   if (phdr.a_flags & A_SEP) {
9247                           /* Separate I&D: pad text & data separately. */
9248
9249                           copyexec(proc, procf, image, imagef, phdr.a_text);
9250                           copyexec(proc, procf, image, imagef, phdr.a_data);
9251                  } else {
9252                           /* Common I&D: keep text and data together. */
9253
9254                           copyexec(proc, procf, image, imagef,
9255                                                   phdr.a_text + phdr.a_data);
9256                  }
9257
9258                   /* Done with proc. */
9259                   (void) fclose(procf);
9260          }
9261           /* Done with image. */
9262
9263           if (fclose(imagef) == EOF) fatal(image);
9264
9265           printf("  ------  ------  ------  -------\n");
9266           printf("%8ld%8ld%8ld%9ld  total\n",
9267                   total_text, total_data, total_bss,
9268                   total_text + total_data + total_bss);
9269   }
9270
9271   void extractexec(FILE *imagef, char *image, FILE *procf, char *proc,
9272                                                   long count, off_t *alen)
9273   /* Copy a segment of an executable.  It is padded to a sector in image. */
9274   {
9275           char buf[SECTOR_SIZE];
9276
9277           while (count > 0) {
9278                   bread(imagef, image, buf, sizeof(buf));
9279                   *alen-= sizeof(buf);
9280
9281                   bwrite(procf, proc, buf,
9282                           count < sizeof(buf) ? (size_t) count : sizeof(buf));
9283                   count-= sizeof(buf);
9284          }
9285   }
9286
9287   void extract_image(char *image)
9288   /* Extract the executables from an image. */
9289   {
9290           FILE *imagef, *procf;
9291           off_t len;
9292           struct stat st;
9293           struct image_header ihdr;
9294           struct exec phdr;
9295           char buf[SECTOR_SIZE];
9296
9297           if (stat(image, &st) < 0) fatal(image);
9298
9299           /* Size of the image. */
9300           len= S_ISREG(st.st_mode) ? st.st_size : -1;
9301
9302           if ((imagef= fopen(image, "r")) == nil) fatal(image);
9303
9304           while (len != 0) {
9305                   /* Extract a program, first sector is an extended header. */
9306                   bread(imagef, image, buf, sizeof(buf));
9307                   len-= sizeof(buf);
9308
9309                   memcpy(&ihdr, buf, sizeof(ihdr));
9310                   phdr= ihdr.process;
9311
9312                   /* Check header. */
9313                   read_header(1, ihdr.name, nil, &ihdr);
9314
9315                   if ((procf= fopen(ihdr.name, "w")) == nil) fatal(ihdr.name);
9316
9317                   if (phdr.a_flags & A_PAL) {
9318                           /* A page aligned process contains a header in text. */
9319                           phdr.a_text+= phdr.a_hdrlen;
9320                  } else {
9321                           bwrite(procf, ihdr.name, &ihdr.process, phdr.a_hdrlen);
9322                  }
9323
9324                   /* Extract text and data segments. */
9325                   if (phdr.a_flags & A_SEP) {
9326                           extractexec(imagef, image, procf, ihdr.name,
9327                                                   phdr.a_text, &len);
9328                           extractexec(imagef, image, procf, ihdr.name,
9329                                                   phdr.a_data, &len);
9330                  } else {
9331                           extractexec(imagef, image, procf, ihdr.name,
9332                                   phdr.a_text + phdr.a_data, &len);
9333                  }
9334
9335                   if (fclose(procf) == EOF) fatal(ihdr.name);
9336          }
9337   }
9338
9339   int rawfd;      /* File descriptor to open device. */
9340   char *rawdev;   /* Name of device. */
9341
9342   void readblock(off_t blk, char *buf)
9343   /* For rawfs, so that it can read blocks. */
9344   {
9345           int n;
9346
9347           if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0
9348                   || (n= read(rawfd, buf, BLOCK_SIZE)) < 0
9349           ) fatal(rawdev);
9350
9351           if (n < BLOCK_SIZE) {
9352                   fprintf(stderr, "installboot: unexpected EOF on %s\n", rawdev);
9353                   exit(1);
9354          }
9355   }
9356
9357   void writeblock(off_t blk, char *buf)
9358   /* Add a function to write blocks for local use. */
9359   {
9360           if (lseek(rawfd, blk * BLOCK_SIZE, SEEK_SET) < 0
9361                   || write(rawfd, buf, BLOCK_SIZE) < 0
9362           ) fatal(rawdev);
9363   }
9364
9365   int raw_install(char *file, off_t *start, off_t *len)
9366   /* Copy bootcode or an image to the boot device at the given absolute disk
9367    * block number.  This "raw" installation is used to place bootcode and
9368    * image on a disk without a filesystem to make a simple boot disk.  Useful
9369    * in automated scripts for J. Random User.
9370    * Note: *len == 0 when an image is read.  It is set right afterwards.
9371    */
9372   {
9373           static char buf[BLOCK_SIZE];    /* Nonvolatile block buffer. */
9374           FILE *f;
9375           off_t sec;
9376           unsigned long devsize;
9377           static int banner= 0;
9378           struct partition entry;
9379
9380           /* See if the device has a maximum size. */
9381           devsize= -1;
9382           if (ioctl(rawfd, DIOCGETP, &entry) == 0) devsize= cv64ul(entry.size);
9383
9384           if ((f= fopen(file, "r")) == nil) fatal(file);
9385
9386           /* Copy sectors from file onto the boot device. */
9387           sec= *start;
9388           do {
9389                   int off= sec % RATIO;
9390
9391                   if (fread(buf + off * SECTOR_SIZE, 1, SECTOR_SIZE, f) == 0)
9392                           break;
9393
9394                   if (sec >= devsize) {
9395                           fprintf(stderr,
9396                           "installboot: %s can't be attached to %s\n",
9397                                   file, rawdev);
9398                           return 0;
9399                  }
9400
9401                   if (off == RATIO - 1) writeblock(sec / RATIO, buf);
9402          } while (++sec != *start + *len);
9403
9404           if (ferror(f)) fatal(file);
9405           (void) fclose(f);
9406
9407           /* Write a partial block, this may be the last image. */
9408           if (sec % RATIO != 0) writeblock(sec / RATIO, buf);
9409
9410           if (!banner) {
9411                   printf("  sector  length\n");
9412                   banner= 1;
9413          }
9414           *len= sec - *start;
9415           printf("%8ld%8ld  %s\n", *start, *len, file);
9416           *start= sec;
9417           return 1;
9418   }
9419
9420   enum howto { FS, BOOT };
9421
9422   void make_bootable(enum howto how, char *device, char *bootblock,
9423                                           char *bootcode, char **imagev)
9424   /* Install bootblock on the bootsector of device with the disk addresses to
9425    * bootcode patched into the data segment of bootblock.  "How" tells if there
9426    * should or shoudn't be a file system on the disk.  The images in the imagev
9427    * vector are added to the end of the device.
9428    */
9429   {
9430           char buf[BLOCK_SIZE + 256], *adrp, *parmp;
9431           struct fileaddr {
9432                   off_t   address;
9433                   int     count;
9434          } bootaddr[BOOT_MAX + 1], *bap= bootaddr;
9435           struct exec boothdr;
9436           struct image_header dummy;
9437           struct stat st;
9438           ino_t ino;
9439           off_t sector, max_sector;
9440           FILE *bootf;
9441           off_t addr, fssize, pos, len;
9442           char *labels, *label, *image;
9443           int nolabel;
9444
9445           /* Open device and set variables for readblock. */
9446           if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device);
9447
9448           /* Read and check the superblock. */
9449           fssize= r_super();
9450
9451           switch (how) {
9452           case FS:
9453                   if (fssize == 0) {
9454                           fprintf(stderr,
9455                                   "installboot: %s is not a Minix file system\n",
9456                                   device);
9457                           exit(1);
9458                  }
9459                   break;
9460           case BOOT:
9461                   if (fssize != 0) {
9462                           int s;
9463                           printf("%s contains a file system!\n", device);
9464                           printf("Scribbling in 10 seconds");
9465                           for (s= 0; s < 10; s++) {
9466                                   fputc('.', stdout);
9467                                   fflush(stdout);
9468                                   sleep(1);
9469                          }
9470                           fputc('\n', stdout);
9471                  }
9472                   fssize= 1;      /* Just a boot block. */
9473          }
9474
9475           if (how == FS) {
9476                   /* See if the boot code can be found on the file system. */
9477                   if ((ino= r_lookup(ROOT_INO, bootcode)) == 0) {
9478                           if (errno != ENOENT) fatal(bootcode);
9479                  }
9480          } else {
9481                   /* Boot code must be attached at the end. */
9482                   ino= 0;
9483          }
9484
9485           if (ino == 0) {
9486                   /* For a raw installation, we need to copy the boot code onto
9487                    * the device, so we need to look at the file to be copied.
9488                    */
9489                   if (stat(bootcode, &st) < 0) fatal(bootcode);
9490
9491                   if ((bootf= fopen(bootcode, "r")) == nil) fatal(bootcode);
9492          } else {
9493                   /* Boot code is present in the file system. */
9494                   r_stat(ino, &st);
9495
9496                   /* Get the header from the first block. */
9497                   if ((addr= r_vir2abs((off_t) 0)) == 0) {
9498                           boothdr.a_magic[0]= !A_MAGIC0;
9499                  } else {
9500                           readblock(addr, buf);
9501                           memcpy(&boothdr, buf, sizeof(struct exec));
9502                  }
9503                   bootf= nil;
9504                   dummy.process= boothdr;
9505          }
9506           /* See if it is an executable (read_header does the check). */
9507           read_header(0, bootcode, bootf, &dummy);
9508           boothdr= dummy.process;
9509
9510           if (bootf != nil) fclose(bootf);
9511
9512           /* Get all the sector addresses of the secondary boot code. */
9513           max_sector= (boothdr.a_hdrlen + boothdr.a_text
9514                           + boothdr.a_data + SECTOR_SIZE - 1) / SECTOR_SIZE;
9515
9516           if (max_sector > BOOT_MAX * RATIO) {
9517                   fprintf(stderr, "installboot: %s is way too big\n", bootcode);
9518                   exit(0);
9519          }
9520
9521           /* Determine the addresses to the boot code to be patched into the
9522            * boot block.
9523            */
9524           bap->count= 0;  /* Trick to get the address recording going. */
9525
9526           for (sector= 0; sector < max_sector; sector++) {
9527                   if (ino == 0) {
9528                           addr= fssize + (sector / RATIO);
9529                  } else
9530                   if ((addr= r_vir2abs(sector / RATIO)) == 0) {
9531                           fprintf(stderr, "installboot: %s has holes!\n",
9532                                                                   bootcode);
9533                           exit(1);
9534                  }
9535                   addr= (addr * RATIO) + (sector % RATIO);
9536
9537                   /* First address of the addresses array? */
9538                   if (bap->count == 0) bap->address= addr;
9539
9540                   /* Paste sectors together in a multisector read. */
9541                   if (bap->address + bap->count == addr)
9542                           bap->count++;
9543                   else {
9544                           /* New address. */
9545                           bap++;
9546                           bap->address= addr;
9547                           bap->count= 1;
9548                  }
9549          }
9550           (++bap)->count= 0;      /* No more. */
9551
9552           /* Get the boot block and patch the pieces in. */
9553           readblock(BOOTBLOCK, buf);
9554
9555           if ((bootf= fopen(bootblock, "r")) == nil) fatal(bootblock);
9556
9557           read_header(0, bootblock, bootf, &dummy);
9558           boothdr= dummy.process;
9559
9560           if (boothdr.a_text + boothdr.a_data +
9561                                            4 * (bap - bootaddr) + 1 > SIGPOS) {
9562                   fprintf(stderr,
9563           "installboot: %s + addresses to %s don't fit in the boot sector\n",
9564                           bootblock, bootcode);
9565                   fprintf(stderr,
9566                       "You can try copying/reinstalling %s to defragment it\n",
9567                           bootcode);
9568                   exit(1);
9569          }
9570
9571           /* All checks out right.  Read bootblock into the boot block! */
9572           bread(bootf, bootblock, buf, boothdr.a_text + boothdr.a_data);
9573           (void) fclose(bootf);
9574
9575           /* Patch the addresses in. */
9576           adrp= buf + (int) (boothdr.a_text + boothdr.a_data);
9577           for (bap= bootaddr; bap->count != 0; bap++) {
9578                   *adrp++= bap->count;
9579                   *adrp++= (bap->address >>  0) & 0xFF;
9580                   *adrp++= (bap->address >>  8) & 0xFF;
9581                   *adrp++= (bap->address >> 16) & 0xFF;
9582          }
9583           /* Zero count stops bootblock's reading loop. */
9584           *adrp++= 0;
9585
9586           if (bap > bootaddr+1) {
9587                   printf("%s and %d addresses to %s patched into %s\n",
9588                           bootblock, (int)(bap - bootaddr), bootcode, device);
9589          }
9590
9591           /* Boot block signature. */
9592           adrp= buf + SIGPOS;
9593           *adrp++= (SIGNATURE >> 0) & 0xFF;
9594           *adrp++= (SIGNATURE >> 8) & 0xFF;
9595
9596           /* Sector 2 of the boot block is used for boot parameters, initially
9597            * filled with null commands (newlines).  Initialize it only if
9598            * necessary.
9599            */
9600           for (parmp= buf + SECTOR_SIZE; parmp < buf + 2*SECTOR_SIZE; parmp++) {
9601                   if (*imagev != nil || (control(*parmp) && *parmp != '\n')) {
9602                           /* Param sector must be initialized. */
9603                           memset(buf + SECTOR_SIZE, '\n', SECTOR_SIZE);
9604                           break;
9605                  }
9606          }
9607
9608           /* Offset to the end of the file system to add boot code and images. */
9609           pos= fssize * RATIO;
9610
9611           if (ino == 0) {
9612                   /* Place the boot code onto the boot device. */
9613                   len= max_sector;
9614                   if (!raw_install(bootcode, &pos, &len)) {
9615                           if (how == FS) {
9616                                   fprintf(stderr,
9617           "\t(Isn't there a copy of %s on %s that can be used?)\n",
9618                                           bootcode, device);
9619                          }
9620                           exit(1);
9621                  }
9622          }
9623
9624           parmp= buf + SECTOR_SIZE;
9625           nolabel= 0;
9626
9627           if (how == BOOT) {
9628                   /* A boot only disk needs to have floppies swapped. */
9629                   strcpy(parmp,
9630           "trailer()echo \\nInsert the root diskette then hit RETURN\\n\\w\\c\n");
9631                   parmp+= strlen(parmp);
9632          }
9633
9634           while ((labels= *imagev++) != nil) {
9635                   /* Place each kernel image on the boot device. */
9636
9637                   if ((image= strchr(labels, ':')) != nil)
9638                           *image++= 0;
9639                   else {
9640                           if (nolabel) {
9641                                   fprintf(stderr,
9642                               "installboot: Only one image can be the default\n");
9643                                   exit(1);
9644                          }
9645                           nolabel= 1;
9646                           image= labels;
9647                           labels= nil;
9648                  }
9649                   len= 0;
9650                   if (!raw_install(image, &pos, &len)) exit(1);
9651
9652                   if (labels == nil) {
9653                           /* Let this image be the default. */
9654                           sprintf(parmp, "image=%ld:%ld\n", pos-len, len);
9655                           parmp+= strlen(parmp);
9656                  }
9657
9658                   while (labels != nil) {
9659                           /* Image is prefixed by a comma separated list of
9660                            * labels.  Define functions to select label and image.
9661                            */
9662                           label= labels;
9663                           if ((labels= strchr(labels, ',')) != nil) *labels++ = 0;
9664
9665                           sprintf(parmp,
9666                   "%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n",
9667                                   label,
9668                                   between('A', label[0], 'Z')
9669                                           ? label[0]-'A'+'a' : label[0],
9670                                   label, pos-len, len, label);
9671                           parmp+= strlen(parmp);
9672                  }
9673
9674                   if (parmp > buf + BLOCK_SIZE) {
9675                           fprintf(stderr,
9676                   "installboot: Out of parameter space, too many images\n");
9677                           exit(1);
9678                  }
9679          }
9680           /* Install boot block. */
9681           writeblock((off_t) BOOTBLOCK, buf);
9682
9683           if (pos > fssize * RATIO) {
9684                   /* Tell the total size of the data on the device. */
9685                   printf("%16ld  (%ld kb) total\n", pos,
9686                                                   (pos + RATIO - 1) / RATIO);
9687          }
9688   }
9689
9690   void install_master(char *device, char *masterboot, char *fix)
9691   /* Booting a hard disk is a two stage process:  The master bootstrap in sector
9692    * 0 loads the bootstrap from sector 0 of the active partition which in turn
9693    * starts the operating system.  This code installs such a master bootstrap
9694    * on a hard disk.  If fix is non-null then the master bootstrap is locked
9695    * into booting device /dev/hd'fix'.
9696    */
9697   {
9698           FILE *masf;
9699           unsigned long size;
9700           struct stat st;
9701           char buf[BLOCK_SIZE];
9702
9703           /* Open device. */
9704           if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device);
9705
9706           /* Open the master boot code. */
9707           if ((masf= fopen(masterboot, "r")) == nil) fatal(masterboot);
9708
9709           /* See if the user is cloning a device. */
9710           if (fstat(fileno(masf), &st) >=0 && S_ISBLK(st.st_mode))
9711                   size= PARTPOS;
9712           else {
9713                   /* Read and check header otherwise. */
9714                   struct image_header ihdr;
9715
9716                   read_header(1, masterboot, masf, &ihdr);
9717                   size= ihdr.process.a_text + ihdr.process.a_data;
9718          }
9719           if (size > PARTPOS) {
9720                   fprintf(stderr, "installboot: %s is too big\n", masterboot);
9721                   exit(1);
9722          }
9723           /* Read the master boot block, patch it, write. */
9724           readblock(BOOTBLOCK, buf);
9725
9726           (void) bread(masf, masterboot, buf, size);
9727
9728           if (fix != nil) {
9729                   /* Fixate partition to boot. */
9730                   int device= 0, logical= 0;
9731                   char *pf= fix;
9732
9733                   while (between('0', *pf, '9')) {
9734                           device= 10 * device + (*pf - '0');
9735                           if (device >= 40) break;
9736                           pf++;
9737                  }
9738                   if (between('a', *pf, 'd')) {
9739                           logical= 1 + (*pf - 'a');
9740                           pf++;
9741                  }
9742                   if (*pf != 0) {
9743                           fprintf(stderr, "installboot: bad fix key '%s'\n", fix);
9744                           exit(1);
9745                  }
9746
9747                   if (buf[0] == (char) JMP && buf[1] == (char) JMPOFFM
9748                                   && buf[3] == (char) XOR && logical == 0) {
9749                           /* Minix masterboot; patch device number. */
9750                           buf[2]= device;
9751                  } else
9752                   if (buf[0] == (char) JMP && buf[1] == (char) JMPOFFE
9753                                   && buf[4] == (char) XOR && logical != 0) {
9754                           /* Minix extboot; patch device and logical number. */
9755                           buf[2]= device;
9756                           buf[3]= logical;
9757                  } else {
9758                           fprintf(stderr,
9759                                   "installboot: can't put fix flag '%s' on %s\n",
9760                                   fix, masterboot);
9761                           exit(1);
9762                  }
9763          }
9764
9765           /* Install signature. */
9766           buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF;
9767           buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF;
9768
9769           writeblock(BOOTBLOCK, buf);
9770   }
9771
9772   void usage(void)
9773   {
9774           fprintf(stderr,
9775             "Usage: installboot -i(mage) image kernel mm fs ... init\n"
9776             "       installboot -(e)x(tract) image\n"
9777             "       installboot -d(evice) device bootblock boot [image ...]\n"
9778             "       installboot -b(oot) device bootblock boot image ...\n"
9779             "       installboot -m(aster) [fix] device masterboot\n");
9780           exit(1);
9781   }
9782
9783   int isoption(char *option, char *test)
9784   /* Check if the option argument is equals "test".  Also accept -i as short
9785    * for -image, and the special case -x for -extract.
9786    */
9787   {
9788           if (strcmp(option, test) == 0) return 1;
9789           if (option[0] != '-' && strlen(option) != 2) return 0;
9790           if (option[1] == test[1]) return 1;
9791           if (option[1] == 'x' && test[1] == 'e') return 1;
9792           return 0;
9793   }
9794
9795   int main(int argc, char **argv)
9796   {
9797           if (argc < 2) usage();
9798
9799           if (argc >= 4 && isoption(argv[1], "-image")) {
9800                   make_image(argv[2], argv + 3);
9801          } else
9802           if (argc == 3 && isoption(argv[1], "-extract")) {
9803                   extract_image(argv[2]);
9804          } else
9805           if (argc >= 5 && isoption(argv[1], "-device")) {
9806                   make_bootable(FS, argv[2], argv[3], argv[4], argv + 5);
9807          } else
9808           if (argc >= 6 && isoption(argv[1], "-boot")) {
9809                   make_bootable(BOOT, argv[2], argv[3], argv[4], argv + 5);
9810          } else
9811           if (argc == 4 && isoption(argv[1], "-master")) {
9812                   install_master(argv[2], argv[3], nil);
9813          } else
9814           if (argc == 5 && isoption(argv[1], "-master")
9815                                       && between('0', argv[2][0], '9')) {
9816                   install_master(argv[3], argv[4], argv[2]);
9817          } else {
9818                   usage();
9819          }
9820           exit(0);
9821   }