1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Install the extlinux boot block on an ext2/3/4 and btrfs filesystem
20 #define _GNU_SOURCE /* Enable everything */
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
37 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/mount.h>
43 #include <linux/fd.h> /* Floppy geometry */
44 #include <linux/hdreg.h> /* Hard disk geometry */
45 #define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */
46 #include <linux/fs.h> /* FIGETBSZ, FIBMAP */
51 #include "../version.h"
55 # define dprintf printf
57 # define dprintf(...) ((void)0)
60 /* Global option handling */
61 /* Global fs_type for handling ext2/3/4 vs btrfs */
68 /* These are the options we can set and their values */
77 .sectors = 0,.heads = 0,.raid_mode = 0,.stupid_mode = 0,.reset_adv =
80 static void __attribute__ ((noreturn)) usage(int rv)
83 "Usage: %s [options] directory\n"
84 " --install -i Install over the current bootsector\n"
85 " --update -U Update a previous EXTLINUX installation\n"
86 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
87 " --sectors=# -S Force the number of sectors per track\n"
88 " --heads=# -H Force number of heads\n"
89 " --stupid -s Slow, safe and stupid mode\n"
90 " --raid -r Fall back to the next device on boot failure\n"
91 " --once=... -o Execute a command once upon boot\n"
92 " --clear-once -O Clear the boot-once command\n"
93 " --reset-adv Reset auxilliary data\n"
95 " Note: geometry is determined at boot time for devices which\n"
96 " are considered hard disks by the BIOS. Unfortunately, this is\n"
97 " not possible for devices which are considered floppy disks,\n"
98 " which includes zipdisks and LS-120 superfloppies.\n"
100 " The -z option is useful for USB devices which are considered\n"
101 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
112 static const struct option long_options[] = {
113 {"install", 0, NULL, 'i'},
114 {"update", 0, NULL, 'U'},
115 {"zipdrive", 0, NULL, 'z'},
116 {"sectors", 1, NULL, 'S'},
117 {"stupid", 0, NULL, 's'},
118 {"heads", 1, NULL, 'H'},
119 {"raid-mode", 0, NULL, 'r'},
120 {"version", 0, NULL, 'v'},
121 {"help", 0, NULL, 'h'},
122 {"once", 1, NULL, 'o'},
123 {"clear-once", 0, NULL, 'O'},
124 {"reset-adv", 0, NULL, OPT_RESET_ADV},
128 static const char short_options[] = "iUuzS:H:rvho:O";
130 #if defined(__linux__) && !defined(BLKGETSIZE64)
131 /* This takes a u64, but the size field says size_t. Someone screwed big. */
132 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
135 #ifndef EXT2_SUPER_OFFSET
136 #define EXT2_SUPER_OFFSET 1024
139 /* the btrfs partition first 64K blank area is used to store boot sector and
140 boot image, the boot sector is from 0~512, the boot image starts at 2K */
141 #define BTRFS_EXTLINUX_OFFSET (2*1024)
145 extern unsigned char extlinux_bootsect[];
146 extern unsigned int extlinux_bootsect_len;
147 #define boot_block extlinux_bootsect
148 #define boot_block_len extlinux_bootsect_len
153 extern unsigned char extlinux_image[];
154 extern unsigned int extlinux_image_len;
155 #define boot_image extlinux_image
156 #define boot_image_len extlinux_image_len
159 * Common abort function
161 void __attribute__ ((noreturn)) die(const char *msg)
168 * read/write wrapper functions
170 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
172 char *bufp = (char *)buf;
177 rv = pread(fd, bufp, count, offset);
180 } else if (rv == -1) {
181 if (errno == EINTR) {
184 die(strerror(errno));
197 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
199 const char *bufp = (const char *)buf;
204 rv = pwrite(fd, bufp, count, offset);
207 } else if (rv == -1) {
208 if (errno == EINTR) {
211 die(strerror(errno));
227 int sectmap(int fd, uint32_t * sectors, int nsectors)
229 unsigned int blksize, blk, nblk;
233 if (ioctl(fd, FIGETBSZ, &blksize))
236 /* Number of sectors per block */
237 blksize >>= SECTOR_SHIFT;
243 dprintf("querying block %u\n", blk);
244 if (ioctl(fd, FIBMAP, &blk))
248 for (i = 0; i < blksize; i++) {
252 dprintf("Sector: %10u\n", blk);
262 * Get the size of a block device
264 uint64_t get_size(int devfd)
271 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
274 if (!ioctl(devfd, BLKGETSIZE, §s))
275 return (uint64_t) sects << 9;
276 else if (!fstat(devfd, &st) && st.st_size)
283 * Get device geometry and partition offset
285 struct geometry_table {
287 struct hd_geometry g;
290 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
291 (x/64/32) is the final fallback. I don't know what LS-240 has
292 as its geometry, since I don't have one and don't know anyone that does,
293 and Google wasn't helpful... */
294 static const struct geometry_table standard_geometries[] = {
295 {360 * 1024, {2, 9, 40, 0}},
296 {720 * 1024, {2, 9, 80, 0}},
297 {1200 * 1024, {2, 15, 80, 0}},
298 {1440 * 1024, {2, 18, 80, 0}},
299 {1680 * 1024, {2, 21, 80, 0}},
300 {1722 * 1024, {2, 21, 80, 0}},
301 {2880 * 1024, {2, 36, 80, 0}},
302 {3840 * 1024, {2, 48, 80, 0}},
303 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
307 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
309 struct floppy_struct fd_str;
310 const struct geometry_table *gp;
312 memset(geo, 0, sizeof *geo);
314 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
316 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
317 geo->heads = fd_str.head;
318 geo->sectors = fd_str.sect;
319 geo->cylinders = fd_str.track;
324 /* Didn't work. Let's see if this is one of the standard geometries */
325 for (gp = standard_geometries; gp->bytes; gp++) {
326 if (gp->bytes == totalbytes) {
327 memcpy(geo, &gp->g, sizeof *geo);
332 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
333 what zipdisks use, so this would help if someone has a USB key that
334 they're booting in USB-ZIP mode. */
336 geo->heads = opt.heads ? : 64;
337 geo->sectors = opt.sectors ? : 32;
338 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
341 if (!opt.sectors && !opt.heads)
343 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
344 " (on hard disks, this is usually harmless.)\n",
345 geo->heads, geo->sectors);
351 * Query the device geometry and put it into the boot sector.
352 * Map the file and put the map in the boot sector and file.
353 * Stick the "current directory" inode number into the file.
355 * Returns the number of modified bytes in the boot file.
357 int patch_file_and_bootblock(int fd, int dirfd, int devfd)
360 struct hd_geometry geo;
362 uint64_t totalbytes, totalsectors;
365 struct boot_sector *bs;
366 struct patch_area *patcharea;
372 if (fstat(dirfd, &dirst)) {
373 perror("fstat dirfd");
374 exit(255); /* This should never happen */
377 totalbytes = get_size(devfd);
378 get_geometry(devfd, totalbytes, &geo);
381 geo.heads = opt.heads;
383 geo.sectors = opt.sectors;
385 /* Patch this into a fake FAT superblock. This isn't because
386 FAT is a good format in any way, it's because it lets the
387 early bootstrap share code with the FAT version. */
388 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
390 bs = (struct boot_sector *)boot_block;
392 totalsectors = totalbytes >> SECTOR_SHIFT;
393 if (totalsectors >= 65536) {
394 set_16(&bs->bsSectors, 0);
396 set_16(&bs->bsSectors, totalsectors);
398 set_32(&bs->bsHugeSectors, totalsectors);
400 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
401 set_16(&bs->bsSecPerTrack, geo.sectors);
402 set_16(&bs->bsHeads, geo.heads);
403 set_32(&bs->bsHiddenSecs, geo.start);
405 /* If we're in RAID mode then patch the appropriate instruction;
406 either way write the proper boot signature */
407 i = get_16(&bs->bsSignature);
409 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
411 set_16(&bs->bsSignature, 0xAA55);
413 /* Construct the boot file */
415 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
416 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
417 nsect += 2; /* Two sectors for the ADV */
418 sectp = alloca(sizeof(uint32_t) * nsect);
419 if (fs_type == EXT2) {
420 if (sectmap(fd, sectp, nsect)) {
424 } else if (fs_type == BTRFS) {
427 for (i = 0; i < nsect; i++)
428 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
431 /* First sector need pointer in boot sector */
432 set_32(&bs->NextSector, *sectp++);
435 set_16(&bs->MaxTransfer, 1);
437 /* Search for LDLINUX_MAGIC to find the patch area */
438 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
439 patcharea = (struct patch_area *)wp;
441 /* Set up the totals */
442 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
443 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
444 set_16(&patcharea->adv_sectors, 2);
445 set_32(&patcharea->dwords, dw);
446 set_32(&patcharea->currentdir, dirst.st_ino);
448 /* Set the sector pointers */
449 secptroffset = get_16(&patcharea->secptroffset);
450 wp = (uint32_t *) ((char *)boot_image + secptroffset);
451 nptrs = get_16(&patcharea->secptrcnt);
453 memset(wp, 0, nptrs * 4);
455 set_32(wp++, *sectp++);
457 /* Now produce a checksum */
458 set_32(&patcharea->checksum, 0);
460 csum = LDLINUX_MAGIC;
461 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
462 csum -= get_32(wp); /* Negative checksum */
464 set_32(&patcharea->checksum, csum);
466 return secptroffset + nptrs*4;
470 * Read the ADV from an existing instance, or initialize if invalid.
471 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
474 int read_adv(const char *path, int devfd)
481 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
482 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
483 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
484 perror("writing adv");
489 asprintf(&file, "%s%sextlinux.sys",
490 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
497 fd = open(file, O_RDONLY);
499 if (errno != ENOENT) {
502 syslinux_reset_adv(syslinux_adv);
504 } else if (fstat(fd, &st)) {
506 } else if (st.st_size < 2 * ADV_SIZE) {
507 /* Too small to be useful */
508 syslinux_reset_adv(syslinux_adv);
509 err = 0; /* Nothing to read... */
510 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
511 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
514 /* We got it... maybe? */
515 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
530 * Update the ADV in an existing installation.
532 int write_adv(const char *path, int devfd)
534 unsigned char advtmp[2 * ADV_SIZE];
541 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
542 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
543 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
544 perror("writing adv");
549 asprintf(&file, "%s%sextlinux.sys",
550 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
557 fd = open(file, O_RDONLY);
560 } else if (fstat(fd, &st)) {
562 } else if (st.st_size < 2 * ADV_SIZE) {
563 /* Too small to be useful */
565 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
566 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
569 /* We got it... maybe? */
570 err = syslinux_validate_adv(advtmp) ? -2 : 0;
572 /* Got a good one, write our own ADV here */
573 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
574 nflags = flags & ~EXT2_IMMUTABLE_FL;
576 ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
578 if (!(st.st_mode & S_IWUSR))
579 fchmod(fd, st.st_mode | S_IWUSR);
581 /* Need to re-open read-write */
583 fd = open(file, O_RDWR | O_SYNC);
586 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
587 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
588 fprintf(stderr, "%s: race condition on write\n", file);
591 /* Write our own version ... */
592 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
593 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
599 if (!(st.st_mode & S_IWUSR))
600 fchmod(fd, st.st_mode);
603 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
608 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
622 * Make any user-specified ADV modifications
629 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
630 fprintf(stderr, "%s: not enough space for boot-once command\n",
640 * Install the boot block on the specified device.
641 * Must be run AFTER install_file()!
643 int install_bootblock(int fd, const char *device)
645 struct ext2_super_block sb;
646 struct btrfs_super_block sb2;
649 if (fs_type == EXT2) {
650 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
651 perror("reading superblock");
654 if (sb.s_magic == EXT2_SUPER_MAGIC)
656 } else if (fs_type == BTRFS) {
657 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
659 perror("reading superblock");
662 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
666 fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
670 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
671 perror("writing bootblock");
678 int ext2_install_file(const char *path, int devfd, struct stat *rst)
681 int fd = -1, dirfd = -1, flags;
685 asprintf(&file, "%s%sextlinux.sys",
686 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
692 dirfd = open(path, O_RDONLY | O_DIRECTORY);
698 fd = open(file, O_RDONLY);
700 if (errno != ENOENT) {
705 /* If file exist, remove the immutable flag and set u+w mode */
706 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
707 flags &= ~EXT2_IMMUTABLE_FL;
708 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
710 if (!fstat(fd, &st)) {
711 fchmod(fd, st.st_mode | S_IWUSR);
716 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
717 S_IRUSR | S_IRGRP | S_IROTH);
723 /* Write it the first time */
724 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
725 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
726 boot_image_len) != 2 * ADV_SIZE) {
727 fprintf(stderr, "%s: write failure on %s\n", program, file);
731 /* Map the file, and patch the initial sector accordingly */
732 modbytes = patch_file_and_bootblock(fd, dirfd, devfd);
734 /* Write the patch area again - this relies on the file being
735 overwritten in place! */
736 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
737 fprintf(stderr, "%s: write failure on %s\n", program, file);
741 /* Attempt to set immutable flag and remove all write access */
742 /* Only set immutable flag if file is owned by root */
743 if (!fstat(fd, &st)) {
744 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
745 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
746 flags |= EXT2_IMMUTABLE_FL;
747 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
751 if (fstat(fd, rst)) {
769 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
770 is not managered by btrfs tree, so actually this is not installed as files.
771 since the cow feature of btrfs will move the extlinux.sys every where */
772 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
774 patch_file_and_bootblock(-1, -1, devfd);
775 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
777 perror("writing bootblock");
780 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
781 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
782 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
783 perror("writing adv");
786 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
787 if (stat(path, rst)) {
794 int install_file(const char *path, int devfd, struct stat *rst)
797 return ext2_install_file(path, devfd, rst);
798 else if (fs_type == BTRFS)
799 return btrfs_install_file(path, devfd, rst);
803 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
804 sector; this is consistent with FAT filesystems. */
805 int already_installed(int devfd)
809 xpread(devfd, buffer, 8, 3);
810 return !memcmp(buffer, "EXTLINUX", 8);
814 static char devname_buf[64];
816 static void device_cleanup(void)
822 /* Verify that a device fd and a pathname agree.
823 Return 0 on valid, -1 on error. */
824 static int validate_device(const char *path, int devfd)
826 struct stat pst, dst;
829 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
831 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
832 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
834 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
838 static const char *find_device(const char *mtab_file, dev_t dev)
843 const char *devname = NULL;
846 mtab = setmntent(mtab_file, "r");
851 while ((mnt = getmntent(mtab))) {
852 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
855 if (!strcmp(mnt->mnt_type, "btrfs") &&
856 !stat(mnt->mnt_dir, &dst) &&
861 if ((!strcmp(mnt->mnt_type, "ext2") ||
862 !strcmp(mnt->mnt_type, "ext3") ||
863 !strcmp(mnt->mnt_type, "ext4")) &&
864 !stat(mnt->mnt_fsname, &dst) &&
865 dst.st_rdev == dev) {
871 devname = strdup(mnt->mnt_fsname);
881 static const char *get_devname(const char *path)
883 const char *devname = NULL;
887 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
888 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
891 if (statfs(path, &sfs)) {
892 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
897 /* klibc doesn't have getmntent and friends; instead, just create
898 a new device with the appropriate device type */
899 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
900 major(st.st_dev), minor(st.st_dev));
902 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
903 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
907 atexit(device_cleanup); /* unlink the device node on exit */
908 devname = devname_buf;
912 devname = find_device("/proc/mounts", st.st_dev);
914 /* Didn't find it in /proc/mounts, try /etc/mtab */
915 devname = find_device("/etc/mtab", st.st_dev);
918 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
922 fprintf(stderr, "%s is device %s\n", path, devname);
927 static int open_device(const char *path, struct stat *st, const char **_devname)
930 const char *devname = NULL;
934 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
935 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
939 if (statfs(path, &sfs)) {
940 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
943 if (sfs.f_type == EXT2_SUPER_MAGIC)
945 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
949 fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
955 devname = get_devname(path);
959 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
960 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
964 /* Verify that the device we opened is the device intended */
965 if (validate_device(path, devfd)) {
966 fprintf(stderr, "%s: path %s doesn't match device %s\n",
967 program, path, devname);
974 int install_loader(const char *path, int update_only)
980 devfd = open_device(path, &st, &devname);
984 if (update_only && !already_installed(devfd)) {
985 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
991 /* Read a pre-existing ADV, if already installed */
993 syslinux_reset_adv(syslinux_adv);
994 else if (read_adv(path, devfd) < 0) {
998 if (modify_adv() < 0) {
1003 /* Install extlinux.sys */
1004 if (install_file(path, devfd, &fst)) {
1008 if (fst.st_dev != st.st_dev) {
1009 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1016 rv = install_bootblock(devfd, devname);
1024 * Modify the ADV of an existing installation
1026 int modify_existing_adv(const char *path)
1030 devfd = open_device(path, NULL, NULL);
1035 syslinux_reset_adv(syslinux_adv);
1036 else if (read_adv(path, devfd) < 0) {
1040 if (modify_adv() < 0) {
1044 if (write_adv(path, devfd) < 0) {
1052 int main(int argc, char *argv[])
1055 const char *directory;
1056 int update_only = -1;
1060 while ((o = getopt_long(argc, argv, short_options,
1061 long_options, NULL)) != EOF) {
1068 opt.sectors = strtoul(optarg, NULL, 0);
1069 if (opt.sectors < 1 || opt.sectors > 63) {
1071 "%s: invalid number of sectors: %u (must be 1-63)\n",
1072 program, opt.sectors);
1077 opt.heads = strtoul(optarg, NULL, 0);
1078 if (opt.heads < 1 || opt.heads > 256) {
1080 "%s: invalid number of heads: %u (must be 1-256)\n",
1081 program, opt.heads);
1089 opt.stupid_mode = 1;
1102 opt.set_once = optarg;
1111 fputs("extlinux " VERSION_STR
1112 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1119 directory = argv[optind];
1124 if (update_only == -1) {
1125 if (opt.reset_adv || opt.set_once) {
1126 return modify_existing_adv(directory);
1132 return install_loader(directory, update_only);