com32: mark the invalid SEG() as __unlikely()
[syslinux/sherbszt.git] / extlinux / main.c
blob73f3fbe1df01ed632c28688e57419e5d4e2f0fc4
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_btrfs(int pathfd, int devfd);
770 static int validate_device(const char *path, int devfd)
772 struct stat pst, dst;
773 struct statfs sfs;
774 int pfd;
775 int rv = -1;
777 pfd = open(path, O_RDONLY|O_DIRECTORY);
778 if (pfd < 0)
779 goto err;
781 if (fstat(pfd, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
782 goto err;
784 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
785 if (fs_type == BTRFS) {
786 if (sfs.f_type == BTRFS_SUPER_MAGIC)
787 rv = validate_device_btrfs(pfd, devfd);
788 } else {
789 rv = (pst.st_dev == dst.st_rdev) ? 0 : -1;
792 err:
793 if (pfd >= 0)
794 close(pfd);
795 return rv;
798 #ifndef __KLIBC__
799 static const char *find_device(const char *mtab_file, dev_t dev)
801 struct mntent *mnt;
802 struct stat dst;
803 FILE *mtab;
804 const char *devname = NULL;
805 bool done;
807 mtab = setmntent(mtab_file, "r");
808 if (!mtab)
809 return NULL;
811 done = false;
812 while ((mnt = getmntent(mtab))) {
813 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
814 switch (fs_type) {
815 case BTRFS:
816 if (!strcmp(mnt->mnt_type, "btrfs") &&
817 !stat(mnt->mnt_dir, &dst) &&
818 dst.st_dev == dev) {
819 if (!subvol[0])
820 get_default_subvol(mnt->mnt_dir, subvol);
821 done = true;
823 break;
824 case EXT2:
825 if ((!strcmp(mnt->mnt_type, "ext2") ||
826 !strcmp(mnt->mnt_type, "ext3") ||
827 !strcmp(mnt->mnt_type, "ext4")) &&
828 !stat(mnt->mnt_fsname, &dst) &&
829 dst.st_rdev == dev) {
830 done = true;
831 break;
833 case VFAT:
834 if ((!strcmp(mnt->mnt_type, "vfat")) &&
835 !stat(mnt->mnt_fsname, &dst) &&
836 dst.st_rdev == dev) {
837 done = true;
838 break;
840 case NTFS:
841 if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ ||
842 !strcmp(mnt->mnt_type, "ntfs")) &&
843 !stat(mnt->mnt_fsname, &dst) &&
844 dst.st_rdev == dev) {
845 done = true;
846 break;
849 break;
850 case NONE:
851 break;
853 if (done) {
854 devname = strdup(mnt->mnt_fsname);
855 break;
858 endmntent(mtab);
860 return devname;
862 #endif
865 * On newer Linux kernels we can use sysfs to get a backwards mapping
866 * from device names to standard filenames
868 static const char *find_device_sysfs(dev_t dev)
870 char sysname[64];
871 char linkname[PATH_MAX];
872 ssize_t llen;
873 char *p, *q;
874 char *buf = NULL;
875 struct stat st;
877 snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
878 major(dev), minor(dev));
880 llen = readlink(sysname, linkname, sizeof linkname);
881 if (llen < 0 || llen >= sizeof linkname)
882 goto err;
884 linkname[llen] = '\0';
886 p = strrchr(linkname, '/');
887 p = p ? p+1 : linkname; /* Leave basename */
889 buf = q = malloc(strlen(p) + 6);
890 if (!buf)
891 goto err;
893 memcpy(q, "/dev/", 5);
894 q += 5;
896 while (*p) {
897 *q++ = (*p == '!') ? '/' : *p;
898 p++;
901 *q = '\0';
903 if (!stat(buf, &st) && st.st_dev == dev)
904 return buf; /* Found it! */
906 err:
907 if (buf)
908 free(buf);
909 return NULL;
912 static const char *find_device_mountinfo(const char *path, dev_t dev)
914 const struct mountinfo *m;
915 struct stat st;
917 m = find_mount(path, NULL);
919 if (m->devpath[0] == '/' && m->dev == dev &&
920 !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
921 return m->devpath;
922 else
923 return NULL;
926 static int validate_device_btrfs(int pfd, int dfd)
928 struct btrfs_ioctl_fs_info_args fsinfo;
929 static struct btrfs_ioctl_dev_info_args devinfo;
930 struct btrfs_super_block sb2;
932 if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
933 return -1;
935 /* We do not support multi-device btrfs yet */
936 if (fsinfo.num_devices != 1)
937 return -1;
939 /* The one device will have the max devid */
940 memset(&devinfo, 0, sizeof devinfo);
941 devinfo.devid = fsinfo.max_id;
942 if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
943 return -1;
945 if (devinfo.path[0] != '/')
946 return -1;
948 if (xpread(dfd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2)
949 return -1;
951 if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
952 return -1;
954 if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
955 return -1;
957 if (sb2.num_devices != 1)
958 return -1;
960 if (sb2.dev_item.devid != devinfo.devid)
961 return -1;
963 if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid))
964 return -1;
966 if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
967 return -1;
969 return 0; /* It's good! */
972 static const char *find_device_btrfs(const char *path)
974 int pfd, dfd;
975 struct btrfs_ioctl_fs_info_args fsinfo;
976 static struct btrfs_ioctl_dev_info_args devinfo;
977 const char *rv = NULL;
979 pfd = dfd = -1;
981 pfd = open(path, O_RDONLY);
982 if (pfd < 0)
983 goto err;
985 if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
986 goto err;
988 /* We do not support multi-device btrfs yet */
989 if (fsinfo.num_devices != 1)
990 goto err;
992 /* The one device will have the max devid */
993 memset(&devinfo, 0, sizeof devinfo);
994 devinfo.devid = fsinfo.max_id;
995 if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
996 goto err;
998 if (devinfo.path[0] != '/')
999 goto err;
1001 dfd = open((const char *)devinfo.path, O_RDONLY);
1002 if (dfd < 0)
1003 goto err;
1005 if (!validate_device_btrfs(pfd, dfd))
1006 rv = (const char *)devinfo.path; /* It's good! */
1008 err:
1009 if (pfd >= 0)
1010 close(pfd);
1011 if (dfd >= 0)
1012 close(dfd);
1013 return rv;
1016 static const char *get_devname(const char *path)
1018 const char *devname = NULL;
1019 struct stat st;
1020 struct statfs sfs;
1022 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
1023 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1024 return devname;
1026 if (statfs(path, &sfs)) {
1027 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1028 return devname;
1031 if (opt.device)
1032 devname = opt.device;
1034 if (!devname){
1035 if (fs_type == BTRFS) {
1036 /* For btrfs try to get the device name from btrfs itself */
1037 devname = find_device_btrfs(path);
1041 if (!devname) {
1042 devname = find_device_mountinfo(path, st.st_dev);
1045 #ifdef __KLIBC__
1046 if (!devname) {
1047 devname = find_device_sysfs(st.st_dev);
1049 if (!devname) {
1050 /* klibc doesn't have getmntent and friends; instead, just create
1051 a new device with the appropriate device type */
1052 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1053 major(st.st_dev), minor(st.st_dev));
1055 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1056 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1057 return devname;
1060 atexit(device_cleanup); /* unlink the device node on exit */
1061 devname = devname_buf;
1064 #else
1065 if (!devname) {
1066 devname = find_device("/proc/mounts", st.st_dev);
1068 if (!devname) {
1069 /* Didn't find it in /proc/mounts, try /etc/mtab */
1070 devname = find_device("/etc/mtab", st.st_dev);
1072 if (!devname) {
1073 devname = find_device_sysfs(st.st_dev);
1075 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1076 return devname;
1079 fprintf(stderr, "%s is device %s\n", path, devname);
1081 #endif
1082 return devname;
1085 static int open_device(const char *path, struct stat *st, const char **_devname)
1087 int devfd;
1088 const char *devname = NULL;
1089 struct statfs sfs;
1091 if (st)
1092 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1093 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1094 return -1;
1097 if (statfs(path, &sfs)) {
1098 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1099 return -1;
1101 if (sfs.f_type == EXT2_SUPER_MAGIC)
1102 fs_type = EXT2;
1103 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1104 fs_type = BTRFS;
1105 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1106 fs_type = VFAT;
1107 else if (sfs.f_type == NTFS_SB_MAGIC ||
1108 sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
1109 fs_type = NTFS;
1111 if (!fs_type) {
1112 fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
1113 program, path);
1114 return -1;
1117 devfd = -1;
1118 devname = get_devname(path);
1119 if (_devname)
1120 *_devname = devname;
1122 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1123 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1124 return -1;
1127 /* Verify that the device we opened is the device intended */
1128 if (validate_device(path, devfd)) {
1129 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1130 program, path, devname);
1131 close(devfd);
1132 return -1;
1134 return devfd;
1137 static int btrfs_read_adv(int devfd)
1139 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE, BTRFS_ADV_OFFSET)
1140 != 2 * ADV_SIZE)
1141 return -1;
1143 return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
1146 static int ext_read_adv(const char *path, int devfd, const char **namep)
1148 int err;
1149 const char *name;
1151 if (fs_type == BTRFS) {
1152 /* btrfs "ldlinux.sys" is in 64k blank area */
1153 return btrfs_read_adv(devfd);
1154 } else {
1155 err = read_adv(path, name = "ldlinux.sys");
1156 if (err == 2) /* ldlinux.sys does not exist */
1157 err = read_adv(path, name = "extlinux.sys");
1158 if (namep)
1159 *namep = name;
1160 return err;
1164 static int ext_write_adv(const char *path, const char *cfg, int devfd)
1166 if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */
1167 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
1168 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
1169 perror("writing adv");
1170 return 1;
1172 return 0;
1174 return write_adv(path, cfg);
1177 int install_loader(const char *path, int update_only)
1179 struct stat st, fst;
1180 int devfd, rv;
1181 const char *devname;
1183 devfd = open_device(path, &st, &devname);
1184 if (devfd < 0)
1185 return 1;
1187 if (update_only && !syslinux_already_installed(devfd)) {
1188 fprintf(stderr, "%s: no previous syslinux boot sector found\n",
1189 program);
1190 close(devfd);
1191 return 1;
1194 /* Read a pre-existing ADV, if already installed */
1195 if (opt.reset_adv) {
1196 syslinux_reset_adv(syslinux_adv);
1197 } else if (ext_read_adv(path, devfd, NULL) < 0) {
1198 close(devfd);
1199 return 1;
1202 if (modify_adv() < 0) {
1203 close(devfd);
1204 return 1;
1207 /* Install ldlinux.sys */
1208 if (install_file(path, devfd, &fst)) {
1209 close(devfd);
1210 return 1;
1212 if (fst.st_dev != st.st_dev) {
1213 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1214 program);
1215 close(devfd);
1216 return 1;
1219 sync();
1220 rv = install_bootblock(devfd, devname);
1221 close(devfd);
1222 sync();
1224 return rv;
1228 * Modify the ADV of an existing installation
1230 int modify_existing_adv(const char *path)
1232 const char *filename;
1233 int devfd;
1235 devfd = open_device(path, NULL, NULL);
1236 if (devfd < 0)
1237 return 1;
1239 if (opt.reset_adv)
1240 syslinux_reset_adv(syslinux_adv);
1241 else if (ext_read_adv(path, devfd, &filename) < 0) {
1242 close(devfd);
1243 return 1;
1245 if (modify_adv() < 0) {
1246 close(devfd);
1247 return 1;
1249 if (ext_write_adv(path, filename, devfd) < 0) {
1250 close(devfd);
1251 return 1;
1253 close(devfd);
1254 return 0;
1257 int main(int argc, char *argv[])
1259 parse_options(argc, argv, MODE_EXTLINUX);
1261 if (!opt.directory || opt.install_mbr || opt.activate_partition)
1262 usage(EX_USAGE, 0);
1264 if (opt.update_only == -1) {
1265 if (opt.reset_adv || opt.set_once || opt.menu_save)
1266 return modify_existing_adv(opt.directory);
1267 else
1268 usage(EX_USAGE, MODE_EXTLINUX);
1271 return install_loader(opt.directory, opt.update_only);