diff options
author | hpa <hpa> | 2004-12-15 10:14:39 +0000 |
---|---|---|
committer | hpa <hpa> | 2004-12-15 10:14:39 +0000 |
commit | 7be70022dd3ae138998fc7185351b9b49ad9a9b2 (patch) | |
tree | 2599a8ede407eec131228fb607a52e22145ba1f9 /mtools | |
parent | 4912df96419eff85394da1160bcb19a4dd2c1c59 (diff) | |
download | syslinux-7be70022dd3ae138998fc7185351b9b49ad9a9b2.tar.gz |
Prepping for new 2.20 version: rewrite main syslinux program to support
FAT32 and EDD, and a new cleaner installer infrastructure.
Diffstat (limited to 'mtools')
-rw-r--r-- | mtools/Makefile | 39 | ||||
-rw-r--r-- | mtools/syslinux.c | 292 |
2 files changed, 331 insertions, 0 deletions
diff --git a/mtools/Makefile b/mtools/Makefile new file mode 100644 index 00000000..a13642cc --- /dev/null +++ b/mtools/Makefile @@ -0,0 +1,39 @@ +CC = gcc +OPTFLAGS = -g -O -Dinline= +INCLUDES = -I. -I.. -I../libfat +CFLAGS = -W -Wall -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES) +LDFLAGS = + +SRCS = syslinux.c ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c $(wildcard ../libfat/*.c) +OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS))) + +.SUFFIXES: .c .o .i .s .S + +VPATH = .:..:../libfat + +all: installer + +tidy: + -rm -f *.o *.i *.s *.a .*.d + +clean: tidy + -rm -f syslinux + +spotless: clean + +installer: syslinux + +syslinux: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) -Wp,-MT,$@,-MMD,.$@.d $(CFLAGS) -c -o $@ $< +%.i: %.c + $(CC) $(CFLAGS) -E -o $@ $< +%.s: %.c + $(CC) $(CFLAGS) -S -o $@ $< + +-include .*.d + + + diff --git a/mtools/syslinux.c b/mtools/syslinux.c new file mode 100644 index 00000000..69d7a3dd --- /dev/null +++ b/mtools/syslinux.c @@ -0,0 +1,292 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2004 H. Peter Anvin - All Rights Reserved + * + * 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 _XOPEN_SOURCE 500 /* Required on glibc 2.x */ +#define _BSD_SOURCE +#include <alloca.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <mntent.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.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" + +char *program; /* Name of program */ +char *device; /* Device to install to */ +pid_t mypid; + +void usage(void) +{ + fprintf(stderr, "Usage: %s [-sf] [-o offset] device\n", program); + 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 ) { + fprintf(stderr, "%s: short read\n", program); + exit(1); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + perror(program); + exit(1); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset) +{ + char *bufp = (char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pwrite(fd, bufp, count, offset); + if ( rv == 0 ) { + fprintf(stderr, "%s: short write\n", program); + exit(1); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + perror(program); + exit(1); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +/* + * Version of the read function suitable for libfat + */ +int libfat_xpread(void *pp, void *buf, size_t secsize, libfat_sector_t sector) +{ + off_t offset = (off_t)sector * secsize; + return xpread((int)pp, buf, secsize, offset); +} + + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[512]; + int dev_fd; + struct stat st; + int status; + char **argp, *opt; + int force = 0; /* -f (force) option */ + off_t offset = 0; /* -o (offset) option */ + char mtools_conf[] = "/tmp/syslinux-mtools-XXXXXX"; + int mtc_fd; + FILE *mtc, *mtp; + struct libfat_filesystem *fs; + libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */ + int32_t ldlinux_cluster; + int nsectors; + + (void)argc; /* Unused */ + + mypid = getpid(); + program = argv[0]; + + device = NULL; + + for ( argp = argv+1 ; *argp ; argp++ ) { + if ( **argp == '-' ) { + opt = *argp + 1; + if ( !*opt ) + usage(); + + while ( *opt ) { + if ( *opt == 's' ) { + syslinux_make_stupid(); /* Use "safe, slow and stupid" code */ + } else if ( *opt == 'f' ) { + force = 1; /* Force install */ + } else if ( *opt == 'o' && argp[1] ) { + offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */ + } else { + usage(); + } + opt++; + } + } else { + if ( device ) + usage(); + device = *argp; + } + } + + if ( !device ) + usage(); + + /* + * First make sure we can open the device at all, and that we have + * read/write permission. + */ + dev_fd = open(device, O_RDWR); + if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) { + perror(device); + exit(1); + } + + if ( !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", device); + exit(1); + } + + xpread(dev_fd, sectbuf, 512, offset); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + if( !syslinux_check_bootsect(sectbuf,device) ) { + exit(1); + } + + /* + * Create an mtools configuration file + */ + mtc_fd = mkstemp(mtools_conf); + if ( mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w")) ) { + perror(program); + exit(1); + } + fprintf(mtc, + "MTOOLS_NO_VFAT=1\n" + "MTOOLS_SKIP_CHECK=1\n" /* Needed for some flash memories */ + "drive s:\n" + " file=\"/proc/%lu/fd/%d\"\n" + " offset=%llu\n", + (unsigned long)mypid, + dev_fd, + (unsigned long long)offset); + fclose(mtc); + + /* + * Run mtools to create the LDLINUX.SYS file + */ + if ( setenv("MTOOLSRC", mtools_conf, 1) ) { + perror(program); + exit(1); + } + + /* This command may fail legitimately */ + system("mattrib -h -r -s s:ldlinux.sys 2>/dev/null"); + + 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) || + (status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status)) ) { + fprintf(stderr, "%s: failed to create ldlinux.sys\n", program); + exit(1); + } + + 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); + } + + unlink(mtools_conf); + + /* + * Now, use libfat to create a block map + */ + fs = libfat_open(libfat_xpread, (void *)dev_fd); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while ( s && nsectors < 65 ) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* + * Patch ldlinux.sys and the boot sector + */ + syslinux_patch(sectors, nsectors); + + /* + * Write the now-patched first sector of ldlinux.sys + */ + xpwrite(dev_fd, syslinux_ldlinux, 512, offset + ((off_t)sectors[0] << 9)); + + /* + * To finish up, write the boot sector + */ + + /* Read the superblock again since it might have changed while mounted */ + xpread(dev_fd, sectbuf, 512, offset); + + /* Copy the syslinux code into the boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write new boot sector */ + xpwrite(dev_fd, sectbuf, 512, offset); + + close(dev_fd); + sync(); + + /* Done! */ + + return 0; +} |