Adding upstream version 4.00~pre54+dfsg.
[syslinux-debian/hramrach.git] / extlinux / main.c
blobc0a37ef1f4d94c550dcc3a67fec1f58dc5ae12f2
1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 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 * ----------------------------------------------------------------------- */
15 * extlinux.c
17 * Install the syslinux boot block on an fat, ext2/3/4 and btrfs filesystem
20 #define _GNU_SOURCE /* Enable everything */
21 #include <inttypes.h>
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
23 typedef uint64_t u64;
24 #include <alloca.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #ifndef __KLIBC__
30 #include <mntent.h>
31 #endif
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <getopt.h>
37 #include <sysexits.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/mount.h>
42 #include <sys/vfs.h>
44 #include "linuxioctl.h"
46 #include "btrfs.h"
47 #include "fat.h"
48 #include "../version.h"
49 #include "syslxint.h"
50 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
51 #include "setadv.h"
52 #include "syslxopt.h" /* unified options */
54 #ifdef DEBUG
55 # define dprintf printf
56 #else
57 # define dprintf(...) ((void)0)
58 #endif
60 #if defined(__linux__) && !defined(BLKGETSIZE64)
61 /* This takes a u64, but the size field says size_t. Someone screwed big. */
62 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
63 #endif
65 #ifndef EXT2_SUPER_OFFSET
66 #define EXT2_SUPER_OFFSET 1024
67 #endif
69 /* the btrfs partition first 64K blank area is used to store boot sector and
70 boot image, the boot sector is from 0~512, the boot image starts at 2K */
71 #define BTRFS_EXTLINUX_OFFSET (2*1024)
72 #define BTRFS_SUBVOL_OPT "subvol="
73 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
74 static char subvol[BTRFS_SUBVOL_MAX];
76 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
79 * Get the size of a block device
81 uint64_t get_size(int devfd)
83 uint64_t bytes;
84 uint32_t sects;
85 struct stat st;
87 #ifdef BLKGETSIZE64
88 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
89 return bytes;
90 #endif
91 if (!ioctl(devfd, BLKGETSIZE, &sects))
92 return (uint64_t) sects << 9;
93 else if (!fstat(devfd, &st) && st.st_size)
94 return st.st_size;
95 else
96 return 0;
100 * Get device geometry and partition offset
102 struct geometry_table {
103 uint64_t bytes;
104 struct hd_geometry g;
107 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
108 (x/64/32) is the final fallback. I don't know what LS-240 has
109 as its geometry, since I don't have one and don't know anyone that does,
110 and Google wasn't helpful... */
111 static const struct geometry_table standard_geometries[] = {
112 {360 * 1024, {2, 9, 40, 0}},
113 {720 * 1024, {2, 9, 80, 0}},
114 {1200 * 1024, {2, 15, 80, 0}},
115 {1440 * 1024, {2, 18, 80, 0}},
116 {1680 * 1024, {2, 21, 80, 0}},
117 {1722 * 1024, {2, 21, 80, 0}},
118 {2880 * 1024, {2, 36, 80, 0}},
119 {3840 * 1024, {2, 48, 80, 0}},
120 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
121 {0, {0, 0, 0, 0}}
124 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
126 struct floppy_struct fd_str;
127 const struct geometry_table *gp;
129 memset(geo, 0, sizeof *geo);
131 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
132 return 0;
133 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
134 geo->heads = fd_str.head;
135 geo->sectors = fd_str.sect;
136 geo->cylinders = fd_str.track;
137 geo->start = 0;
138 return 0;
141 /* Didn't work. Let's see if this is one of the standard geometries */
142 for (gp = standard_geometries; gp->bytes; gp++) {
143 if (gp->bytes == totalbytes) {
144 memcpy(geo, &gp->g, sizeof *geo);
145 return 0;
149 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
150 what zipdisks use, so this would help if someone has a USB key that
151 they're booting in USB-ZIP mode. */
153 geo->heads = opt.heads ? : 64;
154 geo->sectors = opt.sectors ? : 32;
155 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
156 geo->start = 0;
158 if (!opt.sectors && !opt.heads)
159 fprintf(stderr,
160 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
161 " (on hard disks, this is usually harmless.)\n",
162 geo->heads, geo->sectors);
164 return 1;
168 * Query the device geometry and put it into the boot sector.
169 * Map the file and put the map in the boot sector and file.
170 * Stick the "current directory" inode number into the file.
172 * Returns the number of modified bytes in the boot file.
174 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
176 struct stat dirst, xdst;
177 struct hd_geometry geo;
178 sector_t *sectp;
179 uint64_t totalbytes, totalsectors;
180 int nsect;
181 struct boot_sector *sbs;
182 char *dirpath, *subpath, *xdirpath, *xsubpath;
183 int rv;
185 dirpath = realpath(dir, NULL);
186 if (!dirpath || stat(dir, &dirst)) {
187 perror("accessing install directory");
188 exit(255); /* This should never happen */
191 if (lstat(dirpath, &xdst) ||
192 dirst.st_ino != xdst.st_ino ||
193 dirst.st_dev != xdst.st_dev) {
194 perror("realpath returned nonsense");
195 exit(255);
198 subpath = strchr(dirpath, '\0');
199 for (;;) {
200 if (*subpath == '/') {
201 if (subpath > dirpath) {
202 *subpath = '\0';
203 xsubpath = subpath+1;
204 xdirpath = dirpath;
205 } else {
206 xsubpath = subpath;
207 xdirpath = "/";
209 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
210 subpath = strchr(subpath+1, '/');
211 if (!subpath)
212 subpath = "/"; /* It's the root of the filesystem */
213 break;
215 *subpath = '/';
218 if (subpath == dirpath)
219 break;
221 subpath--;
224 /* Now subpath should contain the path relative to the fs base */
225 dprintf("subpath = %s\n", subpath);
227 totalbytes = get_size(devfd);
228 get_geometry(devfd, totalbytes, &geo);
230 if (opt.heads)
231 geo.heads = opt.heads;
232 if (opt.sectors)
233 geo.sectors = opt.sectors;
235 /* Patch this into a fake FAT superblock. This isn't because
236 FAT is a good format in any way, it's because it lets the
237 early bootstrap share code with the FAT version. */
238 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
240 sbs = (struct boot_sector *)syslinux_bootsect;
242 totalsectors = totalbytes >> SECTOR_SHIFT;
243 if (totalsectors >= 65536) {
244 set_16(&sbs->bsSectors, 0);
245 } else {
246 set_16(&sbs->bsSectors, totalsectors);
248 set_32(&sbs->bsHugeSectors, totalsectors);
250 set_16(&sbs->bsBytesPerSec, SECTOR_SIZE);
251 set_16(&sbs->bsSecPerTrack, geo.sectors);
252 set_16(&sbs->bsHeads, geo.heads);
253 set_32(&sbs->bsHiddenSecs, geo.start);
255 /* Construct the boot file map */
257 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
258 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
259 nsect += 2; /* Two sectors for the ADV */
260 sectp = alloca(sizeof(sector_t) * nsect);
261 if (fs_type == EXT2 || fs_type == VFAT) {
262 if (sectmap(fd, sectp, nsect)) {
263 perror("bmap");
264 exit(1);
266 } else if (fs_type == BTRFS) {
267 int i;
269 for (i = 0; i < nsect; i++)
270 sectp[i] = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
273 /* Create the modified image in memory */
274 rv = syslinux_patch(sectp, nsect, opt.stupid_mode,
275 opt.raid_mode, subpath, subvol);
277 free(dirpath);
278 return rv;
282 * Make any user-specified ADV modifications
284 int modify_adv(void)
286 int rv = 0;
288 if (opt.set_once) {
289 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
290 fprintf(stderr, "%s: not enough space for boot-once command\n",
291 program);
292 rv = -1;
295 if (opt.menu_save) {
296 if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
297 fprintf(stderr, "%s: not enough space for menu-save label\n",
298 program);
299 rv = -1;
303 return rv;
307 * Install the boot block on the specified device.
308 * Must be run AFTER install_file()!
310 int install_bootblock(int fd, const char *device)
312 struct ext2_super_block sb;
313 struct btrfs_super_block sb2;
314 struct boot_sector sb3;
315 bool ok = false;
317 if (fs_type == EXT2) {
318 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
319 perror("reading superblock");
320 return 1;
322 if (sb.s_magic == EXT2_SUPER_MAGIC)
323 ok = true;
324 } else if (fs_type == BTRFS) {
325 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
326 != sizeof sb2) {
327 perror("reading superblock");
328 return 1;
330 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
331 ok = true;
332 } else if (fs_type == VFAT) {
333 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
334 perror("reading fat superblock");
335 return 1;
337 if (sb3.bsResSectors && sb3.bsFATs &&
338 (strstr(sb3.bs16.FileSysType, "FAT") ||
339 strstr(sb3.bs32.FileSysType, "FAT")))
340 ok = true;
342 if (!ok) {
343 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
344 device);
345 return 1;
347 if (fs_type == VFAT) {
348 struct boot_sector *sbs = (struct boot_sector *)syslinux_bootsect;
349 if (xpwrite(fd, &sbs->bsHead, bsHeadLen, 0) != bsHeadLen ||
350 xpwrite(fd, &sbs->bsCode, bsCodeLen,
351 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
352 perror("writing fat bootblock");
353 return 1;
355 } else {
356 if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
357 != syslinux_bootsect_len) {
358 perror("writing bootblock");
359 return 1;
363 return 0;
366 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
368 char *file;
369 int fd = -1, dirfd = -1;
370 int modbytes;
372 asprintf(&file, "%s%sldlinux.sys",
373 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
374 if (!file) {
375 perror(program);
376 return 1;
379 dirfd = open(path, O_RDONLY | O_DIRECTORY);
380 if (dirfd < 0) {
381 perror(path);
382 goto bail;
385 fd = open(file, O_RDONLY);
386 if (fd < 0) {
387 if (errno != ENOENT) {
388 perror(file);
389 goto bail;
391 } else {
392 clear_attributes(fd);
394 close(fd);
396 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
397 S_IRUSR | S_IRGRP | S_IROTH);
398 if (fd < 0) {
399 perror(file);
400 goto bail;
403 /* Write it the first time */
404 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
405 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
406 boot_image_len) != 2 * ADV_SIZE) {
407 fprintf(stderr, "%s: write failure on %s\n", program, file);
408 goto bail;
411 /* Map the file, and patch the initial sector accordingly */
412 modbytes = patch_file_and_bootblock(fd, path, devfd);
414 /* Write the patch area again - this relies on the file being
415 overwritten in place! */
416 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
417 fprintf(stderr, "%s: write failure on %s\n", program, file);
418 goto bail;
421 /* Attempt to set immutable flag and remove all write access */
422 /* Only set immutable flag if file is owned by root */
423 set_attributes(fd);
425 if (fstat(fd, rst)) {
426 perror(file);
427 goto bail;
430 close(dirfd);
431 close(fd);
432 return 0;
434 bail:
435 if (dirfd >= 0)
436 close(dirfd);
437 if (fd >= 0)
438 close(fd);
440 return 1;
443 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
444 is not managered by btrfs tree, so actually this is not installed as files.
445 since the cow feature of btrfs will move the ldlinux.sys every where */
446 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
448 patch_file_and_bootblock(-1, path, devfd);
449 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
450 != boot_image_len) {
451 perror("writing bootblock");
452 return 1;
454 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
455 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
456 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
457 perror("writing adv");
458 return 1;
460 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
461 if (stat(path, rst)) {
462 perror(path);
463 return 1;
465 return 0;
468 int install_file(const char *path, int devfd, struct stat *rst)
470 if (fs_type == EXT2 || fs_type == VFAT)
471 return ext2_fat_install_file(path, devfd, rst);
472 else if (fs_type == BTRFS)
473 return btrfs_install_file(path, devfd, rst);
474 return 1;
478 * SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
479 * sector; this is consistent with FAT filesystems. Earlier versions
480 * would install the string "EXTLINUX" instead, handle both.
482 int already_installed(int devfd)
484 char buffer[8];
486 xpread(devfd, buffer, 8, 3);
487 return !memcmp(buffer, "SYSLINUX", 8) || !memcmp(buffer, "EXTLINUX", 8);
490 #ifdef __KLIBC__
491 static char devname_buf[64];
493 static void device_cleanup(void)
495 unlink(devname_buf);
497 #endif
499 /* Verify that a device fd and a pathname agree.
500 Return 0 on valid, -1 on error. */
501 static int validate_device(const char *path, int devfd)
503 struct stat pst, dst;
504 struct statfs sfs;
506 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
507 return -1;
508 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
509 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
510 return 0;
511 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
514 #ifndef __KLIBC__
515 static const char *find_device(const char *mtab_file, dev_t dev)
517 struct mntent *mnt;
518 struct stat dst;
519 FILE *mtab;
520 const char *devname = NULL;
521 bool done;
523 mtab = setmntent(mtab_file, "r");
524 if (!mtab)
525 return NULL;
527 done = false;
528 while ((mnt = getmntent(mtab))) {
529 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
530 switch (fs_type) {
531 case BTRFS:
532 if (!strcmp(mnt->mnt_type, "btrfs") &&
533 !stat(mnt->mnt_dir, &dst) &&
534 dst.st_dev == dev) {
535 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
537 if (opt) {
538 if (!subvol[0]) {
539 char *tmp;
541 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
542 tmp = strchr(subvol, 32);
543 if (tmp)
544 *tmp = '\0';
546 break; /* should break and let upper layer try again */
547 } else
548 done = true;
550 break;
551 case EXT2:
552 if ((!strcmp(mnt->mnt_type, "ext2") ||
553 !strcmp(mnt->mnt_type, "ext3") ||
554 !strcmp(mnt->mnt_type, "ext4")) &&
555 !stat(mnt->mnt_fsname, &dst) &&
556 dst.st_rdev == dev) {
557 done = true;
558 break;
560 case VFAT:
561 if ((!strcmp(mnt->mnt_type, "vfat")) &&
562 !stat(mnt->mnt_fsname, &dst) &&
563 dst.st_rdev == dev) {
564 done = true;
565 break;
567 case NONE:
568 break;
570 if (done) {
571 devname = strdup(mnt->mnt_fsname);
572 break;
575 endmntent(mtab);
577 return devname;
579 #endif
581 static const char *get_devname(const char *path)
583 const char *devname = NULL;
584 struct stat st;
585 struct statfs sfs;
587 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
588 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
589 return devname;
591 if (statfs(path, &sfs)) {
592 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
593 return devname;
595 #ifdef __KLIBC__
597 /* klibc doesn't have getmntent and friends; instead, just create
598 a new device with the appropriate device type */
599 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
600 major(st.st_dev), minor(st.st_dev));
602 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
603 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
604 return devname;
607 atexit(device_cleanup); /* unlink the device node on exit */
608 devname = devname_buf;
610 #else
612 /* check /etc/mtab first, since btrfs subvol info is only in here */
613 devname = find_device("/etc/mtab", st.st_dev);
614 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
615 char parent[256];
616 char *tmp;
618 strcpy(parent, path);
619 tmp = strrchr(parent, '/');
620 if (tmp) {
621 *tmp = '\0';
622 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
623 devname = get_devname(parent);
624 } else
625 devname = NULL;
627 if (!devname) {
628 /* Didn't find it in /etc/mtab, try /proc/mounts */
629 devname = find_device("/proc/mounts", st.st_dev);
631 if (!devname) {
632 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
633 return devname;
636 fprintf(stderr, "%s is device %s\n", path, devname);
637 #endif
638 return devname;
641 static int open_device(const char *path, struct stat *st, const char **_devname)
643 int devfd;
644 const char *devname = NULL;
645 struct statfs sfs;
647 if (st)
648 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
649 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
650 return -1;
653 if (statfs(path, &sfs)) {
654 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
655 return -1;
657 if (sfs.f_type == EXT2_SUPER_MAGIC)
658 fs_type = EXT2;
659 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
660 fs_type = BTRFS;
661 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
662 fs_type = VFAT;
664 if (!fs_type) {
665 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
666 program, path);
667 return -1;
670 devfd = -1;
671 devname = get_devname(path);
672 if (_devname)
673 *_devname = devname;
675 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
676 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
677 return -1;
680 /* Verify that the device we opened is the device intended */
681 if (validate_device(path, devfd)) {
682 fprintf(stderr, "%s: path %s doesn't match device %s\n",
683 program, path, devname);
684 close(devfd);
685 return -1;
687 return devfd;
690 static int ext_read_adv(const char *path, const char *cfg, int devfd)
692 if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */
693 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
694 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
695 perror("btrfs writing adv");
696 return 1;
698 return 0;
700 return read_adv(path, cfg);
703 static int ext_write_adv(const char *path, const char *cfg, int devfd)
705 if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */
706 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
707 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
708 perror("writing adv");
709 return 1;
711 return 0;
713 return write_adv(path, cfg);
716 int install_loader(const char *path, int update_only)
718 struct stat st, fst;
719 int devfd, rv;
720 const char *devname;
722 devfd = open_device(path, &st, &devname);
723 if (devfd < 0)
724 return 1;
726 if (update_only && !already_installed(devfd)) {
727 fprintf(stderr, "%s: no previous syslinux boot sector found\n",
728 program);
729 close(devfd);
730 return 1;
733 /* Read a pre-existing ADV, if already installed */
734 if (opt.reset_adv)
735 syslinux_reset_adv(syslinux_adv);
736 else if (ext_read_adv(path, "ldlinux.sys", devfd) < 0) {
737 close(devfd);
738 return 1;
740 if (modify_adv() < 0) {
741 close(devfd);
742 return 1;
745 /* Install ldlinux.sys */
746 if (install_file(path, devfd, &fst)) {
747 close(devfd);
748 return 1;
750 if (fst.st_dev != st.st_dev) {
751 fprintf(stderr, "%s: file system changed under us - aborting!\n",
752 program);
753 close(devfd);
754 return 1;
757 sync();
758 rv = install_bootblock(devfd, devname);
759 close(devfd);
760 sync();
762 return rv;
766 * Modify the ADV of an existing installation
768 int modify_existing_adv(const char *path)
770 int devfd;
772 devfd = open_device(path, NULL, NULL);
773 if (devfd < 0)
774 return 1;
776 if (opt.reset_adv)
777 syslinux_reset_adv(syslinux_adv);
778 else if (ext_read_adv(path, "ldlinux.sys", devfd) < 0) {
779 close(devfd);
780 return 1;
782 if (modify_adv() < 0) {
783 close(devfd);
784 return 1;
786 if (ext_write_adv(path, "ldlinux.sys", devfd) < 0) {
787 close(devfd);
788 return 1;
790 close(devfd);
791 return 0;
794 int main(int argc, char *argv[])
796 parse_options(argc, argv, MODE_EXTLINUX);
798 if (!opt.directory)
799 usage(EX_USAGE, 0);
801 if (opt.update_only == -1) {
802 if (opt.reset_adv || opt.set_once || opt.menu_save)
803 return modify_existing_adv(opt.directory);
804 else
805 usage(EX_USAGE, MODE_EXTLINUX);
808 return install_loader(opt.directory, opt.update_only);