/* ----------------------------------------------------------------------- *
*
* Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
*
* 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
#include <sys/wait.h>
#include <sys/mount.h>
-#include <sys/ioctl.h>
-#include <linux/fs.h> /* FIGETBSZ, FIBMAP */
-#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */
-#ifndef FAT_IOCTL_SET_ATTRIBUTES
-# define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
-#endif
-#undef SECTOR_SIZE
-#undef SECTOR_SHIFT
+#include "linuxioctl.h"
#include <paths.h>
#ifndef _PATH_MOUNT
# 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 */
+
#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);
}
/*
- * 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)
}
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");
}
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");
#endif
}
+/*
+ * 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;
- uint32_t *sectors;
- int ldlinux_sectors;
- int nsectors = 0;
+ char *ldlinux_name;
+ char *ldlinux_path;
+ char *subdir;
+ sector_t *sectors = NULL;
+ 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);
-
- 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;
+ parse_options(argc, argv, MODE_SYSLINUX);
+
+ /* Note: subdir is guaranteed to start and end in / */
+ if (opt.directory && opt.directory[0]) {
+ int len = strlen(opt.directory);
+ int rv = asprintf(&subdir, "%s%s%s",
+ opt.directory[0] == '/' ? "" : "/",
+ opt.directory,
+ opt.directory[len-1] == '/' ? "" : "/");
+ if (rv < 0 || !subdir) {
+ perror(program);
+ exit(1);
}
+ } else {
+ subdir = "/";
}
- if (!device)
- usage();
+ if (!opt.device)
+ usage(EX_USAGE, MODE_SYSLINUX);
/*
* 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);
}
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);
}
Make a temp dir and pass all the gunky options to mount. */
if (chdir(_PATH_TMP)) {
- perror(program);
+ fprintf(stderr, "%s: Cannot access the %s directory.\n",
+ program, _PATH_TMP);
exit(1);
}
#define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX)
die("mount failed");
}
- ldlinux_name = alloca(strlen(mntpath) + 14 +
- (subdir ? strlen(subdir) + 2 : 0));
+ ldlinux_path = alloca(strlen(mntpath) + strlen(subdir) + 1);
+ sprintf(ldlinux_path, "%s%s", mntpath, 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, "%sldlinux.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 {
+ fprintf(stderr, "%s: please specify --install or --update for the future\n", argv[0]);
+ opt.update_only = 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;
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);
/*
* 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();
/*
* 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, subdir, NULL);
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));
}
/*
*/
/* 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();