diff options
Diffstat (limited to 'extlinux/main.c')
-rw-r--r-- | extlinux/main.c | 313 |
1 files changed, 275 insertions, 38 deletions
diff --git a/extlinux/main.c b/extlinux/main.c index dbf538a1..aa20e1bd 100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -14,7 +14,8 @@ /* * extlinux.c * - * Install the syslinux boot block on an fat, ntfs, ext2/3/4 and btrfs filesystem + * Install the syslinux boot block on an fat, ntfs, ext2/3/4, btrfs and xfs + * filesystem. */ #define _GNU_SOURCE /* Enable everything */ @@ -46,6 +47,10 @@ #include "btrfs.h" #include "fat.h" #include "ntfs.h" +#include "xfs.h" +#include "xfs_types.h" +#include "xfs_sb.h" +#include "misc.h" #include "../version.h" #include "syslxint.h" #include "syslxcom.h" /* common functions shared with extlinux and syslinux */ @@ -64,6 +69,13 @@ #define EXT2_SUPER_OFFSET 1024 #endif +/* Since we have unused 2048 bytes in the primary AG of an XFS partition, + * we will use the first 0~512 bytes starting from 2048 for the Syslinux + * boot sector. + */ +#define XFS_BOOTSECT_OFFSET (4 << SECTOR_SHIFT) +#define XFS_SUPPORTED_BLOCKSIZE 4096 /* 4 KiB filesystem block size */ + /* the btrfs partition first 64K blank area is used to store boot sector and boot image, the boot sector is from 0~512, the boot image starts after */ #define BTRFS_BOOTSECT_AREA 65536 @@ -295,7 +307,8 @@ static int patch_file_and_bootblock(int fd, const char *dir, int devfd) nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT; nsect += 2; /* Two sectors for the ADV */ sectp = alloca(sizeof(sector_t) * nsect); - if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) { + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS || + fs_type == XFS) { if (sectmap(fd, sectp, nsect)) { perror("bmap"); exit(1); @@ -328,6 +341,7 @@ int install_bootblock(int fd, const char *device) struct btrfs_super_block sb2; struct fat_boot_sector sb3; struct ntfs_boot_sector sb4; + xfs_sb_t sb5; bool ok = false; if (fs_type == EXT2) { @@ -335,6 +349,7 @@ int install_bootblock(int fd, const char *device) perror("reading superblock"); return 1; } + if (sb.s_magic == EXT2_SUPER_MAGIC) ok = true; } else if (fs_type == BTRFS) { @@ -350,6 +365,7 @@ int install_bootblock(int fd, const char *device) perror("reading fat superblock"); return 1; } + if (fat_check_sb_fields(&sb3)) ok = true; } else if (fs_type == NTFS) { @@ -360,12 +376,34 @@ int install_bootblock(int fd, const char *device) if (ntfs_check_sb_fields(&sb4)) ok = true; + } else if (fs_type == XFS) { + if (xpread(fd, &sb5, sizeof sb5, 0) != sizeof sb5) { + perror("reading xfs superblock"); + return 1; + } + + if (sb5.sb_magicnum == *(u32 *)XFS_SB_MAGIC) { + if (be32_to_cpu(sb5.sb_blocksize) != XFS_SUPPORTED_BLOCKSIZE) { + fprintf(stderr, + "You need to have 4 KiB filesystem block size for " + " being able to install Syslinux in your XFS " + "partition (because there is no enough space in MBR to " + "determine where Syslinux bootsector can be installed " + "regardless the filesystem block size)\n"); + return 1; + } + + ok = true; + } } + if (!ok) { - fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n", - device); + fprintf(stderr, + "no fat, ntfs, ext2/3/4, btrfs or xfs superblock found on %s\n", + device); return 1; } + if (fs_type == VFAT) { struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect; if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen || @@ -385,6 +423,12 @@ int install_bootblock(int fd, const char *device) perror("writing ntfs bootblock"); return 1; } + } else if (fs_type == XFS) { + if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, + XFS_BOOTSECT_OFFSET) != syslinux_bootsect_len) { + perror("writing xfs bootblock"); + return 1; + } } else { if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0) != syslinux_bootsect_len) { @@ -396,18 +440,66 @@ int install_bootblock(int fd, const char *device) return 0; } +static int rewrite_boot_image(int devfd, const char *path, const char *filename) +{ + int fd; + int ret; + int modbytes; + + /* Let's create LDLINUX.SYS file again (if it already exists, of course) */ + fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, + S_IRUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + perror(filename); + return -1; + } + + /* Write boot image data into LDLINUX.SYS file */ + ret = xpwrite(fd, boot_image, boot_image_len, 0); + if (ret != boot_image_len) { + perror("writing bootblock"); + goto error; + } + + /* Write ADV */ + ret = xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, boot_image_len); + if (ret != 2 * ADV_SIZE) { + fprintf(stderr, "%s: write failure on %s\n", program, filename); + goto error; + } + + /* Map the file, and patch the initial sector accordingly */ + modbytes = patch_file_and_bootblock(fd, path, devfd); + + /* Write the patch area again - this relies on the file being overwritten + * in place! */ + ret = xpwrite(fd, boot_image, modbytes, 0); + if (ret != modbytes) { + fprintf(stderr, "%s: write failure on %s\n", program, filename); + goto error; + } + + return fd; + +error: + close(fd); + + return -1; +} + int ext2_fat_install_file(const char *path, int devfd, struct stat *rst) { - char *file, *oldfile; + char *file, *oldfile, *c32file; int fd = -1, dirfd = -1; - int modbytes; - int r1, r2; + int r1, r2, r3; r1 = asprintf(&file, "%s%sldlinux.sys", path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); r2 = asprintf(&oldfile, "%s%sextlinux.sys", path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); - if (r1 < 0 || !file || r2 < 0 || !oldfile) { + r3 = asprintf(&c32file, "%s%sldlinux.c32", + path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); + if (r1 < 0 || !file || r2 < 0 || !oldfile || r3 < 0 || !c32file) { perror(program); return 1; } @@ -429,30 +521,9 @@ int ext2_fat_install_file(const char *path, int devfd, struct stat *rst) } close(fd); - fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, - S_IRUSR | S_IRGRP | S_IROTH); - if (fd < 0) { - perror(file); + fd = rewrite_boot_image(devfd, path, file); + if (fd < 0) goto bail; - } - - /* Write it the first time */ - if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len || - xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, - boot_image_len) != 2 * ADV_SIZE) { - fprintf(stderr, "%s: write failure on %s\n", program, file); - goto bail; - } - - /* Map the file, and patch the initial sector accordingly */ - modbytes = patch_file_and_bootblock(fd, path, devfd); - - /* Write the patch area again - this relies on the file being - overwritten in place! */ - if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) { - fprintf(stderr, "%s: write failure on %s\n", program, file); - goto bail; - } /* Attempt to set immutable flag and remove all write access */ /* Only set immutable flag if file is owned by root */ @@ -474,8 +545,22 @@ int ext2_fat_install_file(const char *path, int devfd, struct stat *rst) unlink(oldfile); } + fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, + S_IRUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + perror(c32file); + goto bail; + } + + r3 = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0); + if (r3 != syslinux_ldlinuxc32_len) { + fprintf(stderr, "%s: write failure on %s\n", program, c32file); + goto bail; + } + free(file); free(oldfile); + free(c32file); return 0; bail: @@ -486,6 +571,7 @@ bail: free(file); free(oldfile); + free(c32file); return 1; } @@ -494,6 +580,9 @@ bail: since the cow feature of btrfs will move the ldlinux.sys every where */ int btrfs_install_file(const char *path, int devfd, struct stat *rst) { + char *file; + int fd, rv; + patch_file_and_bootblock(-1, path, devfd); if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET) != boot_image_len) { @@ -511,7 +600,125 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst) perror(path); return 1; } + + /* + * Note that we *can* install ldinux.c32 as a regular file because + * it doesn't need to be within the first 64K. The Syslinux core + * has enough smarts to search the btrfs dirs and find this file. + */ + rv = asprintf(&file, "%s%sldlinux.c32", + path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); + if (rv < 0 || !file) { + perror(program); + return 1; + } + + fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, + S_IRUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + perror(file); + free(file); + return 1; + } + + rv = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0); + if (rv != (int)syslinux_ldlinuxc32_len) { + fprintf(stderr, "%s: write failure on %s\n", program, file); + rv = 1; + } else + rv = 0; + + close(fd); + free(file); + return rv; +} + +/* + * Due to historical reasons (SGI IRIX's design of disk layouts), the first + * sector in the primary AG on XFS filesystems contains the superblock, which is + * a problem with bootloaders that rely on BIOSes (that load VBRs which are + * (located in the first sector of the partition). + * + * Thus, we need to handle this issue, otherwise Syslinux will damage the XFS's + * superblock. + */ +static int xfs_install_file(const char *path, int devfd, struct stat *rst) +{ + static char file[PATH_MAX + 1]; + static char c32file[PATH_MAX + 1]; + int dirfd = -1; + int fd = -1; + int retval; + + snprintf(file, PATH_MAX + 1, "%s%sldlinux.sys", path, + path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); + snprintf(c32file, PATH_MAX + 1, "%s%sldlinux.c32", path, + path[0] && path[strlen(path) - 1] == '/' ? "" : "/"); + + dirfd = open(path, O_RDONLY | O_DIRECTORY); + if (dirfd < 0) { + perror(path); + goto bail; + } + + fd = open(file, O_RDONLY); + if (fd < 0) { + if (errno != ENOENT) { + perror(file); + goto bail; + } + } else { + clear_attributes(fd); + } + + close(fd); + + fd = rewrite_boot_image(devfd, path, file); + if (fd < 0) + goto bail; + + /* Attempt to set immutable flag and remove all write access */ + /* Only set immutable flag if file is owned by root */ + set_attributes(fd); + + if (fstat(fd, rst)) { + perror(file); + goto bail; + } + + close(dirfd); + close(fd); + + dirfd = -1; + fd = -1; + + fd = open(c32file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, + S_IRUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + perror(c32file); + goto bail; + } + + retval = xpwrite(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len, 0); + if (retval != (int)syslinux_ldlinuxc32_len) { + fprintf(stderr, "%s: write failure on %s\n", program, file); + goto bail; + } + + close(fd); + + sync(); + return 0; + +bail: + if (dirfd >= 0) + close(dirfd); + + if (fd >= 0) + close(fd); + + return 1; } /* @@ -748,11 +955,14 @@ static char * get_default_subvol(char * rootdir, char * subvol) static int install_file(const char *path, int devfd, struct stat *rst) { - if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) - return ext2_fat_install_file(path, devfd, rst); - else if (fs_type == BTRFS) - return btrfs_install_file(path, devfd, rst); - return 1; + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) + return ext2_fat_install_file(path, devfd, rst); + else if (fs_type == BTRFS) + return btrfs_install_file(path, devfd, rst); + else if (fs_type == XFS) + return xfs_install_file(path, devfd, rst); + + return 1; } #ifdef __KLIBC__ @@ -847,14 +1057,22 @@ static const char *find_device(const char *mtab_file, dev_t dev) } break; + case XFS: + if (!strcmp(mnt->mnt_type, "xfs") && !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } case NONE: break; } + if (done) { devname = strdup(mnt->mnt_fsname); break; } } + endmntent(mtab); return devname; @@ -915,6 +1133,8 @@ static const char *find_device_mountinfo(const char *path, dev_t dev) struct stat st; m = find_mount(path, NULL); + if (!m) + return NULL; if (m->devpath[0] == '/' && m->dev == dev && !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev) @@ -1098,6 +1318,7 @@ static int open_device(const char *path, struct stat *st, const char **_devname) fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno)); return -1; } + if (sfs.f_type == EXT2_SUPER_MAGIC) fs_type = EXT2; else if (sfs.f_type == BTRFS_SUPER_MAGIC) @@ -1106,10 +1327,13 @@ static int open_device(const char *path, struct stat *st, const char **_devname) fs_type = VFAT; else if (sfs.f_type == NTFS_SB_MAGIC || sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */) - fs_type = NTFS; + fs_type = NTFS; + else if (sfs.f_type == XFS_SUPER_MAGIC) + fs_type = XFS; if (!fs_type) { - fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n", + fprintf(stderr, + "%s: not a fat, ntfs, ext2/3/4, btrfs or xfs filesystem: %s\n", program, path); return -1; } @@ -1143,6 +1367,16 @@ static int btrfs_read_adv(int devfd) return syslinux_validate_adv(syslinux_adv) ? 1 : 0; } +static inline int xfs_read_adv(int devfd) +{ + const size_t adv_size = 2 * ADV_SIZE; + + if (xpread(devfd, syslinux_adv, adv_size, boot_image_len) != adv_size) + return -1; + + return syslinux_validate_adv(syslinux_adv) ? 1 : 0; +} + static int ext_read_adv(const char *path, int devfd, const char **namep) { int err; @@ -1151,6 +1385,9 @@ static int ext_read_adv(const char *path, int devfd, const char **namep) if (fs_type == BTRFS) { /* btrfs "ldlinux.sys" is in 64k blank area */ return btrfs_read_adv(devfd); + } else if (fs_type == XFS) { + /* XFS "ldlinux.sys" is in the first 2048 bytes of the primary AG */ + return xfs_read_adv(devfd); } else { err = read_adv(path, name = "ldlinux.sys"); if (err == 2) /* ldlinux.sys does not exist */ |