diff options
author | Feng Tang <feng.tang@intel.com> | 2010-07-12 15:45:05 +0800 |
---|---|---|
committer | Feng Tang <feng.tang@intel.com> | 2010-07-20 11:10:04 +0800 |
commit | fe87f81f3ba8dc1828965fe2cd113dbdfd2248bf (patch) | |
tree | 3f4469512474b5297776a4d4f869892dfe1da7e5 /libinstaller | |
parent | 3b04c7baad33e5ac7750ab2ce3c207492faa7ea8 (diff) | |
download | syslinux-fe87f81f3ba8dc1828965fe2cd113dbdfd2248bf.tar.gz |
elflink: merge these files
com32/include/sys/times.h
com32/include/syslinux/config.h
com32/include/syslinux/pmapi.h
com32/include/unistd.h
com32/include/zconf.h
com32/include/zlib.h
dos/Makefile
dos/errno.h
dos/string.h
dos/syslinux.c
dosutil/eltorito.asm
dosutil/mdiskchk.c
dosutil/mdiskchk.com
extlinux/Makefile
extlinux/fat.h
extlinux/main.c
libfat/libfat.h
libfat/open.c
libinstaller/Makefile
libinstaller/setadv.c
libinstaller/syslinux.h
libinstaller/syslxcom.c
libinstaller/syslxcom.h
libinstaller/syslxint.h
libinstaller/syslxmod.c
libinstaller/syslxopt.c
libinstaller/syslxopt.h
linux/Makefile
linux/syslinux.c
Diffstat (limited to 'libinstaller')
-rw-r--r-- | libinstaller/Makefile | 7 | ||||
-rw-r--r-- | libinstaller/setadv.c | 131 | ||||
-rw-r--r-- | libinstaller/syslinux.h | 11 | ||||
-rw-r--r-- | libinstaller/syslxcom.c | 120 | ||||
-rw-r--r-- | libinstaller/syslxcom.h | 4 | ||||
-rw-r--r-- | libinstaller/syslxint.h | 142 | ||||
-rw-r--r-- | libinstaller/syslxmod.c | 327 | ||||
-rw-r--r-- | libinstaller/syslxopt.c | 104 | ||||
-rw-r--r-- | libinstaller/syslxopt.h | 12 |
9 files changed, 435 insertions, 423 deletions
diff --git a/libinstaller/Makefile b/libinstaller/Makefile index 82c1990e..2beb9315 100644 --- a/libinstaller/Makefile +++ b/libinstaller/Makefile @@ -1,6 +1,5 @@ # _bin.c files required by both BTARGET and ITARGET installers BINFILES = bootsect_bin.c ldlinux_bin.c \ - extlinux_bss_bin.c extlinux_sys_bin.c \ mbr_bin.c gptmbr_bin.c PERL = perl @@ -13,12 +12,6 @@ bootsect_bin.c: ../core/ldlinux.bss bin2c.pl ldlinux_bin.c: ../core/ldlinux.sys bin2c.pl $(PERL) bin2c.pl syslinux_ldlinux 512 < $< > $@ -extlinux_bss_bin.c: ../core/extlinux.bss bin2c.pl - $(PERL) bin2c.pl extlinux_bootsect < $< > $@ - -extlinux_sys_bin.c: ../core/extlinux.sys bin2c.pl - $(PERL) bin2c.pl extlinux_image 512 < $< > $@ - mbr_bin.c: ../mbr/mbr.bin bin2c.pl $(PERL) bin2c.pl syslinux_mbr < $< > $@ diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c index 682b883e..9cf6f188 100644 --- a/libinstaller/setadv.c +++ b/libinstaller/setadv.c @@ -1,6 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Intel Corporation; author: H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,13 +27,7 @@ #include <stddef.h> #include <stdint.h> #include <string.h> -#include <getopt.h> -#include <unistd.h> #include <errno.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> #include "syslxint.h" #include "syslxcom.h" @@ -170,127 +165,3 @@ int syslinux_validate_adv(unsigned char *advbuf) return -1; } } - -/* - * Read the ADV from an existing instance, or initialize if invalid. - * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid - * ADV was found. - */ -int read_adv(const char *path, const char *cfg) -{ - char *file; - int fd = -1; - struct stat st; - int err = 0; - - err = asprintf(&file, "%s%s%s", - path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); - - if (!file) { - perror(program); - return -1; - } - - fd = open(file, O_RDONLY); - if (fd < 0) { - if (errno != ENOENT) { - err = -1; - } else { - syslinux_reset_adv(syslinux_adv); - } - } else if (fstat(fd, &st)) { - err = -1; - } else if (st.st_size < 2 * ADV_SIZE) { - /* Too small to be useful */ - syslinux_reset_adv(syslinux_adv); - err = 0; /* Nothing to read... */ - } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE, - st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { - err = -1; - } else { - /* We got it... maybe? */ - err = syslinux_validate_adv(syslinux_adv) ? 1 : 0; - } - - if (err < 0) - perror(file); - - if (fd >= 0) - close(fd); - if (file) - free(file); - - return err; -} - -/* - * Update the ADV in an existing installation. - */ -int write_adv(const char *path, const char *cfg) -{ - unsigned char advtmp[2 * ADV_SIZE]; - char *file; - int fd = -1; - struct stat st, xst; - int err = 0; - - err = asprintf(&file, "%s%s%s", - path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg); - - if (!file) { - perror(program); - return -1; - } - - fd = open(file, O_RDONLY); - if (fd < 0) { - err = -1; - } else if (fstat(fd, &st)) { - err = -1; - } else if (st.st_size < 2 * ADV_SIZE) { - /* Too small to be useful */ - err = -2; - } else if (xpread(fd, advtmp, 2 * ADV_SIZE, - st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { - err = -1; - } else { - /* We got it... maybe? */ - err = syslinux_validate_adv(advtmp) ? -2 : 0; - if (!err) { - /* Got a good one, write our own ADV here */ - clear_attributes(fd); - - /* Need to re-open read-write */ - close(fd); - fd = open(file, O_RDWR | O_SYNC); - if (fd < 0) { - err = -1; - } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino || - xst.st_dev != st.st_dev || xst.st_size != st.st_size) { - fprintf(stderr, "%s: race condition on write\n", file); - err = -2; - } - /* Write our own version ... */ - if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE, - st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) { - err = -1; - } - - sync(); - set_attributes(fd); - } - } - - if (err == -2) - fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n", - file); - else if (err == -1) - perror(file); - - if (fd >= 0) - close(fd); - if (file) - free(file); - - return err; -} diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h index 8d0212c8..710d30e5 100644 --- a/libinstaller/syslinux.h +++ b/libinstaller/syslinux.h @@ -26,6 +26,11 @@ extern unsigned char syslinux_ldlinux[]; extern const unsigned int syslinux_ldlinux_len; extern const int syslinux_ldlinux_mtime; +#define boot_sector syslinux_bootsect +#define boot_sector_len syslinux_bootsect_len +#define boot_image syslinux_ldlinux +#define boot_image_len syslinux_ldlinux_len + extern unsigned char syslinux_mbr[]; extern const unsigned int syslinux_mbr_len; extern const int syslinux_mbr_mtime; @@ -41,7 +46,9 @@ void syslinux_make_bootsect(void *); const char *syslinux_check_bootsect(const void *bs); /* This patches the boot sector and ldlinux.sys based on a sector map */ -int syslinux_patch(const uint32_t * sectors, int nsectors, - int stupid, int raid_mode); +typedef uint64_t sector_t; +int syslinux_patch(const sector_t *sectors, int nsectors, + int stupid, int raid_mode, + const char *subdir, const char *subvol); #endif diff --git a/libinstaller/syslxcom.c b/libinstaller/syslxcom.c index 825419b4..b176f6d7 100644 --- a/libinstaller/syslxcom.c +++ b/libinstaller/syslxcom.c @@ -26,13 +26,11 @@ #include <getopt.h> #include <unistd.h> #include <errno.h> -#include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mount.h> #include <sys/vfs.h> -#include <linux/fs.h> /* FIGETBSZ, FIBMAP */ -#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */ +#include "linuxioctl.h" #include "syslxcom.h" const char *program; @@ -46,15 +44,6 @@ int fs_type; #endif #define SECTOR_SHIFT 9 -#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ - -/* - * ioctl commands - */ -#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) -#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) -#define EXT2_IOC_GETVERSION _IOR('v', 1, long) -#define EXT2_IOC_SETVERSION _IOW('v', 2, long) static void die(const char *msg) { @@ -180,13 +169,86 @@ void set_attributes(int fd) } } -/* - * Produce file map - */ -int sectmap(int fd, uint32_t * sectors, int nsectors) +/* New FIEMAP based mapping */ +static int sectmap_fie(int fd, sector_t *sectors, int nsectors) +{ + struct fiemap *fm; + struct fiemap_extent *fe; + unsigned int i, nsec; + sector_t sec, *secp, *esec; + struct stat st; + uint64_t maplen; + + if (fstat(fd, &st)) + return -1; + + fm = alloca(sizeof(struct fiemap) + + nsectors * sizeof(struct fiemap_extent)); + + memset(fm, 0, sizeof *fm); + + maplen = (uint64_t)nsectors << SECTOR_SHIFT; + if (maplen > (uint64_t)st.st_size) + maplen = st.st_size; + + fm->fm_start = 0; + fm->fm_length = maplen; + fm->fm_flags = FIEMAP_FLAG_SYNC; + fm->fm_extent_count = nsectors; + + if (ioctl(fd, FS_IOC_FIEMAP, fm)) + return -1; + + memset(sectors, 0, nsectors * sizeof *sectors); + esec = sectors + nsectors; + + fe = fm->fm_extents; + + if (fm->fm_mapped_extents < 1 || + !(fe[fm->fm_mapped_extents-1].fe_flags & FIEMAP_EXTENT_LAST)) + return -1; + + for (i = 0; i < fm->fm_mapped_extents; i++) { + if (fe->fe_flags & FIEMAP_EXTENT_LAST) { + /* If this is the *final* extent, pad the length */ + fe->fe_length = (fe->fe_length + SECTOR_SIZE - 1) + & ~(SECTOR_SIZE - 1); + } + + if ((fe->fe_logical | fe->fe_physical| fe->fe_length) & + (SECTOR_SIZE - 1)) + return -1; + + if (fe->fe_flags & (FIEMAP_EXTENT_UNKNOWN| + FIEMAP_EXTENT_DELALLOC| + FIEMAP_EXTENT_ENCODED| + FIEMAP_EXTENT_DATA_ENCRYPTED| + FIEMAP_EXTENT_UNWRITTEN)) + return -1; + + secp = sectors + (fe->fe_logical >> SECTOR_SHIFT); + sec = fe->fe_physical >> SECTOR_SHIFT; + nsec = fe->fe_length >> SECTOR_SHIFT; + + while (nsec--) { + if (secp >= esec) + break; + *secp++ = sec++; + } + + fe++; + } + + return 0; +} + +/* Legacy FIBMAP based mapping */ +static int sectmap_fib(int fd, sector_t *sectors, int nsectors) { - unsigned int blksize, blk, nblk; + unsigned int blk, nblk; unsigned int i; + unsigned int blksize; + sector_t sec; /* Get block size */ if (ioctl(fd, FIGETBSZ, &blksize)) @@ -197,22 +259,28 @@ int sectmap(int fd, uint32_t * sectors, int nsectors) nblk = 0; while (nsectors) { - blk = nblk++; - dprintf("querying block %u\n", blk); if (ioctl(fd, FIBMAP, &blk)) return -1; - blk *= blksize; + sec = (sector_t)blk * blksize; for (i = 0; i < blksize; i++) { - if (!nsectors) - return 0; - - dprintf("Sector: %10u\n", blk); - *sectors++ = blk++; - nsectors--; + *sectors++ = sec++; + if (! --nsectors) + break; } } return 0; } + +/* + * Produce file map + */ +int sectmap(int fd, sector_t *sectors, int nsectors) +{ + if (!sectmap_fie(fd, sectors, nsectors)) + return 0; + + return sectmap_fib(fd, sectors, nsectors); +} diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h index ba4f1d00..39ca09d3 100644 --- a/libinstaller/syslxcom.h +++ b/libinstaller/syslxcom.h @@ -1,6 +1,8 @@ #ifndef _H_SYSLXCOM_ #define _H_SYSLXCOM_ +#include "syslinux.h" + /* Global fs_type for handling fat, ext2/3/4 and btrfs */ enum filesystem { NONE, @@ -15,6 +17,6 @@ ssize_t xpread(int fd, void *buf, size_t count, off_t offset); ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset); void clear_attributes(int fd); void set_attributes(int fd); -int sectmap(int fd, uint32_t * sectors, int nsectors); +int sectmap(int fd, sector_t *sectors, int nsectors); #endif diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h index 2276bfed..f16c2e5c 100644 --- a/libinstaller/syslxint.h +++ b/libinstaller/syslxint.h @@ -15,6 +15,12 @@ #include "syslinux.h" +#if defined(__386__) || defined(__i386__) || defined(__x86_64__) +# define X86_MEM 1 /* Littleendian and unaligned safe */ +#else +# define X86_MEM 0 +#endif + /* * Access functions for littleendian numbers, possibly misaligned. */ @@ -25,37 +31,47 @@ static inline uint8_t get_8(const uint8_t * p) static inline uint16_t get_16(const uint16_t * p) { -#if defined(__i386__) || defined(__x86_64__) +#if X86_MEM /* Littleendian and unaligned-capable */ return *p; #else const uint8_t *pp = (const uint8_t *)p; - return (uint16_t) pp[0] + ((uint16_t) pp[1] << 8); + return pp[0] + ((uint16_t)pp[1] << 8); #endif } static inline uint32_t get_32(const uint32_t * p) { -#if defined(__i386__) || defined(__x86_64__) +#if X86_MEM /* Littleendian and unaligned-capable */ return *p; #else - const uint8_t *pp = (const uint8_t *)p; - return (uint32_t) pp[0] + ((uint32_t) pp[1] << 8) + - ((uint32_t) pp[2] << 16) + ((uint32_t) pp[3] << 24); + const uint16_t *pp = (const uint16_t *)p; + return get_16(pp[0]) + (uint32_t)get_16(pp[1]); +#endif +} + +static inline uint64_t get_64(const uint64_t * p) +{ +#if X86_MEM + /* Littleendian and unaligned-capable */ + return *p; +#else + const uint32_t *pp = (const uint32_t *)p; + return get_32(pp[0]) + (uint64_t)get_32(pp[1]); #endif } -static inline void set_8(uint8_t * p, uint8_t v) +static inline void set_8(uint8_t *p, uint8_t v) { *p = v; } -static inline void set_16(uint16_t * p, uint16_t v) +static inline void set_16(uint16_t *p, uint16_t v) { -#if defined(__i386__) || defined(__x86_64__) +#if X86_MEM /* Littleendian and unaligned-capable */ - *(uint16_t *) p = v; + *p = v; #else uint8_t *pp = (uint8_t *) p; pp[0] = (v & 0xff); @@ -63,11 +79,11 @@ static inline void set_16(uint16_t * p, uint16_t v) #endif } -static inline void set_32(uint32_t * p, uint32_t v) +static inline void set_32(uint32_t *p, uint32_t v) { -#if defined(__i386__) || defined(__x86_64__) +#if X86_MEM /* Littleendian and unaligned-capable */ - *(uint32_t *) p = v; + *p = v; #else uint8_t *pp = (uint8_t *) p; pp[0] = (v & 0xff); @@ -77,6 +93,68 @@ static inline void set_32(uint32_t * p, uint32_t v) #endif } +static inline void set_64(uint64_t *p, uint64_t v) +{ +#if X86_MEM + /* Littleendian and unaligned-capable */ + *p = v; +#else + uint32_t *pp = (uint32_t *) p; + set_32(pp[0], v); + set_32(pp[1], v >> 32); +#endif +} + +/* + * Special handling for the MS-DOS derivative: syslinux_ldlinux + * is a "far" object... + */ +#ifdef __MSDOS__ + +extern uint16_t ldlinux_seg; /* Defined in dos/syslinux.c */ + +static inline __attribute__ ((const)) +uint16_t ds(void) +{ + uint16_t v; + asm("movw %%ds,%0":"=rm"(v)); + return v; +} + +static inline void *set_fs(const void *p) +{ + uint16_t seg; + + seg = ldlinux_seg + ((size_t) p >> 4); + asm volatile ("movw %0,%%fs"::"rm" (seg)); + return (void *)((size_t) p & 0xf); +} + +uint8_t get_8_sl(const uint8_t * p); +uint16_t get_16_sl(const uint16_t * p); +uint32_t get_32_sl(const uint32_t * p); +uint64_t get_64_sl(const uint64_t * p); +void set_8_sl(uint8_t * p, uint8_t v); +void set_16_sl(uint16_t * p, uint16_t v); +void set_32_sl(uint32_t * p, uint32_t v); +void set_64_sl(uint64_t * p, uint64_t v); +void memcpy_to_sl(void *dst, const void *src, size_t len); + +#else + +/* Sane system ... */ +#define get_8_sl(x) get_8(x) +#define get_16_sl(x) get_16(x) +#define get_32_sl(x) get_32(x) +#define get_64_sl(x) get_64(x) +#define set_8_sl(x,y) set_8(x,y) +#define set_16_sl(x,y) set_16(x,y) +#define set_32_sl(x,y) set_32(x,y) +#define set_64_sl(x,y) set_64(x,y) +#define memcpy_to_sl(d,s,l) memcpy(d,s,l) + +#endif + #define LDLINUX_MAGIC 0x3eb202fe /* Patch area for disk-based installers */ @@ -87,15 +165,31 @@ struct patch_area { uint16_t adv_sectors; uint32_t dwords; uint32_t checksum; - uint16_t diroffset; - uint16_t dirlen; - uint16_t subvoloffset; - uint16_t subvollen; - uint16_t secptroffset; - uint16_t secptrcnt; + uint16_t maxtransfer; + uint16_t epaoffset; /* Pointer to the extended patch area */ }; - /* FAT bootsector format, also used by other disk-based derivatives */ +struct ext_patch_area { + uint16_t advptroffset; /* ADV pointers */ + uint16_t diroffset; /* Current directory field */ + uint16_t dirlen; /* Length of current directory field */ + uint16_t subvoloffset; /* Subvolume field */ + uint16_t subvollen; /* Length of subvolume field */ + uint16_t secptroffset; /* Sector extent pointers */ + uint16_t secptrcnt; /* Number of sector extent pointers */ + + uint16_t sect1ptr0; /* Boot sector offset of sector 1 ptr LSW */ + uint16_t sect1ptr1; /* Boot sector offset of sector 1 ptr MSW */ + uint16_t raidpatch; /* Boot sector RAID mode patch pointer */ +}; + +/* Sector extent */ +struct syslinux_extent { + uint64_t lba; + uint16_t len; +} __attribute__((packed)); + +/* FAT bootsector format, also used by other disk-based derivatives */ struct boot_sector { uint8_t bsJump[3]; char bsOemName[8]; @@ -120,7 +214,7 @@ struct boot_sector { uint32_t VolumeID; char VolumeLabel[11]; char FileSysType[8]; - uint8_t Code[442]; + uint8_t Code[448]; } __attribute__ ((packed)) bs16; struct { uint32_t FATSz32; @@ -136,17 +230,15 @@ struct boot_sector { uint32_t VolumeID; char VolumeLabel[11]; char FileSysType[8]; - uint8_t Code[414]; + uint8_t Code[420]; } __attribute__ ((packed)) bs32; } __attribute__ ((packed)); - uint32_t NextSector; /* Pointer to the first unused sector */ - uint16_t MaxTransfer; /* Max sectors per transfer */ uint16_t bsSignature; } __attribute__ ((packed)); #define bsHead bsJump -#define bsHeadLen offsetof(struct boot_sector, bsOemName) +#define bsHeadLen offsetof(struct boot_sector, bsBytesPerSec) #define bsCode bs32.Code /* The common safe choice */ #define bsCodeLen (offsetof(struct boot_sector, bsSignature) - \ offsetof(struct boot_sector, bsCode)) diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c index 9ab139c6..a68f19fb 100644 --- a/libinstaller/syslxmod.c +++ b/libinstaller/syslxmod.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009 Intel Corporation; author H. Peter Anvin + * Copyright 2009-2010 Intel Corporation; author H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,200 +21,67 @@ #include <inttypes.h> #include <string.h> #include <stddef.h> +#include <stdlib.h> #include "syslinux.h" #include "syslxint.h" -void syslinux_make_bootsect(void *bs) -{ - struct boot_sector *bootsect = bs; - const struct boot_sector *sbs = - (const struct boot_sector *)syslinux_bootsect; - - memcpy(&bootsect->bsHead, &sbs->bsHead, bsHeadLen); - memcpy(&bootsect->bsCode, &sbs->bsCode, bsCodeLen); -} /* - * Check to see that what we got was indeed an MS-DOS boot sector/superblock; - * Return NULL if OK and otherwise an error message; + * Generate sector extents */ -const char *syslinux_check_bootsect(const void *bs) -{ - int veryold; - int sectorsize; - long long sectors, fatsectors, dsectors; - long long clusters; - int rootdirents, clustersize; - const struct boot_sector *sectbuf = bs; - - veryold = 0; - - /* Must be 0xF0 or 0xF8..0xFF */ - if (get_8(§buf->bsMedia) != 0xF0 && get_8(§buf->bsMedia) < 0xF8) - return "invalid media signature (not a FAT filesystem?)"; - - sectorsize = get_16(§buf->bsBytesPerSec); - if (sectorsize == SECTOR_SIZE) - ; /* ok */ - else if (sectorsize >= 512 && sectorsize <= 4096 && - (sectorsize & (sectorsize - 1)) == 0) - return "unsupported sectors size"; - else - return "impossible sector size"; - - clustersize = get_8(§buf->bsSecPerClust); - if (clustersize == 0 || (clustersize & (clustersize - 1))) - return "impossible cluster size"; - - sectors = get_16(§buf->bsSectors); - sectors = sectors ? sectors : get_32(§buf->bsHugeSectors); - - dsectors = sectors - get_16(§buf->bsResSectors); - - fatsectors = get_16(§buf->bsFATsecs); - fatsectors = fatsectors ? fatsectors : get_32(§buf->bs32.FATSz32); - fatsectors *= get_8(§buf->bsFATs); - dsectors -= fatsectors; - - rootdirents = get_16(§buf->bsRootDirEnts); - dsectors -= (rootdirents + sectorsize / 32 - 1) / sectorsize; - - if (dsectors < 0) - return "negative number of data sectors"; - - if (fatsectors == 0) - return "zero FAT sectors"; - - clusters = dsectors / clustersize; - - if (clusters < 0xFFF5) { - /* FAT12 or FAT16 */ - - if (!get_16(§buf->bsFATsecs)) - return "zero FAT sectors (FAT12/16)"; - - if (get_8(§buf->bs16.BootSignature) == 0x29) { - if (!memcmp(§buf->bs16.FileSysType, "FAT12 ", 8)) { - if (clusters >= 0xFF5) - return "more than 4084 clusters but claims FAT12"; - } else if (!memcmp(§buf->bs16.FileSysType, "FAT16 ", 8)) { - if (clusters < 0xFF5) - return "less than 4084 clusters but claims FAT16"; - } else if (!memcmp(§buf->bs16.FileSysType, "FAT32 ", 8)) { - return "less than 65525 clusters but claims FAT32"; - } else if (memcmp(§buf->bs16.FileSysType, "FAT ", 8)) { - static char fserr[] = - "filesystem type \"????????\" not supported"; - memcpy(fserr + 17, §buf->bs16.FileSysType, 8); - return fserr; - } - } - } else if (clusters < 0x0FFFFFF5) { - /* - * FAT32... - * - * Moving the FileSysType and BootSignature was a lovely stroke - * of M$ idiocy... - */ - if (get_8(§buf->bs32.BootSignature) != 0x29 || - memcmp(§buf->bs32.FileSysType, "FAT32 ", 8)) - return "missing FAT32 signature"; - } else { - return "impossibly large number of clusters"; - } - - return NULL; -} - -/* - * Special handling for the MS-DOS derivative: syslinux_ldlinux - * is a "far" object... - */ -#ifdef __MSDOS__ - -#define __noinline __attribute__((noinline)) - -extern uint16_t ldlinux_seg; /* Defined in dos/syslinux.c */ - -static inline __attribute__ ((const)) -uint16_t ds(void) -{ - uint16_t v; -asm("movw %%ds,%0":"=rm"(v)); - return v; -} - -static inline void *set_fs(const void *p) +static void generate_extents(struct syslinux_extent *ex, int nptrs, + const sector_t *sectp, int nsect) { - uint16_t seg; - - seg = ldlinux_seg + ((size_t) p >> 4); - asm volatile ("movw %0,%%fs"::"rm" (seg)); - return (void *)((size_t) p & 0xf); -} + uint32_t addr = 0x7c00 + 2*SECTOR_SIZE; + uint32_t base; + sector_t sect, lba; + unsigned int len; -#if 0 /* unused */ -static __noinline uint8_t get_8_sl(const uint8_t * p) -{ - uint8_t v; + len = lba = base = 0; - p = set_fs(p); - asm volatile ("movb %%fs:%1,%0":"=q" (v):"m"(*p)); - return v; -} -#endif + memset(ex, 0, nptrs * sizeof *ex); -static __noinline uint16_t get_16_sl(const uint16_t * p) -{ - uint16_t v; + while (nsect) { + sect = *sectp++; - p = set_fs(p); - asm volatile ("movw %%fs:%1,%0":"=r" (v):"m"(*p)); - return v; -} + if (len && sect == lba + len && + ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) { + /* We can add to the current extent */ + len++; + goto next; + } -static __noinline uint32_t get_32_sl(const uint32_t * p) -{ - uint32_t v; + if (len) { + set_64_sl(&ex->lba, lba); + set_16_sl(&ex->len, len); + ex++; + } - p = set_fs(p); - asm volatile ("movl %%fs:%1,%0":"=r" (v):"m"(*p)); - return v; -} + base = addr; + lba = sect; + len = 1; -#if 0 /* unused */ -static __noinline void set_8_sl(uint8_t * p, uint8_t v) -{ - p = set_fs(p); - asm volatile ("movb %1,%%fs:%0":"=m" (*p):"qi"(v)); -} -#endif + next: + addr += SECTOR_SIZE; + nsect--; + } -static __noinline void set_16_sl(uint16_t * p, uint16_t v) -{ - p = set_fs(p); - asm volatile ("movw %1,%%fs:%0":"=m" (*p):"ri"(v)); + if (len) { + set_64_sl(&ex->lba, lba); + set_16_sl(&ex->len, len); + ex++; + } } -static __noinline void set_32_sl(uint32_t * p, uint32_t v) +/* + * Form a pointer based on a 16-bit patcharea/epa field + */ +static inline void *ptr(void *img, uint16_t *offset_p) { - p = set_fs(p); - asm volatile ("movl %1,%%fs:%0":"=m" (*p):"ri"(v)); + return (char *)img + get_16_sl(offset_p); } -#else - -/* Sane system ... */ -#define get_8_sl(x) get_8(x) -#define get_16_sl(x) get_16(x) -#define get_32_sl(x) get_32(x) -#define set_8_sl(x,y) set_8(x,y) -#define set_16_sl(x,y) set_16(x,y) -#define set_32_sl(x,y) set_32(x,y) - -#endif - /* * This patches the boot sector and the beginning of ldlinux.sys * based on an ldlinux.sys sector map passed in. Typically this is @@ -226,67 +93,105 @@ static __noinline void set_32_sl(uint32_t * p, uint32_t v) * Returns the number of modified bytes in ldlinux.sys if successful, * otherwise -1. */ -int syslinux_patch(const uint32_t * sectors, int nsectors, - int stupid, int raid_mode) +#define NADV 2 + +int syslinux_patch(const sector_t *sectp, int nsectors, + int stupid, int raid_mode, + const char *subdir, const char *subvol) { struct patch_area *patcharea; + struct ext_patch_area *epa; + struct syslinux_extent *ex; uint32_t *wp; - int nsect = (syslinux_ldlinux_len + 511) >> 9; + int nsect = ((boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT) + 2; uint32_t csum; - int i, dw, nptrs, rv; - struct boot_sector *sbs = (struct boot_sector *)syslinux_bootsect; + int i, dw, nptrs; + struct boot_sector *sbs = (struct boot_sector *)boot_sector; + uint64_t *advptrs; if (nsectors < nsect) - return -1; - - /* Patch in options, as appropriate */ - if (stupid) { - /* Access only one sector at a time */ - set_16(&sbs->MaxTransfer, 1); - } - - i = get_16(&sbs->bsSignature); - if (raid_mode) - set_16((uint16_t *) ((char *)sbs + i), 0x18CD); /* INT 18h */ - set_16(&sbs->bsSignature, 0xAA55); - - /* First sector need pointer in boot sector */ - set_32(&sbs->NextSector, *sectors++); + return -1; /* The actual file is too small for content */ /* Search for LDLINUX_MAGIC to find the patch area */ - for (wp = (uint32_t *) syslinux_ldlinux; get_32_sl(wp) != LDLINUX_MAGIC; - wp++) ; + for (wp = (uint32_t *)boot_image; get_32_sl(wp) != LDLINUX_MAGIC; + wp++) + ; patcharea = (struct patch_area *)wp; + epa = ptr(boot_image, &patcharea->epaoffset); + + /* First sector need pointer in boot sector */ + set_32(ptr(sbs, &epa->sect1ptr0), sectp[0]); + set_32(ptr(sbs, &epa->sect1ptr1), sectp[0] >> 32); + sectp++; + + /* Handle RAID mode */ + if (raid_mode) { + /* Patch in INT 18h = CD 18 */ + set_16(ptr(sbs, &epa->raidpatch), 0x18CD); + } /* Set up the totals */ - dw = syslinux_ldlinux_len >> 2; /* COMPLETE dwords, excluding ADV */ - set_16_sl(&patcharea->data_sectors, nsect); /* Not including ADVs */ + dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */ + set_16_sl(&patcharea->data_sectors, nsect - 2); /* Not including ADVs */ set_16_sl(&patcharea->adv_sectors, 2); /* ADVs need 2 sectors */ set_32_sl(&patcharea->dwords, dw); - /* Set the sector pointers */ - wp = (uint32_t *) ((char *)syslinux_ldlinux + - get_16_sl(&patcharea->secptroffset)); - nptrs = get_16_sl(&patcharea->secptrcnt); + /* Handle Stupid mode */ + if (stupid) { + /* Access only one sector at a time */ + set_16_sl(&patcharea->maxtransfer, 1); + } + + /* Set the sector extents */ + ex = ptr(boot_image, &epa->secptroffset); + nptrs = get_16_sl(&epa->secptrcnt); - nsect += 2; - while (--nsect) { /* the first sector is in bs->NextSector */ - set_32_sl(wp++, *sectors++); - nptrs--; + if (nsect > nptrs) { + /* Not necessarily an error in this case, but a general problem */ + fprintf(stderr, "Insufficient extent space, build error!\n"); + exit(1); } - while (nptrs--) - set_32_sl(wp++, 0); - rv = (char *)wp - (char *)syslinux_ldlinux; + /* -1 for the pointer in the boot sector, -2 for the two ADVs */ + generate_extents(ex, nptrs, sectp, nsect-1-2); + + /* ADV pointers */ + advptrs = ptr(boot_image, &epa->advptroffset); + set_64_sl(&advptrs[0], sectp[nsect-1-2]); + set_64_sl(&advptrs[1], sectp[nsect-1-1]); + + /* Poke in the base directory path */ + if (subdir) { + int sublen = strlen(subdir) + 1; + if (get_16_sl(&epa->dirlen) < sublen) { + fprintf(stderr, "Subdirectory path too long... aborting install!\n"); + exit(1); + } + memcpy_to_sl(ptr(boot_image, &epa->diroffset), subdir, sublen); + } + + /* Poke in the subvolume information */ + if (subvol) { + int sublen = strlen(subvol) + 1; + if (get_16_sl(&epa->subvollen) < sublen) { + fprintf(stderr, "Subvol name too long... aborting install!\n"); + exit(1); + } + memcpy_to_sl(ptr(boot_image, &epa->subvoloffset), subvol, sublen); + } /* Now produce a checksum */ set_32_sl(&patcharea->checksum, 0); csum = LDLINUX_MAGIC; - for (i = 0, wp = (uint32_t *) syslinux_ldlinux; i < dw; i++, wp++) + for (i = 0, wp = (uint32_t *)boot_image; i < dw; i++, wp++) csum -= get_32_sl(wp); /* Negative checksum */ set_32_sl(&patcharea->checksum, csum); - return rv; + /* + * Assume all bytes modified. This can be optimized at the expense + * of keeping track of what the highest modified address ever was. + */ + return dw << 2; } diff --git a/libinstaller/syslxopt.c b/libinstaller/syslxopt.c index 7718de3a..0ff2efbb 100644 --- a/libinstaller/syslxopt.c +++ b/libinstaller/syslxopt.c @@ -20,6 +20,7 @@ #include <stdlib.h> #include <stddef.h> #include <stdint.h> +#include <string.h> #include <getopt.h> #include <sysexits.h> #include "../version.h" @@ -38,12 +39,14 @@ struct sys_options opt = { .directory = NULL, .device = NULL, .offset = 0, + .menu_save = NULL, }; const struct option long_options[] = { + {"force", 0, NULL, 'f'}, /* dummy option for compatibility */ {"install", 0, NULL, 'i'}, {"directory", 1, NULL, 'd'}, - {"offset", 1, NULL, 'f'}, + {"offset", 1, NULL, 't'}, {"update", 0, NULL, 'U'}, {"zipdrive", 0, NULL, 'z'}, {"sectors", 1, NULL, 'S'}, @@ -52,26 +55,36 @@ const struct option long_options[] = { {"raid-mode", 0, NULL, 'r'}, {"version", 0, NULL, 'v'}, {"help", 0, NULL, 'h'}, - {"once", 1, NULL, 'o'}, + {"once", 1, NULL, OPT_ONCE}, {"clear-once", 0, NULL, 'O'}, {"reset-adv", 0, NULL, OPT_RESET_ADV}, + {"menu-save", 1, NULL, 'M'}, {0, 0, 0, 0} }; -const char short_options[] = "id:f:UuzS:H:rvho:O"; +const char short_options[] = "t:fid:UuzS:H:rvho:OM:"; -void __attribute__ ((noreturn)) usage(int rv, int mode) +void __attribute__ ((noreturn)) usage(int rv, enum syslinux_mode mode) { - if (mode) /* for unmounted fs installation */ + switch (mode) { + case MODE_SYSLINUX: + /* For unmounted fs installation (syslinux) */ fprintf(stderr, "Usage: %s [options] device\n" - " --offset -f Offset of the file system on the device \n" + " --offset -t Offset of the file system on the device \n" " --directory -d Directory for installation target\n", program); - else /* actually extlinux can also use -d to provide directory too */ + break; + + case MODE_EXTLINUX: + /* Mounted fs installation (extlinux) */ + /* Actually extlinux can also use -d to provide a directory too... */ fprintf(stderr, "Usage: %s [options] directory\n", program); + break; + } + fprintf(stderr, " --install -i Install over the current bootsector\n" " --update -U Update a previous EXTLINUX installation\n" @@ -80,9 +93,10 @@ void __attribute__ ((noreturn)) usage(int rv, int mode) " --heads=# -H Force number of heads\n" " --stupid -s Slow, safe and stupid mode\n" " --raid -r Fall back to the next device on boot failure\n" - " --once=... -o Execute a command once upon boot\n" + " --once=... %s Execute a command once upon boot\n" " --clear-once -O Clear the boot-once command\n" " --reset-adv Reset auxilliary data\n" + " --menu-save= -M Set the label to select as default on the next boot\n" "\n" " Note: geometry is determined at boot time for devices which\n" " are considered hard disks by the BIOS. Unfortunately, this is\n" @@ -90,13 +104,13 @@ void __attribute__ ((noreturn)) usage(int rv, int mode) " which includes zipdisks and LS-120 superfloppies.\n" "\n" " The -z option is useful for USB devices which are considered\n" - " hard disks by some BIOSes and zipdrives by other BIOSes.\n" - ); + " hard disks by some BIOSes and zipdrives by other BIOSes.\n", + mode == MODE_SYSLINUX ? " " : "-o"); exit(rv); } -void parse_options(int argc, char *argv[], int mode) +void parse_options(int argc, char *argv[], enum syslinux_mode mode) { int o; @@ -104,6 +118,8 @@ void parse_options(int argc, char *argv[], int mode) while ((o = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF) { switch (o) { + case 'f': + break; case 'z': opt.heads = 64; opt.sectors = 32; @@ -143,29 +159,79 @@ void parse_options(int argc, char *argv[], int mode) usage(0, mode); break; case 'o': + if (mode == MODE_SYSLINUX) { + fprintf(stderr, "%s: -o will change meaning in a future version, use -t or --offset\n", program); + goto opt_offset; + } + /* else fall through */ + case OPT_ONCE: opt.set_once = optarg; break; - case 'f': + case 't': + opt_offset: opt.offset = strtoul(optarg, NULL, 0); + break; case 'O': opt.set_once = ""; break; case 'd': opt.directory = optarg; + break; case OPT_RESET_ADV: opt.reset_adv = 1; break; + case 'M': + opt.menu_save = optarg; + break; case 'v': - fputs(program, stderr); - fputs(" " VERSION_STR - " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr); + fprintf(stderr, + "%s " VERSION_STR " Copyright 1994-" YEAR_STR + " H. Peter Anvin et al\n", program); exit(0); default: + fprintf(stderr, "%s: Unknown option: -%c\n", program, optopt); usage(EX_USAGE, mode); } } - if (mode) - opt.device = argv[optind]; - else if (!opt.directory) - opt.directory = argv[optind]; + + switch (mode) { + case MODE_SYSLINUX: + opt.device = argv[optind++]; + break; + case MODE_EXTLINUX: + if (!opt.directory) + opt.directory = argv[optind++]; + break; + } + + if (argv[optind]) + usage(EX_USAGE, mode); /* Excess arguments */ +} + +/* + * Make any user-specified ADV modifications in memory + */ +int modify_adv(void) +{ + int rv = 0; + + if (opt.reset_adv) + syslinux_reset_adv(syslinux_adv); + + if (opt.set_once) { + if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) { + fprintf(stderr, "%s: not enough space for boot-once command\n", + program); + rv = -1; + } + } + if (opt.menu_save) { + if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) { + fprintf(stderr, "%s: not enough space for menu-save label\n", + program); + rv = -1; + } + } + + return rv; } diff --git a/libinstaller/syslxopt.h b/libinstaller/syslxopt.h index d925fa34..446ab9af 100644 --- a/libinstaller/syslxopt.h +++ b/libinstaller/syslxopt.h @@ -13,15 +13,23 @@ struct sys_options { const char *directory; const char *device; unsigned int offset; + const char *menu_save; }; enum long_only_opt { OPT_NONE, OPT_RESET_ADV, + OPT_ONCE, }; -void __attribute__ ((noreturn)) usage(int rv, int mode); -void parse_options(int argc, char *argv[], int mode); +enum syslinux_mode { + MODE_SYSLINUX, /* Unmounted filesystem */ + MODE_EXTLINUX, +}; + +void __attribute__ ((noreturn)) usage(int rv, enum syslinux_mode mode); +void parse_options(int argc, char *argv[], enum syslinux_mode mode); +int modify_adv(void); extern struct sys_options opt; extern const struct option long_options[]; |