/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Construit une image de la disquette de demarrage. * * Utilisation : build [options] bootsect setup kernel [rootdir] * bootsect le secteur de boot * setup le code de chargement du noyau * kernel le noyau * rootdir le repertoire a copier sur la disquette * Options : * -b filename lire la liste des secteurs defectueux depuis le fichier * filename. Si filename est -, lit la liste des secteurs * defectueux depuis l'entree standard. * -o filename ecrire l'image dans le fichier filename, au lieu de la sortie * standard. * -q mode silencieux. */ #define _GNU_SOURCE #include "format.h" #include "floppy.h" #include #include #include #include #include #include /* nombre de secteurs occupes */ #define N_SEC(size) (((size) + (BYTSPERSEC - 1)) / BYTSPERSEC) struct write_reserved_args_struct { const char *setup_filename; const char *kernel_filename; }; struct build_args_struct { char *bootsect_filename; char *setup_filename; char *kernel_filename; char *rootdir_filename; char *badblocks_filename; FILE *output; }; struct stats_struct { off_t setup_size; off_t kernel_size; unsigned short reserved_sectors; }; int quiet; static void die(const char *s) { error(EXIT_FAILURE, errno, s); } static void __attribute__ ((noreturn)) die_bad_usage(const char *s) { if (s) error(0, 0, s); else fflush(stdout); fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name); exit(EXIT_FAILURE); } static void print_help() { printf("Usage: %s [OPTION]... BOOTSECT SETUP KERNEL [ROOTDIR]\n", program_invocation_name); puts("Create a bootable floppy disk image of XOS.\n" "\n" "Mandatory arguments to long options are mandatory for short options too.\n" " -b, --badblocks-file=FILE read bad blocks from FILE\n" " -h, --help display this help and exit\n" " -o, --output=FILE write output in FILE instead of standard output\n" " -q, --quiet quiet output"); } static void read_bootsect(const char *filename, char bootsect[]) { FILE *fp; int n; if (!(fp = fopen(filename, "r"))) die(filename); n = fread(bootsect, 1, 512, fp); if (ferror(fp)) die("fread"); if (n < 512) memset(bootsect + n, 0, 512 - n); fclose(fp); } static inline void mark_badblock(char *badblocks, unsigned short n) { if (n >= SECTORS) return; badblocks[n >> 3] |= 1 << (n & 7); } static void read_badblocks(const char *filename, char *badblocks) { FILE *fp; unsigned short n; if (!strcmp(filename, "-")) fp = stdin; else if (!(fp = fopen(filename, "r"))) die(filename); while (fscanf(fp, "%hu", &n) == 1) mark_badblock(badblocks, n); if (ferror(fp)) die("fscanf"); if (!feof(fp)) error(EXIT_FAILURE, 0, "Failed to read bad blocks"); if (fp != stdin) fclose(fp); } static unsigned short compute_reserved_sectors(off_t setup_size, off_t kernel_size) { unsigned short reserved_sectors; reserved_sectors = N_SEC(512); /* secteur de demarrage */ reserved_sectors += N_SEC(setup_size); /* setup */ reserved_sectors += N_SEC(kernel_size); /* noyau */ return reserved_sectors; } /* Ecrit un fichier sur la disquette, par secteurs entiers. */ static void write_file(const char *filename, struct output_struct *output, void (*write_sectors)(const void *, unsigned short, struct output_struct *)) { FILE *fp; char buf[BYTSPERSEC]; size_t n; if (!(fp = fopen(filename, "r"))) die(filename); do { n = fread(buf, 1, BYTSPERSEC, fp); if (ferror(fp)) die("fread"); if (n) { if (n < BYTSPERSEC) memset(buf + n, 0, BYTSPERSEC - n); write_sectors(buf, 1, output); } } while (!feof(fp)); fclose(fp); } /* Ecrit la partie reservee de la disquette FAT, sauf le secteur de demarrage * qui est deja ecrit. */ static void write_reserved(void (*write_sectors)(const void *, unsigned short, struct output_struct *), void *arg, struct output_struct *output) { struct write_reserved_args_struct *args; args = (struct write_reserved_args_struct *)arg; write_file(args->setup_filename, output, write_sectors); write_file(args->kernel_filename, output, write_sectors); } static void build(const struct build_args_struct *args, struct stats_struct *stats) { struct stat statbuf; char bootsect[512]; char badblocks[(SECTORS + 7) >> 3]; struct write_reserved_args_struct write_reserved_args; /* lecture du secteur de demarrage */ if (stat(args->bootsect_filename, &statbuf) == -1) die(args->bootsect_filename); if (S_ISDIR(statbuf.st_mode)) { errno = EISDIR; die(args->bootsect_filename); } read_bootsect(args->bootsect_filename, bootsect); /* lecture des blocs defectueux */ memset(badblocks, 0, sizeof badblocks); if (args->badblocks_filename) { if (strcmp(args->badblocks_filename, "-")) { if (stat(args->badblocks_filename, &statbuf) == -1) die(args->badblocks_filename); if (S_ISDIR(statbuf.st_mode)) { errno = EISDIR; die(args->badblocks_filename); } } read_badblocks(args->badblocks_filename, badblocks); } /* taille de setup */ if (stat(args->setup_filename, &statbuf) == -1) die(args->setup_filename); if (S_ISDIR(statbuf.st_mode)) { errno = EISDIR; die(args->setup_filename); } stats->setup_size = statbuf.st_size; /* taille du noyau */ if (stat(args->kernel_filename, &statbuf) == -1) die(args->kernel_filename); if (S_ISDIR(statbuf.st_mode)) { errno = EISDIR; die(args->kernel_filename); } stats->kernel_size = statbuf.st_size; stats->reserved_sectors = compute_reserved_sectors(stats->setup_size, stats->kernel_size); write_reserved_args.setup_filename = args->setup_filename; write_reserved_args.kernel_filename = args->kernel_filename; format(args->output, stats->reserved_sectors, bootsect, write_reserved, &write_reserved_args, args->rootdir_filename, badblocks); } static void show_statistics(const struct stats_struct *stats, FILE *stream) { fprintf(stream, "Boot sector: %7lu bytes %4hu sector(s)\n", 512UL, N_SEC(512)); fprintf(stream, "Setup: %7lu bytes %4hu sector(s)\n", stats->setup_size, (unsigned short)N_SEC(stats->setup_size)); fprintf(stream, "Kernel: %7lu bytes %4hu sector(s)\n", stats->kernel_size, (unsigned short)N_SEC(stats->kernel_size)); fprintf(stream, "Total reserved: %4hu sector(s) %2u%%\n", stats->reserved_sectors, stats->reserved_sectors * 100 / SECTORS); } int main(int argc, char **argv) { static const struct option longopts[] = { {"badblocks-file", required_argument, NULL, 'b'}, {"help", no_argument, NULL, 'h'}, {"output", required_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {NULL, 0, NULL, 0} }; char *badblocks_filename; char *output_filename; struct build_args_struct args; struct stats_struct stats; int c; /* options par defaut */ quiet = 0; badblocks_filename = NULL; output_filename = NULL; /* lecture des options de la ligne de commande */ while ((c = getopt_long(argc, argv, "b:ho:q", longopts, NULL)) != -1) switch (c) { case 'b': if (badblocks_filename) free(badblocks_filename); if (!(badblocks_filename = strdup(optarg))) die("strdup"); break; case 'h': print_help(); return EXIT_SUCCESS; case 'o': if (output_filename) free(output_filename); if (!(output_filename = strdup(optarg))) die("strdup"); break; case 'q': quiet = 1; break; case '?': die_bad_usage(NULL); } /* lecture des autres arguments */ if (optind == argc) die_bad_usage("missing bootsect operand"); if (!(args.bootsect_filename = strdup(argv[optind++]))) die("strdup"); if (optind == argc) die_bad_usage("missing setup operand"); if (!(args.setup_filename = strdup(argv[optind++]))) die("strdup"); if (optind == argc) die_bad_usage("missing kernel operand"); if (!(args.kernel_filename = strdup(argv[optind++]))) die("strdup"); if (optind < argc) { if (!(args.rootdir_filename = strdup(argv[optind]))) die("strdup"); } else args.rootdir_filename = NULL; args.badblocks_filename = badblocks_filename; if (output_filename) { if (!(args.output = fopen(output_filename, "w+"))) die(output_filename); free(output_filename); } else args.output = stdout; build(&args, &stats); if (!quiet) show_statistics(&stats, args.output == stdout ? stderr : stdout); free(args.bootsect_filename); free(args.setup_filename); free(args.kernel_filename); if (args.rootdir_filename) free(args.rootdir_filename); if (args.badblocks_filename) free(badblocks_filename); if (args.output != stdout) fclose(args.output); return EXIT_SUCCESS; }