diff options
Diffstat (limited to 'contrib/syslinux-4.02/mtools/syslinux.c')
-rwxr-xr-x | contrib/syslinux-4.02/mtools/syslinux.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/mtools/syslinux.c b/contrib/syslinux-4.02/mtools/syslinux.c new file mode 100755 index 0000000..ac189c6 --- /dev/null +++ b/contrib/syslinux-4.02/mtools/syslinux.c @@ -0,0 +1,370 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux.c - Linux installer program for SYSLINUX + * + * This program now requires mtools. It turned out to be a lot + * easier to deal with than dealing with needing mount privileges. + * We need device write permission anyway. + */ + +#define _GNU_SOURCE +#include <alloca.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <inttypes.h> +#include <mntent.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sysexits.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include "syslinux.h" +#include "libfat.h" +#include "setadv.h" +#include "syslxopt.h" + +char *program; /* Name of program */ +pid_t mypid; + +void __attribute__ ((noreturn)) die(const char *msg) +{ + fprintf(stderr, "%s: %s\n", program, msg); + exit(1); +} + +void __attribute__ ((noreturn)) die_err(const char *msg) +{ + fprintf(stderr, "%s: %s: %s\n", program, msg, strerror(errno)); + exit(1); +} + +/* + * read/write wrapper functions + */ +ssize_t xpread(int fd, void *buf, size_t count, off_t offset) +{ + char *bufp = (char *)buf; + ssize_t rv; + ssize_t done = 0; + + while (count) { + rv = pread(fd, bufp, count, offset); + if (rv == 0) { + die("short read"); + } else if (rv == -1) { + if (errno == EINTR) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset) +{ + const char *bufp = (const char *)buf; + ssize_t rv; + ssize_t done = 0; + + while (count) { + rv = pwrite(fd, bufp, count, offset); + if (rv == 0) { + die("short write"); + } else if (rv == -1) { + if (errno == EINTR) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +/* + * Version of the read function suitable for libfat + */ +int libfat_xpread(intptr_t pp, void *buf, size_t secsize, + libfat_sector_t sector) +{ + off_t offset = (off_t) sector * secsize + opt.offset; + return xpread(pp, buf, secsize, offset); +} + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[SECTOR_SIZE]; + int dev_fd; + struct stat st; + int status; + const char *tmpdir; + char *mtools_conf; + int mtc_fd; + FILE *mtc, *mtp; + struct libfat_filesystem *fs; + libfat_sector_t s, *secp; + libfat_sector_t *sectors; + int32_t ldlinux_cluster; + int nsectors; + const char *errmsg; + int ldlinux_sectors, patch_sectors; + int i; + + (void)argc; /* Unused */ + + mypid = getpid(); + program = argv[0]; + + parse_options(argc, argv, MODE_SYSLINUX); + + if (!opt.device) + usage(EX_USAGE, MODE_SYSLINUX); + + if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once + || (opt.update_only > 0) || opt.menu_save) { + fprintf(stderr, + "At least one specified option not yet implemented" + " for this installer.\n"); + exit(1); + } + + /* + * Temp directory of choice... + */ + tmpdir = getenv("TMPDIR"); + if (!tmpdir) { +#ifdef P_tmpdir + tmpdir = P_tmpdir; +#elif defined(_PATH_TMP) + tmpdir = _PATH_TMP; +#else + tmpdir = "/tmp"; +#endif + } + + /* + * First make sure we can open the device at all, and that we have + * read/write permission. + */ + dev_fd = open(opt.device, O_RDWR); + if (dev_fd < 0 || fstat(dev_fd, &st) < 0) { + die_err(opt.device); + exit(1); + } + + if (!opt.force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) { + fprintf(stderr, + "%s: not a block device or regular file (use -f to override)\n", + opt.device); + exit(1); + } + + xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + if ((errmsg = syslinux_check_bootsect(sectbuf))) { + die(errmsg); + } + + /* + * Create an mtools configuration file + */ + if (asprintf(&mtools_conf, "%s//syslinux-mtools-XXXXXX", tmpdir) < 0 || + !mtools_conf) + die_err(tmpdir); + + mtc_fd = mkstemp(mtools_conf); + if (mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w"))) + die_err(mtools_conf); + + fprintf(mtc, + /* These are needed for some flash memories */ + "MTOOLS_SKIP_CHECK=1\n" + "MTOOLS_FAT_COMPATIBILITY=1\n" + "drive s:\n" + " file=\"/proc/%lu/fd/%d\"\n" + " offset=%llu\n", + (unsigned long)mypid, + dev_fd, (unsigned long long)opt.offset); + + if (ferror(mtc) || fclose(mtc)) + die_err(mtools_conf); + + /* + * Run mtools to create the LDLINUX.SYS file + */ + if (setenv("MTOOLSRC", mtools_conf, 1)) { + perror(program); + exit(1); + } + + /* + * Create a vacuous ADV in memory. This should be smarter. + */ + syslinux_reset_adv(syslinux_adv); + + /* This command may fail legitimately */ + status = system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null"); + (void)status; /* Keep _FORTIFY_SOURCE happy */ + + mtp = popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w"); + if (!mtp || + fwrite(syslinux_ldlinux, 1, syslinux_ldlinux_len, mtp) + != syslinux_ldlinux_len || + fwrite(syslinux_adv, 1, 2 * ADV_SIZE, mtp) + != 2 * ADV_SIZE || + (status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status))) { + die("failed to create ldlinux.sys"); + } + + /* + * Now, use libfat to create a block map + */ + ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + + SECTOR_SIZE - 1) >> SECTOR_SHIFT; + sectors = calloc(ldlinux_sectors, sizeof *sectors); + fs = libfat_open(libfat_xpread, dev_fd); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while (s && nsectors < ldlinux_sectors) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* Patch ldlinux.sys and the boot sector */ + i = syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, + opt.directory, NULL); + patch_sectors = (i + SECTOR_SIZE - 1) >> SECTOR_SHIFT; + + /* Write the now-patched first sectors of ldlinux.sys */ + for (i = 0; i < patch_sectors; i++) { + xpwrite(dev_fd, syslinux_ldlinux + i * SECTOR_SIZE, SECTOR_SIZE, + opt.offset + ((off_t) sectors[i] << SECTOR_SHIFT)); + } + + /* Move ldlinux.sys to the desired location */ + if (opt.directory) { + char target_file[4096], command[5120]; + char *cp = target_file, *ep = target_file + sizeof target_file - 16; + const char *sd; + int slash = 1; + + cp += sprintf(cp, "'s:/"); + for (sd = opt.directory; *sd; sd++) { + if (*sd == '/' || *sd == '\\') { + if (slash) + continue; /* Remove duplicated slashes */ + slash = 1; + } else if (*sd == '\'' || *sd == '!') { + slash = 0; + if (cp < ep) + *cp++ = '\''; + if (cp < ep) + *cp++ = '\\'; + if (cp < ep) + *cp++ = *sd; + if (cp < ep) + *cp++ = '\''; + continue; + } else { + slash = 0; + } + + if (cp < ep) + *cp++ = *sd; + } + if (!slash) + *cp++ = '/'; + strcpy(cp, "ldlinux.sys'"); + + /* This command may fail legitimately */ + sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file); + status = system(command); + (void)status; /* Keep _FORTIFY_SOURCE happy */ + + sprintf(command, "mmove -D o -D O s:/ldlinux.sys %s", target_file); + status = system(command); + + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, + "%s: warning: unable to move ldlinux.sys\n", program); + + status = system("mattrib +r +h +s s:/ldlinux.sys"); + } else { + sprintf(command, "mattrib +r +h +s %s", target_file); + status = system(command); + } + } else { + status = system("mattrib +r +h +s s:/ldlinux.sys"); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, + "%s: warning: failed to set system bit on ldlinux.sys\n", + program); + } + + /* + * Cleanup + */ + unlink(mtools_conf); + + /* + * To finish up, write the boot sector + */ + + /* Read the superblock again since it might have changed while mounted */ + xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); + + /* Copy the syslinux code into the boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write new boot sector */ + xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); + + close(dev_fd); + sync(); + + /* Done! */ + + return 0; +} |