unify common parts of extlinux and syslinux installer
authorAlek Du <alek.du@intel.com>
Wed, 19 May 2010 01:39:57 +0000 (09:39 +0800)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 21 May 2010 02:57:46 +0000 (19:57 -0700)
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>
14 files changed:
extlinux/Makefile
extlinux/ext2_fs.h
extlinux/main.c
libinstaller/Makefile
libinstaller/setadv.c
libinstaller/setadv.h [new file with mode: 0644]
libinstaller/syslinux.h
libinstaller/syslxcom.c [new file with mode: 0644]
libinstaller/syslxcom.h [new file with mode: 0644]
libinstaller/syslxmod.c
libinstaller/syslxopt.c [new file with mode: 0644]
libinstaller/syslxopt.h [new file with mode: 0644]
linux/Makefile
linux/syslinux.c

index 23ffd40..ab92c2c 100644 (file)
@@ -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
index 1351d8c..45f3831 100644 (file)
@@ -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 {
index 6bf6872..0669bb9 100644 (file)
@@ -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);
 }
index ef3711d..82c1990 100644 (file)
@@ -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 < $< > $@
index d18ac92..682b883 100644 (file)
  * 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 (file)
index 0000000..32bdfec
--- /dev/null
@@ -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
index 8ed1edb..8d0212c 100644 (file)
@@ -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 (file)
index 0000000..825419b
--- /dev/null
@@ -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 (file)
index 0000000..ba4f1d0
--- /dev/null
@@ -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
index be06b9a..9ab139c 100644 (file)
@@ -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 (file)
index 0000000..7718de3
--- /dev/null
@@ -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 (file)
index 0000000..d925fa3
--- /dev/null
@@ -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
index 8872c0f..9bf56d1 100644 (file)
@@ -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
index 0ac9de9..3032edc 100644 (file)
 # 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();