extlinux: use sysfs to find the device node if need be
[syslinux/sherbszt.git] / extlinux / main.c
blob5da89e2d78bf4a0bbb085fe4fd9e66fe8cac0bcd
1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2012 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, ntfs, 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 #include <alloca.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <dirent.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 "ntfs.h"
49 #include "../version.h"
50 #include "syslxint.h"
51 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
52 #include "syslxfs.h"
53 #include "setadv.h"
54 #include "syslxopt.h" /* unified options */
56 #ifdef DEBUG
57 # define dprintf printf
58 #else
59 # define dprintf(...) ((void)0)
60 #endif
62 #ifndef EXT2_SUPER_OFFSET
63 #define EXT2_SUPER_OFFSET 1024
64 #endif
66 /* the btrfs partition first 64K blank area is used to store boot sector and
67 boot image, the boot sector is from 0~512, the boot image starts after */
68 #define BTRFS_BOOTSECT_AREA 65536
69 #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
70 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
71 static char subvol[BTRFS_SUBVOL_MAX];
73 #define BTRFS_ADV_OFFSET (BTRFS_BOOTSECT_AREA - 2 * ADV_SIZE)
76 * Get the size of a block device
78 uint64_t get_size(int devfd)
80 uint64_t bytes;
81 uint32_t sects;
82 struct stat st;
84 #ifdef BLKGETSIZE64
85 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
86 return bytes;
87 #endif
88 if (!ioctl(devfd, BLKGETSIZE, &sects))
89 return (uint64_t) sects << 9;
90 else if (!fstat(devfd, &st) && st.st_size)
91 return st.st_size;
92 else
93 return 0;
97 * Get device geometry and partition offset
99 struct geometry_table {
100 uint64_t bytes;
101 struct hd_geometry g;
104 static int sysfs_get_offset(int devfd, unsigned long *start)
106 struct stat st;
107 char sysfs_name[128];
108 FILE *f;
109 int rv;
111 if (fstat(devfd, &st))
112 return -1;
114 if ((size_t)snprintf(sysfs_name, sizeof sysfs_name,
115 "/sys/dev/block/%u:%u/start",
116 major(st.st_rdev), minor(st.st_rdev))
117 >= sizeof sysfs_name)
118 return -1;
120 f = fopen(sysfs_name, "r");
121 if (!f)
122 return -1;
124 rv = fscanf(f, "%lu", start);
125 fclose(f);
127 return (rv == 1) ? 0 : -1;
130 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
131 (x/64/32) is the final fallback. I don't know what LS-240 has
132 as its geometry, since I don't have one and don't know anyone that does,
133 and Google wasn't helpful... */
134 static const struct geometry_table standard_geometries[] = {
135 {360 * 1024, {2, 9, 40, 0}},
136 {720 * 1024, {2, 9, 80, 0}},
137 {1200 * 1024, {2, 15, 80, 0}},
138 {1440 * 1024, {2, 18, 80, 0}},
139 {1680 * 1024, {2, 21, 80, 0}},
140 {1722 * 1024, {2, 21, 80, 0}},
141 {2880 * 1024, {2, 36, 80, 0}},
142 {3840 * 1024, {2, 48, 80, 0}},
143 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
144 {0, {0, 0, 0, 0}}
147 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
149 struct floppy_struct fd_str;
150 struct loop_info li;
151 struct loop_info64 li64;
152 const struct geometry_table *gp;
153 int rv = 0;
155 memset(geo, 0, sizeof *geo);
157 if (!ioctl(devfd, HDIO_GETGEO, geo)) {
158 goto ok;
159 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
160 geo->heads = fd_str.head;
161 geo->sectors = fd_str.sect;
162 geo->cylinders = fd_str.track;
163 geo->start = 0;
164 goto ok;
167 /* Didn't work. Let's see if this is one of the standard geometries */
168 for (gp = standard_geometries; gp->bytes; gp++) {
169 if (gp->bytes == totalbytes) {
170 memcpy(geo, &gp->g, sizeof *geo);
171 goto ok;
175 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
176 what zipdisks use, so this would help if someone has a USB key that
177 they're booting in USB-ZIP mode. */
179 geo->heads = opt.heads ? : 64;
180 geo->sectors = opt.sectors ? : 32;
181 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
182 geo->start = 0;
184 if (!opt.sectors && !opt.heads) {
185 fprintf(stderr,
186 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
187 " (on hard disks, this is usually harmless.)\n",
188 geo->heads, geo->sectors);
189 rv = 1; /* Suboptimal result */
193 /* If this is a loopback device, try to set the start */
194 if (!ioctl(devfd, LOOP_GET_STATUS64, &li64))
195 geo->start = li64.lo_offset >> SECTOR_SHIFT;
196 else if (!ioctl(devfd, LOOP_GET_STATUS, &li))
197 geo->start = (unsigned int)li.lo_offset >> SECTOR_SHIFT;
198 else if (!sysfs_get_offset(devfd, &geo->start)) {
199 /* OK */
202 return rv;
206 * Query the device geometry and put it into the boot sector.
207 * Map the file and put the map in the boot sector and file.
208 * Stick the "current directory" inode number into the file.
210 * Returns the number of modified bytes in the boot file.
212 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
214 struct stat dirst, xdst;
215 struct hd_geometry geo;
216 sector_t *sectp;
217 uint64_t totalbytes, totalsectors;
218 int nsect;
219 struct fat_boot_sector *sbs;
220 char *dirpath, *subpath, *xdirpath;
221 int rv;
223 dirpath = realpath(dir, NULL);
224 if (!dirpath || stat(dir, &dirst)) {
225 perror("accessing install directory");
226 exit(255); /* This should never happen */
229 if (lstat(dirpath, &xdst) ||
230 dirst.st_ino != xdst.st_ino ||
231 dirst.st_dev != xdst.st_dev) {
232 perror("realpath returned nonsense");
233 exit(255);
236 subpath = strchr(dirpath, '\0');
237 for (;;) {
238 if (*subpath == '/') {
239 if (subpath > dirpath) {
240 *subpath = '\0';
241 xdirpath = dirpath;
242 } else {
243 xdirpath = "/";
245 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
246 subpath = strchr(subpath+1, '/');
247 if (!subpath)
248 subpath = "/"; /* It's the root of the filesystem */
249 break;
251 *subpath = '/';
254 if (subpath == dirpath)
255 break;
257 subpath--;
260 /* Now subpath should contain the path relative to the fs base */
261 dprintf("subpath = %s\n", subpath);
263 totalbytes = get_size(devfd);
264 get_geometry(devfd, totalbytes, &geo);
266 if (opt.heads)
267 geo.heads = opt.heads;
268 if (opt.sectors)
269 geo.sectors = opt.sectors;
271 /* Patch this into a fake FAT superblock. This isn't because
272 FAT is a good format in any way, it's because it lets the
273 early bootstrap share code with the FAT version. */
274 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
276 sbs = (struct fat_boot_sector *)syslinux_bootsect;
278 totalsectors = totalbytes >> SECTOR_SHIFT;
279 if (totalsectors >= 65536) {
280 set_16(&sbs->bsSectors, 0);
281 } else {
282 set_16(&sbs->bsSectors, totalsectors);
284 set_32(&sbs->bsHugeSectors, totalsectors);
286 set_16(&sbs->bsBytesPerSec, SECTOR_SIZE);
287 set_16(&sbs->bsSecPerTrack, geo.sectors);
288 set_16(&sbs->bsHeads, geo.heads);
289 set_32(&sbs->bsHiddenSecs, geo.start);
291 /* Construct the boot file map */
293 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
294 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
295 nsect += 2; /* Two sectors for the ADV */
296 sectp = alloca(sizeof(sector_t) * nsect);
297 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) {
298 if (sectmap(fd, sectp, nsect)) {
299 perror("bmap");
300 exit(1);
302 } else if (fs_type == BTRFS) {
303 int i;
304 sector_t *sp = sectp;
306 for (i = 0; i < nsect - 2; i++)
307 *sp++ = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
308 for (i = 0; i < 2; i++)
309 *sp++ = BTRFS_ADV_OFFSET/SECTOR_SIZE + i;
312 /* Create the modified image in memory */
313 rv = syslinux_patch(sectp, nsect, opt.stupid_mode,
314 opt.raid_mode, subpath, subvol);
316 free(dirpath);
317 return rv;
321 * Install the boot block on the specified device.
322 * Must be run AFTER install_file()!
324 int install_bootblock(int fd, const char *device)
326 struct ext2_super_block sb;
327 struct btrfs_super_block sb2;
328 struct fat_boot_sector sb3;
329 struct ntfs_boot_sector sb4;
330 bool ok = false;
332 if (fs_type == EXT2) {
333 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
334 perror("reading superblock");
335 return 1;
337 if (sb.s_magic == EXT2_SUPER_MAGIC)
338 ok = true;
339 } else if (fs_type == BTRFS) {
340 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
341 != sizeof sb2) {
342 perror("reading superblock");
343 return 1;
345 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
346 ok = true;
347 } else if (fs_type == VFAT) {
348 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
349 perror("reading fat superblock");
350 return 1;
352 if (fat_check_sb_fields(&sb3))
353 ok = true;
354 } else if (fs_type == NTFS) {
355 if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) {
356 perror("reading ntfs superblock");
357 return 1;
360 if (ntfs_check_sb_fields(&sb4))
361 ok = true;
363 if (!ok) {
364 fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n",
365 device);
366 return 1;
368 if (fs_type == VFAT) {
369 struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect;
370 if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen ||
371 xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen,
372 offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) {
373 perror("writing fat bootblock");
374 return 1;
376 } else if (fs_type == NTFS) {
377 struct ntfs_boot_sector *sbs =
378 (struct ntfs_boot_sector *)syslinux_bootsect;
379 if (xpwrite(fd, &sbs->NTFS_bsHead,
380 NTFS_bsHeadLen, 0) != NTFS_bsHeadLen ||
381 xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen,
382 offsetof(struct ntfs_boot_sector,
383 NTFS_bsCode)) != NTFS_bsCodeLen) {
384 perror("writing ntfs bootblock");
385 return 1;
387 } else {
388 if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
389 != syslinux_bootsect_len) {
390 perror("writing bootblock");
391 return 1;
395 return 0;
398 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
400 char *file, *oldfile;
401 int fd = -1, dirfd = -1;
402 int modbytes;
403 int r1, r2;
405 r1 = asprintf(&file, "%s%sldlinux.sys",
406 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
407 r2 = asprintf(&oldfile, "%s%sextlinux.sys",
408 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
409 if (r1 < 0 || !file || r2 < 0 || !oldfile) {
410 perror(program);
411 return 1;
414 dirfd = open(path, O_RDONLY | O_DIRECTORY);
415 if (dirfd < 0) {
416 perror(path);
417 goto bail;
420 fd = open(file, O_RDONLY);
421 if (fd < 0) {
422 if (errno != ENOENT) {
423 perror(file);
424 goto bail;
426 } else {
427 clear_attributes(fd);
429 close(fd);
431 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
432 S_IRUSR | S_IRGRP | S_IROTH);
433 if (fd < 0) {
434 perror(file);
435 goto bail;
438 /* Write it the first time */
439 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
440 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
441 boot_image_len) != 2 * ADV_SIZE) {
442 fprintf(stderr, "%s: write failure on %s\n", program, file);
443 goto bail;
446 /* Map the file, and patch the initial sector accordingly */
447 modbytes = patch_file_and_bootblock(fd, path, devfd);
449 /* Write the patch area again - this relies on the file being
450 overwritten in place! */
451 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
452 fprintf(stderr, "%s: write failure on %s\n", program, file);
453 goto bail;
456 /* Attempt to set immutable flag and remove all write access */
457 /* Only set immutable flag if file is owned by root */
458 set_attributes(fd);
460 if (fstat(fd, rst)) {
461 perror(file);
462 goto bail;
465 close(dirfd);
466 close(fd);
468 /* Look if we have the old filename */
469 fd = open(oldfile, O_RDONLY);
470 if (fd >= 0) {
471 clear_attributes(fd);
472 close(fd);
473 unlink(oldfile);
476 free(file);
477 free(oldfile);
478 return 0;
480 bail:
481 if (dirfd >= 0)
482 close(dirfd);
483 if (fd >= 0)
484 close(fd);
486 free(file);
487 free(oldfile);
488 return 1;
491 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
492 is not managered by btrfs tree, so actually this is not installed as files.
493 since the cow feature of btrfs will move the ldlinux.sys every where */
494 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
496 patch_file_and_bootblock(-1, path, devfd);
497 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
498 != boot_image_len) {
499 perror("writing bootblock");
500 return 1;
502 dprintf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
503 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE, BTRFS_ADV_OFFSET)
504 != 2 * ADV_SIZE) {
505 perror("writing adv");
506 return 1;
508 dprintf("write adv to 0x%x\n", BTRFS_ADV_OFFSET);
509 if (stat(path, rst)) {
510 perror(path);
511 return 1;
513 return 0;
517 * * test if path is a subvolume:
518 * * this function return
519 * * 0-> path exists but it is not a subvolume
520 * * 1-> path exists and it is a subvolume
521 * * -1 -> path is unaccessible
522 * */
523 static int test_issubvolume(char *path)
526 struct stat st;
527 int res;
529 res = stat(path, &st);
530 if(res < 0 )
531 return -1;
533 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
538 * Get file handle for a file or dir
540 static int open_file_or_dir(const char *fname)
542 int ret;
543 struct stat st;
544 DIR *dirstream;
545 int fd;
547 ret = stat(fname, &st);
548 if (ret < 0) {
549 return -1;
551 if (S_ISDIR(st.st_mode)) {
552 dirstream = opendir(fname);
553 if (!dirstream) {
554 return -2;
556 fd = dirfd(dirstream);
557 } else {
558 fd = open(fname, O_RDWR);
560 if (fd < 0) {
561 return -3;
563 return fd;
567 * Get the default subvolume of a btrfs filesystem
568 * rootdir: btrfs root dir
569 * subvol: this function will save the default subvolume name here
571 static char * get_default_subvol(char * rootdir, char * subvol)
573 struct btrfs_ioctl_search_args args;
574 struct btrfs_ioctl_search_key *sk = &args.key;
575 struct btrfs_ioctl_search_header *sh;
576 int ret, i;
577 int fd;
578 struct btrfs_root_ref *ref;
579 struct btrfs_dir_item *dir_item;
580 unsigned long off = 0;
581 int name_len;
582 char *name;
583 char dirname[4096];
584 u64 defaultsubvolid = 0;
586 ret = test_issubvolume(rootdir);
587 if (ret == 1) {
588 fd = open_file_or_dir(rootdir);
589 if (fd < 0) {
590 fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
592 ret = fd;
594 if (ret <= 0) {
595 subvol[0] = '\0';
596 return NULL;
599 memset(&args, 0, sizeof(args));
601 /* search in the tree of tree roots */
602 sk->tree_id = 1;
605 * set the min and max to backref keys. The search will
606 * only send back this type of key now.
608 sk->max_type = BTRFS_DIR_ITEM_KEY;
609 sk->min_type = BTRFS_DIR_ITEM_KEY;
612 * set all the other params to the max, we'll take any objectid
613 * and any trans
615 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
616 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
618 sk->max_offset = (u64)-1;
619 sk->min_offset = 0;
620 sk->max_transid = (u64)-1;
622 /* just a big number, doesn't matter much */
623 sk->nr_items = 4096;
625 while(1) {
626 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
627 if (ret < 0) {
628 fprintf(stderr, "ERROR: can't perform the search\n");
629 subvol[0] = '\0';
630 return NULL;
632 /* the ioctl returns the number of item it found in nr_items */
633 if (sk->nr_items == 0) {
634 break;
637 off = 0;
640 * for each item, pull the key out of the header and then
641 * read the root_ref item it contains
643 for (i = 0; i < sk->nr_items; i++) {
644 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
645 off += sizeof(*sh);
646 if (sh->type == BTRFS_DIR_ITEM_KEY) {
647 dir_item = (struct btrfs_dir_item *)(args.buf + off);
648 name_len = dir_item->name_len;
649 name = (char *)(dir_item + 1);
652 /*add_root(&root_lookup, sh->objectid, sh->offset,
653 dir_id, name, name_len);*/
654 strncpy(dirname, name, name_len);
655 dirname[name_len] = '\0';
656 if (strcmp(dirname, "default") == 0) {
657 defaultsubvolid = dir_item->location.objectid;
658 break;
661 off += sh->len;
664 * record the mins in sk so we can make sure the
665 * next search doesn't repeat this root
667 sk->min_objectid = sh->objectid;
668 sk->min_type = sh->type;
669 sk->max_type = sh->type;
670 sk->min_offset = sh->offset;
672 if (defaultsubvolid != 0)
673 break;
674 sk->nr_items = 4096;
675 /* this iteration is done, step forward one root for the next
676 * ioctl
678 if (sk->min_objectid < (u64)-1) {
679 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
680 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
681 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
682 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
683 sk->min_offset = 0;
684 } else
685 break;
688 if (defaultsubvolid == 0) {
689 subvol[0] = '\0';
690 return NULL;
693 memset(&args, 0, sizeof(args));
695 /* search in the tree of tree roots */
696 sk->tree_id = 1;
699 * set the min and max to backref keys. The search will
700 * only send back this type of key now.
702 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
703 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
706 * set all the other params to the max, we'll take any objectid
707 * and any trans
709 sk->max_objectid = (u64)-1;
710 sk->max_offset = (u64)-1;
711 sk->max_transid = (u64)-1;
713 /* just a big number, doesn't matter much */
714 sk->nr_items = 4096;
716 while(1) {
717 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
718 if (ret < 0) {
719 fprintf(stderr, "ERROR: can't perform the search\n");
720 subvol[0] = '\0';
721 return NULL;
723 /* the ioctl returns the number of item it found in nr_items */
724 if (sk->nr_items == 0)
725 break;
727 off = 0;
730 * for each item, pull the key out of the header and then
731 * read the root_ref item it contains
733 for (i = 0; i < sk->nr_items; i++) {
734 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
735 off += sizeof(*sh);
736 if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
737 ref = (struct btrfs_root_ref *)(args.buf + off);
738 name_len = ref->name_len;
739 name = (char *)(ref + 1);
741 if (sh->objectid == defaultsubvolid) {
742 strncpy(subvol, name, name_len);
743 subvol[name_len] = '\0';
744 dprintf("The default subvolume: %s, ID: %llu\n",
745 subvol, sh->objectid);
746 break;
751 off += sh->len;
754 * record the mins in sk so we can make sure the
755 * next search doesn't repeat this root
757 sk->min_objectid = sh->objectid;
758 sk->min_type = sh->type;
759 sk->min_offset = sh->offset;
761 if (subvol[0] != '\0')
762 break;
763 sk->nr_items = 4096;
764 /* this iteration is done, step forward one root for the next
765 * ioctl
767 if (sk->min_objectid < (u64)-1) {
768 sk->min_objectid++;
769 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
770 sk->min_offset = 0;
771 } else
772 break;
774 return subvol;
777 int install_file(const char *path, int devfd, struct stat *rst)
779 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
780 return ext2_fat_install_file(path, devfd, rst);
781 else if (fs_type == BTRFS)
782 return btrfs_install_file(path, devfd, rst);
783 return 1;
786 #ifdef __KLIBC__
787 static char devname_buf[64];
789 static void device_cleanup(void)
791 unlink(devname_buf);
793 #endif
795 /* Verify that a device fd and a pathname agree.
796 Return 0 on valid, -1 on error. */
797 static int validate_device(const char *path, int devfd)
799 struct stat pst, dst;
800 struct statfs sfs;
802 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
803 return -1;
804 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
805 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
806 return 0;
807 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
810 #ifndef __KLIBC__
811 static const char *find_device(const char *mtab_file, dev_t dev)
813 struct mntent *mnt;
814 struct stat dst;
815 FILE *mtab;
816 const char *devname = NULL;
817 bool done;
819 mtab = setmntent(mtab_file, "r");
820 if (!mtab)
821 return NULL;
823 done = false;
824 while ((mnt = getmntent(mtab))) {
825 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
826 switch (fs_type) {
827 case BTRFS:
828 if (!strcmp(mnt->mnt_type, "btrfs") &&
829 !stat(mnt->mnt_dir, &dst) &&
830 dst.st_dev == dev) {
831 if (!subvol[0])
832 get_default_subvol(mnt->mnt_dir, subvol);
833 done = true;
835 break;
836 case EXT2:
837 if ((!strcmp(mnt->mnt_type, "ext2") ||
838 !strcmp(mnt->mnt_type, "ext3") ||
839 !strcmp(mnt->mnt_type, "ext4")) &&
840 !stat(mnt->mnt_fsname, &dst) &&
841 dst.st_rdev == dev) {
842 done = true;
843 break;
845 case VFAT:
846 if ((!strcmp(mnt->mnt_type, "vfat")) &&
847 !stat(mnt->mnt_fsname, &dst) &&
848 dst.st_rdev == dev) {
849 done = true;
850 break;
852 case NTFS:
853 if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ ||
854 !strcmp(mnt->mnt_type, "ntfs")) &&
855 !stat(mnt->mnt_fsname, &dst) &&
856 dst.st_rdev == dev) {
857 done = true;
858 break;
861 break;
862 case NONE:
863 break;
865 if (done) {
866 devname = strdup(mnt->mnt_fsname);
867 break;
870 endmntent(mtab);
872 return devname;
874 #endif
877 * On newer Linux kernels we can use sysfs to get a backwards mapping
878 * from device names to standard filenames
880 static const char *find_device_sysfs(dev_t dev)
882 char sysname[64];
883 char linkname[PATH_MAX];
884 ssize_t llen;
885 char *p, *q;
886 char *buf = NULL;
887 struct stat st;
889 snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
890 major(dev), minor(dev));
892 llen = readlink(sysname, linkname, sizeof linkname);
893 if (llen < 0 || llen >= sizeof linkname)
894 goto err;
896 linkname[llen] = '\0';
898 p = strrchr(linkname, '/');
899 p = p ? p+1 : linkname; /* Leave basename */
901 buf = q = malloc(strlen(p) + 6);
902 if (!buf)
903 goto err;
905 memcpy(q, "/dev/", 5);
906 q += 5;
908 while (*p) {
909 *q++ = (*p == '!') ? '/' : *p;
910 p++;
913 *q = '\0';
915 if (!stat(buf, &st) && st.st_dev == dev)
916 return buf; /* Found it! */
918 err:
919 if (buf)
920 free(buf);
921 return NULL;
924 static const char *get_devname(const char *path)
926 const char *devname = NULL;
927 struct stat st;
928 struct statfs sfs;
930 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
931 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
932 return devname;
934 if (statfs(path, &sfs)) {
935 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
936 return devname;
939 #ifdef __KLIBC__
941 devname = find_device_sysfs(st.st_dev);
943 if (!devname) {
944 /* klibc doesn't have getmntent and friends; instead, just create
945 a new device with the appropriate device type */
946 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
947 major(st.st_dev), minor(st.st_dev));
949 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
950 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
951 return devname;
954 atexit(device_cleanup); /* unlink the device node on exit */
955 devname = devname_buf;
958 #else
960 devname = find_device("/proc/mounts", st.st_dev);
961 if (!devname) {
962 /* Didn't find it in /proc/mounts, try /etc/mtab */
963 devname = find_device("/etc/mtab", st.st_dev);
965 if (!devname) {
966 devname = find_device_sysfs(st.st_dev);
968 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
969 return devname;
972 fprintf(stderr, "%s is device %s\n", path, devname);
974 #endif
975 return devname;
978 static int open_device(const char *path, struct stat *st, const char **_devname)
980 int devfd;
981 const char *devname = NULL;
982 struct statfs sfs;
984 if (st)
985 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
986 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
987 return -1;
990 if (statfs(path, &sfs)) {
991 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
992 return -1;
994 if (sfs.f_type == EXT2_SUPER_MAGIC)
995 fs_type = EXT2;
996 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
997 fs_type = BTRFS;
998 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
999 fs_type = VFAT;
1000 else if (sfs.f_type == NTFS_SB_MAGIC ||
1001 sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
1002 fs_type = NTFS;
1004 if (!fs_type) {
1005 fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
1006 program, path);
1007 return -1;
1010 devfd = -1;
1011 devname = get_devname(path);
1012 if (_devname)
1013 *_devname = devname;
1015 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1016 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1017 return -1;
1020 /* Verify that the device we opened is the device intended */
1021 if (validate_device(path, devfd)) {
1022 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1023 program, path, devname);
1024 close(devfd);
1025 return -1;
1027 return devfd;
1030 static int btrfs_read_adv(int devfd)
1032 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE, BTRFS_ADV_OFFSET)
1033 != 2 * ADV_SIZE)
1034 return -1;
1036 return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
1039 static int ext_read_adv(const char *path, int devfd, const char **namep)
1041 int err;
1042 const char *name;
1044 if (fs_type == BTRFS) {
1045 /* btrfs "ldlinux.sys" is in 64k blank area */
1046 return btrfs_read_adv(devfd);
1047 } else {
1048 err = read_adv(path, name = "ldlinux.sys");
1049 if (err == 2) /* ldlinux.sys does not exist */
1050 err = read_adv(path, name = "extlinux.sys");
1051 if (namep)
1052 *namep = name;
1053 return err;
1057 static int ext_write_adv(const char *path, const char *cfg, int devfd)
1059 if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */
1060 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
1061 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
1062 perror("writing adv");
1063 return 1;
1065 return 0;
1067 return write_adv(path, cfg);
1070 int install_loader(const char *path, int update_only)
1072 struct stat st, fst;
1073 int devfd, rv;
1074 const char *devname;
1076 devfd = open_device(path, &st, &devname);
1077 if (devfd < 0)
1078 return 1;
1080 if (update_only && !syslinux_already_installed(devfd)) {
1081 fprintf(stderr, "%s: no previous syslinux boot sector found\n",
1082 program);
1083 close(devfd);
1084 return 1;
1087 /* Read a pre-existing ADV, if already installed */
1088 if (opt.reset_adv) {
1089 syslinux_reset_adv(syslinux_adv);
1090 } else if (ext_read_adv(path, devfd, NULL) < 0) {
1091 close(devfd);
1092 return 1;
1095 if (modify_adv() < 0) {
1096 close(devfd);
1097 return 1;
1100 /* Install ldlinux.sys */
1101 if (install_file(path, devfd, &fst)) {
1102 close(devfd);
1103 return 1;
1105 if (fst.st_dev != st.st_dev) {
1106 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1107 program);
1108 close(devfd);
1109 return 1;
1112 sync();
1113 rv = install_bootblock(devfd, devname);
1114 close(devfd);
1115 sync();
1117 return rv;
1121 * Modify the ADV of an existing installation
1123 int modify_existing_adv(const char *path)
1125 const char *filename;
1126 int devfd;
1128 devfd = open_device(path, NULL, NULL);
1129 if (devfd < 0)
1130 return 1;
1132 if (opt.reset_adv)
1133 syslinux_reset_adv(syslinux_adv);
1134 else if (ext_read_adv(path, devfd, &filename) < 0) {
1135 close(devfd);
1136 return 1;
1138 if (modify_adv() < 0) {
1139 close(devfd);
1140 return 1;
1142 if (ext_write_adv(path, filename, devfd) < 0) {
1143 close(devfd);
1144 return 1;
1146 close(devfd);
1147 return 0;
1150 int main(int argc, char *argv[])
1152 parse_options(argc, argv, MODE_EXTLINUX);
1154 if (!opt.directory || opt.install_mbr || opt.activate_partition)
1155 usage(EX_USAGE, 0);
1157 if (opt.update_only == -1) {
1158 if (opt.reset_adv || opt.set_once || opt.menu_save)
1159 return modify_existing_adv(opt.directory);
1160 else
1161 usage(EX_USAGE, MODE_EXTLINUX);
1164 return install_loader(opt.directory, opt.update_only);