[syslinux:pathbased] unify common parts of extlinux and syslinux installer

syslinux-bot for Alek Du alek.du at intel.com
Thu May 20 20:39:05 PDT 2010


Commit-ID:  e4fc443f9b70f188963ff33e0a16ccb72a553540
Gitweb:     http://syslinux.zytor.com/commit/e4fc443f9b70f188963ff33e0a16ccb72a553540
Author:     Alek Du <alek.du at intel.com>
AuthorDate: Wed, 19 May 2010 09:39:57 +0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Thu, 20 May 2010 19:57:46 -0700

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 at intel.com>
Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 extlinux/Makefile       |    4 +-
 extlinux/ext2_fs.h      |    8 -
 extlinux/main.c         |  496 ++++-------------------------------------------
 libinstaller/Makefile   |    2 +-
 libinstaller/setadv.c   |  136 +++++++++++++
 libinstaller/setadv.h   |   16 ++
 libinstaller/syslinux.h |   10 +-
 libinstaller/syslxcom.c |  218 +++++++++++++++++++++
 libinstaller/syslxcom.h |   20 ++
 libinstaller/syslxmod.c |    3 +-
 libinstaller/syslxopt.c |  171 ++++++++++++++++
 libinstaller/syslxopt.h |   30 +++
 linux/Makefile          |    3 +
 linux/syslinux.c        |  295 +++++++++++------------------
 14 files changed, 754 insertions(+), 658 deletions(-)

diff --git a/extlinux/Makefile b/extlinux/Makefile
index 23ffd40..ab92c2c 100644
--- a/extlinux/Makefile
+++ b/extlinux/Makefile
@@ -11,7 +11,7 @@
 ## -----------------------------------------------------------------------
 
 ##
-## Linux ext2/ext3 installer
+## Linux vfat, ext2/ext3/ext4 and btrfs installer
 ##
 
 topdir = ..
@@ -24,6 +24,8 @@ CFLAGS	 = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \
 LDFLAGS	 = # -s
 
 SRCS     = main.c \
+	   ../libinstaller/syslxopt.c \
+	   ../libinstaller/syslxcom.c \
 	   ../libinstaller/setadv.c \
 	   ../libinstaller/extlinux_bss_bin.c \
 	   ../libinstaller/extlinux_sys_bin.c
diff --git a/extlinux/ext2_fs.h b/extlinux/ext2_fs.h
index 1351d8c..45f3831 100644
--- a/extlinux/ext2_fs.h
+++ b/extlinux/ext2_fs.h
@@ -170,14 +170,6 @@ struct ext2_group_desc {
 #define EXT2_FL_USER_MODIFIABLE		0x000000FF	/* User modifiable flags */
 
 /*
- * 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)
-
-/*
  * Structure of an inode on the disk
  */
 struct ext2_inode {
diff --git a/extlinux/main.c b/extlinux/main.c
index 6bf6872..0669bb9 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -57,6 +57,9 @@ typedef uint64_t u64;
 #include "fat.h"
 #include "../version.h"
 #include "syslxint.h"
+#include "syslxcom.h" /* common functions shared with extlinux and syslinux */
+#include "setadv.h"
+#include "syslxopt.h" /* unified options */
 
 #ifdef DEBUG
 # define dprintf printf
@@ -64,79 +67,6 @@ typedef uint64_t u64;
 # define dprintf(...) ((void)0)
 #endif
 
-/* Global option handling */
-/* Global fs_type for handling fat, ext2/3/4 and btrfs */
-static enum filesystem {
-    NONE,
-    EXT2,
-    BTRFS,
-    VFAT,
-} fs_type;
-
-const char *program;
-
-/* These are the options we can set and their values */
-struct my_options {
-    unsigned int sectors;
-    unsigned int heads;
-    int raid_mode;
-    int stupid_mode;
-    int reset_adv;
-    const char *set_once;
-} opt = {
-.sectors = 0,.heads = 0,.raid_mode = 0,.stupid_mode = 0,.reset_adv =
-	0,.set_once = NULL,};
-
-static void __attribute__ ((noreturn)) usage(int rv)
-{
-    fprintf(stderr,
-	    "Usage: %s [options] directory\n"
-	    "  --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",
-	    program);
-
-    exit(rv);
-}
-
-enum long_only_opt {
-    OPT_NONE,
-    OPT_RESET_ADV,
-};
-
-static const struct option long_options[] = {
-    {"install", 0, NULL, 'i'},
-    {"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}
-};
-
-static const char short_options[] = "iUuzS:H:rvho:O";
-
 #if defined(__linux__) && !defined(BLKGETSIZE64)
 /* This takes a u64, but the size field says size_t.  Someone screwed big. */
 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
@@ -168,169 +98,7 @@ extern unsigned int extlinux_image_len;
 #define boot_image	extlinux_image
 #define boot_image_len  extlinux_image_len
 
-/*
- * Common abort function
- */
-void __attribute__ ((noreturn)) 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
- */
-static 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);
-    }
-}
-
-static 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;
-}
+#define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
 
 /*
  * Get the size of a block device
@@ -600,146 +368,6 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
 }
 
 /*
- * 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, int devfd)
-{
-    char *file;
-    int fd = -1;
-    struct stat st;
-    int err = 0;
-
-    if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
-	if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
-		BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
-		perror("writing adv");
-		return 1;
-	}
-	return 0;
-    }
-    asprintf(&file, "%s%sextlinux.sys",
-	     path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
-
-    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, int devfd)
-{
-    unsigned char advtmp[2 * ADV_SIZE];
-    char *file;
-    int fd = -1;
-    struct stat st, xst;
-    int err = 0;
-
-    if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
-	if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
-		BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
-		perror("writing adv");
-		return 1;
-	}
-	return 0;
-    }
-    asprintf(&file, "%s%sextlinux.sys",
-	     path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
-
-    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;
-}
-
-/*
  * Make any user-specified ADV modifications
  */
 int modify_adv(void)
@@ -1137,6 +765,32 @@ static int open_device(const char *path, struct stat *st, const char **_devname)
     return devfd;
 }
 
+static int ext_read_adv(const char *path, const char *cfg, int devfd)
+{
+    if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
+	if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
+		BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
+		perror("btrfs writing adv");
+		return 1;
+	}
+	return 0;
+    }
+    return read_adv(path, cfg);
+}
+
+static int ext_write_adv(const char *path, const char *cfg, int devfd)
+{
+    if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
+	if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
+		BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
+		perror("writing adv");
+		return 1;
+	}
+	return 0;
+    }
+    return write_adv(path, cfg);
+}
+
 int install_loader(const char *path, int update_only)
 {
     struct stat st, fst;
@@ -1157,7 +811,7 @@ int install_loader(const char *path, int update_only)
     /* Read a pre-existing ADV, if already installed */
     if (opt.reset_adv)
 	syslinux_reset_adv(syslinux_adv);
-    else if (read_adv(path, devfd) < 0) {
+    else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
 	close(devfd);
 	return 1;
     }
@@ -1199,7 +853,7 @@ int modify_existing_adv(const char *path)
 
     if (opt.reset_adv)
 	syslinux_reset_adv(syslinux_adv);
-    else if (read_adv(path, devfd) < 0) {
+    else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
 	close(devfd);
 	return 1;
     }
@@ -1207,7 +861,7 @@ int modify_existing_adv(const char *path)
 	close(devfd);
 	return 1;
     }
-    if (write_adv(path, devfd) < 0) {
+    if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
 	close(devfd);
 	return 1;
     }
@@ -1217,83 +871,17 @@ int modify_existing_adv(const char *path)
 
 int main(int argc, char *argv[])
 {
-    int o;
-    const char *directory;
-    int update_only = -1;
-
-    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':
-	    update_only = 0;
-	    break;
-	case 'u':
-	case 'U':
-	    update_only = 1;
-	    break;
-	case 'h':
-	    usage(0);
-	    break;
-	case 'o':
-	    opt.set_once = optarg;
-	    break;
-	case 'O':
-	    opt.set_once = "";
-	    break;
-	case OPT_RESET_ADV:
-	    opt.reset_adv = 1;
-	    break;
-	case 'v':
-	    fputs("extlinux " VERSION_STR
-		  "  Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
-	    exit(0);
-	default:
-	    usage(EX_USAGE);
-	}
-    }
-
-    directory = argv[optind];
+    parse_options(argc, argv, 0);
 
-    if (!directory)
-	usage(EX_USAGE);
+    if (!opt.directory)
+	usage(EX_USAGE, 0);
 
-    if (update_only == -1) {
-	if (opt.reset_adv || opt.set_once) {
-	    return modify_existing_adv(directory);
-	} else {
-	    usage(EX_USAGE);
-	}
+    if (opt.update_only == -1) {
+	if (opt.reset_adv || opt.set_once)
+	    return modify_existing_adv(opt.directory);
+	else
+	    usage(EX_USAGE, 0);
     }
 
-    return install_loader(directory, update_only);
+    return install_loader(opt.directory, opt.update_only);
 }
diff --git a/libinstaller/Makefile b/libinstaller/Makefile
index ef3711d..82c1990 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 d18ac92..682b883 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 0000000..32bdfec
--- /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 8ed1edb..8d0212c 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 0000000..825419b
--- /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 0000000..ba4f1d0
--- /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 be06b9a..9ab139c 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 0000000..7718de3
--- /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 0000000..d925fa3
--- /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
diff --git a/linux/Makefile b/linux/Makefile
index 8872c0f..9bf56d1 100644
--- a/linux/Makefile
+++ b/linux/Makefile
@@ -23,6 +23,9 @@ CFLAGS	 = $(GCCWARN) -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES)
 LDFLAGS	 = -s
 
 SRCS     = syslinux.c \
+	   ../libinstaller/syslxopt.c \
+	   ../libinstaller/syslxcom.c \
+	   ../libinstaller/setadv.c \
            ../libinstaller/syslxmod.c \
 	   ../libinstaller/bootsect_bin.c \
 	   ../libinstaller/ldlinux_bin.c
diff --git a/linux/syslinux.c b/linux/syslinux.c
index 0ac9de9..3032edc 100644
--- a/linux/syslinux.c
+++ b/linux/syslinux.c
@@ -72,22 +72,27 @@
 # include <linux/loop.h>
 #endif
 
-const char *program;		/* Name of program */
-const char *device;		/* Device to install to */
+#include <getopt.h>
+#include <sysexits.h>
+#include "syslxcom.h"
+#include "setadv.h"
+#include "syslxopt.h" /* unified options */
+
+extern const char *program;	/* Name of program */
+
 pid_t mypid;
 char *mntpath = NULL;		/* Path on which to mount */
-off_t filesystem_offset = 0;	/* Filesystem offset */
+
+/*
+ * Image file
+ */
+#define boot_image	syslinux_ldlinux
+#define boot_image_len  syslinux_ldlinux_len
+
 #if DO_DIRECT_MOUNT
 int loop_fd = -1;		/* Loop device */
 #endif
 
-void __attribute__ ((noreturn)) usage(void)
-{
-    fprintf(stderr, "Usage: %s [-sfr][-d directory][-o offset] device\n",
-	    program);
-    exit(1);
-}
-
 void __attribute__ ((noreturn)) die(const char *msg)
 {
     fprintf(stderr, "%s: %s\n", program, msg);
@@ -107,98 +112,6 @@ void __attribute__ ((noreturn)) die(const char *msg)
 }
 
 /*
- * 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;
-}
-
-/*
- * Create a block map for ldlinux.sys
- */
-int make_block_map(uint32_t * sectors, int len, int dev_fd, int fd)
-{
-    int nsectors = 0;
-    int blocksize, nblock, block;
-    int i;
-
-    (void)dev_fd;
-
-    if (ioctl(fd, FIGETBSZ, &blocksize) < 0)
-	die("ioctl FIGETBSZ failed");
-
-    blocksize >>= SECTOR_SHIFT;	/* sectors/block */
-
-    nblock = 0;
-    while (len > 0) {
-	block = nblock++;
-	if (ioctl(fd, FIBMAP, &block) < 0)
-	    die("ioctl FIBMAP failed");
-
-	for (i = 0; i < blocksize; i++) {
-	    if (len <= 0)
-		break;
-
-	    *sectors++ = (block * blocksize) + i;
-	    nsectors++;
-	    len -= (1 << SECTOR_SHIFT);
-	}
-    }
-
-    return nsectors;
-}
-
-/*
  * Mount routine
  */
 int do_mount(int dev_fd, int *cookie, const char *mntpath, const char *fstype)
@@ -234,7 +147,7 @@ int do_mount(int dev_fd, int *cookie, const char *mntpath, const char *fstype)
 		}
 
 		if (ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo) ||
-		    (loopinfo.lo_offset = filesystem_offset,
+		    (loopinfo.lo_offset = opt.offset,
 		     ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo)))
 		    die("cannot set up loopback device");
 	    }
@@ -265,7 +178,7 @@ int do_mount(int dev_fd, int *cookie, const char *mntpath, const char *fstype)
 	    if (!S_ISBLK(st.st_mode)) {
 		snprintf(mnt_opts, sizeof mnt_opts,
 			 "rw,nodev,noexec,loop,offset=%llu,umask=077,quiet",
-			 (unsigned long long)filesystem_offset);
+			 (unsigned long long)opt.offset);
 	    } else {
 		snprintf(mnt_opts, sizeof mnt_opts,
 			 "rw,nodev,noexec,umask=077,quiet");
@@ -317,79 +230,76 @@ void do_umount(const char *mntpath, int cookie)
 #endif
 }
 
+/*
+ * Make any user-specified ADV modifications
+ */
+int modify_adv(void)
+{
+    int rv = 0;
+
+    if (opt.set_once) {
+	if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
+	    fprintf(stderr, "%s: not enough space for boot-once command\n",
+		    program);
+	    rv = -1;
+	}
+    }
+
+    return rv;
+}
+
+/*
+ * Modify the ADV of an existing installation
+ */
+int modify_existing_adv(const char *path)
+{
+    if (opt.reset_adv)
+	syslinux_reset_adv(syslinux_adv);
+    else if (read_adv(path, "ldlinux.sys") < 0)
+	return 1;
+
+    if (modify_adv() < 0)
+	return 1;
+
+    if (write_adv(path, "ldlinux.sys") < 0)
+	return 1;
+
+    return 0;
+}
+
 int main(int argc, char *argv[])
 {
     static unsigned char sectbuf[SECTOR_SIZE];
-    unsigned char *dp;
-    const unsigned char *cdp;
     int dev_fd, fd;
     struct stat st;
-    int nb, left;
     int err = 0;
     char mntname[128];
-    char *ldlinux_name, **argp, *opt;
-    const char *subdir = NULL;
+    char *ldlinux_name;
+    char *ldlinux_path;
+    const char *subdir;
     uint32_t *sectors = NULL;
-    int ldlinux_sectors;
-    int nsectors = 0;
+    int ldlinux_sectors = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
     const char *errmsg;
     int mnt_cookie;
     int patch_sectors;
     int i;
 
-    int force = 0;		/* -f (force) option */
-    int stupid = 0;		/* -s (stupid) option */
-    int raid_mode = 0;		/* -r (RAID) option */
-
-    (void)argc;			/* Unused */
-
-    program = argv[0];
     mypid = getpid();
-
-    device = NULL;
-
     umask(077);
+    parse_options(argc, argv, 1);
 
-    for (argp = argv + 1; *argp; argp++) {
-	if (**argp == '-') {
-	    opt = *argp + 1;
-	    if (!*opt)
-		usage();
-
-	    while (*opt) {
-		if (*opt == 's') {
-		    stupid = 1;
-		} else if (*opt == 'r') {
-		    raid_mode = 1;
-		} else if (*opt == 'f') {
-		    force = 1;	/* Force install */
-		} else if (*opt == 'd' && argp[1]) {
-		    subdir = *++argp;
-		} else if (*opt == 'o' && argp[1]) {
-		    /* Byte offset */
-		    filesystem_offset = (off_t) strtoull(*++argp, NULL, 0);
-		} else {
-		    usage();
-		}
-		opt++;
-	    }
-	} else {
-	    if (device)
-		usage();
-	    device = *argp;
-	}
-    }
+    subdir = opt.directory;
 
-    if (!device)
-	usage();
+    if (!opt.device)
+	usage(EX_USAGE, 1);
 
     /*
      * First make sure we can open the device at all, and that we have
      * read/write permission.
      */
-    dev_fd = open(device, O_RDWR);
+    dev_fd = open(opt.device, O_RDWR);
     if (dev_fd < 0 || fstat(dev_fd, &st) < 0) {
-	perror(device);
+	perror(opt.device);
 	exit(1);
     }
 
@@ -397,18 +307,19 @@ int main(int argc, char *argv[])
 	die("not a device or regular file");
     }
 
-    if (filesystem_offset && S_ISBLK(st.st_mode)) {
+    if (opt.offset && S_ISBLK(st.st_mode)) {
 	die("can't combine an offset with a block device");
     }
 
-    xpread(dev_fd, sectbuf, SECTOR_SIZE, filesystem_offset);
+    fs_type = VFAT;
+    xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset);
     fsync(dev_fd);
 
     /*
      * Check to see that what we got was indeed an MS-DOS boot sector/superblock
      */
     if ((errmsg = syslinux_check_bootsect(sectbuf))) {
-	fprintf(stderr, "%s: %s\n", device, errmsg);
+	fprintf(stderr, "%s: %s\n", opt.device, errmsg);
 	exit(1);
     }
 
@@ -468,15 +379,37 @@ int main(int argc, char *argv[])
 	die("mount failed");
     }
 
-    ldlinux_name = alloca(strlen(mntpath) + 14 +
-			  (subdir ? strlen(subdir) + 2 : 0));
+    ldlinux_path = alloca(strlen(mntpath) +  (subdir ? strlen(subdir) + 2 : 0));
+    sprintf(ldlinux_path, "%s%s%s",
+	    mntpath, subdir ? "//" : "", subdir ? subdir : "");
+
+    ldlinux_name = alloca(strlen(ldlinux_path) + 14);
     if (!ldlinux_name) {
 	perror(program);
 	err = 1;
 	goto umount;
     }
-    sprintf(ldlinux_name, "%s%s%s//ldlinux.sys",
-	    mntpath, subdir ? "//" : "", subdir ? subdir : "");
+    sprintf(ldlinux_name, "%s//ldlinux.sys", ldlinux_path);
+
+    /* update ADV only ? */
+    if (opt.update_only == -1) {
+	if (opt.reset_adv || opt.set_once) {
+	    modify_existing_adv(ldlinux_path);
+	    do_umount(mntpath, mnt_cookie);
+	    sync();
+	    rmdir(mntpath);
+	    exit(0);
+	} else
+	    usage(EX_USAGE, 0);
+    }
+
+    /* Read a pre-existing ADV, if already installed */
+    if (opt.reset_adv)
+	syslinux_reset_adv(syslinux_adv);
+    else if (read_adv(ldlinux_path, "ldlinux.sys") < 0)
+	syslinux_reset_adv(syslinux_adv);
+    if (modify_adv() < 0)
+	exit(1);
 
     if ((fd = open(ldlinux_name, O_RDONLY)) >= 0) {
 	uint32_t zero_attr = 0;
@@ -487,25 +420,17 @@ int main(int argc, char *argv[])
     unlink(ldlinux_name);
     fd = open(ldlinux_name, O_WRONLY | O_CREAT | O_TRUNC, 0444);
     if (fd < 0) {
-	perror(device);
+	perror(opt.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;
+    /* Write it the first time */
+    if (xpwrite(fd, boot_image, boot_image_len, 0) != (int)boot_image_len ||
+	xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
+		boot_image_len) != 2 * ADV_SIZE) {
+	fprintf(stderr, "%s: write failure on %s\n", program, ldlinux_name);
+	exit(1);
     }
 
     fsync(fd);
@@ -520,10 +445,12 @@ int main(int argc, char *argv[])
     /*
      * Create a block map.
      */
-    ldlinux_sectors = (syslinux_ldlinux_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
+    ldlinux_sectors += 2; /* 2 ADV sectors */
     sectors = calloc(ldlinux_sectors, sizeof *sectors);
-    nsectors = make_block_map(sectors, syslinux_ldlinux_len, dev_fd, fd);
-
+    if (sectmap(fd, sectors, ldlinux_sectors)) {
+	perror("bmap");
+	exit(1);
+    }
     close(fd);
     sync();
 
@@ -538,15 +465,15 @@ umount:
     /*
      * Patch ldlinux.sys and the boot sector
      */
-    i = syslinux_patch(sectors, nsectors, stupid, raid_mode);
+    i = syslinux_patch(sectors, ldlinux_sectors, opt.stupid_mode, opt.raid_mode);
     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,
-		filesystem_offset + ((off_t) sectors[i] << SECTOR_SHIFT));
+	xpwrite(dev_fd, boot_image + i * SECTOR_SIZE, SECTOR_SIZE,
+		opt.offset + ((off_t) sectors[i] << SECTOR_SHIFT));
     }
 
     /*
@@ -554,13 +481,13 @@ umount:
      */
 
     /* Read the superblock again since it might have changed while mounted */
-    xpread(dev_fd, sectbuf, SECTOR_SIZE, filesystem_offset);
+    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, filesystem_offset);
+    xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset);
 
     close(dev_fd);
     sync();



More information about the Syslinux-commits mailing list