Implement !GPT protocol in Syslinux core; handle offset > 2 TB
[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
318     /* Search for LDLINUX_MAGIC to find the patch area */
319     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
320     patcharea = (struct patch_area *)wp;
321
322     /* Set up the totals */
323     dw = boot_image_len >> 2;   /* COMPLETE dwords, excluding ADV */
324     set_16(&patcharea->data_sectors, nsect - 2);        /* -2 for the ADVs */
325     set_16(&patcharea->adv_sectors, 2);
326     set_32(&patcharea->dwords, dw);
327
328     /* Stupid mode? */
329     if (opt.stupid_mode)
330         set_16(&patcharea->maxtransfer, 1);
331
332     /* Set the sector pointers */
333     secptroffset = get_16(&patcharea->secptroffset);
334     wp = (uint32_t *) ((char *)boot_image + secptroffset);
335     nptrs = get_16(&patcharea->secptrcnt);
336
337     memset(wp, 0, nptrs * 4);
338     while (--nsect) /* the first sector in bs->NextSector */
339         set_32(wp++, *sectp++);
340
341     /* Poke in the base directory path */
342     diroffset = get_16(&patcharea->diroffset);
343     dirlen = get_16(&patcharea->dirlen);
344     if (dirlen <= strlen(subpath)) {
345         fprintf(stderr, "Subdirectory path too long... aborting install!\n");
346         exit(1);
347     }
348     strncpy((char *)boot_image + diroffset, subpath, dirlen);
349     free(dirpath);
350
351     /* write subvol info if we have */
352     subvoloffset = get_16(&patcharea->subvoloffset);
353     subvollen = get_16(&patcharea->subvollen);
354     if (subvollen <= strlen(subvol)) {
355         fprintf(stderr, "Subvol name too long... aborting install!\n");
356         exit(1);
357     }
358     strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
359
360     /* Now produce a checksum */
361     set_32(&patcharea->checksum, 0);
362
363     csum = LDLINUX_MAGIC;
364     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
365         csum -= get_32(wp);     /* Negative checksum */
366
367     set_32(&patcharea->checksum, csum);
368
369     /*
370      * Assume all bytes modified.  This can be optimized at the expense
371      * of keeping track of what the highest modified address ever was.
372      */
373     return dw << 2;
374 }
375
376 /*
377  * Make any user-specified ADV modifications
378  */
379 int modify_adv(void)
380 {
381     int rv = 0;
382
383     if (opt.set_once) {
384         if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
385             fprintf(stderr, "%s: not enough space for boot-once command\n",
386                     program);
387             rv = -1;
388         }
389     }
390     if (opt.menu_save) {
391         if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
392             fprintf(stderr, "%s: not enough space for menu-save label\n",
393                     program);
394             rv = -1;
395         }
396     }
397
398     return rv;
399 }
400
401 /*
402  * Install the boot block on the specified device.
403  * Must be run AFTER install_file()!
404  */
405 int install_bootblock(int fd, const char *device)
406 {
407     struct ext2_super_block sb;
408     struct btrfs_super_block sb2;
409     struct boot_sector sb3;
410     bool ok = false;
411
412     if (fs_type == EXT2) {
413         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
414                 perror("reading superblock");
415                 return 1;
416         }
417         if (sb.s_magic == EXT2_SUPER_MAGIC)
418                 ok = true;
419     } else if (fs_type == BTRFS) {
420         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
421                         != sizeof sb2) {
422                 perror("reading superblock");
423                 return 1;
424         }
425         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
426                 ok = true;
427     } else if (fs_type == VFAT) {
428         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
429                 perror("reading fat superblock");
430                 return 1;
431         }
432         if (sb3.bsResSectors && sb3.bsFATs &&
433             (strstr(sb3.bs16.FileSysType, "FAT") ||
434              strstr(sb3.bs32.FileSysType, "FAT")))
435                 ok = true;
436     }
437     if (!ok) {
438         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
439                         device);
440         return 1;
441     }
442     if (fs_type == VFAT) {
443         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
444         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
445             xpwrite(fd, &bs->bsCode, bsCodeLen,
446                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
447             perror("writing fat bootblock");
448             return 1;
449         }
450     } else {
451         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
452             perror("writing bootblock");
453             return 1;
454         }
455     }
456
457     return 0;
458 }
459
460 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
461 {
462     char *file;
463     int fd = -1, dirfd = -1;
464     int modbytes;
465
466     asprintf(&file, "%s%sextlinux.sys",
467              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
468     if (!file) {
469         perror(program);
470         return 1;
471     }
472
473     dirfd = open(path, O_RDONLY | O_DIRECTORY);
474     if (dirfd < 0) {
475         perror(path);
476         goto bail;
477     }
478
479     fd = open(file, O_RDONLY);
480     if (fd < 0) {
481         if (errno != ENOENT) {
482             perror(file);
483             goto bail;
484         }
485     } else {
486         clear_attributes(fd);
487     }
488     close(fd);
489
490     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
491               S_IRUSR | S_IRGRP | S_IROTH);
492     if (fd < 0) {
493         perror(file);
494         goto bail;
495     }
496
497     /* Write it the first time */
498     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
499         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
500                 boot_image_len) != 2 * ADV_SIZE) {
501         fprintf(stderr, "%s: write failure on %s\n", program, file);
502         goto bail;
503     }
504
505     /* Map the file, and patch the initial sector accordingly */
506     modbytes = patch_file_and_bootblock(fd, path, devfd);
507
508     /* Write the patch area again - this relies on the file being
509        overwritten in place! */
510     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
511         fprintf(stderr, "%s: write failure on %s\n", program, file);
512         goto bail;
513     }
514
515     /* Attempt to set immutable flag and remove all write access */
516     /* Only set immutable flag if file is owned by root */
517     set_attributes(fd);
518
519     if (fstat(fd, rst)) {
520         perror(file);
521         goto bail;
522     }
523
524     close(dirfd);
525     close(fd);
526     return 0;
527
528 bail:
529     if (dirfd >= 0)
530         close(dirfd);
531     if (fd >= 0)
532         close(fd);
533
534     return 1;
535 }
536
537 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
538    is not managered by btrfs tree, so actually this is not installed as files.
539    since the cow feature of btrfs will move the extlinux.sys every where */
540 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
541 {
542     patch_file_and_bootblock(-1, path, devfd);
543     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
544                 != boot_image_len) {
545         perror("writing bootblock");
546         return 1;
547     }
548     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
549     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
550                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
551         perror("writing adv");
552         return 1;
553     }
554     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
555     if (stat(path, rst)) {
556         perror(path);
557         return 1;
558     }
559     return 0;
560 }
561
562 int install_file(const char *path, int devfd, struct stat *rst)
563 {
564         if (fs_type == EXT2 || fs_type == VFAT)
565                 return ext2_fat_install_file(path, devfd, rst);
566         else if (fs_type == BTRFS)
567                 return btrfs_install_file(path, devfd, rst);
568         return 1;
569 }
570
571 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
572    sector; this is consistent with FAT filesystems. */
573 int already_installed(int devfd)
574 {
575     char buffer[8];
576
577     xpread(devfd, buffer, 8, 3);
578     return !memcmp(buffer, "EXTLINUX", 8);
579 }
580
581 #ifdef __KLIBC__
582 static char devname_buf[64];
583
584 static void device_cleanup(void)
585 {
586     unlink(devname_buf);
587 }
588 #endif
589
590 /* Verify that a device fd and a pathname agree.
591    Return 0 on valid, -1 on error. */
592 static int validate_device(const char *path, int devfd)
593 {
594     struct stat pst, dst;
595     struct statfs sfs;
596
597     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
598         return -1;
599     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
600     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
601         return 0;
602     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
603 }
604
605 #ifndef __KLIBC__
606 static const char *find_device(const char *mtab_file, dev_t dev)
607 {
608     struct mntent *mnt;
609     struct stat dst;
610     FILE *mtab;
611     const char *devname = NULL;
612     bool done;
613
614     mtab = setmntent(mtab_file, "r");
615     if (!mtab)
616         return NULL;
617
618     done = false;
619     while ((mnt = getmntent(mtab))) {
620         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
621         switch (fs_type) {
622         case BTRFS:
623                 if (!strcmp(mnt->mnt_type, "btrfs") &&
624                     !stat(mnt->mnt_dir, &dst) &&
625                     dst.st_dev == dev) {
626                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
627
628                     if (opt) {
629                         if (!subvol[0]) {
630                             char *tmp;
631
632                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
633                             tmp = strchr(subvol, 32);
634                             if (tmp)
635                                 *tmp = '\0';
636                         }
637                         break; /* should break and let upper layer try again */
638                     } else
639                         done = true;
640                 }
641                 break;
642         case EXT2:
643                 if ((!strcmp(mnt->mnt_type, "ext2") ||
644                      !strcmp(mnt->mnt_type, "ext3") ||
645                      !strcmp(mnt->mnt_type, "ext4")) &&
646                     !stat(mnt->mnt_fsname, &dst) &&
647                     dst.st_rdev == dev) {
648                     done = true;
649                     break;
650                 }
651         case VFAT:
652                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
653                     !stat(mnt->mnt_fsname, &dst) &&
654                     dst.st_rdev == dev) {
655                     done = true;
656                     break;
657                 }
658         case NONE:
659             break;
660         }
661         if (done) {
662                 devname = strdup(mnt->mnt_fsname);
663                 break;
664         }
665     }
666     endmntent(mtab);
667
668     return devname;
669 }
670 #endif
671
672 static const char *get_devname(const char *path)
673 {
674     const char *devname = NULL;
675     struct stat st;
676     struct statfs sfs;
677
678     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
679         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
680         return devname;
681     }
682     if (statfs(path, &sfs)) {
683         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
684         return devname;
685     }
686 #ifdef __KLIBC__
687
688     /* klibc doesn't have getmntent and friends; instead, just create
689        a new device with the appropriate device type */
690     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
691              major(st.st_dev), minor(st.st_dev));
692
693     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
694         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
695         return devname;
696     }
697
698     atexit(device_cleanup);     /* unlink the device node on exit */
699     devname = devname_buf;
700
701 #else
702
703     /* check /etc/mtab first, since btrfs subvol info is only in here */
704     devname = find_device("/etc/mtab", st.st_dev);
705     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
706         char parent[256];
707         char *tmp;
708
709         strcpy(parent, path);
710         tmp = strrchr(parent, '/');
711         if (tmp) {
712             *tmp = '\0';
713             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
714             devname = get_devname(parent);
715         } else
716             devname = NULL;
717     }
718     if (!devname) {
719         /* Didn't find it in /etc/mtab, try /proc/mounts */
720         devname = find_device("/proc/mounts", st.st_dev);
721     }
722     if (!devname) {
723         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
724         return devname;
725     }
726
727     fprintf(stderr, "%s is device %s\n", path, devname);
728 #endif
729     return devname;
730 }
731
732 static int open_device(const char *path, struct stat *st, const char **_devname)
733 {
734     int devfd;
735     const char *devname = NULL;
736     struct statfs sfs;
737
738     if (st)
739         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
740                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
741                 return -1;
742         }
743
744     if (statfs(path, &sfs)) {
745         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
746         return -1;
747     }
748     if (sfs.f_type == EXT2_SUPER_MAGIC)
749         fs_type = EXT2;
750     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
751         fs_type = BTRFS;
752     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
753         fs_type = VFAT;
754
755     if (!fs_type) {
756         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
757                 program, path);
758         return -1;
759     }
760
761     devfd = -1;
762     devname = get_devname(path);
763     if (_devname)
764         *_devname = devname;
765
766     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
767         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
768         return -1;
769     }
770
771     /* Verify that the device we opened is the device intended */
772     if (validate_device(path, devfd)) {
773         fprintf(stderr, "%s: path %s doesn't match device %s\n",
774                 program, path, devname);
775         close(devfd);
776         return -1;
777     }
778     return devfd;
779 }
780
781 static int ext_read_adv(const char *path, const char *cfg, int devfd)
782 {
783     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
784         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
785                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
786                 perror("btrfs writing adv");
787                 return 1;
788         }
789         return 0;
790     }
791     return read_adv(path, cfg);
792 }
793
794 static int ext_write_adv(const char *path, const char *cfg, int devfd)
795 {
796     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
797         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
798                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
799                 perror("writing adv");
800                 return 1;
801         }
802         return 0;
803     }
804     return write_adv(path, cfg);
805 }
806
807 int install_loader(const char *path, int update_only)
808 {
809     struct stat st, fst;
810     int devfd, rv;
811     const char *devname;
812
813     devfd = open_device(path, &st, &devname);
814     if (devfd < 0)
815         return 1;
816
817     if (update_only && !already_installed(devfd)) {
818         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
819                 program);
820         close(devfd);
821         return 1;
822     }
823
824     /* Read a pre-existing ADV, if already installed */
825     if (opt.reset_adv)
826         syslinux_reset_adv(syslinux_adv);
827     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
828         close(devfd);
829         return 1;
830     }
831     if (modify_adv() < 0) {
832         close(devfd);
833         return 1;
834     }
835
836     /* Install extlinux.sys */
837     if (install_file(path, devfd, &fst)) {
838         close(devfd);
839         return 1;
840     }
841     if (fst.st_dev != st.st_dev) {
842         fprintf(stderr, "%s: file system changed under us - aborting!\n",
843                 program);
844         close(devfd);
845         return 1;
846     }
847
848     sync();
849     rv = install_bootblock(devfd, devname);
850     close(devfd);
851     sync();
852
853     return rv;
854 }
855
856 /*
857  * Modify the ADV of an existing installation
858  */
859 int modify_existing_adv(const char *path)
860 {
861     int devfd;
862
863     devfd = open_device(path, NULL, NULL);
864     if (devfd < 0)
865         return 1;
866
867     if (opt.reset_adv)
868         syslinux_reset_adv(syslinux_adv);
869     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
870         close(devfd);
871         return 1;
872     }
873     if (modify_adv() < 0) {
874         close(devfd);
875         return 1;
876     }
877     if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
878         close(devfd);
879         return 1;
880     }
881     close(devfd);
882     return 0;
883 }
884
885 int main(int argc, char *argv[])
886 {
887     parse_options(argc, argv, MODE_EXTLINUX);
888
889     if (!opt.directory)
890         usage(EX_USAGE, 0);
891
892     if (opt.update_only == -1) {
893         if (opt.reset_adv || opt.set_once || opt.menu_save)
894             return modify_existing_adv(opt.directory);
895         else
896             usage(EX_USAGE, MODE_EXTLINUX);
897     }
898
899     return install_loader(opt.directory, opt.update_only);
900 }