Clean up warnings in previous checkin
[profile/ivi/syslinux.git] / extlinux / main.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *
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.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * extlinux.c
16  *
17  * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
18  */
19
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>
43
44 #include <linux/fd.h>           /* Floppy geometry */
45 #include <linux/hdreg.h>        /* Hard disk geometry */
46 #define statfs _kernel_statfs   /* HACK to deal with broken 2.4 distros */
47 #include <linux/fs.h>           /* FIGETBSZ, FIBMAP */
48 #include <linux/msdos_fs.h>     /* FAT_IOCTL_SET_ATTRIBUTES */
49 #ifndef FAT_IOCTL_SET_ATTRIBUTES
50 # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
51 #endif
52 #undef statfs
53 #undef SECTOR_SIZE              /* Garbage from <linux/msdos_fs.h> */
54
55 #include "ext2_fs.h"
56 #include "btrfs.h"
57 #include "fat.h"
58 #include "../version.h"
59 #include "syslxint.h"
60 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
61 #include "setadv.h"
62 #include "syslxopt.h" /* unified options */
63
64 #ifdef DEBUG
65 # define dprintf printf
66 #else
67 # define dprintf(...) ((void)0)
68 #endif
69
70 #if defined(__linux__) && !defined(BLKGETSIZE64)
71 /* This takes a u64, but the size field says size_t.  Someone screwed big. */
72 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
73 #endif
74
75 #ifndef EXT2_SUPER_OFFSET
76 #define EXT2_SUPER_OFFSET 1024
77 #endif
78
79 /* the btrfs partition first 64K blank area is used to store boot sector and
80    boot image, the boot sector is from 0~512, the boot image starts at 2K */
81 #define BTRFS_EXTLINUX_OFFSET (2*1024)
82 #define BTRFS_SUBVOL_OPT "subvol="
83 #define BTRFS_SUBVOL_MAX 256    /* By btrfs specification */
84 static char subvol[BTRFS_SUBVOL_MAX];
85
86 /*
87  * Boot block
88  */
89 extern unsigned char extlinux_bootsect[];
90 extern unsigned int extlinux_bootsect_len;
91 #undef  boot_block
92 #undef  boot_block_len
93 #define boot_block      extlinux_bootsect
94 #define boot_block_len  extlinux_bootsect_len
95
96 /*
97  * Image file
98  */
99 extern unsigned char extlinux_image[];
100 extern unsigned int extlinux_image_len;
101 #undef  boot_image
102 #undef  boot_image_len
103 #define boot_image      extlinux_image
104 #define boot_image_len  extlinux_image_len
105
106 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
107
108 /*
109  * Get the size of a block device
110  */
111 uint64_t get_size(int devfd)
112 {
113     uint64_t bytes;
114     uint32_t sects;
115     struct stat st;
116
117 #ifdef BLKGETSIZE64
118     if (!ioctl(devfd, BLKGETSIZE64, &bytes))
119         return bytes;
120 #endif
121     if (!ioctl(devfd, BLKGETSIZE, &sects))
122         return (uint64_t) sects << 9;
123     else if (!fstat(devfd, &st) && st.st_size)
124         return st.st_size;
125     else
126         return 0;
127 }
128
129 /*
130  * Get device geometry and partition offset
131  */
132 struct geometry_table {
133     uint64_t bytes;
134     struct hd_geometry g;
135 };
136
137 /* Standard floppy disk geometries, plus LS-120.  Zipdisk geometry
138    (x/64/32) is the final fallback.  I don't know what LS-240 has
139    as its geometry, since I don't have one and don't know anyone that does,
140    and Google wasn't helpful... */
141 static const struct geometry_table standard_geometries[] = {
142     {360 * 1024, {2, 9, 40, 0}},
143     {720 * 1024, {2, 9, 80, 0}},
144     {1200 * 1024, {2, 15, 80, 0}},
145     {1440 * 1024, {2, 18, 80, 0}},
146     {1680 * 1024, {2, 21, 80, 0}},
147     {1722 * 1024, {2, 21, 80, 0}},
148     {2880 * 1024, {2, 36, 80, 0}},
149     {3840 * 1024, {2, 48, 80, 0}},
150     {123264 * 1024, {8, 32, 963, 0}},   /* LS120 */
151     {0, {0, 0, 0, 0}}
152 };
153
154 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
155 {
156     struct floppy_struct fd_str;
157     const struct geometry_table *gp;
158
159     memset(geo, 0, sizeof *geo);
160
161     if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
162         return 0;
163     } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
164         geo->heads = fd_str.head;
165         geo->sectors = fd_str.sect;
166         geo->cylinders = fd_str.track;
167         geo->start = 0;
168         return 0;
169     }
170
171     /* Didn't work.  Let's see if this is one of the standard geometries */
172     for (gp = standard_geometries; gp->bytes; gp++) {
173         if (gp->bytes == totalbytes) {
174             memcpy(geo, &gp->g, sizeof *geo);
175             return 0;
176         }
177     }
178
179     /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
180        what zipdisks use, so this would help if someone has a USB key that
181        they're booting in USB-ZIP mode. */
182
183     geo->heads = opt.heads ? : 64;
184     geo->sectors = opt.sectors ? : 32;
185     geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
186     geo->start = 0;
187
188     if (!opt.sectors && !opt.heads)
189         fprintf(stderr,
190                 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
191                 "         (on hard disks, this is usually harmless.)\n",
192                 geo->heads, geo->sectors);
193
194     return 1;
195 }
196
197 /*
198  * Query the device geometry and put it into the boot sector.
199  * Map the file and put the map in the boot sector and file.
200  * Stick the "current directory" inode number into the file.
201  *
202  * Returns the number of modified bytes in the boot file.
203  */
204 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
205 {
206     struct stat dirst, xdst;
207     struct hd_geometry geo;
208     uint32_t *sectp;
209     uint64_t totalbytes, totalsectors;
210     int nsect;
211     uint32_t *wp;
212     struct boot_sector *bs;
213     struct patch_area *patcharea;
214     int i, dw, nptrs;
215     uint32_t csum;
216     int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
217     char *dirpath, *subpath, *xdirpath, *xsubpath;
218
219     dirpath = realpath(dir, NULL);
220     if (!dirpath || stat(dir, &dirst)) {
221         perror("accessing install directory");
222         exit(255);              /* This should never happen */
223     }
224
225     if (lstat(dirpath, &xdst) ||
226         dirst.st_ino != xdst.st_ino ||
227         dirst.st_dev != xdst.st_dev) {
228         perror("realpath returned nonsense");
229         exit(255);
230     }
231
232     subpath = strchr(dirpath, '\0');
233     for (;;) {
234         if (*subpath == '/') {
235             if (subpath > dirpath) {
236                 *subpath = '\0';
237                 xsubpath = subpath+1;
238                 xdirpath = dirpath;
239             } else {
240                 xsubpath = subpath;
241                 xdirpath = "/";
242             }
243             if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
244                 subpath = strchr(subpath+1, '/');
245                 if (!subpath)
246                     subpath = "/"; /* It's the root of the filesystem */
247                 break;
248             }
249             *subpath = '/';
250         }
251
252         if (subpath == dirpath)
253             break;
254
255         subpath--;
256     }
257
258     /* Now subpath should contain the path relative to the fs base */
259     dprintf("subpath = %s\n", subpath);
260
261     totalbytes = get_size(devfd);
262     get_geometry(devfd, totalbytes, &geo);
263
264     if (opt.heads)
265         geo.heads = opt.heads;
266     if (opt.sectors)
267         geo.sectors = opt.sectors;
268
269     /* Patch this into a fake FAT superblock.  This isn't because
270        FAT is a good format in any way, it's because it lets the
271        early bootstrap share code with the FAT version. */
272     dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
273
274     bs = (struct boot_sector *)boot_block;
275
276     totalsectors = totalbytes >> SECTOR_SHIFT;
277     if (totalsectors >= 65536) {
278         set_16(&bs->bsSectors, 0);
279     } else {
280         set_16(&bs->bsSectors, totalsectors);
281     }
282     set_32(&bs->bsHugeSectors, totalsectors);
283
284     set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
285     set_16(&bs->bsSecPerTrack, geo.sectors);
286     set_16(&bs->bsHeads, geo.heads);
287     set_32(&bs->bsHiddenSecs, geo.start);
288
289     /* If we're in RAID mode then patch the appropriate instruction;
290        either way write the proper boot signature */
291     i = get_16(&bs->bsSignature);
292     if (opt.raid_mode)
293         set_16((uint16_t *) (boot_block + i), 0x18CD);  /* INT 18h */
294
295     set_16(&bs->bsSignature, 0xAA55);
296
297     /* Construct the boot file */
298
299     dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
300     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
301     nsect += 2;                 /* Two sectors for the ADV */
302     sectp = alloca(sizeof(uint32_t) * nsect);
303     if (fs_type == EXT2 || fs_type == VFAT) {
304         if (sectmap(fd, sectp, nsect)) {
305                 perror("bmap");
306                 exit(1);
307         }
308     } else if (fs_type == BTRFS) {
309         int i;
310
311         for (i = 0; i < nsect; i++)
312                 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
313     }
314
315     /* First sector need pointer in boot sector */
316     set_32(&bs->NextSector, *sectp++);
317     /* Stupid mode? */
318     if (opt.stupid_mode)
319         set_16(&bs->MaxTransfer, 1);
320
321     /* Search for LDLINUX_MAGIC to find the patch area */
322     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
323     patcharea = (struct patch_area *)wp;
324
325     /* Set up the totals */
326     dw = boot_image_len >> 2;   /* COMPLETE dwords, excluding ADV */
327     set_16(&patcharea->data_sectors, nsect - 2);        /* -2 for the ADVs */
328     set_16(&patcharea->adv_sectors, 2);
329     set_32(&patcharea->dwords, dw);
330
331     /* Set the sector pointers */
332     secptroffset = get_16(&patcharea->secptroffset);
333     wp = (uint32_t *) ((char *)boot_image + secptroffset);
334     nptrs = get_16(&patcharea->secptrcnt);
335
336     memset(wp, 0, nptrs * 4);
337     while (--nsect) /* the first sector in bs->NextSector */
338         set_32(wp++, *sectp++);
339
340     /* Poke in the base directory path */
341     diroffset = get_16(&patcharea->diroffset);
342     dirlen = get_16(&patcharea->dirlen);
343     if (dirlen <= strlen(subpath)) {
344         fprintf(stderr, "Subdirectory path too long... aborting install!\n");
345         exit(1);
346     }
347     strncpy((char *)boot_image + diroffset, subpath, dirlen);
348     free(dirpath);
349
350     /* write subvol info if we have */
351     subvoloffset = get_16(&patcharea->subvoloffset);
352     subvollen = get_16(&patcharea->subvollen);
353     if (subvollen <= strlen(subvol)) {
354         fprintf(stderr, "Subvol name too long... aborting install!\n");
355         exit(1);
356     }
357     strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
358
359     /* Now produce a checksum */
360     set_32(&patcharea->checksum, 0);
361
362     csum = LDLINUX_MAGIC;
363     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
364         csum -= get_32(wp);     /* Negative checksum */
365
366     set_32(&patcharea->checksum, csum);
367
368     /*
369      * Assume all bytes modified.  This can be optimized at the expense
370      * of keeping track of what the highest modified address ever was.
371      */
372     return dw << 2;
373 }
374
375 /*
376  * Make any user-specified ADV modifications
377  */
378 int modify_adv(void)
379 {
380     int rv = 0;
381
382     if (opt.set_once) {
383         if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
384             fprintf(stderr, "%s: not enough space for boot-once command\n",
385                     program);
386             rv = -1;
387         }
388     }
389     if (opt.menu_save) {
390         if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
391             fprintf(stderr, "%s: not enough space for menu-save label\n",
392                     program);
393             rv = -1;
394         }
395     }
396
397     return rv;
398 }
399
400 /*
401  * Install the boot block on the specified device.
402  * Must be run AFTER install_file()!
403  */
404 int install_bootblock(int fd, const char *device)
405 {
406     struct ext2_super_block sb;
407     struct btrfs_super_block sb2;
408     struct boot_sector sb3;
409     bool ok = false;
410
411     if (fs_type == EXT2) {
412         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
413                 perror("reading superblock");
414                 return 1;
415         }
416         if (sb.s_magic == EXT2_SUPER_MAGIC)
417                 ok = true;
418     } else if (fs_type == BTRFS) {
419         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
420                         != sizeof sb2) {
421                 perror("reading superblock");
422                 return 1;
423         }
424         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
425                 ok = true;
426     } else if (fs_type == VFAT) {
427         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
428                 perror("reading fat superblock");
429                 return 1;
430         }
431         if (sb3.bsResSectors && sb3.bsFATs &&
432             (strstr(sb3.bs16.FileSysType, "FAT") ||
433              strstr(sb3.bs32.FileSysType, "FAT")))
434                 ok = true;
435     }
436     if (!ok) {
437         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
438                         device);
439         return 1;
440     }
441     if (fs_type == VFAT) {
442         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
443         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
444             xpwrite(fd, &bs->bsCode, bsCodeLen,
445                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
446             perror("writing fat bootblock");
447             return 1;
448         }
449     } else {
450         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
451             perror("writing bootblock");
452             return 1;
453         }
454     }
455
456     return 0;
457 }
458
459 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
460 {
461     char *file;
462     int fd = -1, dirfd = -1;
463     int modbytes;
464
465     asprintf(&file, "%s%sextlinux.sys",
466              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
467     if (!file) {
468         perror(program);
469         return 1;
470     }
471
472     dirfd = open(path, O_RDONLY | O_DIRECTORY);
473     if (dirfd < 0) {
474         perror(path);
475         goto bail;
476     }
477
478     fd = open(file, O_RDONLY);
479     if (fd < 0) {
480         if (errno != ENOENT) {
481             perror(file);
482             goto bail;
483         }
484     } else {
485         clear_attributes(fd);
486     }
487     close(fd);
488
489     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
490               S_IRUSR | S_IRGRP | S_IROTH);
491     if (fd < 0) {
492         perror(file);
493         goto bail;
494     }
495
496     /* Write it the first time */
497     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
498         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
499                 boot_image_len) != 2 * ADV_SIZE) {
500         fprintf(stderr, "%s: write failure on %s\n", program, file);
501         goto bail;
502     }
503
504     /* Map the file, and patch the initial sector accordingly */
505     modbytes = patch_file_and_bootblock(fd, path, devfd);
506
507     /* Write the patch area again - this relies on the file being
508        overwritten in place! */
509     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
510         fprintf(stderr, "%s: write failure on %s\n", program, file);
511         goto bail;
512     }
513
514     /* Attempt to set immutable flag and remove all write access */
515     /* Only set immutable flag if file is owned by root */
516     set_attributes(fd);
517
518     if (fstat(fd, rst)) {
519         perror(file);
520         goto bail;
521     }
522
523     close(dirfd);
524     close(fd);
525     return 0;
526
527 bail:
528     if (dirfd >= 0)
529         close(dirfd);
530     if (fd >= 0)
531         close(fd);
532
533     return 1;
534 }
535
536 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
537    is not managered by btrfs tree, so actually this is not installed as files.
538    since the cow feature of btrfs will move the extlinux.sys every where */
539 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
540 {
541     patch_file_and_bootblock(-1, path, devfd);
542     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
543                 != boot_image_len) {
544         perror("writing bootblock");
545         return 1;
546     }
547     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
548     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
549                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
550         perror("writing adv");
551         return 1;
552     }
553     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
554     if (stat(path, rst)) {
555         perror(path);
556         return 1;
557     }
558     return 0;
559 }
560
561 int install_file(const char *path, int devfd, struct stat *rst)
562 {
563         if (fs_type == EXT2 || fs_type == VFAT)
564                 return ext2_fat_install_file(path, devfd, rst);
565         else if (fs_type == BTRFS)
566                 return btrfs_install_file(path, devfd, rst);
567         return 1;
568 }
569
570 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
571    sector; this is consistent with FAT filesystems. */
572 int already_installed(int devfd)
573 {
574     char buffer[8];
575
576     xpread(devfd, buffer, 8, 3);
577     return !memcmp(buffer, "EXTLINUX", 8);
578 }
579
580 #ifdef __KLIBC__
581 static char devname_buf[64];
582
583 static void device_cleanup(void)
584 {
585     unlink(devname_buf);
586 }
587 #endif
588
589 /* Verify that a device fd and a pathname agree.
590    Return 0 on valid, -1 on error. */
591 static int validate_device(const char *path, int devfd)
592 {
593     struct stat pst, dst;
594     struct statfs sfs;
595
596     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
597         return -1;
598     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
599     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
600         return 0;
601     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
602 }
603
604 #ifndef __KLIBC__
605 static const char *find_device(const char *mtab_file, dev_t dev)
606 {
607     struct mntent *mnt;
608     struct stat dst;
609     FILE *mtab;
610     const char *devname = NULL;
611     bool done;
612
613     mtab = setmntent(mtab_file, "r");
614     if (!mtab)
615         return NULL;
616
617     done = false;
618     while ((mnt = getmntent(mtab))) {
619         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
620         switch (fs_type) {
621         case BTRFS:
622                 if (!strcmp(mnt->mnt_type, "btrfs") &&
623                     !stat(mnt->mnt_dir, &dst) &&
624                     dst.st_dev == dev) {
625                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
626
627                     if (opt) {
628                         if (!subvol[0]) {
629                             char *tmp;
630
631                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
632                             tmp = strchr(subvol, 32);
633                             if (tmp)
634                                 *tmp = '\0';
635                         }
636                         break; /* should break and let upper layer try again */
637                     } else
638                         done = true;
639                 }
640                 break;
641         case EXT2:
642                 if ((!strcmp(mnt->mnt_type, "ext2") ||
643                      !strcmp(mnt->mnt_type, "ext3") ||
644                      !strcmp(mnt->mnt_type, "ext4")) &&
645                     !stat(mnt->mnt_fsname, &dst) &&
646                     dst.st_rdev == dev) {
647                     done = true;
648                     break;
649                 }
650         case VFAT:
651                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
652                     !stat(mnt->mnt_fsname, &dst) &&
653                     dst.st_rdev == dev) {
654                     done = true;
655                     break;
656                 }
657         case NONE:
658             break;
659         }
660         if (done) {
661                 devname = strdup(mnt->mnt_fsname);
662                 break;
663         }
664     }
665     endmntent(mtab);
666
667     return devname;
668 }
669 #endif
670
671 static const char *get_devname(const char *path)
672 {
673     const char *devname = NULL;
674     struct stat st;
675     struct statfs sfs;
676
677     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
678         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
679         return devname;
680     }
681     if (statfs(path, &sfs)) {
682         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
683         return devname;
684     }
685 #ifdef __KLIBC__
686
687     /* klibc doesn't have getmntent and friends; instead, just create
688        a new device with the appropriate device type */
689     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
690              major(st.st_dev), minor(st.st_dev));
691
692     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
693         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
694         return devname;
695     }
696
697     atexit(device_cleanup);     /* unlink the device node on exit */
698     devname = devname_buf;
699
700 #else
701
702     /* check /etc/mtab first, since btrfs subvol info is only in here */
703     devname = find_device("/etc/mtab", st.st_dev);
704     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
705         char parent[256];
706         char *tmp;
707
708         strcpy(parent, path);
709         tmp = strrchr(parent, '/');
710         if (tmp) {
711             *tmp = '\0';
712             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
713             devname = get_devname(parent);
714         } else
715             devname = NULL;
716     }
717     if (!devname) {
718         /* Didn't find it in /etc/mtab, try /proc/mounts */
719         devname = find_device("/proc/mounts", st.st_dev);
720     }
721     if (!devname) {
722         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
723         return devname;
724     }
725
726     fprintf(stderr, "%s is device %s\n", path, devname);
727 #endif
728     return devname;
729 }
730
731 static int open_device(const char *path, struct stat *st, const char **_devname)
732 {
733     int devfd;
734     const char *devname = NULL;
735     struct statfs sfs;
736
737     if (st)
738         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
739                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
740                 return -1;
741         }
742
743     if (statfs(path, &sfs)) {
744         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
745         return -1;
746     }
747     if (sfs.f_type == EXT2_SUPER_MAGIC)
748         fs_type = EXT2;
749     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
750         fs_type = BTRFS;
751     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
752         fs_type = VFAT;
753
754     if (!fs_type) {
755         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
756                 program, path);
757         return -1;
758     }
759
760     devfd = -1;
761     devname = get_devname(path);
762     if (_devname)
763         *_devname = devname;
764
765     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
766         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
767         return -1;
768     }
769
770     /* Verify that the device we opened is the device intended */
771     if (validate_device(path, devfd)) {
772         fprintf(stderr, "%s: path %s doesn't match device %s\n",
773                 program, path, devname);
774         close(devfd);
775         return -1;
776     }
777     return devfd;
778 }
779
780 static int ext_read_adv(const char *path, const char *cfg, int devfd)
781 {
782     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
783         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
784                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
785                 perror("btrfs writing adv");
786                 return 1;
787         }
788         return 0;
789     }
790     return read_adv(path, cfg);
791 }
792
793 static int ext_write_adv(const char *path, const char *cfg, int devfd)
794 {
795     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
796         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
797                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
798                 perror("writing adv");
799                 return 1;
800         }
801         return 0;
802     }
803     return write_adv(path, cfg);
804 }
805
806 int install_loader(const char *path, int update_only)
807 {
808     struct stat st, fst;
809     int devfd, rv;
810     const char *devname;
811
812     devfd = open_device(path, &st, &devname);
813     if (devfd < 0)
814         return 1;
815
816     if (update_only && !already_installed(devfd)) {
817         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
818                 program);
819         close(devfd);
820         return 1;
821     }
822
823     /* Read a pre-existing ADV, if already installed */
824     if (opt.reset_adv)
825         syslinux_reset_adv(syslinux_adv);
826     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
827         close(devfd);
828         return 1;
829     }
830     if (modify_adv() < 0) {
831         close(devfd);
832         return 1;
833     }
834
835     /* Install extlinux.sys */
836     if (install_file(path, devfd, &fst)) {
837         close(devfd);
838         return 1;
839     }
840     if (fst.st_dev != st.st_dev) {
841         fprintf(stderr, "%s: file system changed under us - aborting!\n",
842                 program);
843         close(devfd);
844         return 1;
845     }
846
847     sync();
848     rv = install_bootblock(devfd, devname);
849     close(devfd);
850     sync();
851
852     return rv;
853 }
854
855 /*
856  * Modify the ADV of an existing installation
857  */
858 int modify_existing_adv(const char *path)
859 {
860     int devfd;
861
862     devfd = open_device(path, NULL, NULL);
863     if (devfd < 0)
864         return 1;
865
866     if (opt.reset_adv)
867         syslinux_reset_adv(syslinux_adv);
868     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
869         close(devfd);
870         return 1;
871     }
872     if (modify_adv() < 0) {
873         close(devfd);
874         return 1;
875     }
876     if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
877         close(devfd);
878         return 1;
879     }
880     close(devfd);
881     return 0;
882 }
883
884 int main(int argc, char *argv[])
885 {
886     parse_options(argc, argv, 0);
887
888     if (!opt.directory)
889         usage(EX_USAGE, 0);
890
891     if (opt.update_only == -1) {
892         if (opt.reset_adv || opt.set_once || opt.menu_save)
893             return modify_existing_adv(opt.directory);
894         else
895             usage(EX_USAGE, 0);
896     }
897
898     return install_loader(opt.directory, opt.update_only);
899 }