extlinux: pull in sys/sysmacros.h for major/minor/makedev
[syslinux.git] / extlinux / main.c
blobebff7eae0ebb3fb7a6a412aa8bde7fc425a7c302
1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2014 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 a fat, ntfs, ext2/3/4, btrfs, xfs,
18 * and ufs1/2 filesystem.
21 #define _GNU_SOURCE /* Enable everything */
22 #include <inttypes.h>
23 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
24 #include <alloca.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #ifndef __KLIBC__
31 #include <mntent.h>
32 #endif
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <getopt.h>
38 #include <sysexits.h>
39 #include <sys/ioctl.h>
40 #include <sys/stat.h>
41 #include <sys/sysmacros.h>
42 #include <sys/types.h>
43 #include <sys/mount.h>
44 #include <sys/vfs.h>
46 #include "linuxioctl.h"
48 #include "btrfs.h"
49 #include "fat.h"
50 #include "ntfs.h"
51 #include "xfs.h"
52 #include "xfs_types.h"
53 #include "xfs_sb.h"
54 #include "ufs.h"
55 #include "ufs_fs.h"
56 #include "misc.h"
57 #include "version.h"
58 #include "syslxint.h"
59 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
60 #include "syslxrw.h"
61 #include "syslxfs.h"
62 #include "setadv.h"
63 #include "syslxopt.h" /* unified options */
64 #include "mountinfo.h"
66 #ifdef DEBUG
67 # define dprintf printf
68 #else
69 # define dprintf(...) ((void)0)
70 #endif
72 #ifndef EXT2_SUPER_OFFSET
73 #define EXT2_SUPER_OFFSET 1024
74 #endif
76 /* Since we have unused 2048 bytes in the primary AG of an XFS partition,
77 * we will use the first 0~512 bytes starting from 2048 for the Syslinux
78 * boot sector.
80 #define XFS_BOOTSECT_OFFSET (4 << SECTOR_SHIFT)
81 #define XFS_SUPPORTED_BLOCKSIZE 4096 /* 4 KiB filesystem block size */
84 * btrfs has two discontiguous areas reserved for the boot loader.
85 * Use the first one (Boot Area A) for the boot sector and the ADV,
86 * and the second one for "ldlinux.sys".
88 #define BTRFS_EXTLINUX_OFFSET BTRFS_BOOT_AREA_B_OFFSET
89 #define BTRFS_EXTLINUX_SIZE BTRFS_BOOT_AREA_B_SIZE
90 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
91 static char subvol[BTRFS_SUBVOL_MAX];
93 #define BTRFS_ADV_OFFSET (BTRFS_BOOT_AREA_A_OFFSET + BTRFS_BOOT_AREA_A_SIZE \
94 - 2*ADV_SIZE)
97 * Get the size of a block device
99 static uint64_t get_size(int devfd)
101 uint64_t bytes;
102 uint32_t sects;
103 struct stat st;
105 #ifdef BLKGETSIZE64
106 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
107 return bytes;
108 #endif
109 if (!ioctl(devfd, BLKGETSIZE, &sects))
110 return (uint64_t) sects << 9;
111 else if (!fstat(devfd, &st) && st.st_size)
112 return st.st_size;
113 else
114 return 0;
118 * Get device geometry and partition offset
120 struct geometry_table {
121 uint64_t bytes;
122 struct hd_geometry g;
125 static int sysfs_get_offset(int devfd, unsigned long *start)
127 struct stat st;
128 char sysfs_name[128];
129 FILE *f;
130 int rv;
132 if (fstat(devfd, &st))
133 return -1;
135 if ((size_t)snprintf(sysfs_name, sizeof sysfs_name,
136 "/sys/dev/block/%u:%u/start",
137 major(st.st_rdev), minor(st.st_rdev))
138 >= sizeof sysfs_name)
139 return -1;
141 f = fopen(sysfs_name, "r");
142 if (!f)
143 return -1;
145 rv = fscanf(f, "%lu", start);
146 fclose(f);
148 return (rv == 1) ? 0 : -1;
151 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
152 (x/64/32) is the final fallback. I don't know what LS-240 has
153 as its geometry, since I don't have one and don't know anyone that does,
154 and Google wasn't helpful... */
155 static const struct geometry_table standard_geometries[] = {
156 {360 * 1024, {2, 9, 40, 0}},
157 {720 * 1024, {2, 9, 80, 0}},
158 {1200 * 1024, {2, 15, 80, 0}},
159 {1440 * 1024, {2, 18, 80, 0}},
160 {1680 * 1024, {2, 21, 80, 0}},
161 {1722 * 1024, {2, 21, 80, 0}},
162 {2880 * 1024, {2, 36, 80, 0}},
163 {3840 * 1024, {2, 48, 80, 0}},
164 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
165 {0, {0, 0, 0, 0}}
168 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
170 struct floppy_struct fd_str;
171 struct loop_info li;
172 struct loop_info64 li64;
173 const struct geometry_table *gp;
174 int rv = 0;
176 memset(geo, 0, sizeof *geo);
178 if (!ioctl(devfd, HDIO_GETGEO, geo)) {
179 goto ok;
180 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
181 geo->heads = fd_str.head;
182 geo->sectors = fd_str.sect;
183 geo->cylinders = fd_str.track;
184 geo->start = 0;
185 goto ok;
188 /* Didn't work. Let's see if this is one of the standard geometries */
189 for (gp = standard_geometries; gp->bytes; gp++) {
190 if (gp->bytes == totalbytes) {
191 memcpy(geo, &gp->g, sizeof *geo);
192 goto ok;
196 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
197 what zipdisks use, so this would help if someone has a USB key that
198 they're booting in USB-ZIP mode. */
200 geo->heads = opt.heads ? : 64;
201 geo->sectors = opt.sectors ? : 32;
202 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
203 geo->start = 0;
205 if (!opt.sectors && !opt.heads) {
206 fprintf(stderr,
207 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
208 " (on hard disks, this is usually harmless.)\n",
209 geo->heads, geo->sectors);
210 rv = 1; /* Suboptimal result */
214 /* If this is a loopback device, try to set the start */
215 if (!ioctl(devfd, LOOP_GET_STATUS64, &li64))
216 geo->start = li64.lo_offset >> SECTOR_SHIFT;
217 else if (!ioctl(devfd, LOOP_GET_STATUS, &li))
218 geo->start = (unsigned int)li.lo_offset >> SECTOR_SHIFT;
219 else if (!sysfs_get_offset(devfd, &geo->start)) {
220 /* OK */
223 return rv;
227 * Query the device geometry and put it into the boot sector.
228 * Map the file and put the map in the boot sector and file.
229 * Stick the "current directory" inode number into the file.
231 * Returns the number of modified bytes in the boot file.
233 static int patch_file_and_bootblock(int fd, const char *dir, int devfd)
235 struct stat dirst, xdst;
236 struct hd_geometry geo;
237 sector_t *sectp;
238 uint64_t totalbytes, totalsectors;
239 int nsect;
240 struct fat_boot_sector *sbs;
241 char *dirpath, *subpath, *xdirpath;
242 int rv;
244 dirpath = realpath(dir, NULL);
245 if (!dirpath || stat(dir, &dirst)) {
246 perror("accessing install directory");
247 exit(255); /* This should never happen */
250 if (lstat(dirpath, &xdst) ||
251 dirst.st_ino != xdst.st_ino ||
252 dirst.st_dev != xdst.st_dev) {
253 perror("realpath returned nonsense");
254 exit(255);
257 subpath = strchr(dirpath, '\0');
258 for (;;) {
259 if (*subpath == '/') {
260 if (subpath > dirpath) {
261 *subpath = '\0';
262 xdirpath = dirpath;
263 } else {
264 xdirpath = "/";
266 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
267 subpath = strchr(subpath+1, '/');
268 if (!subpath)
269 subpath = "/"; /* It's the root of the filesystem */
270 break;
272 *subpath = '/';
275 if (subpath == dirpath)
276 break;
278 subpath--;
281 /* Now subpath should contain the path relative to the fs base */
282 dprintf("subpath = %s\n", subpath);
284 totalbytes = get_size(devfd);
285 get_geometry(devfd, totalbytes, &geo);
287 if (opt.heads)
288 geo.heads = opt.heads;
289 if (opt.sectors)
290 geo.sectors = opt.sectors;
292 /* Patch this into a fake FAT superblock. This isn't because
293 FAT is a good format in any way, it's because it lets the
294 early bootstrap share code with the FAT version. */
295 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
297 sbs = (struct fat_boot_sector *)syslinux_bootsect;
299 totalsectors = totalbytes >> SECTOR_SHIFT;
300 if (totalsectors >= 65536) {
301 set_16(&sbs->bsSectors, 0);
302 } else {
303 set_16(&sbs->bsSectors, totalsectors);
305 set_32(&sbs->bsHugeSectors, totalsectors);
307 set_16(&sbs->bsBytesPerSec, SECTOR_SIZE);
308 set_16(&sbs->bsSecPerTrack, geo.sectors);
309 set_16(&sbs->bsHeads, geo.heads);
310 set_32(&sbs->bsHiddenSecs, geo.start);
312 /* Construct the boot file map */
314 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
315 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
316 nsect += 2; /* Two sectors for the ADV */
317 sectp = alloca(sizeof(sector_t) * nsect);
318 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS ||
319 fs_type == XFS || fs_type == UFS1 || fs_type == UFS2) {
320 if (sectmap(fd, sectp, nsect)) {
321 perror("bmap");
322 exit(1);
324 } else if (fs_type == BTRFS) {
325 int i;
326 sector_t *sp = sectp;
328 for (i = 0; i < nsect - 2; i++)
329 *sp++ = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
330 for (i = 0; i < 2; i++)
331 *sp++ = BTRFS_ADV_OFFSET/SECTOR_SIZE + i;
334 /* Create the modified image in memory */
335 rv = syslinux_patch(sectp, nsect, opt.stupid_mode,
336 opt.raid_mode, subpath, subvol);
338 free(dirpath);
339 return rv;
342 static int ext_read_adv_offset(int devfd, off_t offset)
344 const size_t adv_size = 2 * ADV_SIZE;
346 if (xpread(devfd, syslinux_adv, adv_size, offset) != adv_size)
347 return -1;
349 return syslinux_validate_adv(syslinux_adv) ? 1 : 0;
352 static int ext_read_adv(const char *path, int devfd, const char **namep)
354 int err;
355 const char *name;
357 if (fs_type == BTRFS) {
358 /* btrfs "ldlinux.sys" is in 64k blank area */
359 return ext_read_adv_offset(devfd, BTRFS_ADV_OFFSET);
360 } else if (fs_type == XFS) {
361 /* XFS "ldlinux.sys" is in the first 2048 bytes of the primary AG */
362 return ext_read_adv_offset(devfd, boot_image_len);
363 } else {
364 err = read_adv(path, name = "ldlinux.sys");
365 if (err == 2) /* ldlinux.sys does not exist */
366 err = read_adv(path, name = "extlinux.sys");
367 if (namep)
368 *namep = name;
369 return err;
373 static int ext_write_adv_offset(int devfd, off_t offset)
375 const size_t adv_size = 2 * ADV_SIZE;
377 if (xpwrite(devfd, syslinux_adv, adv_size, offset) != adv_size) {
378 perror("writing adv");
379 return 1;
382 return 0;
385 static int ext_write_adv(const char *path, const char *cfg, int devfd)
387 if (fs_type == BTRFS) {
388 /* btrfs "ldlinux.sys" is in 64k blank area */
389 return ext_write_adv_offset(devfd, BTRFS_ADV_OFFSET);
390 } else {
391 return write_adv(path, cfg);
396 * Install the boot block on the specified device.
397 * Must be run AFTER install_file()!
399 int install_bootblock(int fd, const char *device)
401 struct ext2_super_block sb;
402 struct btrfs_super_block sb2;
403 struct fat_boot_sector sb3;
404 struct ntfs_boot_sector sb4;
405 xfs_sb_t sb5;
406 struct ufs_super_block sb6;
407 bool ok = false;
409 if (fs_type == EXT2) {
410 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
411 perror("reading superblock");
412 return 1;
415 if (sb.s_magic == EXT2_SUPER_MAGIC)
416 ok = true;
417 } else if (fs_type == BTRFS) {
418 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
419 != sizeof sb2) {
420 perror("reading superblock");
421 return 1;
423 if (!memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
424 ok = true;
425 } else if (fs_type == VFAT) {
426 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
427 perror("reading fat superblock");
428 return 1;
431 if (fat_check_sb_fields(&sb3))
432 ok = true;
433 } else if (fs_type == NTFS) {
434 if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) {
435 perror("reading ntfs superblock");
436 return 1;
439 if (ntfs_check_sb_fields(&sb4))
440 ok = true;
441 } else if (fs_type == XFS) {
442 if (xpread(fd, &sb5, sizeof sb5, 0) != sizeof sb5) {
443 perror("reading xfs superblock");
444 return 1;
447 if (sb5.sb_magicnum == *(u32 *)XFS_SB_MAGIC) {
448 if (be32_to_cpu(sb5.sb_blocksize) != XFS_SUPPORTED_BLOCKSIZE) {
449 fprintf(stderr,
450 "You need to have 4 KiB filesystem block size for"
451 " being able to install Syslinux in your XFS"
452 " partition (because there is not enough space in MBR to"
453 " determine where Syslinux bootsector can be installed"
454 " regardless of the filesystem block size)\n");
455 return 1;
458 ok = true;
460 } else if (fs_type == UFS1 || fs_type == UFS2) {
461 uint32_t sblock_off = (fs_type == UFS1) ?
462 SBLOCK_UFS1 : SBLOCK_UFS2;
463 uint32_t ufs_smagic = (fs_type == UFS1) ?
464 UFS1_SUPER_MAGIC : UFS2_SUPER_MAGIC;
466 if (xpread(fd, &sb6, sizeof sb6, sblock_off) != sizeof sb6) {
467 perror("reading superblock");
468 return 1;
471 if (sb6.fs_magic == ufs_smagic)
472 ok = true;
475 if (!ok) {
476 fprintf(stderr,
477 "no fat, ntfs, ext2/3/4, btrfs, xfs"
478 " or ufs1/2 superblock found on %s\n",
479 device);
480 return 1;
483 if (fs_type == VFAT) {
484 struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect;
485 if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen ||
486 xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen,
487 offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) {
488 perror("writing fat bootblock");
489 return 1;
491 } else if (fs_type == NTFS) {
492 struct ntfs_boot_sector *sbs =
493 (struct ntfs_boot_sector *)syslinux_bootsect;
494 if (xpwrite(fd, &sbs->NTFS_bsHead,
495 NTFS_bsHeadLen, 0) != NTFS_bsHeadLen ||
496 xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen,
497 offsetof(struct ntfs_boot_sector,
498 NTFS_bsCode)) != NTFS_bsCodeLen) {
499 perror("writing ntfs bootblock");
500 return 1;
502 } else if (fs_type == XFS) {
503 if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len,
504 XFS_BOOTSECT_OFFSET) != syslinux_bootsect_len) {
505 perror("writing xfs bootblock");
506 return 1;
508 } else {
509 if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
510 != syslinux_bootsect_len) {
511 perror("writing bootblock");
512 return 1;
516 return 0;
519 static int rewrite_boot_image(int devfd, const char *path, const char *filename)
521 int fd;
522 int ret;
523 int modbytes;
525 /* Let's create LDLINUX.SYS file again (if it already exists, of course) */
526 fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
527 S_IRUSR | S_IRGRP | S_IROTH);
528 if (fd < 0) {
529 perror(filename);
530 return -1;
533 /* Write boot image data into LDLINUX.SYS file */
534 ret = xpwrite(fd, (const char _force *)boot_image, boot_image_len, 0);
535 if (ret != boot_image_len) {
536 perror("writing bootblock");
537 goto error;
540 /* Write ADV */
541 if (ext_write_adv_offset(fd, boot_image_len)) {
542 fprintf(stderr, "%s: write failure on %s\n", program, filename);
543 goto error;
546 /* Map the file, and patch the initial sector accordingly */
547 modbytes = patch_file_and_bootblock(fd, path, devfd);
549 /* Write the patch area again - this relies on the file being overwritten
550 * in place! */
551 ret = xpwrite(fd, (const char _force *)boot_image, modbytes, 0);
552 if (ret != modbytes) {
553 fprintf(stderr, "%s: write failure on %s\n", program, filename);
554 goto error;
557 return fd;
559 error:
560 close(fd);
562 return -1;
565 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
567 char *file, *oldfile, *c32file;
568 int fd = -1, dirfd = -1;
569 int r1, r2, r3;
571 r1 = asprintf(&file, "%s%sldlinux.sys",
572 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
573 r2 = asprintf(&oldfile, "%s%sextlinux.sys",
574 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
575 r3 = asprintf(&c32file, "%s%sldlinux.c32",
576 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
577 if (r1 < 0 || !file || r2 < 0 || !oldfile || r3 < 0 || !c32file) {
578 perror(program);
579 return 1;
582 dirfd = open(path, O_RDONLY | O_DIRECTORY);
583 if (dirfd < 0) {
584 perror(path);
585 goto bail;
588 fd = open(file, O_RDONLY);
589 if (fd < 0) {
590 if (errno != ENOENT) {
591 perror(file);
592 goto bail;
594 } else {
595 clear_attributes(fd);
597 close(fd);
599 fd = rewrite_boot_image(devfd, path, file);
600 if (fd < 0)
601 goto bail;
603 /* Attempt to set immutable flag and remove all write access */
604 /* Only set immutable flag if file is owned by root */
605 set_attributes(fd);
607 if (fstat(fd, rst)) {
608 perror(file);
609 goto bail;
612 close(dirfd);
613 close(fd);
615 /* Look if we have the old filename */
616 fd = open(oldfile, O_RDONLY);
617 if (fd >= 0) {
618 clear_attributes(fd);
619 close(fd);
620 unlink(oldfile);
623 fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
624 S_IRUSR | S_IRGRP | S_IROTH);
625 if (fd < 0) {
626 perror(c32file);
627 goto bail;
630 r3 = xpwrite(fd, (const char _force *)syslinux_ldlinuxc32,
631 syslinux_ldlinuxc32_len, 0);
632 if (r3 != syslinux_ldlinuxc32_len) {
633 fprintf(stderr, "%s: write failure on %s\n", program, c32file);
634 goto bail;
637 free(file);
638 free(oldfile);
639 free(c32file);
640 return 0;
642 bail:
643 if (dirfd >= 0)
644 close(dirfd);
645 if (fd >= 0)
646 close(fd);
648 free(file);
649 free(oldfile);
650 free(c32file);
651 return 1;
654 /* btrfs has to install ldlinux.sys to a boot area, which is not managed by
655 btrfs tree, so actually this is not installed as a file, since the cow
656 feature of btrfs would move the ldlinux.sys file everywhere. Older
657 versions installed it to the first 64kiB (aka Boot Area A) but as of
658 commit ID 37eef640 (before 6.03-pre12, before 6.03), it is now in Boot
659 Area B (a 768kiB blank space at offset 256kiB). */
660 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
662 char *file;
663 int fd, rv;
665 patch_file_and_bootblock(-1, path, devfd);
666 if (xpwrite(devfd, (const char _force *)boot_image,
667 boot_image_len, BTRFS_EXTLINUX_OFFSET)
668 != boot_image_len) {
669 perror("writing bootblock");
670 return 1;
672 dprintf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
673 if (ext_write_adv_offset(devfd, BTRFS_ADV_OFFSET)) {
674 return 1;
676 dprintf("write adv to 0x%x\n", BTRFS_ADV_OFFSET);
677 if (stat(path, rst)) {
678 perror(path);
679 return 1;
683 * Note that we *can* install ldinux.c32 as a regular file because
684 * it doesn't need to be within the first 64K. The Syslinux core
685 * has enough smarts to search the btrfs dirs and find this file.
687 rv = asprintf(&file, "%s%sldlinux.c32",
688 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
689 if (rv < 0 || !file) {
690 perror(program);
691 return 1;
694 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
695 S_IRUSR | S_IRGRP | S_IROTH);
696 if (fd < 0) {
697 perror(file);
698 free(file);
699 return 1;
702 rv = xpwrite(fd, (const char _force *)syslinux_ldlinuxc32,
703 syslinux_ldlinuxc32_len, 0);
704 if (rv != (int)syslinux_ldlinuxc32_len) {
705 fprintf(stderr, "%s: write failure on %s\n", program, file);
706 rv = 1;
707 } else
708 rv = 0;
710 close(fd);
711 free(file);
712 return rv;
716 * Due to historical reasons (SGI IRIX's design of disk layouts), the first
717 * sector in the primary AG on XFS filesystems contains the superblock, which is
718 * a problem with bootloaders that rely on BIOSes (that load VBRs which are
719 * located in the first sector of the partition).
721 * Thus, we need to handle this issue, otherwise Syslinux will damage the XFS's
722 * superblock.
724 static int xfs_install_file(const char *path, int devfd, struct stat *rst)
726 static char file[PATH_MAX + 1];
727 static char c32file[PATH_MAX + 1];
728 int dirfd = -1;
729 int fd = -1;
730 int retval;
732 snprintf(file, PATH_MAX + 1, "%s%sldlinux.sys", path,
733 path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
734 snprintf(c32file, PATH_MAX + 1, "%s%sldlinux.c32", path,
735 path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
737 dirfd = open(path, O_RDONLY | O_DIRECTORY);
738 if (dirfd < 0) {
739 perror(path);
740 goto bail;
743 fd = open(file, O_RDONLY);
744 if (fd < 0) {
745 if (errno != ENOENT) {
746 perror(file);
747 goto bail;
749 } else {
750 clear_attributes(fd);
753 close(fd);
755 fd = rewrite_boot_image(devfd, path, file);
756 if (fd < 0)
757 goto bail;
759 /* Attempt to set immutable flag and remove all write access */
760 /* Only set immutable flag if file is owned by root */
761 set_attributes(fd);
763 if (fstat(fd, rst)) {
764 perror(file);
765 goto bail;
768 close(dirfd);
769 close(fd);
771 dirfd = -1;
772 fd = -1;
774 fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
775 S_IRUSR | S_IRGRP | S_IROTH);
776 if (fd < 0) {
777 perror(c32file);
778 goto bail;
781 retval = xpwrite(fd, (const char _force *)syslinux_ldlinuxc32,
782 syslinux_ldlinuxc32_len, 0);
783 if (retval != (int)syslinux_ldlinuxc32_len) {
784 fprintf(stderr, "%s: write failure on %s\n", program, file);
785 goto bail;
788 close(fd);
790 sync();
792 return 0;
794 bail:
795 if (dirfd >= 0)
796 close(dirfd);
798 if (fd >= 0)
799 close(fd);
801 return 1;
805 * * test if path is a subvolume:
806 * * this function return
807 * * 0-> path exists but it is not a subvolume
808 * * 1-> path exists and it is a subvolume
809 * * -1 -> path is unaccessible
810 * */
811 static int test_issubvolume(char *path)
814 struct stat st;
815 int res;
817 res = stat(path, &st);
818 if(res < 0 )
819 return -1;
821 return (st.st_ino == 256) && S_ISDIR(st.st_mode);
826 * Get the default subvolume of a btrfs filesystem
827 * rootdir: btrfs root dir
828 * subvol: this function will save the default subvolume name here
830 static char * get_default_subvol(char * rootdir, char * subvol)
832 struct btrfs_ioctl_search_args args;
833 struct btrfs_ioctl_search_key *sk = &args.key;
834 struct btrfs_ioctl_search_header *sh;
835 int ret, i;
836 int fd;
837 struct btrfs_root_ref *ref;
838 struct btrfs_dir_item *dir_item;
839 unsigned long off = 0;
840 int name_len;
841 char *name;
842 char dirname[4096];
843 u64 defaultsubvolid = 0;
845 ret = test_issubvolume(rootdir);
846 if (ret == 1) {
847 fd = open(rootdir, O_RDONLY);
848 if (fd < 0) {
849 fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
851 ret = fd;
853 if (ret <= 0) {
854 subvol[0] = '\0';
855 return NULL;
858 memset(&args, 0, sizeof(args));
860 /* search in the tree of tree roots */
861 sk->tree_id = 1;
864 * set the min and max to backref keys. The search will
865 * only send back this type of key now.
867 sk->max_type = BTRFS_DIR_ITEM_KEY;
868 sk->min_type = BTRFS_DIR_ITEM_KEY;
871 * set all the other params to the max, we'll take any objectid
872 * and any trans
874 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
875 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
877 sk->max_offset = (u64)-1;
878 sk->min_offset = 0;
879 sk->max_transid = (u64)-1;
881 /* just a big number, doesn't matter much */
882 sk->nr_items = 4096;
884 while(1) {
885 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
886 if (ret < 0) {
887 fprintf(stderr, "ERROR: can't perform the search\n");
888 subvol[0] = '\0';
889 return NULL;
891 /* the ioctl returns the number of item it found in nr_items */
892 if (sk->nr_items == 0) {
893 break;
896 off = 0;
899 * for each item, pull the key out of the header and then
900 * read the root_ref item it contains
902 for (i = 0; i < sk->nr_items; i++) {
903 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
904 off += sizeof(*sh);
905 if (sh->type == BTRFS_DIR_ITEM_KEY) {
906 dir_item = (struct btrfs_dir_item *)(args.buf + off);
907 name_len = dir_item->name_len;
908 name = (char *)(dir_item + 1);
911 /*add_root(&root_lookup, sh->objectid, sh->offset,
912 dir_id, name, name_len);*/
913 strncpy(dirname, name, name_len);
914 dirname[name_len] = '\0';
915 if (strcmp(dirname, "default") == 0) {
916 defaultsubvolid = dir_item->location.objectid;
917 break;
920 off += sh->len;
923 * record the mins in sk so we can make sure the
924 * next search doesn't repeat this root
926 sk->min_objectid = sh->objectid;
927 sk->min_type = sh->type;
928 sk->max_type = sh->type;
929 sk->min_offset = sh->offset;
931 if (defaultsubvolid != 0)
932 break;
933 sk->nr_items = 4096;
934 /* this iteration is done, step forward one root for the next
935 * ioctl
937 if (sk->min_objectid < (u64)-1) {
938 sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
939 sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
940 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
941 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
942 sk->min_offset = 0;
943 } else
944 break;
947 if (defaultsubvolid == 0) {
948 subvol[0] = '\0';
949 return NULL;
952 memset(&args, 0, sizeof(args));
954 /* search in the tree of tree roots */
955 sk->tree_id = 1;
958 * set the min and max to backref keys. The search will
959 * only send back this type of key now.
961 sk->max_type = BTRFS_ROOT_BACKREF_KEY;
962 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
965 * set all the other params to the max, we'll take any objectid
966 * and any trans
968 sk->max_objectid = (u64)-1;
969 sk->max_offset = (u64)-1;
970 sk->max_transid = (u64)-1;
972 /* just a big number, doesn't matter much */
973 sk->nr_items = 4096;
975 while(1) {
976 ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
977 if (ret < 0) {
978 fprintf(stderr, "ERROR: can't perform the search\n");
979 subvol[0] = '\0';
980 return NULL;
982 /* the ioctl returns the number of item it found in nr_items */
983 if (sk->nr_items == 0)
984 break;
986 off = 0;
989 * for each item, pull the key out of the header and then
990 * read the root_ref item it contains
992 for (i = 0; i < sk->nr_items; i++) {
993 sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
994 off += sizeof(*sh);
995 if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
996 ref = (struct btrfs_root_ref *)(args.buf + off);
997 name_len = ref->name_len;
998 name = (char *)(ref + 1);
1000 if (sh->objectid == defaultsubvolid) {
1001 strncpy(subvol, name, name_len);
1002 subvol[name_len] = '\0';
1003 dprintf("The default subvolume: %s, ID: %llu\n",
1004 subvol, sh->objectid);
1005 break;
1010 off += sh->len;
1013 * record the mins in sk so we can make sure the
1014 * next search doesn't repeat this root
1016 sk->min_objectid = sh->objectid;
1017 sk->min_type = sh->type;
1018 sk->min_offset = sh->offset;
1020 if (subvol[0] != '\0')
1021 break;
1022 sk->nr_items = 4096;
1023 /* this iteration is done, step forward one root for the next
1024 * ioctl
1026 if (sk->min_objectid < (u64)-1) {
1027 sk->min_objectid++;
1028 sk->min_type = BTRFS_ROOT_BACKREF_KEY;
1029 sk->min_offset = 0;
1030 } else
1031 break;
1033 return subvol;
1036 static int install_file(const char *path, int devfd, struct stat *rst)
1038 if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS
1039 || fs_type == UFS1 || fs_type == UFS2)
1040 return ext2_fat_install_file(path, devfd, rst);
1041 else if (fs_type == BTRFS)
1042 return btrfs_install_file(path, devfd, rst);
1043 else if (fs_type == XFS)
1044 return xfs_install_file(path, devfd, rst);
1046 return 1;
1049 #ifdef __KLIBC__
1050 static char devname_buf[64];
1052 static void device_cleanup(void)
1054 unlink(devname_buf);
1056 #endif
1058 /* Verify that a device fd and a pathname agree.
1059 Return 0 on valid, -1 on error. */
1060 static int validate_device_btrfs(int pathfd, int devfd);
1061 static int validate_device(const char *path, int devfd)
1063 struct stat pst, dst;
1064 struct statfs sfs;
1065 int pfd;
1066 int rv = -1;
1068 pfd = open(path, O_RDONLY|O_DIRECTORY);
1069 if (pfd < 0)
1070 goto err;
1072 if (fstat(pfd, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
1073 goto err;
1075 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
1076 if (fs_type == BTRFS) {
1077 if (sfs.f_type == BTRFS_SUPER_MAGIC)
1078 rv = validate_device_btrfs(pfd, devfd);
1079 } else {
1080 rv = (pst.st_dev == dst.st_rdev) ? 0 : -1;
1083 err:
1084 if (pfd >= 0)
1085 close(pfd);
1086 return rv;
1089 #ifndef __KLIBC__
1090 static char *find_device(const char *mtab_file, dev_t dev)
1092 struct mntent *mnt;
1093 struct stat dst;
1094 FILE *mtab;
1095 char *devname = NULL;
1096 bool done;
1098 mtab = setmntent(mtab_file, "r");
1099 if (!mtab)
1100 return NULL;
1102 done = false;
1103 while ((mnt = getmntent(mtab))) {
1104 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
1105 switch (fs_type) {
1106 case BTRFS:
1107 if (!strcmp(mnt->mnt_type, "btrfs") &&
1108 !stat(mnt->mnt_dir, &dst) &&
1109 dst.st_dev == dev) {
1110 if (!subvol[0])
1111 get_default_subvol(mnt->mnt_dir, subvol);
1112 done = true;
1114 break;
1115 case EXT2:
1116 if ((!strcmp(mnt->mnt_type, "ext2") ||
1117 !strcmp(mnt->mnt_type, "ext3") ||
1118 !strcmp(mnt->mnt_type, "ext4")) &&
1119 !stat(mnt->mnt_fsname, &dst) &&
1120 dst.st_rdev == dev) {
1121 done = true;
1122 break;
1124 case VFAT:
1125 if ((!strcmp(mnt->mnt_type, "vfat")) &&
1126 !stat(mnt->mnt_fsname, &dst) &&
1127 dst.st_rdev == dev) {
1128 done = true;
1129 break;
1131 case NTFS:
1132 if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ ||
1133 !strcmp(mnt->mnt_type, "ntfs")) &&
1134 !stat(mnt->mnt_fsname, &dst) &&
1135 dst.st_rdev == dev) {
1136 done = true;
1137 break;
1140 break;
1141 case XFS:
1142 if (!strcmp(mnt->mnt_type, "xfs") && !stat(mnt->mnt_fsname, &dst) &&
1143 dst.st_rdev == dev) {
1144 done = true;
1145 break;
1148 break;
1149 case UFS1:
1150 case UFS2:
1151 if (!strcmp(mnt->mnt_type, "ufs") && !stat(mnt->mnt_fsname, &dst) &&
1152 dst.st_rdev == dev) {
1153 done = true;
1156 break;
1157 case NONE:
1158 break;
1161 if (done) {
1162 devname = strdup(mnt->mnt_fsname);
1163 break;
1167 endmntent(mtab);
1169 return devname;
1171 #endif
1174 * On newer Linux kernels we can use sysfs to get a backwards mapping
1175 * from device names to standard filenames
1177 static char *find_device_sysfs(dev_t dev)
1179 char sysname[64];
1180 char linkname[PATH_MAX];
1181 ssize_t llen;
1182 char *p, *q;
1183 char *buf = NULL;
1184 struct stat st;
1186 snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
1187 major(dev), minor(dev));
1189 llen = readlink(sysname, linkname, sizeof linkname);
1190 if (llen < 0 || llen >= sizeof linkname)
1191 goto err;
1193 linkname[llen] = '\0';
1195 p = strrchr(linkname, '/');
1196 p = p ? p+1 : linkname; /* Leave basename */
1198 buf = q = malloc(strlen(p) + 6);
1199 if (!buf)
1200 goto err;
1202 memcpy(q, "/dev/", 5);
1203 q += 5;
1205 while (*p) {
1206 *q++ = (*p == '!') ? '/' : *p;
1207 p++;
1210 *q = '\0';
1212 if (!stat(buf, &st) && st.st_dev == dev)
1213 return buf; /* Found it! */
1215 err:
1216 if (buf)
1217 free(buf);
1218 return NULL;
1221 static const char *find_device_mountinfo(const char *path, dev_t dev)
1223 const struct mountinfo *m;
1224 struct stat st;
1226 m = find_mount(path, NULL);
1227 if (!m)
1228 return NULL;
1230 if (m->devpath[0] == '/' && m->dev == dev &&
1231 !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
1232 return m->devpath;
1233 else
1234 return NULL;
1237 static int validate_device_btrfs(int pfd, int dfd)
1239 struct btrfs_ioctl_fs_info_args fsinfo;
1240 static struct btrfs_ioctl_dev_info_args devinfo;
1241 struct btrfs_super_block sb2;
1243 if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
1244 return -1;
1246 /* We do not support multi-device btrfs yet */
1247 if (fsinfo.num_devices != 1)
1248 return -1;
1250 /* The one device will have the max devid */
1251 memset(&devinfo, 0, sizeof devinfo);
1252 devinfo.devid = fsinfo.max_id;
1253 if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
1254 return -1;
1256 if (devinfo.path[0] != '/')
1257 return -1;
1259 if (xpread(dfd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2)
1260 return -1;
1262 if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
1263 return -1;
1265 if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
1266 return -1;
1268 if (sb2.num_devices != 1)
1269 return -1;
1271 if (sb2.dev_item.devid != devinfo.devid)
1272 return -1;
1274 if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid))
1275 return -1;
1277 if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
1278 return -1;
1280 return 0; /* It's good! */
1283 static const char *find_device_btrfs(const char *path)
1285 int pfd, dfd;
1286 struct btrfs_ioctl_fs_info_args fsinfo;
1287 static struct btrfs_ioctl_dev_info_args devinfo;
1288 const char *rv = NULL;
1290 pfd = dfd = -1;
1292 pfd = open(path, O_RDONLY);
1293 if (pfd < 0)
1294 goto err;
1296 if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
1297 goto err;
1299 /* We do not support multi-device btrfs yet */
1300 if (fsinfo.num_devices != 1)
1301 goto err;
1303 /* The one device will have the max devid */
1304 memset(&devinfo, 0, sizeof devinfo);
1305 devinfo.devid = fsinfo.max_id;
1306 if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
1307 goto err;
1309 if (devinfo.path[0] != '/')
1310 goto err;
1312 dfd = open((const char *)devinfo.path, O_RDONLY);
1313 if (dfd < 0)
1314 goto err;
1316 if (!validate_device_btrfs(pfd, dfd))
1317 rv = (const char *)devinfo.path; /* It's good! */
1319 err:
1320 if (pfd >= 0)
1321 close(pfd);
1322 if (dfd >= 0)
1323 close(dfd);
1324 return rv;
1327 static char *dupname(const char *name)
1329 char *out = NULL;
1330 int len = 0;
1331 if (name)
1332 len = strlen(name);
1333 if (len > 0)
1334 out = strndup(name, len);
1335 return out;
1338 static char *get_devname(const char *path)
1340 char *devname = NULL;
1341 struct stat st;
1342 struct statfs sfs;
1344 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
1345 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1346 return devname;
1348 if (statfs(path, &sfs)) {
1349 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1350 return devname;
1353 if (opt.device)
1354 devname = strdup(opt.device);
1356 if (!devname){
1357 if (fs_type == BTRFS) {
1358 /* For btrfs try to get the device name from btrfs itself */
1359 devname = dupname(find_device_btrfs(path));
1363 if (!devname) {
1364 devname = dupname(find_device_mountinfo(path, st.st_dev));
1367 #ifdef __KLIBC__
1368 if (!devname) {
1369 devname = find_device_sysfs(st.st_dev);
1371 if (!devname) {
1372 /* klibc doesn't have getmntent and friends; instead, just create
1373 a new device with the appropriate device type */
1374 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1375 major(st.st_dev), minor(st.st_dev));
1377 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1378 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1379 return devname;
1382 atexit(device_cleanup); /* unlink the device node on exit */
1383 devname = dupname(devname_buf);
1386 #else
1387 if (!devname) {
1388 devname = find_device("/proc/mounts", st.st_dev);
1390 if (!devname) {
1391 /* Didn't find it in /proc/mounts, try /etc/mtab */
1392 devname = find_device("/etc/mtab", st.st_dev);
1394 if (!devname) {
1395 devname = find_device_sysfs(st.st_dev);
1397 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1398 return devname;
1401 fprintf(stderr, "%s is device %s\n", path, devname);
1403 #endif
1404 return devname;
1407 static int open_device(const char *path, struct stat *st, char **_devname)
1409 int devfd;
1410 char *devname = NULL;
1411 struct statfs sfs;
1413 if (st)
1414 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1415 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1416 return -1;
1419 if (statfs(path, &sfs)) {
1420 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1421 return -1;
1424 if (sfs.f_type == EXT2_SUPER_MAGIC)
1425 fs_type = EXT2;
1426 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1427 fs_type = BTRFS;
1428 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1429 fs_type = VFAT;
1430 else if (sfs.f_type == NTFS_SB_MAGIC ||
1431 sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
1432 fs_type = NTFS;
1433 else if (sfs.f_type == XFS_SUPER_MAGIC)
1434 fs_type = XFS;
1435 else if (sfs.f_type == UFS1_SUPER_MAGIC)
1436 fs_type = UFS1;
1437 else if (sfs.f_type == UFS2_SUPER_MAGIC)
1438 fs_type = UFS2;
1440 if (!fs_type) {
1441 fprintf(stderr,
1442 "%s: not a fat, ntfs, ext2/3/4, btrfs, xfs"
1443 " or ufs1/2 filesystem: %s\n",
1444 program, path);
1445 return -1;
1448 devfd = -1;
1449 devname = get_devname(path);
1451 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1452 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1453 free(devname);
1454 return -1;
1457 /* Verify that the device we opened is the device intended */
1458 if (validate_device(path, devfd)) {
1459 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1460 program, path, devname);
1461 free(devname);
1462 close(devfd);
1463 return -1;
1465 if (_devname)
1466 *_devname = devname;
1467 else
1468 free(devname);
1469 return devfd;
1472 static int install_loader(const char *path, int update_only)
1474 struct stat st, fst;
1475 int devfd, rv;
1476 char *devname = NULL;
1478 devfd = open_device(path, &st, &devname);
1479 if (devfd < 0)
1480 return 1;
1482 if (update_only && !syslinux_already_installed(devfd)) {
1483 fprintf(stderr, "%s: no previous syslinux boot sector found\n",
1484 program);
1485 close(devfd);
1486 return 1;
1489 /* Read a pre-existing ADV, if already installed */
1490 if (opt.reset_adv) {
1491 syslinux_reset_adv(syslinux_adv);
1492 } else if (ext_read_adv(path, devfd, NULL) < 0) {
1493 close(devfd);
1494 return 1;
1497 if (modify_adv() < 0) {
1498 close(devfd);
1499 return 1;
1502 /* Install ldlinux.sys */
1503 if (install_file(path, devfd, &fst)) {
1504 close(devfd);
1505 return 1;
1507 if (fst.st_dev != st.st_dev) {
1508 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1509 program);
1510 close(devfd);
1511 return 1;
1514 sync();
1515 rv = install_bootblock(devfd, devname);
1516 free(devname);
1517 close(devfd);
1518 sync();
1520 return rv;
1524 * Modify the ADV of an existing installation
1526 int modify_existing_adv(const char *path)
1528 const char *filename;
1529 int devfd;
1531 devfd = open_device(path, NULL, NULL);
1532 if (devfd < 0)
1533 return 1;
1535 if (ext_read_adv(path, devfd, &filename) < 0) {
1536 close(devfd);
1537 return 1;
1539 if (modify_adv() < 0) {
1540 close(devfd);
1541 return 1;
1543 if (ext_write_adv(path, filename, devfd) < 0) {
1544 close(devfd);
1545 return 1;
1547 close(devfd);
1548 return 0;
1551 int main(int argc, char *argv[])
1553 parse_options(argc, argv, MODE_EXTLINUX);
1555 if (!opt.directory || opt.install_mbr || opt.activate_partition)
1556 usage(EX_USAGE, 0);
1558 if (opt.update_only == -1) {
1559 if (opt.reset_adv || opt.set_once || opt.menu_save)
1560 return modify_existing_adv(opt.directory);
1561 else
1562 usage(EX_USAGE, MODE_EXTLINUX);
1565 return install_loader(opt.directory, opt.update_only);