extlinux: better methods for finding device matches
[syslinux/sherbszt.git] / extlinux / main.c
blob15ea03790181eb1542299c7df398fca3b4b12b6e
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 */
55 #include "mountinfo.h"
57 #ifdef DEBUG
58 # define dprintf printf
59 #else
60 # define dprintf(...) ((void)0)
61 #endif
63 #ifndef EXT2_SUPER_OFFSET
64 #define EXT2_SUPER_OFFSET 1024
65 #endif
67 /* the btrfs partition first 64K blank area is used to store boot sector and
68 boot image, the boot sector is from 0~512, the boot image starts after */
69 #define BTRFS_BOOTSECT_AREA 65536
70 #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
71 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
72 static char subvol[BTRFS_SUBVOL_MAX];
74 #define BTRFS_ADV_OFFSET (BTRFS_BOOTSECT_AREA - 2 * ADV_SIZE)
77 * Get the size of a block device
79 uint64_t get_size(int devfd)
81 uint64_t bytes;
82 uint32_t sects;
83 struct stat st;
85 #ifdef BLKGETSIZE64
86 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
87 return bytes;
88 #endif
89 if (!ioctl(devfd, BLKGETSIZE, &sects))
90 return (uint64_t) sects << 9;
91 else if (!fstat(devfd, &st) && st.st_size)
92 return st.st_size;
93 else
94 return 0;
98 * Get device geometry and partition offset
100 struct geometry_table {
101 uint64_t bytes;
102 struct hd_geometry g;
105 static int sysfs_get_offset(int devfd, unsigned long *start)
107 struct stat st;
108 char sysfs_name[128];
109 FILE *f;
110 int rv;
112 if (fstat(devfd, &st))
113 return -1;
115 if ((size_t)snprintf(sysfs_name, sizeof sysfs_name,
116 "/sys/dev/block/%u:%u/start",
117 major(st.st_rdev), minor(st.st_rdev))
118 >= sizeof sysfs_name)
119 return -1;
121 f = fopen(sysfs_name, "r");
122 if (!f)
123 return -1;
125 rv = fscanf(f, "%lu", start);
126 fclose(f);
128 return (rv == 1) ? 0 : -1;
131 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
132 (x/64/32) is the final fallback. I don't know what LS-240 has
133 as its geometry, since I don't have one and don't know anyone that does,
134 and Google wasn't helpful... */
135 static const struct geometry_table standard_geometries[] = {
136 {360 * 1024, {2, 9, 40, 0}},
137 {720 * 1024, {2, 9, 80, 0}},
138 {1200 * 1024, {2, 15, 80, 0}},
139 {1440 * 1024, {2, 18, 80, 0}},
140 {1680 * 1024, {2, 21, 80, 0}},
141 {1722 * 1024, {2, 21, 80, 0}},
142 {2880 * 1024, {2, 36, 80, 0}},
143 {3840 * 1024, {2, 48, 80, 0}},
144 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
145 {0, {0, 0, 0, 0}}
148 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
150 struct floppy_struct fd_str;
151 struct loop_info li;
152 struct loop_info64 li64;
153 const struct geometry_table *gp;
154 int rv = 0;
156 memset(geo, 0, sizeof *geo);
158 if (!ioctl(devfd, HDIO_GETGEO, geo)) {
159 goto ok;
160 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
161 geo->heads = fd_str.head;
162 geo->sectors = fd_str.sect;
163 geo->cylinders = fd_str.track;
164 geo->start = 0;
165 goto ok;
168 /* Didn't work. Let's see if this is one of the standard geometries */
169 for (gp = standard_geometries; gp->bytes; gp++) {
170 if (gp->bytes == totalbytes) {
171 memcpy(geo, &gp->g, sizeof *geo);
172 goto ok;
176 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
177 what zipdisks use, so this would help if someone has a USB key that
178 they're booting in USB-ZIP mode. */
180 geo->heads = opt.heads ? : 64;
181 geo->sectors = opt.sectors ? : 32;
182 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
183 geo->start = 0;
185 if (!opt.sectors && !opt.heads) {
186 fprintf(stderr,
187 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
188 " (on hard disks, this is usually harmless.)\n",
189 geo->heads, geo->sectors);
190 rv = 1; /* Suboptimal result */
194 /* If this is a loopback device, try to set the start */
195 if (!ioctl(devfd, LOOP_GET_STATUS64, &li64))
196 geo->start = li64.lo_offset >> SECTOR_SHIFT;
197 else if (!ioctl(devfd, LOOP_GET_STATUS, &li))
198 geo->start = (unsigned int)li.lo_offset >> SECTOR_SHIFT;
199 else if (!sysfs_get_offset(devfd, &geo->start)) {
200 /* OK */
203 return rv;
207 * Query the device geometry and put it into the boot sector.
208 * Map the file and put the map in the boot sector and file.
209 * Stick the "current directory" inode number into the file.
211 * Returns the number of modified bytes in the boot file.
213 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
215 struct stat dirst, xdst;
216 struct hd_geometry geo;
217 sector_t *sectp;
218 uint64_t totalbytes, totalsectors;
219 int nsect;
220 struct fat_boot_sector *sbs;
221 char *dirpath, *subpath, *xdirpath;
222 int rv;
224 dirpath = realpath(dir, NULL);
225 if (!dirpath || stat(dir, &dirst)) {
226 perror("accessing install directory");
227 exit(255); /* This should never happen */
230 if (lstat(dirpath, &xdst) ||
231 dirst.st_ino != xdst.st_ino ||
232 dirst.st_dev != xdst.st_dev) {
233 perror("realpath returned nonsense");
234 exit(255);
237 subpath = strchr(dirpath, '\0');
238 for (;;) {
239 if (*subpath == '/') {
240 if (subpath > dirpath) {
241 *subpath = '\0';
242 xdirpath = dirpath;
243 } else {
244 xdirpath = "/";
246 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
247 subpath = strchr(subpath+1, '/');
248 if (!subpath)
249 subpath = "/"; /* It's the root of the filesystem */
250 break;
252 *subpath = '/';
255 if (subpath == dirpath)
256 break;
258 subpath--;
261 /* Now subpath should contain the path relative to the fs base */
262 dprintf("subpath = %s\n", subpath);
264 totalbytes = get_size(devfd);
265 get_geometry(devfd, totalbytes, &geo);
267 if (opt.heads)
268 geo.heads = opt.heads;
269 if (opt.sectors)
270 geo.sectors = opt.sectors;
272 /* Patch this into a fake FAT superblock. This isn't because
273 FAT is a good format in any way, it's because it lets the
274 early bootstrap share code with the FAT version. */
275 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
277 sbs = (struct fat_boot_sector *)syslinux_bootsect;
279 totalsectors = totalbytes >> SECTOR_SHIFT;
280 if (totalsectors >= 65536) {
281 set_16(&sbs->bsSectors, 0);
282 } else {
283 set_16(&sbs->bsSectors, totalsectors);
285 set_32(&sbs->bsHugeSectors, totalsectors);
287 set_16(&sbs->bsBytesPerSec, SECTOR_SIZE);
288 set_16(&sbs->bsSecPerTrack, geo.sectors);
289 set_16(&sbs->bsHeads, geo.heads);
290 set_32(&sbs->bsHiddenSecs, geo.start);
292 /* Construct the boot file map */
294 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
295 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
296 nsect += 2; /* Two sectors for the ADV */
297 sectp = alloca(sizeof(sector_t) * nsect);
298 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) {
299 if (sectmap(fd, sectp, nsect)) {
300 perror("bmap");
301 exit(1);
303 } else if (fs_type == BTRFS) {
304 int i;
305 sector_t *sp = sectp;
307 for (i = 0; i < nsect - 2; i++)
308 *sp++ = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
309 for (i = 0; i < 2; i++)
310 *sp++ = BTRFS_ADV_OFFSET/SECTOR_SIZE + i;
313 /* Create the modified image in memory */
314 rv = syslinux_patch(sectp, nsect, opt.stupid_mode,
315 opt.raid_mode, subpath, subvol);
317 free(dirpath);
318 return rv;
322 * Install the boot block on the specified device.
323 * Must be run AFTER install_file()!
325 int install_bootblock(int fd, const char *device)
327 struct ext2_super_block sb;
328 struct btrfs_super_block sb2;
329 struct fat_boot_sector sb3;
330 struct ntfs_boot_sector sb4;
331 bool ok = false;
333 if (fs_type == EXT2) {
334 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
335 perror("reading superblock");
336 return 1;
338 if (sb.s_magic == EXT2_SUPER_MAGIC)
339 ok = true;
340 } else if (fs_type == BTRFS) {
341 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
342 != sizeof sb2) {
343 perror("reading superblock");
344 return 1;
346 if (!memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
347 ok = true;
348 } else if (fs_type == VFAT) {
349 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
350 perror("reading fat superblock");
351 return 1;
353 if (fat_check_sb_fields(&sb3))
354 ok = true;
355 } else if (fs_type == NTFS) {
356 if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) {
357 perror("reading ntfs superblock");
358 return 1;
361 if (ntfs_check_sb_fields(&sb4))
362 ok = true;
364 if (!ok) {
365 fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n",
366 device);
367 return 1;
369 if (fs_type == VFAT) {
370 struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect;
371 if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen ||
372 xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen,
373 offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) {
374 perror("writing fat bootblock");
375 return 1;
377 } else if (fs_type == NTFS) {
378 struct ntfs_boot_sector *sbs =
379 (struct ntfs_boot_sector *)syslinux_bootsect;
380 if (xpwrite(fd, &sbs->NTFS_bsHead,
381 NTFS_bsHeadLen, 0) != NTFS_bsHeadLen ||
382 xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen,
383 offsetof(struct ntfs_boot_sector,
384 NTFS_bsCode)) != NTFS_bsCodeLen) {
385 perror("writing ntfs bootblock");
386 return 1;
388 } else {
389 if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
390 != syslinux_bootsect_len) {
391 perror("writing bootblock");
392 return 1;
396 return 0;
399 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
401 char *file, *oldfile;
402 int fd = -1, dirfd = -1;
403 int modbytes;
404 int r1, r2;
406 r1 = asprintf(&file, "%s%sldlinux.sys",
407 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
408 r2 = asprintf(&oldfile, "%s%sextlinux.sys",
409 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
410 if (r1 < 0 || !file || r2 < 0 || !oldfile) {
411 perror(program);
412 return 1;
415 dirfd = open(path, O_RDONLY | O_DIRECTORY);
416 if (dirfd < 0) {
417 perror(path);
418 goto bail;
421 fd = open(file, O_RDONLY);
422 if (fd < 0) {
423 if (errno != ENOENT) {
424 perror(file);
425 goto bail;
427 } else {
428 clear_attributes(fd);
430 close(fd);
432 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
433 S_IRUSR | S_IRGRP | S_IROTH);
434 if (fd < 0) {
435 perror(file);
436 goto bail;
439 /* Write it the first time */
440 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
441 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
442 boot_image_len) != 2 * ADV_SIZE) {
443 fprintf(stderr, "%s: write failure on %s\n", program, file);
444 goto bail;
447 /* Map the file, and patch the initial sector accordingly */
448 modbytes = patch_file_and_bootblock(fd, path, devfd);
450 /* Write the patch area again - this relies on the file being
451 overwritten in place! */
452 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
453 fprintf(stderr, "%s: write failure on %s\n", program, file);
454 goto bail;
457 /* Attempt to set immutable flag and remove all write access */
458 /* Only set immutable flag if file is owned by root */
459 set_attributes(fd);
461 if (fstat(fd, rst)) {
462 perror(file);
463 goto bail;
466 close(dirfd);
467 close(fd);
469 /* Look if we have the old filename */
470 fd = open(oldfile, O_RDONLY);
471 if (fd >= 0) {
472 clear_attributes(fd);
473 close(fd);
474 unlink(oldfile);
477 free(file);
478 free(oldfile);
479 return 0;
481 bail:
482 if (dirfd >= 0)
483 close(dirfd);
484 if (fd >= 0)
485 close(fd);
487 free(file);
488 free(oldfile);
489 return 1;
492 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
493 is not managered by btrfs tree, so actually this is not installed as files.
494 since the cow feature of btrfs will move the ldlinux.sys every where */
495 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
497 patch_file_and_bootblock(-1, path, devfd);
498 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
499 != boot_image_len) {
500 perror("writing bootblock");
501 return 1;
503 dprintf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
504 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE, BTRFS_ADV_OFFSET)
505 != 2 * ADV_SIZE) {
506 perror("writing adv");
507 return 1;
509 dprintf("write adv to 0x%x\n", BTRFS_ADV_OFFSET);
510 if (stat(path, rst)) {
511 perror(path);
512 return 1;
514 return 0;
518 * * test if path is a subvolume:
519 * * this function return
520 * * 0-> path exists but it is not a subvolume
521 * * 1-> path exists and it is a subvolume
522 * * -1 -> path is unaccessible
523 * */
524 static int test_issubvolume(char *path)
527 struct stat st;
528 int res;
530 res = stat(path, &st);
531 if(res < 0 )
532 return -1;
534 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
539 * Get the default subvolume of a btrfs filesystem
540 * rootdir: btrfs root dir
541 * subvol: this function will save the default subvolume name here
543 static char * get_default_subvol(char * rootdir, char * subvol)
545 struct btrfs_ioctl_search_args args;
546 struct btrfs_ioctl_search_key *sk = &args.key;
547 struct btrfs_ioctl_search_header *sh;
548 int ret, i;
549 int fd;
550 struct btrfs_root_ref *ref;
551 struct btrfs_dir_item *dir_item;
552 unsigned long off = 0;
553 int name_len;
554 char *name;
555 char dirname[4096];
556 u64 defaultsubvolid = 0;
558 ret = test_issubvolume(rootdir);
559 if (ret == 1) {
560 fd = open(rootdir, O_RDONLY);
561 if (fd < 0) {
562 fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
564 ret = fd;
566 if (ret <= 0) {
567 subvol[0] = '\0';
568 return NULL;
571 memset(&args, 0, sizeof(args));
573 /* search in the tree of tree roots */
574 sk->tree_id = 1;
577 * set the min and max to backref keys. The search will
578 * only send back this type of key now.
580 sk->max_type = BTRFS_DIR_ITEM_KEY;
581 sk->min_type = BTRFS_DIR_ITEM_KEY;
584 * set all the other params to the max, we'll take any objectid
585 * and any trans
587 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
588 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
590 sk->max_offset = (u64)-1;
591 sk->min_offset = 0;
592 sk->max_transid = (u64)-1;
594 /* just a big number, doesn't matter much */
595 sk->nr_items = 4096;
597 while(1) {
598 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
599 if (ret < 0) {
600 fprintf(stderr, "ERROR: can't perform the search\n");
601 subvol[0] = '\0';
602 return NULL;
604 /* the ioctl returns the number of item it found in nr_items */
605 if (sk->nr_items == 0) {
606 break;
609 off = 0;
612 * for each item, pull the key out of the header and then
613 * read the root_ref item it contains
615 for (i = 0; i < sk->nr_items; i++) {
616 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
617 off += sizeof(*sh);
618 if (sh->type == BTRFS_DIR_ITEM_KEY) {
619 dir_item = (struct btrfs_dir_item *)(args.buf + off);
620 name_len = dir_item->name_len;
621 name = (char *)(dir_item + 1);
624 /*add_root(&root_lookup, sh->objectid, sh->offset,
625 dir_id, name, name_len);*/
626 strncpy(dirname, name, name_len);
627 dirname[name_len] = '\0';
628 if (strcmp(dirname, "default") == 0) {
629 defaultsubvolid = dir_item->location.objectid;
630 break;
633 off += sh->len;
636 * record the mins in sk so we can make sure the
637 * next search doesn't repeat this root
639 sk->min_objectid = sh->objectid;
640 sk->min_type = sh->type;
641 sk->max_type = sh->type;
642 sk->min_offset = sh->offset;
644 if (defaultsubvolid != 0)
645 break;
646 sk->nr_items = 4096;
647 /* this iteration is done, step forward one root for the next
648 * ioctl
650 if (sk->min_objectid < (u64)-1) {
651 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
652 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
653 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
654 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
655 sk->min_offset = 0;
656 } else
657 break;
660 if (defaultsubvolid == 0) {
661 subvol[0] = '\0';
662 return NULL;
665 memset(&args, 0, sizeof(args));
667 /* search in the tree of tree roots */
668 sk->tree_id = 1;
671 * set the min and max to backref keys. The search will
672 * only send back this type of key now.
674 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
675 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
678 * set all the other params to the max, we'll take any objectid
679 * and any trans
681 sk->max_objectid = (u64)-1;
682 sk->max_offset = (u64)-1;
683 sk->max_transid = (u64)-1;
685 /* just a big number, doesn't matter much */
686 sk->nr_items = 4096;
688 while(1) {
689 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
690 if (ret < 0) {
691 fprintf(stderr, "ERROR: can't perform the search\n");
692 subvol[0] = '\0';
693 return NULL;
695 /* the ioctl returns the number of item it found in nr_items */
696 if (sk->nr_items == 0)
697 break;
699 off = 0;
702 * for each item, pull the key out of the header and then
703 * read the root_ref item it contains
705 for (i = 0; i < sk->nr_items; i++) {
706 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
707 off += sizeof(*sh);
708 if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
709 ref = (struct btrfs_root_ref *)(args.buf + off);
710 name_len = ref->name_len;
711 name = (char *)(ref + 1);
713 if (sh->objectid == defaultsubvolid) {
714 strncpy(subvol, name, name_len);
715 subvol[name_len] = '\0';
716 dprintf("The default subvolume: %s, ID: %llu\n",
717 subvol, sh->objectid);
718 break;
723 off += sh->len;
726 * record the mins in sk so we can make sure the
727 * next search doesn't repeat this root
729 sk->min_objectid = sh->objectid;
730 sk->min_type = sh->type;
731 sk->min_offset = sh->offset;
733 if (subvol[0] != '\0')
734 break;
735 sk->nr_items = 4096;
736 /* this iteration is done, step forward one root for the next
737 * ioctl
739 if (sk->min_objectid < (u64)-1) {
740 sk->min_objectid++;
741 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
742 sk->min_offset = 0;
743 } else
744 break;
746 return subvol;
749 int install_file(const char *path, int devfd, struct stat *rst)
751 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
752 return ext2_fat_install_file(path, devfd, rst);
753 else if (fs_type == BTRFS)
754 return btrfs_install_file(path, devfd, rst);
755 return 1;
758 #ifdef __KLIBC__
759 static char devname_buf[64];
761 static void device_cleanup(void)
763 unlink(devname_buf);
765 #endif
767 /* Verify that a device fd and a pathname agree.
768 Return 0 on valid, -1 on error. */
769 static int validate_device(const char *path, int devfd)
771 struct stat pst, dst;
772 struct statfs sfs;
774 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
775 return -1;
776 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
777 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
778 return 0;
779 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
782 #ifndef __KLIBC__
783 static const char *find_device(const char *mtab_file, dev_t dev)
785 struct mntent *mnt;
786 struct stat dst;
787 FILE *mtab;
788 const char *devname = NULL;
789 bool done;
791 mtab = setmntent(mtab_file, "r");
792 if (!mtab)
793 return NULL;
795 done = false;
796 while ((mnt = getmntent(mtab))) {
797 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
798 switch (fs_type) {
799 case BTRFS:
800 if (!strcmp(mnt->mnt_type, "btrfs") &&
801 !stat(mnt->mnt_dir, &dst) &&
802 dst.st_dev == dev) {
803 if (!subvol[0])
804 get_default_subvol(mnt->mnt_dir, subvol);
805 done = true;
807 break;
808 case EXT2:
809 if ((!strcmp(mnt->mnt_type, "ext2") ||
810 !strcmp(mnt->mnt_type, "ext3") ||
811 !strcmp(mnt->mnt_type, "ext4")) &&
812 !stat(mnt->mnt_fsname, &dst) &&
813 dst.st_rdev == dev) {
814 done = true;
815 break;
817 case VFAT:
818 if ((!strcmp(mnt->mnt_type, "vfat")) &&
819 !stat(mnt->mnt_fsname, &dst) &&
820 dst.st_rdev == dev) {
821 done = true;
822 break;
824 case NTFS:
825 if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ ||
826 !strcmp(mnt->mnt_type, "ntfs")) &&
827 !stat(mnt->mnt_fsname, &dst) &&
828 dst.st_rdev == dev) {
829 done = true;
830 break;
833 break;
834 case NONE:
835 break;
837 if (done) {
838 devname = strdup(mnt->mnt_fsname);
839 break;
842 endmntent(mtab);
844 return devname;
846 #endif
849 * On newer Linux kernels we can use sysfs to get a backwards mapping
850 * from device names to standard filenames
852 static const char *find_device_sysfs(dev_t dev)
854 char sysname[64];
855 char linkname[PATH_MAX];
856 ssize_t llen;
857 char *p, *q;
858 char *buf = NULL;
859 struct stat st;
861 snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
862 major(dev), minor(dev));
864 llen = readlink(sysname, linkname, sizeof linkname);
865 if (llen < 0 || llen >= sizeof linkname)
866 goto err;
868 linkname[llen] = '\0';
870 p = strrchr(linkname, '/');
871 p = p ? p+1 : linkname; /* Leave basename */
873 buf = q = malloc(strlen(p) + 6);
874 if (!buf)
875 goto err;
877 memcpy(q, "/dev/", 5);
878 q += 5;
880 while (*p) {
881 *q++ = (*p == '!') ? '/' : *p;
882 p++;
885 *q = '\0';
887 if (!stat(buf, &st) && st.st_dev == dev)
888 return buf; /* Found it! */
890 err:
891 if (buf)
892 free(buf);
893 return NULL;
896 static const char *find_device_mountinfo(const char *path, dev_t dev)
898 const struct mountinfo *m;
899 struct stat st;
901 m = find_mount(path, NULL);
903 if (m->devpath[0] == '/' && m->dev == dev &&
904 !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
905 return m->devpath;
906 else
907 return NULL;
910 static const char *find_device_btrfs(const char *path)
912 int fd;
913 struct btrfs_ioctl_fs_info_args fsinfo;
914 static struct btrfs_ioctl_dev_info_args devinfo;
915 struct btrfs_super_block sb2;
916 const char *rv = NULL;
918 fd = open(path, O_RDONLY);
919 if (fd < 0)
920 goto err;
922 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsinfo))
923 goto err;
925 /* We do not support multi-device btrfs yet */
926 if (fsinfo.num_devices != 1)
927 goto err;
929 /* The one device will have the max devid */
930 memset(&devinfo, 0, sizeof devinfo);
931 devinfo.devid = fsinfo.max_id;
932 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &devinfo))
933 goto err;
934 close(fd);
935 fd = -1;
937 if (devinfo.path[0] != '/')
938 goto err;
940 fd = open((const char *)devinfo.path, O_RDONLY);
941 if (fd < 0)
942 goto err;
944 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2)
945 goto err;
947 if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
948 goto err;
950 if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
951 goto err;
953 if (sb2.num_devices != 1)
954 goto err;
956 if (sb2.dev_item.devid != devinfo.devid)
957 goto err;
959 if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid))
960 goto err;
962 if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
963 goto err;
965 rv = (const char *)devinfo.path; /* It's good! */
967 err:
968 if (fd >= 0)
969 close(fd);
970 return rv;
973 static const char *get_devname(const char *path)
975 const char *devname = NULL;
976 struct stat st;
977 struct statfs sfs;
979 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
980 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
981 return devname;
983 if (statfs(path, &sfs)) {
984 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
985 return devname;
988 if (fs_type == BTRFS) {
989 /* For btrfs try to get the device name from btrfs itself */
990 devname = find_device_btrfs(path);
993 if (!devname) {
994 devname = find_device_mountinfo(path, st.st_dev);
997 #ifdef __KLIBC__
998 if (!devname) {
999 devname = find_device_sysfs(st.st_dev);
1001 if (!devname) {
1002 /* klibc doesn't have getmntent and friends; instead, just create
1003 a new device with the appropriate device type */
1004 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1005 major(st.st_dev), minor(st.st_dev));
1007 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1008 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1009 return devname;
1012 atexit(device_cleanup); /* unlink the device node on exit */
1013 devname = devname_buf;
1016 #else
1017 if (!devname) {
1018 devname = find_device("/proc/mounts", st.st_dev);
1020 if (!devname) {
1021 /* Didn't find it in /proc/mounts, try /etc/mtab */
1022 devname = find_device("/etc/mtab", st.st_dev);
1024 if (!devname) {
1025 devname = find_device_sysfs(st.st_dev);
1027 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1028 return devname;
1031 fprintf(stderr, "%s is device %s\n", path, devname);
1033 #endif
1034 return devname;
1037 static int open_device(const char *path, struct stat *st, const char **_devname)
1039 int devfd;
1040 const char *devname = NULL;
1041 struct statfs sfs;
1043 if (st)
1044 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1045 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1046 return -1;
1049 if (statfs(path, &sfs)) {
1050 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1051 return -1;
1053 if (sfs.f_type == EXT2_SUPER_MAGIC)
1054 fs_type = EXT2;
1055 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1056 fs_type = BTRFS;
1057 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1058 fs_type = VFAT;
1059 else if (sfs.f_type == NTFS_SB_MAGIC ||
1060 sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
1061 fs_type = NTFS;
1063 if (!fs_type) {
1064 fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
1065 program, path);
1066 return -1;
1069 devfd = -1;
1070 devname = get_devname(path);
1071 if (_devname)
1072 *_devname = devname;
1074 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1075 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1076 return -1;
1079 /* Verify that the device we opened is the device intended */
1080 if (validate_device(path, devfd)) {
1081 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1082 program, path, devname);
1083 close(devfd);
1084 return -1;
1086 return devfd;
1089 static int btrfs_read_adv(int devfd)
1091 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE, BTRFS_ADV_OFFSET)
1092 != 2 * ADV_SIZE)
1093 return -1;
1095 return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
1098 static int ext_read_adv(const char *path, int devfd, const char **namep)
1100 int err;
1101 const char *name;
1103 if (fs_type == BTRFS) {
1104 /* btrfs "ldlinux.sys" is in 64k blank area */
1105 return btrfs_read_adv(devfd);
1106 } else {
1107 err = read_adv(path, name = "ldlinux.sys");
1108 if (err == 2) /* ldlinux.sys does not exist */
1109 err = read_adv(path, name = "extlinux.sys");
1110 if (namep)
1111 *namep = name;
1112 return err;
1116 static int ext_write_adv(const char *path, const char *cfg, int devfd)
1118 if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */
1119 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
1120 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
1121 perror("writing adv");
1122 return 1;
1124 return 0;
1126 return write_adv(path, cfg);
1129 int install_loader(const char *path, int update_only)
1131 struct stat st, fst;
1132 int devfd, rv;
1133 const char *devname;
1135 devfd = open_device(path, &st, &devname);
1136 if (devfd < 0)
1137 return 1;
1139 if (update_only && !syslinux_already_installed(devfd)) {
1140 fprintf(stderr, "%s: no previous syslinux boot sector found\n",
1141 program);
1142 close(devfd);
1143 return 1;
1146 /* Read a pre-existing ADV, if already installed */
1147 if (opt.reset_adv) {
1148 syslinux_reset_adv(syslinux_adv);
1149 } else if (ext_read_adv(path, devfd, NULL) < 0) {
1150 close(devfd);
1151 return 1;
1154 if (modify_adv() < 0) {
1155 close(devfd);
1156 return 1;
1159 /* Install ldlinux.sys */
1160 if (install_file(path, devfd, &fst)) {
1161 close(devfd);
1162 return 1;
1164 if (fst.st_dev != st.st_dev) {
1165 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1166 program);
1167 close(devfd);
1168 return 1;
1171 sync();
1172 rv = install_bootblock(devfd, devname);
1173 close(devfd);
1174 sync();
1176 return rv;
1180 * Modify the ADV of an existing installation
1182 int modify_existing_adv(const char *path)
1184 const char *filename;
1185 int devfd;
1187 devfd = open_device(path, NULL, NULL);
1188 if (devfd < 0)
1189 return 1;
1191 if (opt.reset_adv)
1192 syslinux_reset_adv(syslinux_adv);
1193 else if (ext_read_adv(path, devfd, &filename) < 0) {
1194 close(devfd);
1195 return 1;
1197 if (modify_adv() < 0) {
1198 close(devfd);
1199 return 1;
1201 if (ext_write_adv(path, filename, devfd) < 0) {
1202 close(devfd);
1203 return 1;
1205 close(devfd);
1206 return 0;
1209 int main(int argc, char *argv[])
1211 parse_options(argc, argv, MODE_EXTLINUX);
1213 if (!opt.directory || opt.install_mbr || opt.activate_partition)
1214 usage(EX_USAGE, 0);
1216 if (opt.update_only == -1) {
1217 if (opt.reset_adv || opt.set_once || opt.menu_save)
1218 return modify_existing_adv(opt.directory);
1219 else
1220 usage(EX_USAGE, MODE_EXTLINUX);
1223 return install_loader(opt.directory, opt.update_only);