diff options
author | Alek Du <alek.du@intel.com> | 2010-05-19 09:39:57 +0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-05-20 19:57:46 -0700 |
commit | e4fc443f9b70f188963ff33e0a16ccb72a553540 (patch) | |
tree | 5abf92a66a5a741e61375478ae41bb3383dcec04 /libinstaller | |
parent | 38eb0724824139a81342e3f676910187bc57ba9d (diff) | |
download | syslinux-e4fc443f9b70f188963ff33e0a16ccb72a553540.tar.gz syslinux-e4fc443f9b70f188963ff33e0a16ccb72a553540.tar.xz syslinux-e4fc443f9b70f188963ff33e0a16ccb72a553540.zip |
unify common parts of extlinux and syslinux installer
Thus we can share same command line options and reduce a lot of dup
code...
Seems like a big patch, but the changes are quite safe, no much logical
change.
Signed-off-by: Alek Du <alek.du@intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'libinstaller')
-rw-r--r-- | libinstaller/Makefile | 2 | ||||
-rw-r--r-- | libinstaller/setadv.c | 136 | ||||
-rw-r--r-- | libinstaller/setadv.h | 16 | ||||
-rw-r--r-- | libinstaller/syslinux.h | 10 | ||||
-rw-r--r-- | libinstaller/syslxcom.c | 218 | ||||
-rw-r--r-- | libinstaller/syslxcom.h | 20 | ||||
-rw-r--r-- | libinstaller/syslxmod.c | 3 | ||||
-rw-r--r-- | libinstaller/syslxopt.c | 171 | ||||
-rw-r--r-- | libinstaller/syslxopt.h | 30 |
9 files changed, 595 insertions, 11 deletions
diff --git a/libinstaller/Makefile b/libinstaller/Makefile index ef3711dd..82c1990e 100644 --- a/libinstaller/Makefile +++ b/libinstaller/Makefile @@ -11,7 +11,7 @@ bootsect_bin.c: ../core/ldlinux.bss bin2c.pl $(PERL) bin2c.pl syslinux_bootsect < $< > $@ ldlinux_bin.c: ../core/ldlinux.sys bin2c.pl - $(PERL) bin2c.pl syslinux_ldlinux < $< > $@ + $(PERL) bin2c.pl syslinux_ldlinux 512 < $< > $@ extlinux_bss_bin.c: ../core/extlinux.bss bin2c.pl $(PERL) bin2c.pl extlinux_bootsect < $< > $@ diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c index d18ac927..682b883e 100644 --- a/libinstaller/setadv.c +++ b/libinstaller/setadv.c @@ -19,10 +19,22 @@ * Return 0 on success, -1 on error, and set errno. * */ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> #include <string.h> +#include <getopt.h> +#include <unistd.h> #include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> #include "syslxint.h" +#include "syslxcom.h" unsigned char syslinux_adv[2 * ADV_SIZE]; @@ -158,3 +170,127 @@ int syslinux_validate_adv(unsigned char *advbuf) return -1; } } + +/* + * Read the ADV from an existing instance, or initialize if invalid. + * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid + * ADV was found. + */ +int read_adv(const char *path, const char *cfg) +{ + char *file; + int fd = -1; + struct stat st; + int err = 0; + + err = asprintf(&file, "%s%s%s", + path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); + + if (!file) { + perror(program); + return -1; + } + + fd = open(file, O_RDONLY); + if (fd < 0) { + if (errno != ENOENT) { + err = -1; + } else { + syslinux_reset_adv(syslinux_adv); + } + } else if (fstat(fd, &st)) { + err = -1; + } else if (st.st_size < 2 * ADV_SIZE) { + /* Too small to be useful */ + syslinux_reset_adv(syslinux_adv); + err = 0; /* Nothing to read... */ + } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE, + st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { + err = -1; + } else { + /* We got it... maybe? */ + err = syslinux_validate_adv(syslinux_adv) ? 1 : 0; + } + + if (err < 0) + perror(file); + + if (fd >= 0) + close(fd); + if (file) + free(file); + + return err; +} + +/* + * Update the ADV in an existing installation. + */ +int write_adv(const char *path, const char *cfg) +{ + unsigned char advtmp[2 * ADV_SIZE]; + char *file; + int fd = -1; + struct stat st, xst; + int err = 0; + + err = asprintf(&file, "%s%s%s", + path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); + + if (!file) { + perror(program); + return -1; + } + + fd = open(file, O_RDONLY); + if (fd < 0) { + err = -1; + } else if (fstat(fd, &st)) { + err = -1; + } else if (st.st_size < 2 * ADV_SIZE) { + /* Too small to be useful */ + err = -2; + } else if (xpread(fd, advtmp, 2 * ADV_SIZE, + st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { + err = -1; + } else { + /* We got it... maybe? */ + err = syslinux_validate_adv(advtmp) ? -2 : 0; + if (!err) { + /* Got a good one, write our own ADV here */ + clear_attributes(fd); + + /* Need to re-open read-write */ + close(fd); + fd = open(file, O_RDWR | O_SYNC); + if (fd < 0) { + err = -1; + } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino || + xst.st_dev != st.st_dev || xst.st_size != st.st_size) { + fprintf(stderr, "%s: race condition on write\n", file); + err = -2; + } + /* Write our own version ... */ + if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, + st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { + err = -1; + } + + sync(); + set_attributes(fd); + } + } + + if (err == -2) + fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n", + file); + else if (err == -1) + perror(file); + + if (fd >= 0) + close(fd); + if (file) + free(file); + + return err; +} diff --git a/libinstaller/setadv.h b/libinstaller/setadv.h new file mode 100644 index 00000000..32bdfec1 --- /dev/null +++ b/libinstaller/setadv.h @@ -0,0 +1,16 @@ +#ifndef _H_SET_ADV_ +#define _H_SET_ADV_ + +/* ADV information */ +#define ADV_SIZE 512 /* Total size */ +#define ADV_LEN (ADV_SIZE-3*4) /* Usable data size */ + +extern unsigned char syslinux_adv[2 * ADV_SIZE]; + +int syslinux_setadv(int tag, size_t size, const void *data); +void syslinux_reset_adv(unsigned char *advbuf); +int syslinux_validate_adv(unsigned char *advbuf); +int read_adv(const char *path, const char *cfg); +int write_adv(const char *path, const char *cfg); + +#endif diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h index 8ed1edbe..8d0212c8 100644 --- a/libinstaller/syslinux.h +++ b/libinstaller/syslinux.h @@ -15,6 +15,7 @@ #include <inttypes.h> #include "advconst.h" +#include "setadv.h" /* The standard boot sector and ldlinux image */ extern unsigned char syslinux_bootsect[]; @@ -43,13 +44,4 @@ const char *syslinux_check_bootsect(const void *bs); int syslinux_patch(const uint32_t * sectors, int nsectors, int stupid, int raid_mode); -/* ADV information */ -#define ADV_SIZE 512 /* Total size */ -#define ADV_LEN (ADV_SIZE-3*4) /* Usable data size */ -extern unsigned char syslinux_adv[2 * ADV_SIZE]; - -int syslinux_setadv(int tag, size_t size, const void *data); -void syslinux_reset_adv(unsigned char *advbuf); -int syslinux_validate_adv(unsigned char *advbuf); - #endif diff --git a/libinstaller/syslxcom.c b/libinstaller/syslxcom.c new file mode 100644 index 00000000..825419b4 --- /dev/null +++ b/libinstaller/syslxcom.c @@ -0,0 +1,218 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010 Intel Corp. - 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. + * + * ----------------------------------------------------------------------- */ + +/* + * syslxcom.c + * + * common functions for extlinux & syslinux installer + * + */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/vfs.h> +#include <linux/fs.h> /* FIGETBSZ, FIBMAP */ +#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */ +#include "syslxcom.h" + +const char *program; + +int fs_type; + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +#define SECTOR_SHIFT 9 +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ + +/* + * ioctl commands + */ +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) + +static void die(const char *msg) +{ + fputs(msg, stderr); + 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; +} + +/* + * Set and clear file attributes + */ +void clear_attributes(int fd) +{ + struct stat st; + + if (!fstat(fd, &st)) { + switch (fs_type) { + case EXT2: + { + int flags; + + if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { + flags &= ~EXT2_IMMUTABLE_FL; + ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + } + break; + } + case VFAT: + { + uint32_t attr = 0x00; /* Clear all attributes */ + ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); + break; + } + default: + break; + } + fchmod(fd, st.st_mode | S_IWUSR); + } +} + +void set_attributes(int fd) +{ + struct stat st; + + if (!fstat(fd, &st)) { + fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)); + switch (fs_type) { + case EXT2: + { + int flags; + + if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { + flags |= EXT2_IMMUTABLE_FL; + ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + } + break; + } + case VFAT: + { + uint32_t attr = 0x07; /* Hidden+System+Readonly */ + ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); + break; + } + default: + break; + } + } +} + +/* + * Produce file map + */ +int sectmap(int fd, uint32_t * sectors, int nsectors) +{ + unsigned int blksize, blk, nblk; + unsigned int i; + + /* Get block size */ + if (ioctl(fd, FIGETBSZ, &blksize)) + return -1; + + /* Number of sectors per block */ + blksize >>= SECTOR_SHIFT; + + nblk = 0; + while (nsectors) { + + blk = nblk++; + dprintf("querying block %u\n", blk); + if (ioctl(fd, FIBMAP, &blk)) + return -1; + + blk *= blksize; + for (i = 0; i < blksize; i++) { + if (!nsectors) + return 0; + + dprintf("Sector: %10u\n", blk); + *sectors++ = blk++; + nsectors--; + } + } + + return 0; +} diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h new file mode 100644 index 00000000..ba4f1d00 --- /dev/null +++ b/libinstaller/syslxcom.h @@ -0,0 +1,20 @@ +#ifndef _H_SYSLXCOM_ +#define _H_SYSLXCOM_ + +/* Global fs_type for handling fat, ext2/3/4 and btrfs */ +enum filesystem { + NONE, + EXT2, + BTRFS, + VFAT, +}; + +extern int fs_type; +extern const char *program; +ssize_t xpread(int fd, void *buf, size_t count, off_t offset); +ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset); +void clear_attributes(int fd); +void set_attributes(int fd); +int sectmap(int fd, uint32_t * sectors, int nsectors); + +#endif diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c index be06b9a2..9ab139c6 100644 --- a/libinstaller/syslxmod.c +++ b/libinstaller/syslxmod.c @@ -261,7 +261,7 @@ int syslinux_patch(const uint32_t * sectors, int nsectors, /* Set up the totals */ dw = syslinux_ldlinux_len >> 2; /* COMPLETE dwords, excluding ADV */ set_16_sl(&patcharea->data_sectors, nsect); /* Not including ADVs */ - set_16_sl(&patcharea->adv_sectors, 0); /* ADVs not supported yet */ + set_16_sl(&patcharea->adv_sectors, 2); /* ADVs need 2 sectors */ set_32_sl(&patcharea->dwords, dw); /* Set the sector pointers */ @@ -269,6 +269,7 @@ int syslinux_patch(const uint32_t * sectors, int nsectors, get_16_sl(&patcharea->secptroffset)); nptrs = get_16_sl(&patcharea->secptrcnt); + nsect += 2; while (--nsect) { /* the first sector is in bs->NextSector */ set_32_sl(wp++, *sectors++); nptrs--; diff --git a/libinstaller/syslxopt.c b/libinstaller/syslxopt.c new file mode 100644 index 00000000..7718de3a --- /dev/null +++ b/libinstaller/syslxopt.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010 Intel Corp. - 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. + * + * ----------------------------------------------------------------------- */ + +/* + * syslxopt.c + * + * parse cmdline for extlinux and syslinux installer + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <getopt.h> +#include <sysexits.h> +#include "../version.h" +#include "syslxcom.h" +#include "syslxopt.h" + +/* These are the options we can set their values */ +struct sys_options opt = { + .sectors = 0, + .heads = 0, + .raid_mode = 0, + .stupid_mode = 0, + .reset_adv = 0, + .set_once = NULL, + .update_only = -1, + .directory = NULL, + .device = NULL, + .offset = 0, +}; + +const struct option long_options[] = { + {"install", 0, NULL, 'i'}, + {"directory", 1, NULL, 'd'}, + {"offset", 1, NULL, 'f'}, + {"update", 0, NULL, 'U'}, + {"zipdrive", 0, NULL, 'z'}, + {"sectors", 1, NULL, 'S'}, + {"stupid", 0, NULL, 's'}, + {"heads", 1, NULL, 'H'}, + {"raid-mode", 0, NULL, 'r'}, + {"version", 0, NULL, 'v'}, + {"help", 0, NULL, 'h'}, + {"once", 1, NULL, 'o'}, + {"clear-once", 0, NULL, 'O'}, + {"reset-adv", 0, NULL, OPT_RESET_ADV}, + {0, 0, 0, 0} +}; + +const char short_options[] = "id:f:UuzS:H:rvho:O"; + +void __attribute__ ((noreturn)) usage(int rv, int mode) +{ + if (mode) /* for unmounted fs installation */ + fprintf(stderr, + "Usage: %s [options] device\n" + " --offset -f Offset of the file system on the device \n" + " --directory -d Directory for installation target\n", + program); + else /* actually extlinux can also use -d to provide directory too */ + fprintf(stderr, + "Usage: %s [options] directory\n", + program); + fprintf(stderr, + " --install -i Install over the current bootsector\n" + " --update -U Update a previous EXTLINUX installation\n" + " --zip -z Force zipdrive geometry (-H 64 -S 32)\n" + " --sectors=# -S Force the number of sectors per track\n" + " --heads=# -H Force number of heads\n" + " --stupid -s Slow, safe and stupid mode\n" + " --raid -r Fall back to the next device on boot failure\n" + " --once=... -o Execute a command once upon boot\n" + " --clear-once -O Clear the boot-once command\n" + " --reset-adv Reset auxilliary data\n" + "\n" + " Note: geometry is determined at boot time for devices which\n" + " are considered hard disks by the BIOS. Unfortunately, this is\n" + " not possible for devices which are considered floppy disks,\n" + " which includes zipdisks and LS-120 superfloppies.\n" + "\n" + " The -z option is useful for USB devices which are considered\n" + " hard disks by some BIOSes and zipdrives by other BIOSes.\n" + ); + + exit(rv); +} + +void parse_options(int argc, char *argv[], int mode) +{ + int o; + + program = argv[0]; + while ((o = getopt_long(argc, argv, short_options, + long_options, NULL)) != EOF) { + switch (o) { + case 'z': + opt.heads = 64; + opt.sectors = 32; + break; + case 'S': + opt.sectors = strtoul(optarg, NULL, 0); + if (opt.sectors < 1 || opt.sectors > 63) { + fprintf(stderr, + "%s: invalid number of sectors: %u (must be 1-63)\n", + program, opt.sectors); + exit(EX_USAGE); + } + break; + case 'H': + opt.heads = strtoul(optarg, NULL, 0); + if (opt.heads < 1 || opt.heads > 256) { + fprintf(stderr, + "%s: invalid number of heads: %u (must be 1-256)\n", + program, opt.heads); + exit(EX_USAGE); + } + break; + case 'r': + opt.raid_mode = 1; + break; + case 's': + opt.stupid_mode = 1; + break; + case 'i': + opt.update_only = 0; + break; + case 'u': + case 'U': + opt.update_only = 1; + break; + case 'h': + usage(0, mode); + break; + case 'o': + opt.set_once = optarg; + break; + case 'f': + opt.offset = strtoul(optarg, NULL, 0); + case 'O': + opt.set_once = ""; + break; + case 'd': + opt.directory = optarg; + case OPT_RESET_ADV: + opt.reset_adv = 1; + break; + case 'v': + fputs(program, stderr); + fputs(" " VERSION_STR + " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr); + exit(0); + default: + usage(EX_USAGE, mode); + } + } + if (mode) + opt.device = argv[optind]; + else if (!opt.directory) + opt.directory = argv[optind]; +} diff --git a/libinstaller/syslxopt.h b/libinstaller/syslxopt.h new file mode 100644 index 00000000..d925fa34 --- /dev/null +++ b/libinstaller/syslxopt.h @@ -0,0 +1,30 @@ +#ifndef _H_SYSLXOPT_ +#define _H_SYSLXOPT_ + +/* These are the options we can set and their values */ +struct sys_options { + unsigned int sectors; + unsigned int heads; + int raid_mode; + int stupid_mode; + int reset_adv; + const char *set_once; + int update_only; + const char *directory; + const char *device; + unsigned int offset; +}; + +enum long_only_opt { + OPT_NONE, + OPT_RESET_ADV, +}; + +void __attribute__ ((noreturn)) usage(int rv, int mode); +void parse_options(int argc, char *argv[], int mode); + +extern struct sys_options opt; +extern const struct option long_options[]; +extern const char short_options[]; + +#endif |