/* ----------------------------------------------------------------------- *
*
* Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ * 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
/*
* extlinux.c
*
- * Install the extlinux boot block on an ext2/3/4 and btrfs filesystem
+ * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
*/
#define _GNU_SOURCE /* Enable everything */
#include <mntent.h>
#endif
#include <stdbool.h>
+#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/mount.h>
#include <sys/vfs.h>
-#include <linux/fd.h> /* Floppy geometry */
-#include <linux/hdreg.h> /* Hard disk geometry */
-#define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */
-#include <linux/fs.h> /* FIGETBSZ, FIBMAP */
-#undef statfs
+#include "linuxioctl.h"
-#include "ext2_fs.h"
#include "btrfs.h"
+#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
# define dprintf(...) ((void)0)
#endif
-/* Global option handling */
-/* Global fs_type for handling ext2/3/4 vs btrfs */
-#define EXT2 1
-#define BTRFS 2
-int 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)
/* the btrfs partition first 64K blank area is used to store boot sector and
boot image, the boot sector is from 0~512, the boot image starts at 2K */
#define BTRFS_EXTLINUX_OFFSET (2*1024)
+#define BTRFS_SUBVOL_OPT "subvol="
+#define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
+static char subvol[BTRFS_SUBVOL_MAX];
+
/*
* Boot block
*/
extern unsigned char extlinux_bootsect[];
extern unsigned int extlinux_bootsect_len;
+#undef boot_block
+#undef boot_block_len
#define boot_block extlinux_bootsect
#define boot_block_len extlinux_bootsect_len
*/
extern unsigned char extlinux_image[];
extern unsigned int extlinux_image_len;
+#undef boot_image
+#undef boot_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;
-}
-
-/*
- * 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
}
/*
+ * Generate sector extents
+ */
+static void generate_extents(struct syslinux_extent *ex, int nptrs,
+ const sector_t *sectp, int nsect)
+{
+ uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
+ uint32_t base;
+ sector_t sect, lba;
+ unsigned int len;
+
+ len = lba = base = 0;
+
+ memset(ex, 0, nptrs * sizeof *ex);
+
+ while (nsect) {
+ sect = *sectp++;
+
+ if (len && sect == lba + len &&
+ ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
+ /* We can add to the current extent */
+ len++;
+ goto next;
+ }
+
+ if (len) {
+ set_64(&ex->lba, lba);
+ set_16(&ex->len, len);
+ ex++;
+ }
+
+ base = addr;
+ lba = sect;
+ len = 1;
+
+ next:
+ addr += SECTOR_SIZE;
+ nsect--;
+ }
+
+ if (len) {
+ set_64(&ex->lba, lba);
+ set_16(&ex->len, len);
+ ex++;
+ }
+}
+
+/*
* Query the device geometry and put it into the boot sector.
* Map the file and put the map in the boot sector and file.
* Stick the "current directory" inode number into the file.
*
* Returns the number of modified bytes in the boot file.
*/
-int patch_file_and_bootblock(int fd, int dirfd, int devfd)
+int patch_file_and_bootblock(int fd, const char *dir, int devfd)
{
- struct stat dirst;
+ struct stat dirst, xdst;
struct hd_geometry geo;
- uint32_t *sectp;
+ sector_t *sectp;
uint64_t totalbytes, totalsectors;
int nsect;
uint32_t *wp;
struct boot_sector *bs;
struct patch_area *patcharea;
+ struct syslinux_extent *ex;
int i, dw, nptrs;
uint32_t csum;
- int secptroffset;
-
- if (fs_type == EXT2)
- if (fstat(dirfd, &dirst)) {
- perror("fstat dirfd");
- exit(255); /* This should never happen */
+ int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
+ char *dirpath, *subpath, *xdirpath, *xsubpath;
+ uint64_t *advptrs;
+
+ dirpath = realpath(dir, NULL);
+ if (!dirpath || stat(dir, &dirst)) {
+ perror("accessing install directory");
+ exit(255); /* This should never happen */
+ }
+
+ if (lstat(dirpath, &xdst) ||
+ dirst.st_ino != xdst.st_ino ||
+ dirst.st_dev != xdst.st_dev) {
+ perror("realpath returned nonsense");
+ exit(255);
+ }
+
+ subpath = strchr(dirpath, '\0');
+ for (;;) {
+ if (*subpath == '/') {
+ if (subpath > dirpath) {
+ *subpath = '\0';
+ xsubpath = subpath+1;
+ xdirpath = dirpath;
+ } else {
+ xsubpath = subpath;
+ xdirpath = "/";
+ }
+ if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
+ subpath = strchr(subpath+1, '/');
+ if (!subpath)
+ subpath = "/"; /* It's the root of the filesystem */
+ break;
+ }
+ *subpath = '/';
}
+ if (subpath == dirpath)
+ break;
+
+ subpath--;
+ }
+
+ /* Now subpath should contain the path relative to the fs base */
+ dprintf("subpath = %s\n", subpath);
+
totalbytes = get_size(devfd);
get_geometry(devfd, totalbytes, &geo);
dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
nsect += 2; /* Two sectors for the ADV */
- sectp = alloca(sizeof(uint32_t) * nsect);
- if (fs_type == EXT2) {
+ sectp = alloca(sizeof(sector_t) * nsect);
+ if (fs_type == EXT2 || fs_type == VFAT) {
if (sectmap(fd, sectp, nsect)) {
perror("bmap");
exit(1);
}
/* First sector need pointer in boot sector */
- set_32(&bs->NextSector, *sectp++);
- /* Stupid mode? */
- if (opt.stupid_mode)
- set_16(&bs->MaxTransfer, 1);
+ set_64(&bs->NextSector, *sectp++);
/* Search for LDLINUX_MAGIC to find the patch area */
for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
set_16(&patcharea->adv_sectors, 2);
set_32(&patcharea->dwords, dw);
- set_32(&patcharea->currentdir, dirst.st_ino);
- /* Set the sector pointers */
+ /* Stupid mode? */
+ if (opt.stupid_mode)
+ set_16(&patcharea->maxtransfer, 1);
+
+ /* Set the sector extents */
secptroffset = get_16(&patcharea->secptroffset);
- wp = (uint32_t *) ((char *)boot_image + secptroffset);
+ ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
nptrs = get_16(&patcharea->secptrcnt);
- memset(wp, 0, nptrs * 4);
- while (nsect--)
- set_32(wp++, *sectp++);
-
- /* Now produce a checksum */
- set_32(&patcharea->checksum, 0);
-
- csum = LDLINUX_MAGIC;
- for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
- csum -= get_32(wp); /* Negative checksum */
-
- set_32(&patcharea->checksum, csum);
-
- return secptroffset + nptrs*4;
-}
-
-/*
- * 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;
+ if (nsect > nptrs) {
+ /* Not necessarily an error in this case, but a general problem */
+ fprintf(stderr, "Insufficient extent space, build error!\n");
+ exit(1);
}
- asprintf(&file, "%s%sextlinux.sys",
- path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
- if (!file) {
- perror(program);
- return -1;
- }
+ /* -1 for the pointer in the boot sector, -2 for the two ADVs */
+ generate_extents(ex, nptrs, sectp, nsect-1-2);
- 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;
- int flags, nflags;
+ /* ADV pointers */
+ advptrs = (uint64_t *)((char *)boot_image +
+ get_16(&patcharea->advptroffset));
+ set_64(&advptrs[0], sectp[nsect-1-2]);
+ set_64(&advptrs[1], sectp[nsect-1-1]);
- 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;
+ /* Poke in the base directory path */
+ diroffset = get_16(&patcharea->diroffset);
+ dirlen = get_16(&patcharea->dirlen);
+ if (dirlen <= strlen(subpath)) {
+ fprintf(stderr, "Subdirectory path too long... aborting install!\n");
+ exit(1);
}
- asprintf(&file, "%s%sextlinux.sys",
- path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
+ strncpy((char *)boot_image + diroffset, subpath, dirlen);
+ free(dirpath);
- if (!file) {
- perror(program);
- return -1;
+ /* write subvol info if we have */
+ subvoloffset = get_16(&patcharea->subvoloffset);
+ subvollen = get_16(&patcharea->subvollen);
+ if (subvollen <= strlen(subvol)) {
+ fprintf(stderr, "Subvol name too long... aborting install!\n");
+ exit(1);
}
+ strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
- 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 */
- if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
- nflags = flags & ~EXT2_IMMUTABLE_FL;
- if (nflags != flags)
- ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
- }
- if (!(st.st_mode & S_IWUSR))
- fchmod(fd, st.st_mode | S_IWUSR);
-
- /* 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();
-
- if (!(st.st_mode & S_IWUSR))
- fchmod(fd, st.st_mode);
-
- if (nflags != flags)
- ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
- }
- }
+ /* Now produce a checksum */
+ set_32(&patcharea->checksum, 0);
- if (err == -2)
- fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
- file);
- else if (err == -1)
- perror(file);
+ csum = LDLINUX_MAGIC;
+ for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
+ csum -= get_32(wp); /* Negative checksum */
- if (fd >= 0)
- close(fd);
- if (file)
- free(file);
+ set_32(&patcharea->checksum, csum);
- return err;
+ /*
+ * Assume all bytes modified. This can be optimized at the expense
+ * of keeping track of what the highest modified address ever was.
+ */
+ return dw << 2;
}
/*
rv = -1;
}
}
+ if (opt.menu_save) {
+ if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
+ fprintf(stderr, "%s: not enough space for menu-save label\n",
+ program);
+ rv = -1;
+ }
+ }
return rv;
}
{
struct ext2_super_block sb;
struct btrfs_super_block sb2;
+ struct boot_sector sb3;
bool ok = false;
if (fs_type == EXT2) {
}
if (sb2.magic == *(u64 *)BTRFS_MAGIC)
ok = true;
+ } else if (fs_type == VFAT) {
+ if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
+ perror("reading fat superblock");
+ return 1;
+ }
+ if (sb3.bsResSectors && sb3.bsFATs &&
+ (strstr(sb3.bs16.FileSysType, "FAT") ||
+ strstr(sb3.bs32.FileSysType, "FAT")))
+ ok = true;
}
if (!ok) {
- fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
+ fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
device);
return 1;
}
- if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
- perror("writing bootblock");
- return 1;
+ if (fs_type == VFAT) {
+ struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
+ if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
+ xpwrite(fd, &bs->bsCode, bsCodeLen,
+ offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
+ perror("writing fat bootblock");
+ return 1;
+ }
+ } else {
+ if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
+ perror("writing bootblock");
+ return 1;
+ }
}
return 0;
}
-int ext2_install_file(const char *path, int devfd, struct stat *rst)
+int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
{
char *file;
- int fd = -1, dirfd = -1, flags;
- struct stat st;
+ int fd = -1, dirfd = -1;
int modbytes;
asprintf(&file, "%s%sextlinux.sys",
goto bail;
}
} else {
- /* If file exist, remove the immutable flag and set u+w mode */
- if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
- flags &= ~EXT2_IMMUTABLE_FL;
- ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
- }
- if (!fstat(fd, &st)) {
- fchmod(fd, st.st_mode | S_IWUSR);
- }
+ clear_attributes(fd);
}
close(fd);
}
/* Map the file, and patch the initial sector accordingly */
- modbytes = patch_file_and_bootblock(fd, dirfd, devfd);
+ modbytes = patch_file_and_bootblock(fd, path, devfd);
/* Write the patch area again - this relies on the file being
overwritten in place! */
/* Attempt to set immutable flag and remove all write access */
/* Only set immutable flag if file is owned by root */
- if (!fstat(fd, &st)) {
- fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
- if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
- flags |= EXT2_IMMUTABLE_FL;
- ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
- }
- }
+ set_attributes(fd);
if (fstat(fd, rst)) {
perror(file);
since the cow feature of btrfs will move the extlinux.sys every where */
int btrfs_install_file(const char *path, int devfd, struct stat *rst)
{
- patch_file_and_bootblock(-1, -1, devfd);
+ patch_file_and_bootblock(-1, path, devfd);
if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
!= boot_image_len) {
perror("writing bootblock");
int install_file(const char *path, int devfd, struct stat *rst)
{
- if (fs_type == EXT2)
- return ext2_install_file(path, devfd, rst);
+ if (fs_type == EXT2 || fs_type == VFAT)
+ return ext2_fat_install_file(path, devfd, rst);
else if (fs_type == BTRFS)
return btrfs_install_file(path, devfd, rst);
return 1;
case BTRFS:
if (!strcmp(mnt->mnt_type, "btrfs") &&
!stat(mnt->mnt_dir, &dst) &&
- dst.st_dev == dev)
- done = true;
+ dst.st_dev == dev) {
+ char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
+
+ if (opt) {
+ if (!subvol[0]) {
+ char *tmp;
+
+ strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
+ tmp = strchr(subvol, 32);
+ if (tmp)
+ *tmp = '\0';
+ }
+ break; /* should break and let upper layer try again */
+ } else
+ done = true;
+ }
break;
case EXT2:
if ((!strcmp(mnt->mnt_type, "ext2") ||
done = true;
break;
}
+ case VFAT:
+ if ((!strcmp(mnt->mnt_type, "vfat")) &&
+ !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
+ case NONE:
+ break;
}
if (done) {
devname = strdup(mnt->mnt_fsname);
#else
- devname = find_device("/proc/mounts", st.st_dev);
+ /* check /etc/mtab first, since btrfs subvol info is only in here */
+ devname = find_device("/etc/mtab", st.st_dev);
+ if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
+ char parent[256];
+ char *tmp;
+
+ strcpy(parent, path);
+ tmp = strrchr(parent, '/');
+ if (tmp) {
+ *tmp = '\0';
+ fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
+ devname = get_devname(parent);
+ } else
+ devname = NULL;
+ }
if (!devname) {
- /* Didn't find it in /proc/mounts, try /etc/mtab */
- devname = find_device("/etc/mtab", st.st_dev);
+ /* Didn't find it in /etc/mtab, try /proc/mounts */
+ devname = find_device("/proc/mounts", st.st_dev);
}
if (!devname) {
fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
fs_type = EXT2;
else if (sfs.f_type == BTRFS_SUPER_MAGIC)
fs_type = BTRFS;
+ else if (sfs.f_type == MSDOS_SUPER_MAGIC)
+ fs_type = VFAT;
if (!fs_type) {
- fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
+ fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
program, path);
return -1;
}
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;
/* 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;
}
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;
}
close(devfd);
return 1;
}
- if (write_adv(path, devfd) < 0) {
+ if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
close(devfd);
return 1;
}
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, MODE_EXTLINUX);
- 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 || opt.menu_save)
+ return modify_existing_adv(opt.directory);
+ else
+ usage(EX_USAGE, MODE_EXTLINUX);
}
- return install_loader(directory, update_only);
+ return install_loader(opt.directory, opt.update_only);
}