diff options
author | hpa <hpa> | 2003-04-16 03:04:57 +0000 |
---|---|---|
committer | hpa <hpa> | 2003-04-16 03:04:57 +0000 |
commit | 5bc83a208a25f61313597d734d8a6f561fba6417 (patch) | |
tree | ffb34bcfb09652c0f155c13eb46c6c9fe7a39c2d | |
parent | 278300aa6fa63aa1dabd52b3ae8c8da63f850fc5 (diff) | |
download | syslinux-5bc83a208a25f61313597d734d8a6f561fba6417.tar.gz |
Resurrect the no-mtools installersyslinux-2.04-pre1
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | syslinux-nomtools.c | 490 |
3 files changed, 499 insertions, 2 deletions
@@ -41,13 +41,14 @@ VERSION = $(shell cat version) # like to keep those uniform for debugging reasons; however, distributors # want to recompile the installers (ITARGET). # -CSRC = syslinux.c syslxmod.c gethostip.c +CSRC = syslinux.c syslinux-nomtools.c syslxmod.c gethostip.c NASMSRC = ldlinux.asm syslinux.asm copybs.asm \ pxelinux.asm mbr.asm isolinux.asm isolinux-debug.asm SOURCES = $(CSRC) *.h $(NASMSRC) *.inc BTARGET = kwdhash.gen version.gen ldlinux.bss ldlinux.sys ldlinux.bin \ pxelinux.0 mbr.bin isolinux.bin isolinux-debug.bin libsyslinux.a -ITARGET = syslinux.com syslinux copybs.com gethostip mkdiskimage +ITARGET = syslinux.com syslinux syslinux-nomtools copybs.com gethostip \ + mkdiskimage DOCS = COPYING NEWS README TODO *.doc sample com32 OTHER = Makefile bin2c.pl now.pl genhash.pl keywords findpatch.pl \ keytab-lilo.pl version version.pl sys2ansi.pl \ @@ -147,6 +148,9 @@ libsyslinux.a: bootsect_bin.o ldlinux_bin.o syslxmod.o syslinux: syslinux.o libsyslinux.a $(CC) $(LDFLAGS) -o $@ $^ +syslinux-nomtools: syslinux-nomtools.o libsyslinux.a + $(CC) $(LDFLAGS) -o $@ $^ + syslxmod.o: syslxmod.c patch.offset $(CC) $(INCLUDE) $(CFLAGS) -DPATCH_OFFSET=`cat patch.offset` \ -c -o $@ $< @@ -9,6 +9,9 @@ Changes in 2.04: derivatives... * PXELINUX: Attempt to negotiate full Ethernet-sized blocks (1468 bytes) using the blksize option. + * SYSLINUX: Resurrect the old no-mtools version of the + installer, although as a root-only tool. Some distributors + have indicated that they need a small standalone installer. Changes in 2.03: * Actually support comment lines in the configuration file. diff --git a/syslinux-nomtools.c b/syslinux-nomtools.c new file mode 100644 index 00000000..9e838358 --- /dev/null +++ b/syslinux-nomtools.c @@ -0,0 +1,490 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2003 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., 675 Mass Ave, Cambridge MA 02139, + * 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 ought to be portable. I hope so, at least. + * + * This is an alternate version of the installer which doesn't require + * mtools, but requires root privilege. + */ + +#define _XOPEN_SOURCE 500 /* Required on glibc 2.x */ +#define _BSD_SOURCE +#include <alloca.h> +#include <errno.h> +#include <fcntl.h> +#include <mntent.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "syslinux.h" + +#ifndef _PATH_MOUNT +#define _PATH_MOUNT "/bin/mount" +#endif + +#ifndef _PATH_UMOUNT +#define _PATH_UMOUNT "/bin/umount" +#endif + +char *program; /* Name of program */ +char *device; /* Device to install to */ +uid_t ruid; /* Real uid */ +uid_t euid; /* Initial euid */ +pid_t mypid; + +enum bs_offsets { + bsJump = 0x00, + bsOemName = 0x03, + bsBytesPerSec = 0x0b, + bsSecPerClust = 0x0d, + bsResSectors = 0x0e, + bsFATs = 0x10, + bsRootDirEnts = 0x11, + bsSectors = 0x13, + bsMedia = 0x15, + bsFATsecs = 0x16, + bsSecPerTrack = 0x18, + bsHeads = 0x1a, + bsHiddenSecs = 0x1c, + bsHugeSectors = 0x20, + bsDriveNumber = 0x24, + bsReserved1 = 0x25, + bsBootSignature = 0x26, + bsVolumeID = 0x27, + bsVolumeLabel = 0x2b, + bsFileSysType = 0x36, + bsCode = 0x3e, + bsSignature = 0x1fe +}; + +/* + * Access functions for littleendian numbers, possibly misaligned. + */ +static u_int16_t get_16(unsigned char *p) +{ + return (u_int16_t)p[0] + ((u_int16_t)p[1] << 8); +} + +static u_int32_t get_32(unsigned char *p) +{ + return (u_int32_t)p[0] + ((u_int32_t)p[1] << 8) + + ((u_int32_t)p[2] << 16) + ((u_int32_t)p[3] << 24); +} + +#if 0 /* Not needed */ +static void set_16(unsigned char *p, u_int16_t v) +{ + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); +} + +static void set_32(unsigned char *p, u_int32_t v) +{ + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); + p[2] = ((v >> 16) & 0xff); + p[3] = ((v >> 24) & 0xff); +} +#endif + +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) +{ + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pread(fd, buf, 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 { + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pwrite(fd, buf, 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 { + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[512]; + unsigned char *dp; + const unsigned char *cdp; + int dev_fd, fd; + struct stat st; + int nb, left, veryold; + unsigned int sectors, clusters; + int err = 0; + pid_t f, w; + int status; + char *mntpath = NULL, mntname[64], devfdname[64]; + char *ldlinux_name, **argp, *opt; + int my_umask; + int force = 0; /* -f (force) option */ + off_t offset = 0; /* -o (offset) option */ + + ruid = getuid(); + euid = geteuid(); + mypid = getpid(); + + if ( !euid ) + setreuid(-1, ruid); /* Run as regular user until we need it */ + + 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 = strtoul(*++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); + } + + if ( !force && offset != 0 && !S_ISREG(st.st_mode) ) { + fprintf(stderr, "%s: not a regular file and an offset specified (use -f to override)\n", device); + exit(1); + } + + if ( lseek(dev_fd, offset, SEEK_SET) != offset ) { + if ( !(force && errno == EBADF) ) { + fprintf(stderr, "%s: seek error", device); + exit(1); + } + } + + xpread(dev_fd, sectbuf, 512, offset); + fsync(dev_fd); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + + if ( sectbuf[bsBootSignature] == 0x29 ) { + /* It's DOS, and it has all the new nice fields */ + + veryold = 0; + + sectors = get_16(sectbuf+bsSectors); + sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); + clusters = sectors / sectbuf[bsSecPerClust]; + + if ( !memcmp(sectbuf+bsFileSysType, "FAT12 ", 8) ) { + if ( clusters > 4086 ) { + fprintf(stderr, "%s: ERROR: FAT12 but claims more than 4086 clusters\n", + device); + exit(1); + } + } else if ( !memcmp(sectbuf+bsFileSysType, "FAT16 ", 8) ) { + if ( clusters <= 4086 ) { + fprintf(stderr, "%s: ERROR: FAT16 but claims less than 4086 clusters\n", + device); + exit(1); + } + } else if ( !memcmp(sectbuf+bsFileSysType, "FAT ", 8) ) { + /* OS/2 sets up the filesystem as just `FAT'. */ + } else { + fprintf(stderr, "%s: filesystem type \"%8.8s\" not supported\n", + device, sectbuf+bsFileSysType); + exit(1); + } + } else { + veryold = 1; + + if ( sectbuf[bsSecPerClust] & (sectbuf[bsSecPerClust] - 1) || + sectbuf[bsSecPerClust] == 0 ) { + fprintf(stderr, "%s: This doesn't look like a FAT filesystem\n", + device); + } + + sectors = get_16(sectbuf+bsSectors); + sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); + clusters = sectors / sectbuf[bsSecPerClust]; + } + + if ( get_16(sectbuf+bsBytesPerSec) != 512 ) { + fprintf(stderr, "%s: Sector sizes other than 512 not supported\n", + device); + exit(1); + } + if ( sectbuf[bsSecPerClust] > 32 ) { + fprintf(stderr, "%s: Cluster sizes larger than 16K not supported\n", + device); + } + + /* + * Now mount the device. + */ + if ( euid ) { + fprintf(stderr, "%s: This program needs root privilege\n", program); + exit(1); + } else { + int i = 0; + struct stat dst; + int rv; + + /* We're root or at least setuid. + Make a temp dir and pass all the gunky options to mount. */ + + if ( chdir("/tmp") ) { + perror(program); + exit(1); + } + +#define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX) + + if ( stat(".", &dst) || !S_ISDIR(dst.st_mode) || + (dst.st_mode & TMP_MODE) != TMP_MODE ) { + fprintf(stderr, "%s: possibly unsafe /tmp permissions\n", program); + exit(1); + } + + for ( i = 0 ; ; i++ ) { + snprintf(mntname, sizeof mntname, "syslinux.mnt.%lu.%d", + (unsigned long)mypid, i); + + if ( lstat(mntname, &dst) != -1 || errno != ENOENT ) + continue; + + seteuid(0); /* *** BECOME ROOT *** */ + rv = mkdir(mntname, 0000); /* AS ROOT */ + seteuid(ruid); + + if ( rv == -1 ) { + if ( errno == EEXIST || errno == EINTR ) + continue; + perror(program); + exit(1); + } + + if ( lstat(mntname, &dst) || dst.st_mode != (S_IFDIR|0000) || + dst.st_uid != 0 ) { + fprintf(stderr, "%s: someone is trying to symlink race us!\n", program); + exit(1); + } + break; /* OK, got something... */ + } + + mntpath = mntname; + + snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d", + (unsigned long)mypid, dev_fd); + + f = fork(); + if ( f < 0 ) { + perror(program); + rmdir(mntpath); + exit(1); + } else if ( f == 0 ) { + char mnt_opts[128]; + seteuid(0); /* ***BECOME ROOT*** */ + setuid(0); + if ( S_ISREG(st.st_mode) ) { + snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,loop,offset=%llu,umask=077,uid=%lu", + (unsigned long long)offset, (unsigned long)ruid); + } else { + snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,umask=077,uid=%lu", + (unsigned long)ruid); + } + execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-o", mnt_opts,\ + devfdname, mntpath, NULL); + _exit(255); /* execl failed */ + } + } + + w = waitpid(f, &status, 0); + if ( w != f || status ) { + if ( !euid ) + rmdir(mntpath); + exit(1); /* Mount failed */ + } + + ldlinux_name = alloca(strlen(mntpath)+13); + if ( !ldlinux_name ) { + perror(program); + err = 1; + goto umount; + } + sprintf(ldlinux_name, "%s/ldlinux.sys", mntpath); + + unlink(ldlinux_name); + fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444); + if ( fd < 0 ) { + perror(device); + err = 1; + goto umount; + } + + cdp = syslinux_ldlinux; + left = syslinux_ldlinux_len; + while ( left ) { + nb = write(fd, cdp, left); + if ( nb == -1 && errno == EINTR ) + continue; + else if ( nb <= 0 ) { + perror(device); + err = 1; + goto umount; + } + + dp += nb; + left -= nb; + } + + /* + * I don't understand why I need this. Does the DOS filesystems + * not honour the mode passed to open()? + */ + my_umask = umask(0777); + umask(my_umask); + fchmod(fd, 0444 & ~my_umask); + + close(fd); + +umount: + f = fork(); + if ( f < 0 ) { + perror("fork"); + exit(1); + } else if ( f == 0 ) { + seteuid(0); /* ***BECOME ROOT*** */ + setuid(0); + execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL); + } + + w = waitpid(f, &status, 0); + if ( w != f || status ) { + exit(1); + } + + sync(); + + if ( !euid ) { + seteuid(0); /* *** BECOME ROOT *** */ + rmdir(mntpath); /* AS ROOT */ + seteuid(ruid); + } + + if ( err ) + exit(err); + + /* + * 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; +} |