diff options
author | H. Peter Anvin <hpa@zytor.com> | 2012-03-30 17:40:50 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2012-03-30 17:40:50 -0700 |
commit | 86a2df0467466d3e197837964db38b312b89ac0b (patch) | |
tree | 30828febadd5e0dd25e949819ccd93c803f14f27 | |
parent | 73caae75a315c3de65ac25c9d5f521c1a4d3847f (diff) | |
parent | a1006762fa6f98750bb77d76dd992cb8ea9f9c99 (diff) | |
download | syslinux-86a2df0467466d3e197837964db38b312b89ac0b.tar.gz |
Merge remote-tracking branch 'origin/master' into lwipsyslinux-4.10-pre18
Resolved Conflicts:
NEWS
version
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
76 files changed, 8445 insertions, 2168 deletions
@@ -34,7 +34,7 @@ include $(MAKEDIR)/syslinux.mk MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \ com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \ com32/hdt/*.c32 com32/rosh/*.c32 com32/gfxboot/*.c32 \ - com32/sysdump/*.c32 com32/lua/src/*.c32 + com32/sysdump/*.c32 com32/lua/src/*.c32 com32/chain/*.c32 # syslinux.exe is BTARGET so as to not require everyone to have the # mingw suite installed @@ -16,6 +16,9 @@ Changes in 4.10: prefix _Syslinux_ added, on all http transfers. This can be overridden with the SENDCOOKIES configuration file command. +Changes in 4.06: + * Support for NTFS, by Paulo Alcantara. + Changes in 4.05: * HDT updated, and now supports uploading data to a TFTP server. diff --git a/com32/Makefile b/com32/Makefile index da632a17..b59fd3f9 100644 --- a/com32/Makefile +++ b/com32/Makefile @@ -1,5 +1,5 @@ SUBDIRS = libupload tools lib gpllib libutil modules mboot menu samples rosh cmenu \ - hdt gfxboot sysdump lua/src + hdt gfxboot sysdump lua/src chain all tidy dist clean spotless install: set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done diff --git a/com32/chain/Makefile b/com32/chain/Makefile new file mode 100644 index 00000000..9d398a85 --- /dev/null +++ b/com32/chain/Makefile @@ -0,0 +1,42 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2010 H. Peter Anvin - All Rights Reserved +## Copyright 2010 Michal Soltys +## +## 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 +## the Free Software Foundation, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + + +topdir = ../.. +MAKEDIR = $(topdir)/mk +include $(MAKEDIR)/com32.mk + +OBJS = chain.o partiter.o utility.o options.o mangle.o + +all: chain.c32 + +chain.elf: $(OBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) $(CHAINEXTOPT) -c -o $@ $< + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + + +-include .*.d diff --git a/com32/chain/chain.c b/com32/chain/chain.c new file mode 100644 index 00000000..30153c4d --- /dev/null +++ b/com32/chain/chain.c @@ -0,0 +1,658 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright 2010 Shao Miller + * Copyright 2010 Michal Soltys + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Please see doc/chain.txt for the detailed documentation. + */ + +#include <com32.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <console.h> +#include <consoles.h> +#include <minmax.h> +#include <stdbool.h> +#include <dprintf.h> +#include <errno.h> +#include <unistd.h> +#include <syslinux/loadfile.h> +#include <syslinux/bootrm.h> +#include <syslinux/config.h> +#include <syslinux/disk.h> +#include <syslinux/video.h> +#include "common.h" +#include "chain.h" +#include "utility.h" +#include "options.h" +#include "partiter.h" +#include "mangle.h" + +static int fixed_cnt = 128; /* see comments in main() */ + +static int overlap(const struct data_area *a, const struct data_area *b) +{ + return + a->base + a->size > b->base && + b->base + b->size > a->base; +} + +static int is_phys(uint8_t sdifs) +{ + return + sdifs == SYSLINUX_FS_SYSLINUX || + sdifs == SYSLINUX_FS_EXTLINUX || + sdifs == SYSLINUX_FS_ISOLINUX; +} + +/* + * Search for a specific drive, based on the MBR signature. + * Return drive and iterator at 0th position. + */ +static int find_by_sig(uint32_t mbr_sig, + struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a MBR disk */ + if (boot_part->type != typedos) { + pi_del(&boot_part); + continue; + } + if (boot_part->sub.dos.disk_sig == mbr_sig) { + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +/* + * Search for a specific drive/partition, based on the GPT GUID. + * Return drive and iterator at proper position. + */ +static int find_by_guid(const struct guid *gpt_guid, + struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a GPT disk */ + if (boot_part->type != typegpt) { + pi_del(&boot_part); + continue; + } + /* Check for a matching GPT disk guid */ + if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) { + goto ok; + } + /* disk guid doesn't match, maybe partition guid will */ + while (!pi_next(&boot_part)) { + if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid))) + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +/* + * Search for a specific drive/partition, based on the GPT label. + * Return drive and iterator at proper position. + */ +static int find_by_label(const char *label, struct part_iter **_boot_part) +{ + struct part_iter *boot_part = NULL; + struct disk_info diskinfo; + int drive; + + for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) { + if (disk_get_params(drive, &diskinfo)) + continue; /* Drive doesn't exist */ + if (!(boot_part = pi_begin(&diskinfo, 0))) + continue; + /* Check for a GPT disk */ + if (!(boot_part->type == typegpt)) { + pi_del(&boot_part); + continue; + } + /* Check for a matching partition */ + while (!pi_next(&boot_part)) { + if (!strcmp(label, boot_part->sub.gpt.part_label)) + goto ok; + } + } + drive = -1; +ok: + *_boot_part = boot_part; + return drive; +} + +static void do_boot(struct data_area *data, int ndata) +{ + uint16_t *const bios_fbm = (uint16_t *) 0x413; + addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */ + struct syslinux_memmap *mmap; + struct syslinux_movelist *mlist = NULL; + addr_t endimage; + uint8_t driveno = opt.regs.edx.b[0]; + uint8_t swapdrive = driveno & 0x80; + int i; + + mmap = syslinux_memory_map(); + + if (!mmap) { + error("Cannot read system memory map\n"); + return; + } + + endimage = 0; + for (i = 0; i < ndata; i++) { + if (data[i].base + data[i].size > endimage) + endimage = data[i].base + data[i].size; + } + if (endimage > dosmem) + goto too_big; + + for (i = 0; i < ndata; i++) { + if (syslinux_add_movelist(&mlist, data[i].base, + (addr_t) data[i].data, data[i].size)) + goto enomem; + } + + if (opt.swap && driveno != swapdrive) { + static const uint8_t swapstub_master[] = { + /* The actual swap code */ + 0x53, /* 00: push bx */ + 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */ + 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */ + 0x5b, /* 08: pop bx */ + 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */ + 0x90, 0x90, /* 0E: nop; nop */ + /* Code to install this in the right location */ + /* Entry with DS = CS; ES = SI = 0; CX = 256 */ + 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */ + 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */ + 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */ + 0x4f, /* 1F: dec di */ + 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */ + 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */ + 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */ + 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */ + 0x8e, 0xc7, /* 32: mov es,di */ + 0x31, 0xff, /* 34: xor di,di */ + 0xf3, 0x66, 0xa5, /* 36: rep movsd */ + 0xbe, 0, 0, /* 39: mov si,0 */ + 0xbf, 0, 0, /* 3C: mov di,0 */ + 0x8e, 0xde, /* 3F: mov ds,si */ + 0x8e, 0xc7, /* 41: mov es,di */ + 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */ + 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */ + 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */ + 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */ + /* pad out to segment boundary */ + 0x90, 0x90, /* 5A: ... */ + 0x90, 0x90, 0x90, 0x90, /* 5C: ... */ + }; + static uint8_t swapstub[1024]; + uint8_t *p; + + /* Note: we can't rely on either INT 13h nor the dosmem + vector to be correct at this stage, so we have to use an + installer stub to put things in the right place. + Round the installer location to a 1K boundary so the only + possible overlap is the identity mapping. */ + endimage = (endimage + 1023u) & ~1023u; + + /* Create swap stub */ + memcpy(swapstub, swapstub_master, sizeof swapstub_master); + *(uint16_t *) & swapstub[0x3a] = opt.regs.ds; + *(uint16_t *) & swapstub[0x3d] = opt.regs.es; + *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l; + *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l; + *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l; + *(uint16_t *) & swapstub[0x56] = opt.regs.ip; + *(uint16_t *) & swapstub[0x58] = opt.regs.cs; + p = &swapstub[sizeof swapstub_master]; + + /* Mapping table; start out with identity mapping everything */ + for (i = 0; i < 256; i++) + p[i] = (uint8_t)i; + + /* And the actual swap */ + p[driveno] = swapdrive; + p[swapdrive] = driveno; + + /* Adjust registers */ + opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4); + opt.regs.esi.l = opt.regs.es = 0; + opt.regs.ecx.l = sizeof swapstub >> 2; + opt.regs.ip = 0x10; /* Installer offset */ + opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive; + + if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub, + sizeof swapstub)) + goto enomem; + + endimage += sizeof swapstub; + } + + /* Tell the shuffler not to muck with this area... */ + syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); + + /* Force text mode */ + syslinux_force_text_mode(); + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs); + error("Chainboot failed!\n"); + return; + +too_big: + error("Loader file too large\n"); + return; + +enomem: + error("Out of memory\n"); + return; +} + +int find_dp(struct part_iter **_iter) +{ + struct part_iter *iter = NULL; + struct disk_info diskinfo; + struct guid gpt_guid; + uint64_t fs_lba; + int drive, hd, partition; + const union syslinux_derivative_info *sdi; + + sdi = syslinux_derivative_info(); + + if (!strncmp(opt.drivename, "mbr", 3)) { + if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) { + error("Unable to find requested MBR signature.\n"); + goto bail; + } + } else if (!strncmp(opt.drivename, "guid", 4)) { + if (str_to_guid(opt.drivename + 5, &gpt_guid)) + goto bail; + if (find_by_guid(&gpt_guid, &iter) < 0) { + error("Unable to find requested GPT disk or partition by guid.\n"); + goto bail; + } + } else if (!strncmp(opt.drivename, "label", 5)) { + if (!opt.drivename[6]) { + error("No label specified.\n"); + goto bail; + } + if (find_by_label(opt.drivename + 6, &iter) < 0) { + error("Unable to find requested GPT partition by label.\n"); + goto bail; + } + } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') && + opt.drivename[1] == 'd') { + hd = opt.drivename[0] == 'h' ? 0x80 : 0; + opt.drivename += 2; + drive = hd | strtol(opt.drivename, NULL, 0); + + if (disk_get_params(drive, &diskinfo)) + goto bail; + /* this will start iteration over FDD, possibly raw */ + if (!(iter = pi_begin(&diskinfo, 0))) + goto bail; + + } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) { + if (!is_phys(sdi->c.filesystem)) { + error("When syslinux is not booted from physical disk (or its emulation),\n" + "'boot' and 'fs' are meaningless.\n"); + goto bail; + } + /* offsets match, but in case it changes in the future */ + if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { + drive = sdi->iso.drive_number; + fs_lba = *sdi->iso.partoffset; + } else { + drive = sdi->disk.drive_number; + fs_lba = *sdi->disk.partoffset; + } + if (disk_get_params(drive, &diskinfo)) + goto bail; + /* this will start iteration over disk emulation, possibly raw */ + if (!(iter = pi_begin(&diskinfo, 0))) + goto bail; + + /* 'fs' => we should lookup the syslinux partition number and use it */ + if (!strcmp(opt.drivename, "fs")) { + while (!pi_next(&iter)) { + if (iter->start_lba == fs_lba) + break; + } + /* broken part structure or other problems */ + if (iter->status) { + error("Can't find myself on the drive I booted from.\n"); + goto bail; + } + } + } else { + error("Unparsable drive specification.\n"); + goto bail; + } + /* main options done - only thing left is explicit partition specification, + * if we're still at the disk stage with the iterator AND user supplied + * partition number (including disk pseudo-partition). + */ + if (!iter->index && opt.partition) { + partition = strtol(opt.partition, NULL, 0); + /* search for matching part#, including disk */ + do { + if (iter->index == partition) + break; + } while (!pi_next(&iter)); + if (iter->status) { + error("Requested disk / partition combination not found.\n"); + goto bail; + } + } + + if (!(iter->di.disk & 0x80) && iter->index) { + error("WARNING: Partitions on floppy devices may not work.\n"); + } + + *_iter = iter; + + return 0; + +bail: + pi_del(&iter); + return -1; +} + +static int setup_handover(const struct part_iter *iter, + struct data_area *data) +{ + struct disk_dos_part_entry *ha; + uint32_t synth_size; + uint32_t *plen; + + if (!iter->index) { /* implies typeraw or non-iterated */ + uint32_t len; + /* RAW handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry); + ha = malloc(synth_size); + if (!ha) { + error("Could not build RAW hand-over record!\n"); + goto bail; + } + len = ~0u; + if (iter->length < len) + len = (uint32_t)iter->length; + lba2chs(&ha->start, &iter->di, 0, l2c_cadd); + lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd); + ha->active_flag = 0x80; + ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */ + ha->start_lba = 0; + ha->length = len; + } else if (iter->type == typegpt) { + /* GPT handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry) + + sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size; + ha = malloc(synth_size); + if (!ha) { + error("Could not build GPT hand-over record!\n"); + goto bail; + } + lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd); + lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd); + ha->active_flag = 0x80; + ha->ostype = 0xED; + /* All bits set by default */ + ha->start_lba = ~0u; + ha->length = ~0u; + /* If these fit the precision, pass them on */ + if (iter->start_lba < ha->start_lba) + ha->start_lba = (uint32_t)iter->start_lba; + if (iter->length < ha->length) + ha->length = (uint32_t)iter->length; + /* Next comes the GPT partition record length */ + plen = (uint32_t *) (ha + 1); + plen[0] = (uint32_t)iter->sub.gpt.pe_size; + /* Next comes the GPT partition record copy */ + memcpy(plen + 1, iter->record, plen[0]); +#ifdef DEBUG + dprintf("GPT handover:\n"); + disk_dos_part_dump(ha); + disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1)); +#endif + } else if (iter->type == typedos) { + /* MBR handover protocol */ + synth_size = sizeof(struct disk_dos_part_entry); + ha = malloc(synth_size); + if (!ha) { + error("Could not build MBR hand-over record!\n"); + goto bail; + } + memcpy(ha, iter->record, synth_size); + /* make sure these match bios imaginations and are ebr agnostic */ + lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd); + lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd); + ha->start_lba = (uint32_t)iter->start_lba; + ha->length = (uint32_t)iter->length; + +#ifdef DEBUG + dprintf("MBR handover:\n"); + disk_dos_part_dump(ha); +#endif + } else { + /* shouldn't ever happen */ + goto bail; + } + + data->base = 0x7be; + data->size = synth_size; + data->data = (void *)ha; + + return 0; +bail: + return -1; +} + +int main(int argc, char *argv[]) +{ + struct part_iter *iter = NULL; + void *sbck = NULL; + struct data_area fdat, hdat, sdat, data[3]; + int ndata = 0; + + console_ansi_raw(); + + memset(&fdat, 0, sizeof(fdat)); + memset(&hdat, 0, sizeof(hdat)); + memset(&sdat, 0, sizeof(sdat)); + + opt_set_defs(); + if (opt_parse_args(argc, argv)) + goto bail; + +#if 0 + /* Get max fixed disk number */ + fixed_cnt = *(uint8_t *)(0x475); + + /* + * hmm, looks like we can't do that - + * some bioses/vms just set it to 1 + * and go on living happily + * any better options than hardcoded 0x80 - 0xFF ? + */ +#endif + + /* Get disk/part iterator matching user supplied options */ + if (find_dp(&iter)) + goto bail; + + /* Perform initial partition entry mangling */ + if (manglepe_fixchs(iter)) + goto bail; + if (manglepe_hide(iter)) + goto bail; + + /* Load the boot file */ + if (opt.file) { + fdat.base = (opt.fseg << 4) + opt.foff; + + if (loadfile(opt.file, &fdat.data, &fdat.size)) { + error("Couldn't read the boot file.\n"); + goto bail; + } + if (fdat.base + fdat.size - 1 > ADDRMAX) { + error("The boot file is too big to load at this address.\n"); + goto bail; + } + } + + /* Load the sector */ + if (opt.sect) { + sdat.base = (opt.sseg << 4) + opt.soff; + sdat.size = iter->di.bps; + + if (sdat.base + sdat.size - 1 > ADDRMAX) { + error("The sector cannot be loaded at such high address.\n"); + goto bail; + } + if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) { + error("Couldn't read the sector.\n"); + goto bail; + } + if (opt.save) { + if (!(sbck = malloc(sdat.size))) { + error("Couldn't allocate cmp-buf for option 'save'.\n"); + goto bail; + } + memcpy(sbck, sdat.data, sdat.size); + } + if (opt.file && opt.maps && overlap(&fdat, &sdat)) { + error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n"); + opt.maps = false; + } + } + + /* Prep the handover */ + if (opt.hand) { + if (setup_handover(iter, &hdat)) + goto bail; + /* Verify possible conflicts */ + if ( ( opt.file && overlap(&fdat, &hdat)) || + ( opt.maps && overlap(&sdat, &hdat)) ) { + error("WARNING: Handover area won't be prepared,\n" + "as it would conflict with the boot file and/or the sector.\n"); + opt.hand = false; + } + } + + /* Adjust registers */ + + mangler_init(iter); + mangler_handover(iter, &hdat); + mangler_grldr(iter); + + /* Patching functions */ + + if (manglef_isolinux(&fdat)) + goto bail; + + if (manglef_grub(iter, &fdat)) + goto bail; +#if 0 + if (manglef_drmk(&fdat)) + goto bail; +#endif + if (manglef_bpb(iter, &fdat)) + goto bail; + + if (mangles_bpb(iter, &sdat)) + goto bail; + + if (mangles_save(iter, &sdat, sbck)) + goto bail; + + if (manglesf_bss(&sdat, &fdat)) + goto bail; + + /* This *must* be after BPB saving or copying */ + if (mangles_cmldr(&sdat)) + goto bail; + + /* + * Prepare boot-time mmap data. We should to it here, as manglers could + * potentially alter some of the data. + */ + + if (opt.file) + memcpy(data + ndata++, &fdat, sizeof(fdat)); + if (opt.maps) + memcpy(data + ndata++, &sdat, sizeof(sdat)); + if (opt.hand) + memcpy(data + ndata++, &hdat, sizeof(hdat)); + +#ifdef DEBUG + printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n" + "iter->di C, H, S: %u, %u, %u\n", + iter->di.disk, iter->di.bps, + iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt, + iter->di.cyl, iter->di.head, iter->di.spt); + printf("iter idx: %d\n", iter->index); + printf("iter lba: %"PRIu64"\n", iter->start_lba); + if (opt.hand) + printf("hand lba: %u\n", + ((struct disk_dos_part_entry *)hdat.data)->start_lba); +#endif + + if (opt.warn) { + puts("Press any key to continue booting..."); + wait_key(); + } + + if (ndata && !opt.brkchain) /* boot only if we actually chainload */ + do_boot(data, ndata); + else + error("Service-only run completed, exiting.\n"); +bail: + pi_del(&iter); + /* Free allocated areas */ + free(fdat.data); + free(sdat.data); + free(hdat.data); + free(sbck); + return 255; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/chain.h b/com32/chain/chain.h new file mode 100644 index 00000000..fc481bc6 --- /dev/null +++ b/com32/chain/chain.h @@ -0,0 +1,14 @@ +#ifndef _COM32_CHAIN_CHAIN_H +#define _COM32_CHAIN_CHAIN_H + +#include <syslinux/movebits.h> + +struct data_area { + void *data; + addr_t base; + addr_t size; +}; + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/common.h b/com32/chain/common.h new file mode 100644 index 00000000..b170a732 --- /dev/null +++ b/com32/chain/common.h @@ -0,0 +1,9 @@ +#ifndef _COM32_CHAIN_COMMON_H +#define _COM32_CHAIN_COMMON_H + +#define ADDRMAX 0x9EFFFu +#define ADDRMIN 0x500u + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c new file mode 100644 index 00000000..8358106e --- /dev/null +++ b/com32/chain/mangle.c @@ -0,0 +1,618 @@ +#include <com32.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <dprintf.h> +#include <syslinux/config.h> +#include "common.h" +#include "chain.h" +#include "options.h" +#include "utility.h" +#include "partiter.h" +#include "mangle.h" + +static const char cmldr_signature[8] = "cmdcons"; + +/* Create boot info table: needed when you want to chainload + * another version of ISOLINUX (or another bootlaoder that needs + * the -boot-info-table switch of mkisofs) + * (will only work when run from ISOLINUX) + */ +int manglef_isolinux(struct data_area *data) +{ + const union syslinux_derivative_info *sdi; + unsigned char *isolinux_bin; + uint32_t *checksum, *chkhead, *chktail; + uint32_t file_lba = 0; + + if (!(opt.file && opt.isolinux)) + return 0; + + sdi = syslinux_derivative_info(); + + if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) { + error ("The isolinux= option is only valid when run from ISOLINUX.\n"); + goto bail; + } + + /* Boot info table info (integers in little endian format) + + Offset Name Size Meaning + 8 bi_pvd 4 bytes LBA of primary volume descriptor + 12 bi_file 4 bytes LBA of boot file + 16 bi_length 4 bytes Boot file length in bytes + 20 bi_csum 4 bytes 32-bit checksum + 24 bi_reserved 40 bytes Reserved + + The 32-bit checksum is the sum of all the 32-bit words in the + boot file starting at byte offset 64. All linear block + addresses (LBAs) are given in CD sectors (normally 2048 bytes). + + LBA of primary volume descriptor should already be set to 16. + */ + + isolinux_bin = (unsigned char *)data->data; + + /* Get LBA address of bootfile */ + file_lba = get_file_lba(opt.file); + + if (file_lba == 0) { + error("Failed to find LBA offset of the boot file\n"); + goto bail; + } + /* Set it */ + *((uint32_t *) & isolinux_bin[12]) = file_lba; + + /* Set boot file length */ + *((uint32_t *) & isolinux_bin[16]) = data->size; + + /* Calculate checksum */ + checksum = (uint32_t *) & isolinux_bin[20]; + chkhead = (uint32_t *) & isolinux_bin[64]; + chktail = (uint32_t *) & isolinux_bin[data->size & ~3u]; + *checksum = 0; + while (chkhead < chktail) + *checksum += *chkhead++; + + /* + * Deal with possible fractional dword at the end; + * this *should* never happen... + */ + if (data->size & 3) { + uint32_t xword = 0; + memcpy(&xword, chkhead, data->size & 3); + *checksum += xword; + } + return 0; +bail: + return -1; +} + +/* + * Legacy grub's stage2 chainloading + */ +int manglef_grub(const struct part_iter *iter, struct data_area *data) +{ + /* Layout of stage2 file (from byte 0x0 to 0x270) */ + struct grub_stage2_patch_area { + /* 0x0 to 0x205 */ + char unknown[0x206]; + /* 0x206: compatibility version number major */ + uint8_t compat_version_major; + /* 0x207: compatibility version number minor */ + uint8_t compat_version_minor; + + /* 0x208: install_partition variable */ + struct { + /* 0x208: sub-partition in sub-partition part2 */ + uint8_t part3; + /* 0x209: sub-partition in top-level partition */ + uint8_t part2; + /* 0x20a: top-level partiton number */ + uint8_t part1; + /* 0x20b: BIOS drive number (must be 0) */ + uint8_t drive; + } __attribute__ ((packed)) install_partition; + + /* 0x20c: deprecated (historical reason only) */ + uint32_t saved_entryno; + /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */ + uint8_t stage2_id; + /* 0x211: force LBA */ + uint8_t force_lba; + /* 0x212: version string (will probably be 0.97) */ + char version_string[5]; + /* 0x217: config filename */ + char config_file[89]; + /* 0x270: start of code (after jump from 0x200) */ + char codestart[1]; + } __attribute__ ((packed)) *stage2; + + if (!(opt.file && opt.grub)) + return 0; + + if (data->size < sizeof(struct grub_stage2_patch_area)) { + error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n"); + goto bail; + } + stage2 = data->data; + + /* + * Check the compatibility version number to see if we loaded a real + * stage2 file or a stage2 file that we support. + */ + if (stage2->compat_version_major != 3 + || stage2->compat_version_minor != 2) { + error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n"); + goto bail; + } + + /* + * GRUB Legacy wants the partition number in the install_partition + * variable, located at offset 0x208 of stage2. + * When GRUB Legacy is loaded, it is located at memory address 0x8208. + * + * It looks very similar to the "boot information format" of the + * Multiboot specification: + * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format + * + * 0x208 = part3: sub-partition in sub-partition part2 + * 0x209 = part2: sub-partition in top-level partition + * 0x20a = part1: top-level partition number + * 0x20b = drive: BIOS drive number (must be 0) + * + * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at + * another location. + * + * Partition numbers always start from zero. + * Unused partition bytes must be set to 0xFF. + * + * We only care about top-level partition, so we only need to change + * "part1" to the appropriate value: + * -1: whole drive (default) (-1 = 0xFF) + * 0-3: primary partitions + * 4-*: logical partitions + */ + stage2->install_partition.part1 = (uint8_t)(iter->index - 1); + + /* + * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the + * config filename. The filename passed via grubcfg= will overwrite + * the default config filename "/boot/grub/menu.lst". + */ + if (opt.grubcfg) { + if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) { + error ("The config filename length can't exceed 88 characters.\n"); + goto bail; + } + + strcpy((char *)stage2->config_file, opt.grubcfg); + } + + return 0; +bail: + return -1; +} +#if 0 +/* + * Dell's DRMK chainloading. + */ +int manglef_drmk(struct data_area *data) +{ + /* + * DRMK entry is different than MS-DOS/PC-DOS + * A new size, aligned to 16 bytes to ease use of ds:[bp+28]. + * We only really need 4 new, usable bytes at the end. + */ + + if (!(opt.file && opt.drmk)) + return 0; + + uint32_t tsize = (data->size + 19) & 0xfffffff0; + const union syslinux_derivative_info *sdi; + uint64_t fs_lba; + + sdi = syslinux_derivative_info(); + /* We should lookup the Syslinux partition offset and use it */ + fs_lba = *sdi->disk.partoffset; + + /* + * fs_lba should be verified against the disk as some DRMK + * variants will check and fail if it does not match + */ + dprintf(" fs_lba offset is %d\n", fs_lba); + /* DRMK only uses a DWORD */ + if (fs_lba > 0xffffffff) { + error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n"); + } + opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */ + if (!realloc(data->data, tsize)) { + error("Failed to realloc for DRMK.\n"); + goto bail; + } + data->size = tsize; + /* ds:bp is assumed by DRMK to be the boot sector */ + /* offset 28 is the FAT HiddenSectors value */ + opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2)); + /* "Patch" into tail of the new space */ + *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba; + + return 0; +bail: + return -1; +} +#endif +/* Adjust BPB common function */ +static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag) +{ + unsigned int off; + int type = bpb_detect(data->data, tag); + + /* BPB: hidden sectors 32bit*/ + if (type >= bpbV34) { + if (iter->start_lba < ~0u) + *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba; + else + /* won't really help much, but ... */ + *(uint32_t *) ((char *)data->data + 0x1c) = ~0u; + } + /* BPB: hidden sectors 16bit*/ + if (bpbV30 <= type && type <= bpbV32) { + if (iter->start_lba < 0xFFFF) + *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba; + else + /* won't really help much, but ... */ + *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u; + } + /* BPB: legacy geometry */ + if (type >= bpbV30) { + if (iter->di.cbios) + *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt); + else { + if (iter->di.disk & 0x80) + *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F; + else + *(uint32_t *)((char *)data->data + 0x18) = 0x00020012; + } + } + /* BPB: drive */ + if (drvoff_detect(type, &off)) { + *(uint8_t *)((char *)data->data + off) = (uint8_t) + (opt.swap ? iter->di.disk & 0x80 : iter->di.disk); + } + + return 0; +} + +/* + * Adjust BPB of a BPB-compatible file + */ +int manglef_bpb(const struct part_iter *iter, struct data_area *data) +{ + if (!(opt.file && opt.filebpb)) + return 0; + + return mangle_bpb(iter, data, "file"); +} + +/* + * Adjust BPB of a sector + */ +int mangles_bpb(const struct part_iter *iter, struct data_area *data) +{ + if (!(opt.sect && opt.setbpb)) + return 0; + + return mangle_bpb(iter, data, "sect"); +} + +/* + * This function performs full BPB patching, analogously to syslinux's + * native BSS. + */ +int manglesf_bss(struct data_area *sec, struct data_area *fil) +{ + int type1, type2; + unsigned int cnt = 0; + + if (!(opt.sect && opt.file && opt.bss)) + return 0; + + type1 = bpb_detect(fil->data, "bss/file"); + type2 = bpb_detect(sec->data, "bss/sect"); + + if (!type1 || !type2) { + error("Couldn't determine the BPB type for option 'bss'.\n"); + goto bail; + } + if (type1 != type2) { + error("Option 'bss' can't be used,\n" + "when a sector and a file have incompatible BPBs.\n"); + goto bail; + } + + /* Copy common 2.0 data */ + memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D); + + /* Copy 3.0+ data */ + if (type1 <= bpbV30) { + cnt = 0x06; + } else if (type1 <= bpbV32) { + cnt = 0x08; + } else if (type1 <= bpbV34) { + cnt = 0x0C; + } else if (type1 <= bpbV40) { + cnt = 0x2E; + } else if (type1 <= bpbVNT) { + cnt = 0x3C; + } else if (type1 <= bpbV70) { + cnt = 0x42; + } + memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt); + + return 0; +bail: + return -1; +} + +/* + * Save sector. + */ +int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org) +{ + if (!(opt.sect && opt.save)) + return 0; + + if (memcmp(org, data->data, data->size)) { + if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) { + error("Cannot write the updated sector.\n"); + goto bail; + } + /* function can be called again */ + memcpy(org, data->data, data->size); + } + + return 0; +bail: + return -1; +} + +/* + * To boot the Recovery Console of Windows NT/2K/XP we need to write + * the string "cmdcons\0" to memory location 0000:7C03. + * Memory location 0000:7C00 contains the bootsector of the partition. + */ +int mangles_cmldr(struct data_area *data) +{ + if (!(opt.sect && opt.cmldr)) + return 0; + + memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature)); + return 0; +} + +/* Set common registers */ +int mangler_init(const struct part_iter *iter) +{ + /* Set initial registry values */ + if (opt.file) { + opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg; + opt.regs.ip = (uint16_t)opt.fip; + } else { + opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg; + opt.regs.ip = (uint16_t)opt.sip; + } + + if (opt.regs.ip == 0x7C00 && !opt.regs.cs) + opt.regs.esp.l = 0x7C00; + + /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ + opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk; + + return 0; +} + +/* ds:si & ds:bp */ +int mangler_handover(const struct part_iter *iter, const struct data_area *data) +{ + if (opt.file && opt.maps && !opt.hptr) { + opt.regs.esi.l = opt.regs.ebp.l = opt.soff; + opt.regs.ds = (uint16_t)opt.sseg; + opt.regs.eax.l = 0; + } else if (opt.hand) { + /* base is really 0x7be */ + opt.regs.esi.l = opt.regs.ebp.l = data->base; + opt.regs.ds = 0; + if (iter->index && iter->type == typegpt) /* must be iterated and GPT */ + opt.regs.eax.l = 0x54504721; /* '!GPT' */ + else + opt.regs.eax.l = 0; + } + + return 0; +} + +/* + * GRLDR of GRUB4DOS wants the partition number in DH: + * -1: whole drive (default) + * 0-3: primary partitions + * 4-*: logical partitions + */ +int mangler_grldr(const struct part_iter *iter) +{ + if (opt.grldr) + opt.regs.edx.b[1] = (uint8_t)(iter->index - 1); + + return 0; +} + +/* + * try to copy values from temporary iterator, if positions match + */ +static void push_embr(struct part_iter *diter, struct part_iter *siter) +{ + if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba && + diter->di.disk == siter->di.disk) { + memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr)); + } +} + +static int mpe_sethide(struct part_iter *iter, struct part_iter *miter) +{ + struct disk_dos_part_entry *dp; + static const uint16_t mask = + (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | + (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e); + uint8_t t; + + dp = (struct disk_dos_part_entry *)iter->record; + t = dp->ostype; + + if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) { + /* It's a hideable partition type */ + if (miter->index == iter->index || opt.hide & 4) + t &= (uint8_t)(~0x10u); /* unhide */ + else + t |= 0x10u; /* hide */ + } + if (dp->ostype != t) { + dp->ostype = t; + return -1; + } + return 0; +} + +/* + * miter - iterator we match against + * hide bits meaning: + * ..| - enable (1) / disable (0) + * .|. - all (1) / pri (0) + * |.. - unhide (1) / hide (0) + */ +int manglepe_hide(struct part_iter *miter) +{ + int wb = 0, werr = 0; + struct part_iter *iter = NULL; + struct disk_dos_part_entry *dp; + int ridx; + + if (!opt.hide) + return 0; + + if (miter->type != typedos) { + error("Options '*hide*' is meaningful only for legacy partition scheme.\n"); + return -1; + } + + if (miter->index < 1) + error("WARNING: It's impossible to unhide a disk.\n"); + + if (miter->index > 4 && !(opt.hide & 2)) + error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n"); + + if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */ + return -1; + + while (!pi_next(&iter) && !werr) { + ridx = iter->rawindex; + if (!(opt.hide & 2) && ridx > 4) + break; /* skip when we're constrained to pri only */ + + dp = (struct disk_dos_part_entry *)iter->record; + if (dp->ostype) + wb |= mpe_sethide(iter, miter); + + if (ridx >= 4 && wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + wb = 0; + } + } + + if (iter->status > PI_DONE) + goto bail; + + /* last write */ + if (wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + } + if (werr) + error("WARNING: failed to write E/MBR during '*hide*'\n"); + +bail: + pi_del(&iter); + return 0; +} + +static int mpe_setchs(const struct disk_info *di, + struct disk_dos_part_entry *dp, + uint32_t lba1) +{ + uint32_t ochs1, ochs2; + + ochs1 = *(uint32_t *)dp->start; + ochs2 = *(uint32_t *)dp->end; + + lba2chs(&dp->start, di, lba1, l2c_cadd); + lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd); + + return + *(uint32_t *)dp->start != ochs1 || + *(uint32_t *)dp->end != ochs2; +} + +/* + * miter - iterator we match against + */ +int manglepe_fixchs(struct part_iter *miter) +{ + int wb = 0, werr = 0; + struct part_iter *iter = NULL; + struct disk_dos_part_entry *dp; + int ridx; + + if (!opt.fixchs) + return 0; + + if (miter->type != typedos) { + error("Options 'fixchs' is meaningful only for legacy partition scheme.\n"); + return -1; + } + + if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */ + return -1; + + while (!pi_next(&iter) && !werr) { + ridx = iter->rawindex; + dp = (struct disk_dos_part_entry *)iter->record; + + wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba); + if (ridx > 4) + wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba); + + if (ridx >= 4 && wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + wb = 0; + } + } + + if (iter->status > PI_DONE) + goto bail; + + /* last write */ + if (wb && !werr) { + push_embr(miter, iter); + werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1); + } + if (werr) + error("WARNING: failed to write E/MBR during 'fixchs'\n"); + +bail: + pi_del(&iter); + return 0; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h new file mode 100644 index 00000000..bcefea3b --- /dev/null +++ b/com32/chain/mangle.h @@ -0,0 +1,32 @@ +#ifndef _COM32_CHAIN_MANGLE_H +#define _COM32_CHAIN_MANGLE_H + +#include "chain.h" +#include "partiter.h" + +/* file's manglers */ +int manglef_isolinux(struct data_area *data); +int manglef_grub(const struct part_iter *iter, struct data_area *data); +int manglef_bpb(const struct part_iter *iter, struct data_area *data); +/* int manglef_drmk(struct data_area *data);*/ + +/* sector's manglers */ +int mangles_bpb(const struct part_iter *iter, struct data_area *data); +int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org); +int mangles_cmldr(struct data_area *data); + +/* sector + file's manglers */ +int manglesf_bss(struct data_area *sec, struct data_area *fil); + +/* registers' manglers */ +int mangler_init(const struct part_iter *iter); +int mangler_handover(const struct part_iter *iter, const struct data_area *data); +int mangler_grldr(const struct part_iter *iter); + +/* partition layout's manglers */ +int manglepe_fixchs(struct part_iter *miter); +int manglepe_hide(struct part_iter *miter); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/options.c b/com32/chain/options.c new file mode 100644 index 00000000..658a45ca --- /dev/null +++ b/com32/chain/options.c @@ -0,0 +1,376 @@ +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "chain.h" +#include "utility.h" +#include "options.h" + +struct options opt; + +static int soi_s2n(char *ptr, unsigned int *seg, + unsigned int *off, + unsigned int *ip, + unsigned int def) +{ + unsigned int segval = 0, offval, ipval, val; + char *p; + + offval = def; + ipval = def; + + segval = strtoul(ptr, &p, 0); + if (p[0] == ':' && p[1] && p[1] != ':') + offval = strtoul(p+1, &p, 0); + if (p[0] == ':' && p[1] && p[1] != ':') + ipval = strtoul(p+1, NULL, 0); + + val = (segval << 4) + offval; + + if (val < ADDRMIN || val > ADDRMAX) { + error("Invalid seg:off:* address specified..\n"); + goto bail; + } + + val = (segval << 4) + ipval; + + if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) { + error("Invalid seg:*:ip address specified.\n"); + goto bail; + } + + if (seg) + *seg = segval; + if (off) + *off = offval; + if (ip) + *ip = ipval; + + return 0; +bail: + return -1; +} + +static void usage(void) +{ + unsigned int i; + static const char key[] = "Press any key...\n"; + static const char *const usage[] = { +"\ +Usage:\n\ + chain.c32 [options]\n\ + chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\ + chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\ + chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\ + chain.c32 label{:|=}<label> [<part#>] [options]\n\ + chain.c32 boot{,| }[<part#>] [options]\n\ + chain.c32 fs [options]\n\ +", "\ +\nOptions ('no' prefix specifies default value):\n\ + sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\ + - defaults to 0:0x7C00:0x7C00\n\ + - ommited o/i values default to 0\n\ + maps Map loaded sector into real memory\n\ + nosetbpb Fix BPB fields in loaded sector\n\ + nofilebpb Apply 'setbpb' to loaded file\n\ + nosave Write adjusted sector back to disk\n\ + hand Prepare handover area\n\ + nohptr Force ds:si and ds:bp to point to handover area\n\ + noswap Swap drive numbers, if bootdisk is not fd0/hd0\n\ + nohide Disable all hide variations (also the default)\n\ + hide Hide primary partitions, unhide selected partition\n\ + hideall Hide *all* partitions, unhide selected partition\n\ + unhide Unhide primary partitions\n\ + unhideall Unhide *all* partitions\n\ + nofixchs Walk *all* partitions and fix E/MBRs' chs values\n\ + nokeeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\ + nowarn Wait for a keypress to continue chainloading\n\ + - useful to see emited warnings\n\ + nobreak Actually perform the chainloading\n\ +", "\ +\nOptions continued ...\n\ + file=<file> Load and execute <file>\n\ + seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\ + - defaults to 0:0x7C00:0x7C00\n\ + - ommited o/i values default to 0\n\ + isolinux=<loader> Load another version of ISOLINUX\n\ + ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\ + reactos=<loader> Load ReactOS's loader\n\ + cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\ + freedos=<loader> Load FreeDOS KERNEL.SYS\n\ + msdos=<loader> Load MS-DOS 2.xx - 6.xx IO.SYS\n\ + msdos7=<loader> Load MS-DOS 7+ IO.SYS\n\ + pcdos=<loader> Load PC-DOS IBMBIO.COM\n\ + drmk=<loader> Load DRMK DELLBIO.BIN\n\ + grub=<loader> Load GRUB Legacy stage2\n\ + grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\ + grldr=<loader> Load GRUB4DOS grldr\n\ + bss=<filename> Emulate syslinux's BSS\n\ + bs=<filename> Emulate syslinux's BS\n\ +\nPlease see doc/chain.txt for the detailed documentation.\n\ +" + }; + for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) { + if (i) { + error(key); + wait_key(); + } + error(usage[i]); + } +} + +void opt_set_defs(void) +{ + memset(&opt, 0, sizeof(opt)); + opt.sect = true; /* by def. load sector */ + opt.maps = true; /* by def. map sector */ + opt.hand = true; /* by def. prepare handover */ + opt.brkchain = false; /* by def. do chainload */ + opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00; + opt.drivename = "boot"; +#ifdef DEBUG + opt.warn = true; +#endif +} + +int opt_parse_args(int argc, char *argv[]) +{ + int i; + unsigned int v; + char *p; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "file=", 5)) { + opt.file = argv[i] + 5; + } else if (!strcmp(argv[i], "nofile")) { + opt.file = NULL; + } else if (!strncmp(argv[i], "seg=", 4)) { + if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip, 0)) + goto bail; + } else if (!strncmp(argv[i], "bss=", 4)) { + opt.file = argv[i] + 4; + opt.bss = true; + opt.maps = false; + opt.setbpb = true; + /* opt.save = true; */ + } else if (!strncmp(argv[i], "bs=", 3)) { + opt.file = argv[i] + 3; + opt.sect = false; + opt.filebpb = true; + } else if (!strncmp(argv[i], "isolinux=", 9)) { + opt.file = argv[i] + 9; + opt.isolinux = true; + opt.hand = false; + opt.sect = false; + } else if (!strncmp(argv[i], "ntldr=", 6)) { + opt.fseg = 0x2000; /* NTLDR wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.file = argv[i] + 6; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "reactos=", 8)) { + /* + * settings based on commit + * ad4cf1470977f648ee1dd45e97939589ccb0393c + * note, conflicts with: + * http://reactos.freedoors.org/Reactos%200.3.13/ReactOS-0.3.13-REL-src/boot/freeldr/notes.txt + */ + opt.fseg = 0; + opt.foff = 0x8000; + opt.fip = 0x8100; + opt.file = argv[i] + 8; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "cmldr=", 6)) { + opt.fseg = 0x2000; /* CMLDR wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.file = argv[i] + 6; + opt.cmldr = true; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "freedos=", 8)) { + opt.fseg = 0x60; /* FREEDOS wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.sseg = 0x1FE0; + opt.file = argv[i] + 8; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if ( (v = 6, !strncmp(argv[i], "msdos=", v) || + !strncmp(argv[i], "pcdos=", v)) || + (v = 7, !strncmp(argv[i], "msdos7=", v)) ) { + opt.fseg = 0x70; /* MS-DOS 2.00 .. 6.xx wants this address */ + opt.foff = 0; + opt.fip = v == 7 ? 0x200 : 0; /* MS-DOS 7.0+ wants this ip */ + opt.sseg = 0x8000; + opt.file = argv[i] + v; + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "drmk=", 5)) { + opt.fseg = 0x70; /* DRMK wants this address */ + opt.foff = 0; + opt.fip = 0; + opt.sseg = 0x2000; + opt.soff = 0; + opt.sip = 0; + opt.file = argv[i] + 5; + /* opt.drmk = true; */ + opt.setbpb = true; + /* opt.save = true; */ + opt.hand = false; + } else if (!strncmp(argv[i], "grub=", 5)) { + opt.fseg = 0x800; /* stage2 wants this address */ + opt.foff = 0; + opt.fip = 0x200; + opt.file = argv[i] + 5; + opt.grub = true; + opt.hand = false; + opt.sect = false; + } else if (!strncmp(argv[i], "grubcfg=", 8)) { + opt.grubcfg = argv[i] + 8; + } else if (!strncmp(argv[i], "grldr=", 6)) { + opt.file = argv[i] + 6; + opt.grldr = true; + opt.hand = false; + opt.sect = false; + } else if (!strcmp(argv[i], "keeppxe")) { + opt.keeppxe = 3; + } else if (!strcmp(argv[i], "nokeeppxe")) { + opt.keeppxe = 0; + } else if (!strcmp(argv[i], "maps")) { + opt.maps = true; + } else if (!strcmp(argv[i], "nomaps")) { + opt.maps = false; + } else if (!strcmp(argv[i], "hand")) { + opt.hand = true; + } else if (!strcmp(argv[i], "nohand")) { + opt.hand = false; + } else if (!strcmp(argv[i], "hptr")) { + opt.hptr = true; + } else if (!strcmp(argv[i], "nohptr")) { + opt.hptr = false; + } else if (!strcmp(argv[i], "swap")) { + opt.swap = true; + } else if (!strcmp(argv[i], "noswap")) { + opt.swap = false; + } else if (!strcmp(argv[i], "nohide")) { + opt.hide = 0; + } else if (!strcmp(argv[i], "hide")) { + opt.hide = 1; /* 001b */ + } else if (!strcmp(argv[i], "hideall")) { + opt.hide = 2; /* 010b */ + } else if (!strcmp(argv[i], "unhide")) { + opt.hide = 5; /* 101b */ + } else if (!strcmp(argv[i], "unhideall")) { + opt.hide = 6; /* 110b */ + } else if (!strcmp(argv[i], "setbpb")) { + opt.setbpb = true; + } else if (!strcmp(argv[i], "nosetbpb")) { + opt.setbpb = false; + } else if (!strcmp(argv[i], "filebpb")) { + opt.filebpb = true; + } else if (!strcmp(argv[i], "nofilebpb")) { + opt.filebpb = false; + } else if (!strncmp(argv[i], "sect=", 5) || + !strcmp(argv[i], "sect")) { + if (argv[i][4]) { + if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip, 0)) + goto bail; + } + opt.sect = true; + } else if (!strcmp(argv[i], "nosect")) { + opt.sect = false; + opt.maps = false; + } else if (!strcmp(argv[i], "save")) { + opt.save = true; + } else if (!strcmp(argv[i], "nosave")) { + opt.save = false; + } else if (!strcmp(argv[i], "fixchs")) { + opt.fixchs = true; + } else if (!strcmp(argv[i], "nofixchs")) { + opt.fixchs = false; + } else if (!strcmp(argv[i], "warn")) { + opt.warn = true; + } else if (!strcmp(argv[i], "nowarn")) { + opt.warn = false; + } else if (!strcmp(argv[i], "nobreak")) { + opt.brkchain = false; + } else if (!strcmp(argv[i], "break")) { + opt.brkchain = true; + opt.file = NULL; + opt.maps = false; + opt.hand = false; + } else if (((argv[i][0] == 'h' || argv[i][0] == 'f') + && argv[i][1] == 'd') + || !strncmp(argv[i], "mbr:", 4) + || !strncmp(argv[i], "mbr=", 4) + || !strncmp(argv[i], "guid:", 5) + || !strncmp(argv[i], "guid=", 5) + || !strncmp(argv[i], "label:", 6) + || !strncmp(argv[i], "label=", 6) + || !strcmp(argv[i], "boot") + || !strncmp(argv[i], "boot,", 5) + || !strcmp(argv[i], "fs")) { + opt.drivename = argv[i]; + if (strncmp(argv[i], "label", 5)) + p = strchr(opt.drivename, ','); + else + p = NULL; + if (p) { + *p = '\0'; + opt.partition = p + 1; + } else if (argv[i + 1] && argv[i + 1][0] >= '0' + && argv[i + 1][0] <= '9') { + opt.partition = argv[++i]; + } + } else { + usage(); + goto bail; + } + } + + if (opt.grubcfg && !opt.grub) { + error("grubcfg=<filename> must be used together with grub=<loader>.\n"); + goto bail; + } + +#if 0 + if ((!opt.maps || !opt.sect) && !opt.file) { + error("You have to load something.\n"); + goto bail; + } +#endif + + if (opt.filebpb && !opt.file) { + error("Option 'filebpb' requires a file.\n"); + goto bail; + } + + if (opt.save && !opt.sect) { + error("Option 'save' requires a sector.\n"); + goto bail; + } + + if (opt.setbpb && !opt.sect) { + error("Option 'setbpb' requires a sector.\n"); + goto bail; + } + + if (opt.maps && !opt.sect) { + error("Option 'maps' requires a sector.\n"); + goto bail; + } + + return 0; +bail: + return -1; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/options.h b/com32/chain/options.h new file mode 100644 index 00000000..4493ef1f --- /dev/null +++ b/com32/chain/options.h @@ -0,0 +1,47 @@ +#ifndef _COM32_CHAIN_OPTIONS_H +#define _COM32_CHAIN_OPTIONS_H + +#include <stdint.h> +#include <syslinux/bootrm.h> + +struct options { + unsigned int fseg; + unsigned int foff; + unsigned int fip; + unsigned int sseg; + unsigned int soff; + unsigned int sip; + const char *drivename; + const char *partition; + const char *file; + const char *grubcfg; + bool isolinux; + bool cmldr; + bool drmk; + bool grub; + bool grldr; + bool maps; + bool hand; + bool hptr; + bool swap; + int hide; + bool sect; + bool save; + bool bss; + bool setbpb; + bool filebpb; + bool fixchs; + bool warn; + bool brkchain; + uint16_t keeppxe; + struct syslinux_rm_regs regs; +}; + +extern struct options opt; + +void opt_set_defs(void); +int opt_parse_args(int argc, char *argv[]); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c new file mode 100644 index 00000000..1acd1958 --- /dev/null +++ b/com32/chain/partiter.c @@ -0,0 +1,805 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Shao Miller + * Copyright 2010 Michal Soltys + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * partiter.c + * + * Provides disk / partition iteration. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <zlib.h> +#include <syslinux/disk.h> +#include "common.h" +#include "partiter.h" +#include "utility.h" + +#define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85) +#define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00) +#define sane(s,l) ((s)+(l) > (s)) + +/* forwards */ + +static int iter_ctor(struct part_iter *, va_list *); +static int iter_dos_ctor(struct part_iter *, va_list *); +static int iter_gpt_ctor(struct part_iter *, va_list *); +static void iter_dtor(struct part_iter *); +static struct part_iter *pi_dos_next(struct part_iter *); +static struct part_iter *pi_gpt_next(struct part_iter *); +static struct part_iter *pi_raw_next(struct part_iter *); + +static struct itertype types[] = { + [0] = { + .ctor = &iter_dos_ctor, + .dtor = &iter_dtor, + .next = &pi_dos_next, +}, [1] = { + .ctor = &iter_gpt_ctor, + .dtor = &iter_dtor, + .next = &pi_gpt_next, +}, [2] = { + .ctor = &iter_ctor, + .dtor = &iter_dtor, + .next = &pi_raw_next, +}}; + +const struct itertype * const typedos = types; +const struct itertype * const typegpt = types+1; +const struct itertype * const typeraw = types+2; + +#ifdef DEBUG +static int inv_type(const void *type) +{ + int i, cnt = sizeof(types)/sizeof(types[0]); + for (i = 0; i < cnt; i++) { + if (type == types + i) + return 0; + } + return -1; +} +#endif + +/** + * iter_ctor() - common iterator initialization + * @iter: iterator pointer + * @args(0): disk_info structure used for disk functions + * @args(1): stepall modifier + * + * Second and further arguments are passed as a pointer to va_list + **/ +static int iter_ctor(struct part_iter *iter, va_list *args) +{ + const struct disk_info *di = va_arg(*args, const struct disk_info *); + int stepall = va_arg(*args, int); + +#ifdef DEBUG + if (!di) + return -1; +#endif + + memcpy(&iter->di, di, sizeof(struct disk_info)); + iter->stepall = stepall; + iter->index0 = -1; + iter->length = di->lbacnt; + + return 0; +} + +/** + * iter_dtor() - common iterator cleanup + * @iter: iterator pointer + * + **/ +static void iter_dtor(struct part_iter *iter) +{ + free(iter->data); +} + +/** + * iter_dos_ctor() - MBR/EBR iterator specific initialization + * @iter: iterator pointer + * @args(0): disk_info structure used for disk functions + * @args(1): pointer to buffer with loaded valid MBR + * + * Second and further arguments are passed as a pointer to va_list. + * This function only makes rudimentary checks. If user uses + * pi_new(), he/she is responsible for doing proper sanity checks. + **/ +static int iter_dos_ctor(struct part_iter *iter, va_list *args) +{ + const struct disk_dos_mbr *mbr; + + /* uses args(0) */ + if (iter_ctor(iter, args)) + return -1; + + mbr = va_arg(*args, const struct disk_dos_mbr *); + +#ifdef DEBUG + if (!mbr) + goto bail; +#endif + + if (!(iter->data = malloc(sizeof(struct disk_dos_mbr)))) + goto bail; + + memcpy(iter->data, mbr, sizeof(struct disk_dos_mbr)); + + iter->sub.dos.bebr_index0 = -1; + iter->sub.dos.disk_sig = mbr->disk_sig; + + return 0; +bail: + iter->type->dtor(iter); + return -1; +} + +/** + * iter_gpt_ctor() - GPT iterator specific initialization + * @iter: iterator pointer + * @args(0): ptr to disk_info structure + * @args(1): ptr to buffer with GPT header + * @args(2): ptr to buffer with GPT partition list + * + * Second and further arguments are passed as a pointer to va_list. + * This function only makes rudimentary checks. If user uses + * pi_new(), he/she is responsible for doing proper sanity checks. + **/ +static int iter_gpt_ctor(struct part_iter *iter, va_list *args) +{ + uint64_t siz; + const struct disk_gpt_header *gpth; + const struct disk_gpt_part_entry *gptl; + + /* uses args(0) */ + if (iter_ctor(iter, args)) + return -1; + + gpth = va_arg(*args, const struct disk_gpt_header *); + gptl = va_arg(*args, const struct disk_gpt_part_entry *); + +#ifdef DEBUG + if (!gpth || !gptl) + goto bail; +#endif + + siz = (uint64_t)gpth->part_count * gpth->part_size; + +#ifdef DEBUG + if (!siz || (siz + iter->di.bps - 1) / iter->di.bps > 255u || + gpth->part_size < sizeof(struct disk_gpt_part_entry)) { + goto bail; + } +#endif + + if (!(iter->data = malloc((size_t)siz))) + goto bail; + + memcpy(iter->data, gptl, (size_t)siz); + + iter->sub.gpt.pe_count = (int)gpth->part_count; + iter->sub.gpt.pe_size = (int)gpth->part_size; + iter->sub.gpt.ufirst = gpth->lba_first_usable; + iter->sub.gpt.ulast = gpth->lba_last_usable; + + memcpy(&iter->sub.gpt.disk_guid, &gpth->disk_guid, sizeof(struct guid)); + + return 0; +bail: + iter->type->dtor(iter); + return -1; +} + +/* Logical partition must be sane, meaning: + * - must be data or empty + * - must have non-0 start and length + * - values must not wrap around 32bit + * - must be inside current EBR frame + */ + +static int notsane_logical(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + uint32_t end_log; + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + if (!dp[0].ostype) + return 0; + + if (ost_is_ext(dp[0].ostype)) { + error("1st EBR entry must be data or empty.\n"); + return -1; + } + + end_log = dp[0].start_lba + dp[0].length; + + if (!dp[0].start_lba || + !dp[0].length || + !sane(dp[0].start_lba, dp[0].length) || + end_log > iter->sub.dos.ebr_size) { + + error("Insane logical partition.\n"); + return -1; + } + + return 0; +} + +/* Extended partition must be sane, meaning: + * - must be extended or empty + * - must have non-0 start and length + * - values must not wrap around 32bit + * - must be inside base EBR frame + */ + +static int notsane_extended(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + uint32_t end_ebr; + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + if (!dp[1].ostype) + return 0; + + if (!ost_is_nondata(dp[1].ostype)) { + error("2nd EBR entry must be extended or empty.\n"); + return -1; + } + + end_ebr = dp[1].start_lba + dp[1].length; + + if (!dp[1].start_lba || + !dp[1].length || + !sane(dp[1].start_lba, dp[1].length) || + end_ebr > iter->sub.dos.bebr_size) { + + error("Insane extended partition.\n"); + return -1; + } + + return 0; +} + +/* Primary partition must be sane, meaning: + * - must have non-0 start and length + * - values must not wrap around 32bit + */ + +static int notsane_primary(const struct part_iter *iter) +{ + const struct disk_dos_part_entry *dp; + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; + + if (!dp->ostype) + return 0; + + if (!dp->start_lba || + !dp->length || + !sane(dp->start_lba, dp->length) || + dp->start_lba + dp->length > iter->di.lbacnt) { + error("Insane primary (MBR) partition.\n"); + return -1; + } + + return 0; +} + +static int notsane_gpt(const struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gp; + gp = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + + if (guid_is0(&gp->type)) + return 0; + + if (gp->lba_first < iter->sub.gpt.ufirst || + gp->lba_last > iter->sub.gpt.ulast) { + error("Insane GPT partition.\n"); + return -1; + } + + return 0; +} + +static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba, + struct disk_dos_part_entry **_dp) +{ + struct disk_dos_part_entry *dp; + + while (++iter->index0 < 4) { + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0; + + if (notsane_primary(iter)) { + iter->status = PI_INSANE; + goto bail; + } + + if (ost_is_ext(dp->ostype)) { + if (iter->sub.dos.bebr_index0 >= 0) { + error("You have more than 1 extended partition.\n"); + iter->status = PI_INSANE; + goto bail; + } + /* record base EBR index */ + iter->sub.dos.bebr_index0 = iter->index0; + } + if (!ost_is_nondata(dp->ostype) || iter->stepall) { + *lba = dp->start_lba; + *_dp = dp; + break; + } + } + + return 0; +bail: + return -1; +} + +static int prep_base_ebr(struct part_iter *iter) +{ + struct disk_dos_part_entry *dp; + + if (iter->sub.dos.bebr_index0 < 0) /* if we don't have base extended partition at all */ + return -1; + else if (!iter->sub.dos.bebr_start) { /* if not initialized yet */ + dp = ((struct disk_dos_mbr *)iter->data)->table + iter->sub.dos.bebr_index0; + + iter->sub.dos.bebr_start = dp->start_lba; + iter->sub.dos.bebr_size = dp->length; + + iter->sub.dos.ebr_start = 0; + iter->sub.dos.ebr_size = iter->sub.dos.bebr_size; + + iter->sub.dos.cebr_lba = 0; + iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start; + + iter->index0--; + } + return 0; +} + +static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba, + struct disk_dos_part_entry **_dp) +{ + struct disk_dos_part_entry *dp; + + if (prep_base_ebr(iter)) { + iter->status = PI_DONE; + return -1; + } + + while (++iter->index0 < 1024 && iter->sub.dos.nebr_lba) { + free(iter->data); + if (!(iter->data = + disk_read_sectors(&iter->di, iter->sub.dos.nebr_lba, 1))) { + error("Couldn't load EBR.\n"); + iter->status = PI_ERRLOAD; + return -1; + } + + if (notsane_logical(iter) || notsane_extended(iter)) { + iter->status = PI_INSANE; + return -1; + } + + dp = ((struct disk_dos_mbr *)iter->data)->table; + + iter->sub.dos.cebr_lba = iter->sub.dos.nebr_lba; + + /* setup next frame values */ + if (dp[1].ostype) { + iter->sub.dos.ebr_start = dp[1].start_lba; + iter->sub.dos.ebr_size = dp[1].length; + iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start + dp[1].start_lba; + } else { + iter->sub.dos.ebr_start = 0; + iter->sub.dos.ebr_size = 0; + iter->sub.dos.nebr_lba = 0; + } + + if (!dp[0].ostype) + iter->sub.dos.skipcnt++; + + if (dp[0].ostype || iter->stepall) { + *lba = iter->sub.dos.cebr_lba + dp[0].start_lba; + *_dp = dp; + return 0; + } + /* + * This way it's possible to continue, if some crazy soft left a "hole" + * - EBR with a valid extended partition without a logical one. In + * such case, linux will not reserve a number for such hole - so we + * don't increase index0. If stepall flag is set, we will never reach + * this place. + */ + } + iter->status = PI_DONE; + return -1; +} + +static struct part_iter *pi_dos_next(struct part_iter *iter) +{ + uint32_t start_lba = 0; + struct disk_dos_part_entry *dos_part = NULL; + + if (iter->status) + goto bail; + + /* look for primary partitions */ + if (iter->index0 < 4 && + pi_dos_next_mbr(iter, &start_lba, &dos_part)) + goto bail; + + /* look for logical partitions */ + if (iter->index0 >= 4 && + pi_dos_next_ebr(iter, &start_lba, &dos_part)) + goto bail; + + /* + * note special index handling, if we have stepall set - + * this is made to keep index consistent with non-stepall + * iterators + */ + + if (iter->index0 >= 4 && !dos_part->ostype) + iter->index = -1; + else + iter->index = iter->index0 - iter->sub.dos.skipcnt + 1; + iter->rawindex = iter->index0 + 1; + iter->start_lba = start_lba; + iter->length = dos_part->length; + iter->record = (char *)dos_part; + +#ifdef DEBUG + disk_dos_part_dump(dos_part); +#endif + + return iter; +bail: + return NULL; +} + +static void gpt_conv_label(struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gp; + const int16_t *orig_lab; + + gp = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + orig_lab = (const int16_t *)gp->name; + + /* caveat: this is very crude conversion */ + for (int i = 0; i < PI_GPTLABSIZE/2; i++) { + iter->sub.gpt.part_label[i] = (char)orig_lab[i]; + } + iter->sub.gpt.part_label[PI_GPTLABSIZE/2] = 0; +} + +static struct part_iter *pi_gpt_next(struct part_iter *iter) +{ + const struct disk_gpt_part_entry *gpt_part = NULL; + + if (iter->status) + goto bail; + + while (++iter->index0 < iter->sub.gpt.pe_count) { + gpt_part = (const struct disk_gpt_part_entry *) + (iter->data + iter->index0 * iter->sub.gpt.pe_size); + + if (notsane_gpt(iter)) { + iter->status = PI_INSANE; + goto bail; + } + + if (!guid_is0(&gpt_part->type) || iter->stepall) + break; + } + /* no more partitions ? */ + if (iter->index0 == iter->sub.gpt.pe_count) { + iter->status = PI_DONE; + goto bail; + } + /* gpt_part is guaranteed to be valid here */ + iter->index = iter->index0 + 1; + iter->rawindex = iter->index0 + 1; + iter->start_lba = gpt_part->lba_first; + iter->length = gpt_part->lba_last - gpt_part->lba_first + 1; + iter->record = (char *)gpt_part; + memcpy(&iter->sub.gpt.part_guid, &gpt_part->uid, sizeof(struct guid)); + gpt_conv_label(iter); + +#ifdef DEBUG + disk_gpt_part_dump(gpt_part); +#endif + + return iter; +bail: + return NULL; +} + +static struct part_iter *pi_raw_next(struct part_iter *iter) +{ + iter->status = PI_DONE; + return NULL; +} + +static int check_crc(uint32_t crc_match, const uint8_t *buf, unsigned int siz) +{ + uint32_t crc; + + crc = crc32(0, NULL, 0); + crc = crc32(crc, buf, siz); + + return crc_match != crc; +} + +static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct disk_gpt_header **_gh) +{ + struct disk_gpt_header *gh = *_gh; + uint64_t lba_alt; + uint32_t hold_crc32; + + hold_crc32 = gh->chksum; + gh->chksum = 0; + if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) { + error("WARNING: Primary GPT header checksum invalid.\n"); + /* retry with backup */ + lba_alt = gh->lba_alt; + free(gh); + if (!(gh = *_gh = disk_read_sectors(diskinfo, lba_alt, 1))) { + error("Couldn't read backup GPT header.\n"); + return -1; + } + hold_crc32 = gh->chksum; + gh->chksum = 0; + if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) { + error("Secondary GPT header checksum invalid.\n"); + return -1; + } + } + /* restore old checksum */ + gh->chksum = hold_crc32; + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * Following functions are for users to call. + * ---------------------------------------------------------------------------- + */ + + +int pi_next(struct part_iter **_iter) +{ + struct part_iter *iter; + + if(!_iter || !*_iter) + return 0; + iter = *_iter; +#ifdef DEBUG + if (inv_type(iter->type)) { + error("This is not a valid iterator.\n"); + return 0; + } +#endif + if ((iter = iter->type->next(iter))) { + *_iter = iter; + } + return (*_iter)->status; +} + +/** + * pi_new() - get new iterator + * @itertype: iterator type + * @...: variable arguments passed to ctors + * + * Variable arguments depend on the type. Please see functions: + * iter_gpt_ctor() and iter_dos_ctor() for details. + **/ +struct part_iter *pi_new(const struct itertype *type, ...) +{ + int badctor = 0; + struct part_iter *iter = NULL; + va_list ap; + + va_start(ap, type); + +#ifdef DEBUG + if (inv_type(type)) { + error("Unknown iterator requested.\n"); + goto bail; + } +#endif + + if (!(iter = malloc(sizeof(struct part_iter)))) { + error("Couldn't allocate memory for the iterator.\n"); + goto bail; + } + + memset(iter, 0, sizeof(struct part_iter)); + iter->type = type; + + if (type->ctor(iter, &ap)) { + badctor = -1; + error("Cannot initialize the iterator.\n"); + goto bail; + } + +bail: + va_end(ap); + if (badctor) { + free(iter); + iter = NULL; + } + return iter; +} + +/** + * pi_del() - delete iterator + * @iter: iterator double pointer + * + **/ + +void pi_del(struct part_iter **_iter) +{ + struct part_iter *iter; + + if(!_iter || !*_iter) + return; + iter = *_iter; + +#ifdef DEBUG + if (inv_type(iter->type)) { + error("This is not a valid iterator.\n"); + return; + } +#endif + + iter->type->dtor(iter); + free(iter); + *_iter = NULL; +} + +/** + * pi_begin() - check disk, validate, and get proper iterator + * @di: diskinfo struct pointer + * + * This function checks the disk for GPT or legacy partition table and allocates + * an appropriate iterator. + **/ +struct part_iter *pi_begin(const struct disk_info *di, int stepall) +{ + int setraw = 0; + struct part_iter *iter = NULL; + struct disk_dos_mbr *mbr = NULL; + struct disk_gpt_header *gpth = NULL; + struct disk_gpt_part_entry *gptl = NULL; + + /* Read MBR */ + if (!(mbr = disk_read_sectors(di, 0, 1))) { + error("Couldn't read first disk sector.\n"); + goto bail; + } + + setraw = -1; + + /* Check for MBR magic*/ + if (mbr->sig != disk_mbr_sig_magic) { + error("No MBR magic.\n"); + goto bail; + } + + /* Check for GPT protective MBR */ + if (mbr->table[0].ostype == 0xEE) { + if (!(gpth = disk_read_sectors(di, 1, 1))) { + error("Couldn't read potential GPT header.\n"); + goto bail; + } + } + + if (gpth && gpth->rev.uint32 == 0x00010000 && + !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) { + /* looks like GPT v1.0 */ + uint64_t gpt_loff; /* offset to GPT partition list in sectors */ + uint64_t gpt_lsiz; /* size of GPT partition list in bytes */ + uint64_t gpt_lcnt; /* size of GPT partition in sectors */ +#ifdef DEBUG + puts("Looks like a GPT v1.0 disk."); + disk_gpt_header_dump(gpth); +#endif + /* Verify checksum, fallback to backup, then bail if invalid */ + if (gpt_check_hdr_crc(di, &gpth)) + goto bail; + + gpt_loff = gpth->lba_table; + gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count; + gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps; + + /* + * disk_read_sectors allows reading of max 255 sectors, so we use + * it as a sanity check base. EFI doesn't specify max (AFAIK). + * Apart from that, some extensive sanity checks. + */ + if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u || + gpth->lba_first_usable > gpth->lba_last_usable || + !sane(gpt_loff, gpt_lcnt) || + gpt_loff + gpt_lcnt > gpth->lba_first_usable || + !sane(gpth->lba_last_usable, gpt_lcnt) || + gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt || + gpth->lba_alt >= di->lbacnt || + gpth->part_size < sizeof(struct disk_gpt_part_entry)) { + error("Invalid GPT header's values.\n"); + goto bail; + } + if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) { + error("Couldn't read GPT partition list.\n"); + goto bail; + } + /* Check array checksum(s). */ + if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) { + error("WARNING: GPT partition list checksum invalid, trying backup.\n"); + free(gptl); + /* secondary array directly precedes secondary header */ + if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) { + error("Couldn't read backup GPT partition list.\n"); + goto bail; + } + if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) { + error("Backup GPT partition list checksum invalid.\n"); + goto bail; + } + } + /* allocate iterator and exit */ + iter = pi_new(typegpt, di, stepall, gpth, gptl); + } else { + /* looks like MBR */ + iter = pi_new(typedos, di, stepall, mbr); + } + + setraw = 0; +bail: + if (setraw) { + error("WARNING: treating disk as raw.\n"); + iter = pi_new(typeraw, di, stepall); + } + free(mbr); + free(gpth); + free(gptl); + + return iter; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h new file mode 100644 index 00000000..7deeb534 --- /dev/null +++ b/com32/chain/partiter.h @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved + * Copyright 2010 Michal Soltys + * Copyright 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * partiter.h + * + * Provides disk / partition iteration. + */ + +#ifndef _COM32_CHAIN_PARTITER_H +#define _COM32_CHAIN_PARTITER_H + +#include <stdint.h> +#include <syslinux/disk.h> + +#define PI_ERRLOAD 3 +#define PI_INSANE 2 +#define PI_DONE 1 +#define PI_OK 0 + +struct itertype; +struct part_iter; + +struct itertype { + int (*ctor)(struct part_iter *, va_list *); + void (*dtor)(struct part_iter *); + struct part_iter *(*next) (struct part_iter *); +}; + +#define PI_GPTLABSIZE ((int)sizeof(((struct disk_gpt_part_entry *)0)->name)) + +struct part_iter { + const struct itertype *type; + char *data; + char *record; + uint64_t start_lba; + uint64_t length; + int index; + int rawindex; + struct disk_info di; + int stepall; + int status; + /* internal */ + int index0; + union _sub { + struct _dos { + uint32_t disk_sig; + uint32_t nebr_lba; + uint32_t cebr_lba; + /* internal */ + uint32_t ebr_start; + uint32_t ebr_size; + uint32_t bebr_start; + uint32_t bebr_size; + int bebr_index0; + int skipcnt; + } dos; + struct _gpt { + struct guid disk_guid; + struct guid part_guid; + char part_label[PI_GPTLABSIZE/2+1]; + int pe_count; + int pe_size; + uint64_t ufirst; + uint64_t ulast; + } gpt; + } sub; +}; + +extern const struct itertype * const typedos; +extern const struct itertype * const typegpt; +extern const struct itertype * const typeraw; + +struct part_iter *pi_begin(const struct disk_info *, int stepall); +struct part_iter *pi_new(const struct itertype *, ...); +void pi_del(struct part_iter **); +int pi_next(struct part_iter **); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/utility.c b/com32/chain/utility.c new file mode 100644 index 00000000..fb59551b --- /dev/null +++ b/com32/chain/utility.c @@ -0,0 +1,214 @@ +#include <com32.h> +#include <stdint.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <syslinux/disk.h> +#include "utility.h" + +static const char *bpbtypes[] = { + [0] = "unknown", + [1] = "2.0", + [2] = "3.0", + [3] = "3.2", + [4] = "3.4", + [5] = "4.0", + [6] = "8.0 (NT+)", + [7] = "7.0", +}; + +void error(const char *msg) +{ + fputs(msg, stderr); +} + +int guid_is0(const struct guid *guid) +{ + return !*(const uint64_t *)guid && !*((const uint64_t *)guid + 1); +} + +void wait_key(void) +{ + int cnt; + char junk; + + /* drain */ + do { + errno = 0; + cnt = read(0, &junk, 1); + } while (cnt > 0 || (cnt < 0 && errno == EAGAIN)); + + /* wait */ + do { + errno = 0; + cnt = read(0, &junk, 1); + } while (!cnt || (cnt < 0 && errno == EAGAIN)); +} + +void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode) +{ + uint32_t c, h, s, t; + uint32_t cs, hs, ss; + + /* + * Not much reason here, but if we have no valid CHS geometry, we assume + * "typical" ones to have something to return. + */ + if (di->cbios) { + cs = di->cyl; + hs = di->head; + ss = di->spt; + if (mode == l2c_cadd && cs < 1024 && di->lbacnt > cs*hs*ss) + cs++; + else if (mode == l2c_cmax) + cs = 1024; + } else { + if (di->disk & 0x80) { + cs = 1024; + hs = 255; + ss = 63; + } else { + cs = 80; + hs = 2; + ss = 18; + } + } + + if (lba >= cs*hs*ss) { + s = ss; + h = hs - 1; + c = cs - 1; + } else { + s = ((uint32_t)lba % ss) + 1; + t = (uint32_t)lba / ss; + h = t % hs; + c = t / hs; + } + + (*dst)[0] = h; + (*dst)[1] = s | ((c & 0x300) >> 2); + (*dst)[2] = c; +} + +uint32_t get_file_lba(const char *filename) +{ + com32sys_t inregs; + uint32_t lba; + + /* Start with clean registers */ + memset(&inregs, 0, sizeof(com32sys_t)); + + /* Put the filename in the bounce buffer */ + strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); + + /* Call comapi_open() which returns a structure pointer in SI + * to a structure whose first member happens to be the LBA. + */ + inregs.eax.w[0] = 0x0006; + inregs.esi.w[0] = OFFS(__com32.cs_bounce); + inregs.es = SEG(__com32.cs_bounce); + __com32.cs_intcall(0x22, &inregs, &inregs); + + if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) { + return 0; /* Filename not found */ + } + + /* Since the first member is the LBA, we simply cast */ + lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0])); + + /* Clean the registers for the next call */ + memset(&inregs, 0, sizeof(com32sys_t)); + + /* Put the filename in the bounce buffer */ + strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); + + /* Call comapi_close() to free the structure */ + inregs.eax.w[0] = 0x0008; + inregs.esi.w[0] = OFFS(__com32.cs_bounce); + inregs.es = SEG(__com32.cs_bounce); + __com32.cs_intcall(0x22, &inregs, &inregs); + + return lba; +} + +/* drive offset detection */ +int drvoff_detect(int type, unsigned int *off) +{ + if (bpbV40 <= type && type <= bpbVNT) { + *off = 0x24; + } else if (type == bpbV70) { + *off = 0x40; + } else + return 0; + + return -1; +} + +/* + * heuristics could certainly be improved + */ +int bpb_detect(const uint8_t *sec, const char *tag) +{ + int a, b, c, jmp = -1, rev = 0; + + /* media descriptor check */ + if ((sec[0x15] & 0xF0) != 0xF0) + goto out; + + if (sec[0] == 0xEB) /* jump short */ + jmp = 2 + *(int8_t *)(sec + 1); + else if (sec[0] == 0xE9) /* jump near */ + jmp = 3 + *(int16_t *)(sec + 1); + + if (jmp < 0) /* no boot code at all ? */ + goto nocode; + + /* sanity */ + if (jmp < 0x18 || jmp > 0x1F0) + goto out; + + /* detect by jump */ + if (jmp >= 0x18 && jmp < 0x1E) + rev = bpbV20; + else if (jmp >= 0x1E && jmp < 0x20) + rev = bpbV30; + else if (jmp >= 0x20 && jmp < 0x24) + rev = bpbV32; + else if (jmp >= 0x24 && jmp < 0x46) + rev = bpbV34; + + /* TODO: some better V2 - V3.4 checks ? */ + + if (rev) + goto out; + /* + * BPB info: + * 2.0 == 0x0B - 0x17 + * 3.0 == 2.0 + 0x18 - 0x1D + * 3.2 == 3.0 + 0x1E - 0x1F + * 3.4 ==!2.0 + 0x18 - 0x23 + * 4.0 == 3.4 + 0x24 - 0x45 + * NT ==~3.4 + 0x24 - 0x53 + * 7.0 == 3.4 + 0x24 - 0x59 + */ + +nocode: + a = memcmp(sec + 0x03, "NTFS", 4); + b = memcmp(sec + 0x36, "FAT", 3); + c = memcmp(sec + 0x52, "FAT", 3); /* ext. DOS 7+ bs */ + + if ((sec[0x26] & 0xFE) == 0x28 && !b) { + rev = bpbV40; + } else if (sec[0x26] == 0x80 && !a) { + rev = bpbVNT; + } else if ((sec[0x42] & 0xFE) == 0x28 && !c) { + rev = bpbV70; + } + +out: + printf("BPB detection (%s): %s\n", tag, bpbtypes[rev]); + return rev; +} + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/chain/utility.h b/com32/chain/utility.h new file mode 100644 index 00000000..8a08be71 --- /dev/null +++ b/com32/chain/utility.h @@ -0,0 +1,30 @@ +#ifndef _COM32_CHAIN_UTILITY_H +#define _COM32_CHAIN_UTILITY_H + +#include <stdint.h> +#include <syslinux/disk.h> + +#define bpbUNK 0 +#define bpbV20 1 +#define bpbV30 2 +#define bpbV32 3 +#define bpbV34 4 +#define bpbV40 5 +#define bpbVNT 6 +#define bpbV70 7 + +#define l2c_cnul 0 +#define l2c_cadd 1 +#define l2c_cmax 2 + +void error(const char *msg); +int guid_is0(const struct guid *guid); +void wait_key(void); +void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode); +uint32_t get_file_lba(const char *filename); +int drvoff_detect(int type, unsigned int *off); +int bpb_detect(const uint8_t *bpb, const char *tag); + +#endif + +/* vim: set ts=8 sts=4 sw=4 noet: */ diff --git a/com32/hdt/.gitignore b/com32/hdt/.gitignore index 98927943..82d5b472 100644 --- a/com32/hdt/.gitignore +++ b/com32/hdt/.gitignore @@ -5,3 +5,4 @@ floppy/syslinux.cfg *.iso iso/ *gz +hdt*checksums diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile index f1873465..17fa1ab6 100644 --- a/com32/hdt/Makefile +++ b/com32/hdt/Makefile @@ -110,10 +110,16 @@ hdt.iso: hdt.c32 $(topdir)/core/isolinux.bin $(FLOPPY_DIR)/hdt.cfg memtest mv hdt.iso hdt-$(VERSION).iso ln -sf hdt-$(VERSION).iso hdt.iso -release: spotless hdt.c32 hdt.img hdt.img.gz hdt.iso +hdt-hybrid.iso: hdt.iso ../../utils/isohybrid + cp hdt-$(VERSION).iso hdt-hybrid-$(VERSION).iso + ../../utils/isohybrid --partok hdt-hybrid-$(VERSION).iso + ln -sf hdt-hybrid-$(VERSION).iso hdt-hybrid.iso + +release: spotless hdt.c32 hdt.img hdt.img.gz hdt.iso hdt-hybrid.iso mv hdt.c32 hdt_$(NODASH_VERSION).c32 md5sum hdt_$(NODASH_VERSION).c32 >$(SUM_FILE) md5sum hdt-$(VERSION).iso >>$(SUM_FILE) + md5sum hdt-hybrid-$(VERSION).iso >>$(SUM_FILE) md5sum hdt-$(VERSION).img >>$(SUM_FILE) md5sum hdt-$(VERSION).img.gz >>$(SUM_FILE) diff --git a/com32/hdt/hdt-common.c b/com32/hdt/hdt-common.c index aac50eb9..8e9a9e64 100644 --- a/com32/hdt/hdt-common.c +++ b/com32/hdt/hdt-common.c @@ -106,12 +106,36 @@ void detect_parameters(const int argc, const char *argv[], max_console_lines = MAX_CLI_LINES; } else if (!strncmp(argv[i], "nomenu", 6)) { menumode = false; + } else if (!strncmp(argv[i], "dump_filename=", 14)) { + strlcpy(hardware->dump_filename, argv[i] + 14, + sizeof(hardware->dump_filename)); } else if (!strncmp(argv[i], "dump_path=", 10)) { strlcpy(hardware->dump_path, argv[i] + 10, sizeof(hardware->dump_path)); } else if (!strncmp(argv[i], "tftp_ip=", 8)) { strlcpy(hardware->tftp_ip, argv[i] + 8, sizeof(hardware->tftp_ip)); + } else if (!strncmp(argv[i], "postexec=", 9)) { + /* The postexec= parameter is separated in several argv[] + * as it can contains spaces. + * We use the AUTO_DELIMITER char to define the limits + * of this parameter. + * i.e postexec='linux memtest.bin' + */ + + char *argument = (char*)argv[i]+10; + /* Extracting the first parameter */ + strcpy(hardware->postexec, argument); + + /* While we can't find the other AUTO_DELIMITER, let's process the argv[] */ + while ((strchr(argument, AUTO_DELIMITER) == NULL) && (i+1<argc)) { + i++; + argument = (char *)argv[i]; + strcat(hardware->postexec, " "); + strcat(hardware->postexec, argument); + } + + hardware->postexec[strlen(hardware->postexec) - 1] = 0; } else if (!strncmp(argv[i], "auto=", 5)) { /* The auto= parameter is separated in several argv[] * as it can contains spaces. @@ -204,9 +228,12 @@ void init_hardware(struct s_hardware *hardware) memset(hardware->memtest_label, 0, sizeof hardware->memtest_label); memset(hardware->auto_label, 0, sizeof hardware->auto_label); memset(hardware->dump_path, 0, sizeof hardware->dump_path); + memset(hardware->dump_filename, 0, sizeof hardware->dump_filename); memset(hardware->vesa_background, 0, sizeof hardware->vesa_background); memset(hardware->tftp_ip, 0, sizeof hardware->tftp_ip); + memset(hardware->postexec, 0, sizeof hardware->postexec); strcat(hardware->dump_path, "hdt"); + strcat(hardware->dump_filename, "%{m}+%{p}+%{v}"); strcat(hardware->pciids_path, "pci.ids"); strcat(hardware->modules_pcimap_path, "modules.pcimap"); strcat(hardware->modules_alias_path, "modules.alias"); diff --git a/com32/hdt/hdt-common.h b/com32/hdt/hdt-common.h index d37fcc8a..8c85260b 100644 --- a/com32/hdt/hdt-common.h +++ b/com32/hdt/hdt-common.h @@ -214,10 +214,12 @@ struct s_hardware { char modules_alias_path[255]; char pciids_path[255]; char dump_path[255]; /* Dump path on the tftp server */ + char dump_filename[255]; /* Dump filename on the tftp server */ char tftp_ip[255]; /* IP address of tftp server (dump mode) */ char memtest_label[255]; char auto_label[AUTO_COMMAND_SIZE]; char vesa_background[255]; + char postexec[255]; }; void reset_more_printf(void); diff --git a/com32/hdt/hdt-dump-disks.c b/com32/hdt/hdt-dump-disks.c index dcbcaa9e..ff744b30 100644 --- a/com32/hdt/hdt-dump-disks.c +++ b/com32/hdt/hdt-dump-disks.c @@ -42,6 +42,7 @@ static void show_partition_information(struct driveinfo *drive_info, char ostype[64]={0}; char *parttype; unsigned int start, end; + char bootable[6] = {0}; int i = nb_partitions_seen; start = partition_offset; @@ -52,6 +53,10 @@ static void show_partition_information(struct driveinfo *drive_info, get_label(ptab->ostype, &parttype); get_bootloader_string(drive_info, ptab, bootloader_name, 9); + if (ptab->active_flag == 0x80) + snprintf(bootable,sizeof(bootable),"%s","true"); + else + snprintf(bootable,sizeof(bootable),"%s","false"); snprintf(ostype,sizeof(ostype),"%02X",ptab->ostype); @@ -62,6 +67,7 @@ static void show_partition_information(struct driveinfo *drive_info, add_as("partition->size",size) add_as("partition->type",parttype) add_as("partition->os_type",ostype) + add_as("partition->boot_flag",bootable) END_OF_APPEND; free(parttype); } @@ -117,7 +123,9 @@ void show_disk(struct s_hardware *hardware, ZZJSON_CONFIG *conf, ZZJSON **it, in void dump_disks(struct s_hardware *hardware, ZZJSON_CONFIG *config, ZZJSON **item) { bool found=false; - for (int drive = 0x80; drive < 0xff; drive++) { + + if (hardware->disks_count > 0) + for (int drive = 0x80; drive < 0xff; drive++) { if (hardware->disk_info[drive - 0x80].cbios) { if (found==false) { CREATE_NEW_OBJECT; @@ -131,7 +139,7 @@ void dump_disks(struct s_hardware *hardware, ZZJSON_CONFIG *config, ZZJSON **ite if (found==false) { CREATE_NEW_OBJECT; add_b("disks->is_valid",false); - FLUSH_OBJECT; } + FLUSH_OBJECT; to_cpio("disks"); } diff --git a/com32/hdt/hdt-dump.c b/com32/hdt/hdt-dump.c index 8c221405..b963e19b 100644 --- a/com32/hdt/hdt-dump.c +++ b/com32/hdt/hdt-dump.c @@ -37,56 +37,117 @@ struct print_buf p_buf; -void compute_filename(struct s_hardware *hardware, char *filename, int size) { - - snprintf(filename,size,"%s/",hardware->dump_path); - - if (hardware->is_pxe_valid) { - strncat(filename, hardware->pxe.mac_addr, sizeof(hardware->pxe.mac_addr)); - strncat(filename, "+", 1); - } - - if (hardware->is_dmi_valid) { - strncat(filename, remove_spaces(hardware->dmi.system.product_name), sizeof(hardware->dmi.system.manufacturer)); - strncat(filename, "+", 1); - strncat(filename, remove_spaces(hardware->dmi.system.manufacturer), sizeof(hardware->dmi.system.product_name)); +struct dump_option { + char *flag; + char *item; +}; + +char *get_value_from_option(struct s_hardware *hardware, char *option) +{ + struct dump_option dump_options[10]; + dump_options[0].flag = "%{m}"; + dump_options[0].item = hardware->pxe.mac_addr; + + dump_options[1].flag = "%{v}"; + dump_options[1].item = hardware->dmi.system.manufacturer; + + dump_options[2].flag = "%{p}"; + dump_options[2].item = hardware->dmi.system.product_name; + + dump_options[3].flag = "%{ba}"; + dump_options[3].item = hardware->dmi.base_board.asset_tag; + + dump_options[4].flag = "%{bs}"; + dump_options[4].item = hardware->dmi.base_board.serial; + + dump_options[5].flag = "%{ca}"; + dump_options[5].item = hardware->dmi.chassis.asset_tag; + + dump_options[6].flag = "%{cs}"; + dump_options[6].item = hardware->dmi.chassis.serial; + + dump_options[7].flag = "%{sk}"; + dump_options[7].item = hardware->dmi.system.sku_number; + + dump_options[8].flag = "%{ss}"; + dump_options[8].item = hardware->dmi.system.serial; + + dump_options[9].flag = NULL; + dump_options[9].item = NULL; + + for (int i = 0; i < 9; i++) { + if (strcmp(option, dump_options[i].flag) == 0) { + return remove_spaces(dump_options[i].item); + } + } + + return NULL; +} + +char *compute_filename(struct s_hardware *hardware) +{ + + char *filename = malloc(512); + snprintf(filename, 512, "%s/%s", hardware->dump_path, + hardware->dump_filename); + + /* Until we found some dump parameters */ + char *buffer; + while ((buffer = strstr(filename, "%{"))) { + // Find the end of the parameter + char *buffer_end = strstr(buffer, "}"); + + // Extracting the parameter between %{ and } + char option[8] = { 0 }; + strncpy(option, buffer, buffer_end - buffer + 1); + + /* Replace this option by its value in the filename + * Filename is longer than the previous filename we had + * so let's restart from the beginning */ + filename = + strreplace(filename, option, + get_value_from_option(hardware, option)); } /* We replace the ":" in the filename by some "-" * This will avoid Microsoft FS turning crazy */ - chrreplace(filename,':','-'); + chrreplace(filename, ':', '-'); /* Avoid space to make filename easier to manipulate */ - chrreplace(filename,' ','_'); + chrreplace(filename, ' ', '_'); + return filename; } -int dumpprintf(FILE *p, const char *format, ...) { - va_list ap; - int rv; - - (void) p; - va_start(ap, format); - rv = vbufprintf(&p_buf,format, ap); - va_end(ap); - return rv; +int dumpprintf(FILE * p, const char *format, ...) +{ + va_list ap; + int rv; + + (void)p; + va_start(ap, format); + rv = vbufprintf(&p_buf, format, ap); + va_end(ap); + return rv; } -void to_cpio(char *filename) { - cpio_writefile(upload,filename,p_buf.buf,p_buf.len); - if ((p_buf.buf) && (p_buf.len > 0)){ - memset(p_buf.buf,0,p_buf.len); - free(p_buf.buf); - p_buf.buf=NULL; - p_buf.size=0; - p_buf.len=0; - } +void to_cpio(char *filename) +{ + cpio_writefile(upload, filename, p_buf.buf, p_buf.len); + if ((p_buf.buf) && (p_buf.len > 0)) { + memset(p_buf.buf, 0, p_buf.len); + free(p_buf.buf); + p_buf.buf = NULL; + p_buf.size = 0; + p_buf.len = 0; + } } -void flush (ZZJSON_CONFIG *config, ZZJSON ** item) { - zzjson_print(config, *item); - zzjson_free(config, *item); - *item=NULL; +void flush(ZZJSON_CONFIG * config, ZZJSON ** item) +{ + zzjson_print(config, *item); + zzjson_free(config, *item); + *item = NULL; } /** @@ -94,53 +155,52 @@ void flush (ZZJSON_CONFIG *config, ZZJSON ** item) { **/ void dump(struct s_hardware *hardware) { - if (hardware->is_pxe_valid==false) { - printf("PXE stack was not detected, Dump feature is not available\n"); - return; + if (hardware->is_pxe_valid == false) { + printf("PXE stack was not detected, Dump feature is not available\n"); + return; } const union syslinux_derivative_info *sdi = syslinux_derivative_info(); - int err=0; + int err = 0; ZZJSON *json = NULL; ZZJSON_CONFIG config = { ZZJSON_VERY_STRICT, NULL, - (int(*)(void*)) fgetc, - NULL, - malloc, calloc, free, realloc, - stderr, NULL, stdout, - (int(*)(void *,const char*,...)) dumpprintf, - (int(*)(int,void*)) fputc + (int (*)(void *))fgetc, + NULL, + malloc, calloc, free, realloc, + stderr, NULL, stdout, + (int (*)(void *, const char *,...))dumpprintf, + (int (*)(int, void *))fputc }; - memset(&p_buf,0,sizeof(p_buf)); + memset(&p_buf, 0, sizeof(p_buf)); /* By now, we only support TFTP reporting */ - upload=&upload_tftp; - upload->name="tftp"; + upload = &upload_tftp; + upload->name = "tftp"; /* The following defines the behavior of the reporting */ char *arg[64]; - char filename[512]={0}; - compute_filename(hardware, filename, sizeof(filename)); + char *filename = compute_filename(hardware); /* The filename */ arg[0] = filename; /* The server to upload the file */ if (strlen(hardware->tftp_ip) != 0) { - arg[1] = hardware->tftp_ip; - arg[2] = NULL; + arg[1] = hardware->tftp_ip; + arg[2] = NULL; } else { - arg[1] = NULL; - snprintf(hardware->tftp_ip, sizeof(hardware->tftp_ip), - "%u.%u.%u.%u", - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[0], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[1], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[2], - ((uint8_t *)&sdi->pxe.ipinfo->serverip)[3]); + arg[1] = NULL; + snprintf(hardware->tftp_ip, sizeof(hardware->tftp_ip), + "%u.%u.%u.%u", + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[0], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[1], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[2], + ((uint8_t *) & sdi->pxe.ipinfo->serverip)[3]); } /* We initiate the cpio to send */ - cpio_init(upload,(const char **)arg); + cpio_init(upload, (const char **)arg); dump_cpu(hardware, &config, &json); dump_pxe(hardware, &config, &json); @@ -158,12 +218,12 @@ void dump(struct s_hardware *hardware) /* We close & flush the file to send */ cpio_close(upload); - if ((err=flush_data(upload)) != TFTP_OK) { + if ((err = flush_data(upload)) != TFTP_OK) { /* As we manage a tftp connection, let's display the associated error message */ more_printf("Dump failed !\n"); - more_printf("TFTP ERROR on : %s:/%s \n",hardware->tftp_ip, filename); - more_printf("TFTP ERROR msg : %s \n",tftp_string_error_message[-err]); + more_printf("TFTP ERROR on : %s:/%s \n", hardware->tftp_ip, filename); + more_printf("TFTP ERROR msg : %s \n", tftp_string_error_message[-err]); } else { - more_printf("Dump file sent at %s:/%s\n",hardware->tftp_ip, filename); + more_printf("Dump file sent at %s:/%s\n", hardware->tftp_ip, filename); } } diff --git a/com32/hdt/hdt-menu-summary.c b/com32/hdt/hdt-menu-summary.c index ad87c299..cef7e69e 100644 --- a/com32/hdt/hdt-menu-summary.c +++ b/com32/hdt/hdt-menu-summary.c @@ -52,8 +52,7 @@ void compute_summarymenu(struct s_my_menu *menu, struct s_hardware *hardware) add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); menu->items_count++; - char features[SUBMENULEN + 1]; - memset(features, 0, sizeof(features)); + char features[255]={0}; if (hardware->dmi.processor.thread_count != 0) sprintf(buffer, ", %d thread", hardware->dmi.processor.thread_count); else diff --git a/com32/hdt/hdt.c b/com32/hdt/hdt.c index a1e3923c..851b0462 100644 --- a/com32/hdt/hdt.c +++ b/com32/hdt/hdt.c @@ -74,14 +74,21 @@ int main(const int argc, const char *argv[]) printf("%s\n", version_string); + int return_code = 0; + if (!menumode || automode) start_cli_mode(&hardware); else { - int return_code = start_menu_mode(&hardware, version_string); + return_code = start_menu_mode(&hardware, version_string); if (return_code == HDT_RETURN_TO_CLI) start_cli_mode(&hardware); - else - return return_code; } - return 0; + + /* Do we got request to do something at exit time ? */ + if (strlen(hardware.postexec)>0) { + printf("Executing postexec instructions : %s\n",hardware.postexec); + runsyslinuxcmd(hardware.postexec); + } + + return return_code; } diff --git a/com32/hdt/hdt.h b/com32/hdt/hdt.h index 7b35236e..9b9e8a10 100644 --- a/com32/hdt/hdt.h +++ b/com32/hdt/hdt.h @@ -33,8 +33,8 @@ #define AUTHOR "Erwan Velu" #define CORE_DEVELOPER "Pierre-Alexandre Meyer" #define CONTACT "hdt@zytor.com" -#define VERSION "0.5.0" -#define CODENAME "Van De Keizer" +#define VERSION "0.5.2-pre1" +#define CODENAME "Manon" #define NB_CONTRIBUTORS 3 #define CONTRIBUTORS {"Sebastien Gonzalve (Patches)", "Gert Hulselmans (Tests)", "Alexander Andino (Design)"} #define WEBSITE_URL "http://hdt-project.org" diff --git a/com32/include/string.h b/com32/include/string.h index af9792b6..d847440d 100644 --- a/com32/include/string.h +++ b/com32/include/string.h @@ -42,5 +42,6 @@ __extern char *strsep(char **, const char *); __extern size_t strspn(const char *, const char *); __extern char *strstr(const char *, const char *); __extern char *strtok(char *, const char *); +__extern char *strreplace(const char *, const char *, const char *); #endif /* _STRING_H */ diff --git a/com32/include/syslinux/disk.h b/com32/include/syslinux/disk.h new file mode 100644 index 00000000..f96ca686 --- /dev/null +++ b/com32/include/syslinux/disk.h @@ -0,0 +1,180 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright (C) 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/** + * @file syslinux/disk.h + * + * Deal with disks and partitions + */ + +#ifndef _SYSLINUX_DISK_H +#define _SYSLINUX_DISK_H + +#include <com32.h> +#include <stdint.h> + +#define SECTOR 512u /* bytes/sector */ + +struct disk_info { + int disk; + int ebios; /* EBIOS supported on this disk */ + int cbios; /* CHS geometry is valid */ + uint32_t bps; /* bytes per sector */ + uint64_t lbacnt; /* total amount of sectors */ + uint32_t cyl; + uint32_t head; + uint32_t spt; +}; + +struct disk_ebios_dapa { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +} __attribute__ ((packed)); + +struct disk_ebios_eparam { + uint16_t len; + uint16_t info; + uint32_t cyl; + uint32_t head; + uint32_t spt; + uint64_t lbacnt; + uint16_t bps; /* bytes per sector */ + uint32_t edd; + uint16_t dpi_sig; + uint8_t dpi_len; + char reserved1[3]; + char hostbus[4]; + char if_type[8]; + char if_path[8]; + char dev_path[8]; + char reserved2; + uint8_t checksum; +} __attribute__ ((packed)); + +/** + * CHS (cylinder, head, sector) value extraction macros. + * Taken from WinVBlock. None expand to an lvalue. +*/ +#define chs_head(chs) chs[0] +#define chs_sector(chs) (chs[1] & 0x3F) +#define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2) +#define chs_cyl_low(chs) ((uint16_t)chs[2]) +#define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs)) +typedef uint8_t disk_chs[3]; + +/* A DOS partition table entry */ +struct disk_dos_part_entry { + uint8_t active_flag; /* 0x80 if "active" */ + disk_chs start; + uint8_t ostype; + disk_chs end; + uint32_t start_lba; + uint32_t length; +} __attribute__ ((packed)); + +/* A DOS MBR */ +struct disk_dos_mbr { + char code[440]; + uint32_t disk_sig; + char pad[2]; + struct disk_dos_part_entry table[4]; + uint16_t sig; +} __attribute__ ((packed)); +#define disk_mbr_sig_magic 0xAA55 + +/** + * A GPT disk/partition GUID + * + * Be careful with endianness, you must adjust it yourself + * iff you are directly using the fourth data chunk. + * There might be a better header for this... + */ +struct guid { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint64_t data4; +} __attribute__ ((packed)); + +/* A GPT partition */ +struct disk_gpt_part_entry { + struct guid type; + struct guid uid; + uint64_t lba_first; + uint64_t lba_last; + uint64_t attribs; + char name[72]; +} __attribute__ ((packed)); + +/* A GPT header */ +struct disk_gpt_header { + char sig[8]; + union { + struct { + uint16_t minor; + uint16_t major; + } fields __attribute__ ((packed)); + uint32_t uint32; + char raw[4]; + } rev __attribute__ ((packed)); + uint32_t hdr_size; + uint32_t chksum; + char reserved1[4]; + uint64_t lba_cur; + uint64_t lba_alt; + uint64_t lba_first_usable; + uint64_t lba_last_usable; + struct guid disk_guid; + uint64_t lba_table; + uint32_t part_count; + uint32_t part_size; + uint32_t table_chksum; + char reserved2[1]; +} __attribute__ ((packed)); +static const char disk_gpt_sig_magic[] = "EFI PART"; + +extern int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg); +extern int disk_get_params(int disk, struct disk_info *const diskinfo); +extern void *disk_read_sectors(const struct disk_info *const diskinfo, + uint64_t lba, uint8_t count); +extern int disk_write_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *data, uint8_t count); +extern int disk_write_verify_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *buf, uint8_t count); +extern void disk_dos_part_dump(const struct disk_dos_part_entry *const part); +extern void guid_to_str(char *buf, const struct guid *const id); +extern int str_to_guid(const char *buf, struct guid *const id); +extern void disk_gpt_part_dump(const struct disk_gpt_part_entry *const + gpt_part); +extern void disk_gpt_header_dump(const struct disk_gpt_header *const gpt); + +#endif /* _SYSLINUX_DISK_H */ diff --git a/com32/lib/Makefile b/com32/lib/Makefile index 7f1c4990..ecfc8730 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -32,6 +32,7 @@ LIBOBJS = \ chrreplace.o \ bufprintf.o \ inet.o \ + strreplace.o \ \ lmalloc.o lstrdup.o \ \ @@ -126,7 +127,9 @@ LIBOBJS = \ syslinux/setadv.o \ \ syslinux/video/fontquery.o syslinux/video/forcetext.o \ - syslinux/video/reportmode.o + syslinux/video/reportmode.o \ + \ + syslinux/disk.o # These are the objects which are also imported into the core LIBCOREOBJS = \ diff --git a/com32/lib/pci/scan.c b/com32/lib/pci/scan.c index e0974f98..fe00fc25 100644 --- a/com32/lib/pci/scan.c +++ b/com32/lib/pci/scan.c @@ -579,14 +579,14 @@ void free_pci_domain(struct pci_domain *domain) free(func->dev_info); free(func); } - free(slot); } + free(slot); } - free(bus); } + free(bus); } - free(domain); } + free(domain); } } diff --git a/com32/lib/strreplace.c b/com32/lib/strreplace.c new file mode 100644 index 00000000..d59efe0e --- /dev/null +++ b/com32/lib/strreplace.c @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include <string.h> +#include <stdlib.h> + +char *strreplace(const char *string, const char *string_to_replace, + const char *string_to_insert) +{ + char *token = NULL; + char *out = NULL; + + size_t slen, srlen, silen; + + token = strstr(string, string_to_replace); + if (!token) + return strdup(string); + + slen = strlen(string); + srlen = strlen(string_to_replace); + silen = strlen(string_to_insert); + + out = malloc(slen - srlen + silen + 1); + if (!out) + return NULL; + + memcpy(out, string, token - string); + memcpy(out + (token - string), string_to_insert, silen); + memcpy(out + (token - string) + silen, token + srlen, + slen - srlen - (token - string) + 1); + + return out; +} diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c new file mode 100644 index 00000000..d6409af6 --- /dev/null +++ b/com32/lib/syslinux/disk.c @@ -0,0 +1,534 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright (C) 2010 Shao Miller + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/** + * @file disk.c + * + * Deal with disks and partitions + */ + +#include <dprintf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslinux/disk.h> + +/** + * Call int 13h, but with retry on failure. Especially floppies need this. + * + * @v inreg CPU register settings upon INT call + * @v outreg CPU register settings returned by INT call + * @ret (int) 0 upon success, -1 upon failure + */ +int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg) +{ + int retry = 6; /* Number of retries */ + com32sys_t tmpregs; + + if (!outreg) + outreg = &tmpregs; + + while (retry--) { + __intcall(0x13, inreg, outreg); + if (!(outreg->eflags.l & EFLAGS_CF)) + return 0; /* CF=0, OK */ + } + + return -1; /* Error */ +} + +/** + * Query disk parameters and EBIOS availability for a particular disk. + * + * @v disk The INT 0x13 disk drive number to process + * @v diskinfo The structure to save the queried params to + * @ret (int) 0 upon success, -1 upon failure + */ +int disk_get_params(int disk, struct disk_info *const diskinfo) +{ + static com32sys_t inreg, outreg; + struct disk_ebios_eparam *eparam = __com32.cs_bounce; + + memset(diskinfo, 0, sizeof *diskinfo); + diskinfo->disk = disk; + diskinfo->bps = SECTOR; + + /* Get EBIOS support */ + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x41; + inreg.ebx.w[0] = 0x55aa; + inreg.edx.b[0] = disk; + inreg.eflags.b[0] = 0x3; /* CF set */ + + __intcall(0x13, &inreg, &outreg); + + if (!(outreg.eflags.l & EFLAGS_CF) && + outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) { + diskinfo->ebios = 1; + } + + /* Get extended disk parameters if ebios == 1 */ + if (diskinfo->ebios) { + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x48; + inreg.edx.b[0] = disk; + inreg.esi.w[0] = OFFS(eparam); + inreg.ds = SEG(eparam); + + memset(eparam, 0, sizeof *eparam); + eparam->len = sizeof *eparam; + + __intcall(0x13, &inreg, &outreg); + + if (!(outreg.eflags.l & EFLAGS_CF)) { + diskinfo->lbacnt = eparam->lbacnt; + if (eparam->bps) + diskinfo->bps = eparam->bps; + /* + * don't think about using geometry data returned by + * 48h, as it can differ from 08h a lot ... + */ + } + } + /* + * Get disk parameters the old way - really only useful for hard + * disks, but if we have a partitioned floppy it's actually our best + * chance... + */ + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x08; + inreg.edx.b[0] = disk; + + __intcall(0x13, &inreg, &outreg); + + if (outreg.eflags.l & EFLAGS_CF) + return diskinfo->ebios ? 0 : -1; + + diskinfo->spt = 0x3f & outreg.ecx.b[0]; + diskinfo->head = 1 + outreg.edx.b[1]; + diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2)); + + if (diskinfo->spt) + diskinfo->cbios = 1; /* Valid geometry */ + else { + diskinfo->head = 1; + diskinfo->spt = 1; + diskinfo->cyl = 1; + } + + if (!diskinfo->lbacnt) + diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt; + + return 0; +} + +/** + * Get disk block(s) and return a malloc'd buffer. + * + * @v diskinfo The disk drive to read from + * @v lba The logical block address to begin reading at + * @v count The number of sectors to read + * @ret data An allocated buffer with the read data + * + * Uses the disk number and information from diskinfo. Read count sectors + * from drive, starting at lba. Return a new buffer, or NULL upon failure. + */ +void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, + uint8_t count) +{ + com32sys_t inreg; + struct disk_ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; + void *data; + uint32_t maxcnt; + + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) + return NULL; + + memset(&inreg, 0, sizeof inreg); + + if (diskinfo->ebios) { + dapa->len = sizeof(*dapa); + dapa->count = count; + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = diskinfo->disk; + inreg.eax.b[1] = 0x42; /* Extended read */ + } else { + unsigned int c, h, s, t; + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus + * 32bits are perfectly enough and lbacnt corresponds to cylinder + * boundary + */ + s = lba % diskinfo->spt; + t = lba / diskinfo->spt; + h = t % diskinfo->head; + c = t / diskinfo->head; + + inreg.eax.b[0] = count; + inreg.eax.b[1] = 0x02; /* Read */ + inreg.ecx.b[1] = c; + inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1); + inreg.edx.b[1] = h; + inreg.edx.b[0] = diskinfo->disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (disk_int13_retry(&inreg, NULL)) + return NULL; + + data = malloc(count * diskinfo->bps); + if (data) + memcpy(data, buf, count * diskinfo->bps); + return data; +} + +/** + * Write disk block(s). + * + * @v diskinfo The disk drive to write to + * @v lba The logical block address to begin writing at + * @v data The data to write + * @v count The number of sectors to write + * @ret (int) 0 upon success, -1 upon failure + * + * Uses the disk number and information from diskinfo. + * Write sector(s) to a disk drive, starting at lba. + */ +int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba, + const void *data, uint8_t count) +{ + com32sys_t inreg; + struct disk_ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + diskinfo->bps; + uint32_t maxcnt; + + maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps; + if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) + return -1; + + memcpy(buf, data, count * diskinfo->bps); + memset(&inreg, 0, sizeof inreg); + + if (diskinfo->ebios) { + dapa->len = sizeof(*dapa); + dapa->count = count; + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = diskinfo->disk; + inreg.eax.b[1] = 0x43; /* Extended write */ + } else { + unsigned int c, h, s, t; + /* + * if we passed lba + count check and we get here, that means that + * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus + * 32bits are perfectly enough and lbacnt corresponds to cylinder + * boundary + */ + s = lba % diskinfo->spt; + t = lba / diskinfo->spt; + h = t % diskinfo->head; + c = t / diskinfo->head; + + inreg.eax.b[0] = count; + inreg.eax.b[1] = 0x03; /* Write */ + inreg.ecx.b[1] = c; + inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1); + inreg.edx.b[1] = h; + inreg.edx.b[0] = diskinfo->disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (disk_int13_retry(&inreg, NULL)) + return -1; + + return 0; /* ok */ +} + +/** + * Write disk blocks and verify they were written. + * + * @v diskinfo The disk drive to write to + * @v lba The logical block address to begin writing at + * @v buf The data to write + * @v count The number of sectors to write + * @ret rv 0 upon success, -1 upon failure + * + * Uses the disk number and information from diskinfo. + * Writes sectors to a disk drive starting at lba, then reads them back + * to verify they were written correctly. + */ +int disk_write_verify_sectors(const struct disk_info *const diskinfo, + uint64_t lba, const void *buf, uint8_t count) +{ + char *rb; + int rv; + + rv = disk_write_sectors(diskinfo, lba, buf, count); + if (rv) + return rv; /* Write failure */ + rb = disk_read_sectors(diskinfo, lba, count); + if (!rb) + return -1; /* Readback failure */ + rv = memcmp(buf, rb, count * diskinfo->bps); + free(rb); + return rv ? -1 : 0; +} + +/** + * Dump info about a DOS partition entry + * + * @v part The 16-byte partition entry to examine + */ +void disk_dos_part_dump(const struct disk_dos_part_entry *const part) +{ + (void)part; + dprintf("Partition status _____ : 0x%.2x\n" + "Partition CHS start\n" + " Cylinder ___________ : 0x%.4x (%u)\n" + " Head _______________ : 0x%.2x (%u)\n" + " Sector _____________ : 0x%.2x (%u)\n" + "Partition type _______ : 0x%.2x\n" + "Partition CHS end\n" + " Cylinder ___________ : 0x%.4x (%u)\n" + " Head _______________ : 0x%.2x (%u)\n" + " Sector _____________ : 0x%.2x (%u)\n" + "Partition LBA start __ : 0x%.8x (%u)\n" + "Partition LBA count __ : 0x%.8x (%u)\n" + "-------------------------------\n", + part->active_flag, + chs_cylinder(part->start), + chs_cylinder(part->start), + chs_head(part->start), + chs_head(part->start), + chs_sector(part->start), + chs_sector(part->start), + part->ostype, + chs_cylinder(part->end), + chs_cylinder(part->end), + chs_head(part->end), + chs_head(part->end), + chs_sector(part->end), + chs_sector(part->end), + part->start_lba, part->start_lba, part->length, part->length); +} + +/* Trivial error message output */ +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/** + * This walk-map effectively reverses the little-endian + * portions of a GPT disk/partition GUID for a string representation. + * There might be a better header for this... + */ +static const char guid_le_walk_map[] = { + 3, -1, -1, -1, 0, + 5, -1, 0, + 3, -1, 0, + 2, 1, 0, + 1, 1, 1, 1, 1, 1 +}; + +/** + * Fill a buffer with a textual GUID representation. + * + * @v buf Points to a minimum array of 37 chars + * @v id The GUID to represent as text + * + * The buffer must be >= char[37] and will be populated + * with an ASCII NUL C string terminator. + * Example: 11111111-2222-3333-4444-444444444444 + * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB + */ +void guid_to_str(char *buf, const struct guid *const id) +{ + unsigned int i = 0; + const char *walker = (const char *)id; + + while (i < sizeof(guid_le_walk_map)) { + walker += guid_le_walk_map[i]; + if (!guid_le_walk_map[i]) + *buf = '-'; + else { + *buf = ((*walker & 0xF0) >> 4) + '0'; + if (*buf > '9') + *buf += 'A' - '9' - 1; + buf++; + *buf = (*walker & 0x0F) + '0'; + if (*buf > '9') + *buf += 'A' - '9' - 1; + } + buf++; + i++; + } + *buf = 0; +} + +/** + * Create a GUID structure from a textual GUID representation. + * + * @v buf Points to a GUID string to parse + * @v id Points to a GUID to be populated + * @ret (int) Returns 0 upon success, -1 upon failure + * + * The input buffer must be >= 32 hexadecimal chars and be + * terminated with an ASCII NUL. Returns non-zero on failure. + * Example: 11111111-2222-3333-4444-444444444444 + * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB + */ +int str_to_guid(const char *buf, struct guid *const id) +{ + char guid_seq[sizeof(struct guid) * 2]; + unsigned int i = 0; + char *walker = (char *)id; + + while (*buf && i < sizeof(guid_seq)) { + switch (*buf) { + /* Skip these three characters */ + case '{': + case '}': + case '-': + break; + default: + /* Copy something useful to the temp. sequence */ + if ((*buf >= '0') && (*buf <= '9')) + guid_seq[i] = *buf - '0'; + else if ((*buf >= 'A') && (*buf <= 'F')) + guid_seq[i] = *buf - 'A' + 10; + else if ((*buf >= 'a') && (*buf <= 'f')) + guid_seq[i] = *buf - 'a' + 10; + else { + /* Or not */ + error("Illegal character in GUID!\n"); + return -1; + } + i++; + } + buf++; + } + /* Check for insufficient valid characters */ + if (i < sizeof(guid_seq)) { + error("Too few GUID characters!\n"); + return -1; + } + buf = guid_seq; + i = 0; + while (i < sizeof(guid_le_walk_map)) { + if (!guid_le_walk_map[i]) + i++; + walker += guid_le_walk_map[i]; + *walker = *buf << 4; + buf++; + *walker |= *buf; + buf++; + i++; + } + return 0; +} + +/** + * Display GPT partition details. + * + * @v gpt_part The GPT partition entry to display + */ +void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part) +{ + unsigned int i; + char guid_text[37]; + + dprintf("----------------------------------\n" + "GPT part. LBA first __ : 0x%.16llx\n" + "GPT part. LBA last ___ : 0x%.16llx\n" + "GPT part. attribs ____ : 0x%.16llx\n" + "GPT part. name _______ : '", + gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs); + for (i = 0; i < sizeof(gpt_part->name); i++) { + if (gpt_part->name[i]) + dprintf("%c", gpt_part->name[i]); + } + dprintf("'"); + guid_to_str(guid_text, &gpt_part->type); + dprintf("GPT part. type GUID __ : {%s}\n", guid_text); + guid_to_str(guid_text, &gpt_part->uid); + dprintf("GPT part. unique ID __ : {%s}\n", guid_text); +} + +/** + * Display GPT header details. + * + * @v gpt The GPT header to display + */ +void disk_gpt_header_dump(const struct disk_gpt_header *const gpt) +{ + char guid_text[37]; + + printf("GPT sig ______________ : '%8.8s'\n" + "GPT major revision ___ : 0x%.4x\n" + "GPT minor revision ___ : 0x%.4x\n" + "GPT header size ______ : 0x%.8x\n" + "GPT header checksum __ : 0x%.8x\n" + "GPT reserved _________ : '%4.4s'\n" + "GPT LBA current ______ : 0x%.16llx\n" + "GPT LBA alternative __ : 0x%.16llx\n" + "GPT LBA first usable _ : 0x%.16llx\n" + "GPT LBA last usable __ : 0x%.16llx\n" + "GPT LBA part. table __ : 0x%.16llx\n" + "GPT partition count __ : 0x%.8x\n" + "GPT partition size ___ : 0x%.8x\n" + "GPT part. table chksum : 0x%.8x\n", + gpt->sig, + gpt->rev.fields.major, + gpt->rev.fields.minor, + gpt->hdr_size, + gpt->chksum, + gpt->reserved1, + gpt->lba_cur, + gpt->lba_alt, + gpt->lba_first_usable, + gpt->lba_last_usable, + gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum); + guid_to_str(guid_text, &gpt->disk_guid); + printf("GPT disk GUID ________ : {%s}\n", guid_text); +} diff --git a/com32/modules/Makefile b/com32/modules/Makefile index e9ce1d1f..1b2854f5 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -19,7 +19,7 @@ topdir = ../.. MAKEDIR = $(topdir)/mk include $(MAKEDIR)/com32.mk -MODULES = chain.c32 config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \ +MODULES = config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \ disk.c32 pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 \ meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \ kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 ls.c32 gpxecmd.c32 \ diff --git a/com32/modules/chain.c b/com32/modules/chain.c deleted file mode 100644 index 2ed0f14c..00000000 --- a/com32/modules/chain.c +++ /dev/null @@ -1,1932 +0,0 @@ -/* ----------------------------------------------------------------------- * - * - * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin - * Significant portions copyright (C) 2010 Shao Miller - * [partition iteration, GPT, "fs"] - * - * 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 - * the Free Software Foundation, Inc., 53 Temple Place Ste 330, - * Boston MA 02111-1307, USA; either version 2 of the License, or - * (at your option) any later version; incorporated herein by reference. - * - * ----------------------------------------------------------------------- */ - -/* - * chain.c - * - * Chainload a hard disk (currently rather braindead.) - * - * Usage: chain [options] - * chain hd<disk#> [<partition>] [options] - * chain fd<disk#> [options] - * chain mbr:<id> [<partition>] [options] - * chain guid:<guid> [<partition>] [options] - * chain label:<label> [<partition>] [options] - * chain boot [<partition>] [options] - * - * For example, "chain msdos=io.sys" will load DOS from the current Syslinux - * filesystem. "chain hd0 1" will boot the first partition on the first hard - * disk. - * - * When none of the "hdX", "fdX", "mbr:", "guid:", "label:", "boot" or "fs" - * options are specified, the default behaviour is equivalent to "boot". - * "boot" means to use the current Syslinux drive, and you can also specify - * a partition. - * - * The mbr: syntax means search all the hard disks until one with a - * specific MBR serial number (bytes 440-443) is found. - * - * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.) - * - * "fs" will use the current Syslinux filesystem as the boot drive/partition. - * When booting from PXELINUX, you will most likely wish to specify a disk. - * - * Options: - * - * file=<loader> - * loads the file <loader> **from the Syslinux filesystem** - * instead of loading the boot sector. - * - * seg=<segment>[:<offset>][{+@}entry] - * loads at <segment>:<offset> and jumps to <seg>:<entry> instead - * of the default 0000:7C00. <offset> and <entry> default to 0 and +0 - * repectively. If <entry> start with + (rather than @) then the - * entry point address is added to the offset. - * - * isolinux=<loader> - * chainload another version/build of the ISOLINUX bootloader and patch - * the loader with appropriate parameters in memory. - * This avoids the need for the -eltorito-alt-boot parameter of mkisofs, - * when you want more than one ISOLINUX per CD/DVD. - * - * ntldr=<loader> - * equivalent to seg=0x2000 file=<loader> sethidden, - * used with WinNT's loaders - * - * cmldr=<loader> - * used with Recovery Console of Windows NT/2K/XP. - * same as ntldr=<loader> & "cmdcons\0" written to - * the system name field in the bootsector - * - * freedos=<loader> - * equivalent to seg=0x60 file=<loader> sethidden, - * used with FreeDOS' kernel.sys. - * - * msdos=<loader> - * pcdos=<loader> - * equivalent to seg=0x70 file=<loader> sethidden, - * used with DOS' io.sys. - * - * drmk=<loader> - * Similar to msdos=<loader> but prepares the special options - * for the Dell Real Mode Kernel. - * - * grub=<loader> - * same as seg=0x800 file=<loader> & jumping to seg 0x820, - * used with GRUB Legacy stage2 files. - * - * grubcfg=<filename> - * set an alternative config filename in stage2 of Grub Legacy, - * only applicable in combination with "grub=<loader>". - * - * grldr=<loader> - * pass the partition number to GRUB4DOS, - * used with GRUB4DOS' grldr. - * - * swap - * if the disk is not fd0/hd0, install a BIOS stub which swaps - * the drive numbers. - * - * hide - * change type of primary partitions with IDs 01, 04, 06, 07, - * 0b, 0c, or 0e to 1x, except for the selected partition, which - * is converted the other way. - * - * sethidden - * update the "hidden sectors" (partition offset) field in a - * FAT/NTFS boot sector. - * - * keeppxe - * keep the PXE and UNDI stacks in memory (PXELINUX only). - * - * freeldr=<loader> - * loads ReactOS' FreeLdr.sys to 0:8000 and jumps to the PE entry-point - */ - -#include <com32.h> -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <console.h> -#include <minmax.h> -#include <stdbool.h> -#include <dprintf.h> -#include <syslinux/loadfile.h> -#include <syslinux/bootrm.h> -#include <syslinux/config.h> -#include <syslinux/video.h> - -#define SECTOR 512 /* bytes/sector */ - -static struct options { - const char *loadfile; - uint16_t keeppxe; - uint16_t seg; - uint16_t offs; - uint16_t entry; - bool isolinux; - bool cmldr; - bool grub; - bool grldr; - const char *grubcfg; - bool swap; - bool hide; - bool sethidden; - bool drmk; -} opt; - -struct data_area { - void *data; - addr_t base; - addr_t size; -}; - -static inline void error(const char *msg) -{ - fputs(msg, stderr); -} - -/* - * Call int 13h, but with retry on failure. Especially floppies need this. - */ -static int int13_retry(const com32sys_t * inreg, com32sys_t * outreg) -{ - int retry = 6; /* Number of retries */ - com32sys_t tmpregs; - - if (!outreg) - outreg = &tmpregs; - - while (retry--) { - __intcall(0x13, inreg, outreg); - if (!(outreg->eflags.l & EFLAGS_CF)) - return 0; /* CF=0, OK */ - } - - return -1; /* Error */ -} - -/* - * Query disk parameters and EBIOS availability for a particular disk. - */ -struct diskinfo { - int disk; - int ebios; /* EBIOS supported on this disk */ - int cbios; /* CHS geometry is valid */ - int head; - int sect; -} disk_info; - -static int get_disk_params(int disk) -{ - static com32sys_t getparm, parm, getebios, ebios; - - disk_info.disk = disk; - disk_info.ebios = disk_info.cbios = 0; - - /* Get EBIOS support */ - getebios.eax.w[0] = 0x4100; - getebios.ebx.w[0] = 0x55aa; - getebios.edx.b[0] = disk; - getebios.eflags.b[0] = 0x3; /* CF set */ - - __intcall(0x13, &getebios, &ebios); - - if (!(ebios.eflags.l & EFLAGS_CF) && - ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) { - disk_info.ebios = 1; - } - - /* Get disk parameters -- really only useful for - hard disks, but if we have a partitioned floppy - it's actually our best chance... */ - getparm.eax.b[1] = 0x08; - getparm.edx.b[0] = disk; - - __intcall(0x13, &getparm, &parm); - - if (parm.eflags.l & EFLAGS_CF) - return disk_info.ebios ? 0 : -1; - - disk_info.head = parm.edx.b[1] + 1; - disk_info.sect = parm.ecx.b[0] & 0x3f; - if (disk_info.sect == 0) { - disk_info.sect = 1; - } else { - disk_info.cbios = 1; /* Valid geometry */ - } - - return 0; -} - -/* - * Get a disk block and return a malloc'd buffer. - * Uses the disk number and information from disk_info. - */ -struct ebios_dapa { - uint16_t len; - uint16_t count; - uint16_t off; - uint16_t seg; - uint64_t lba; -}; - -/* Read count sectors from drive, starting at lba. Return a new buffer */ -static void *read_sectors(uint64_t lba, uint8_t count) -{ - com32sys_t inreg; - struct ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; - void *data; - - if (!count) - /* Silly */ - return NULL; - - memset(&inreg, 0, sizeof inreg); - - if (disk_info.ebios) { - dapa->len = sizeof(*dapa); - dapa->count = count; - dapa->off = OFFS(buf); - dapa->seg = SEG(buf); - dapa->lba = lba; - - inreg.esi.w[0] = OFFS(dapa); - inreg.ds = SEG(dapa); - inreg.edx.b[0] = disk_info.disk; - inreg.eax.b[1] = 0x42; /* Extended read */ - } else { - unsigned int c, h, s, t; - - if (!disk_info.cbios) { - /* We failed to get the geometry */ - - if (lba) - return NULL; /* Can only read MBR */ - - s = h = c = 0; - } else { - s = lba % disk_info.sect; - t = lba / disk_info.sect; /* Track = head*cyl */ - h = t % disk_info.head; - c = t / disk_info.head; - } - - if (s >= 63 || h >= 256 || c >= 1024) - return NULL; - - inreg.eax.b[0] = count; - inreg.eax.b[1] = 0x02; /* Read */ - inreg.ecx.b[1] = c; - inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1); - inreg.edx.b[1] = h; - inreg.edx.b[0] = disk_info.disk; - inreg.ebx.w[0] = OFFS(buf); - inreg.es = SEG(buf); - } - - if (int13_retry(&inreg, NULL)) - return NULL; - - data = malloc(count * SECTOR); - if (data) - memcpy(data, buf, count * SECTOR); - return data; -} - -static int write_sector(unsigned int lba, const void *data) -{ - com32sys_t inreg; - struct ebios_dapa *dapa = __com32.cs_bounce; - void *buf = (char *)__com32.cs_bounce + SECTOR; - - memcpy(buf, data, SECTOR); - memset(&inreg, 0, sizeof inreg); - - if (disk_info.ebios) { - dapa->len = sizeof(*dapa); - dapa->count = 1; /* 1 sector */ - dapa->off = OFFS(buf); - dapa->seg = SEG(buf); - dapa->lba = lba; - - inreg.esi.w[0] = OFFS(dapa); - inreg.ds = SEG(dapa); - inreg.edx.b[0] = disk_info.disk; - inreg.eax.w[0] = 0x4300; /* Extended write */ - } else { - unsigned int c, h, s, t; - - if (!disk_info.cbios) { - /* We failed to get the geometry */ - - if (lba) - return -1; /* Can only write MBR */ - - s = h = c = 0; - } else { - s = lba % disk_info.sect; - t = lba / disk_info.sect; /* Track = head*cyl */ - h = t % disk_info.head; - c = t / disk_info.head; - } - - if (s >= 63 || h >= 256 || c >= 1024) - return -1; - - inreg.eax.w[0] = 0x0301; /* Write one sector */ - inreg.ecx.b[1] = c; - inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s + 1); - inreg.edx.b[1] = h; - inreg.edx.b[0] = disk_info.disk; - inreg.ebx.w[0] = OFFS(buf); - inreg.es = SEG(buf); - } - - if (int13_retry(&inreg, NULL)) - return -1; - - return 0; /* ok */ -} - -static int write_verify_sector(unsigned int lba, const void *buf) -{ - char *rb; - int rv; - - rv = write_sector(lba, buf); - if (rv) - return rv; /* Write failure */ - rb = read_sectors(lba, 1); - if (!rb) - return -1; /* Readback failure */ - rv = memcmp(buf, rb, SECTOR); - free(rb); - return rv ? -1 : 0; -} - -/* - * CHS (cylinder, head, sector) value extraction macros. - * Taken from WinVBlock. Does not expand to an lvalue -*/ -#define chs_head(chs) chs[0] -#define chs_sector(chs) (chs[1] & 0x3F) -#define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2) -#define chs_cyl_low(chs) ((uint16_t)chs[2]) -#define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs)) -typedef uint8_t chs[3]; - -/* A DOS partition table entry */ -struct part_entry { - uint8_t active_flag; /* 0x80 if "active" */ - chs start; - uint8_t ostype; - chs end; - uint32_t start_lba; - uint32_t length; -} __attribute__ ((packed)); - -static void mbr_part_dump(const struct part_entry *part) -{ - (void)part; - dprintf("Partition status _____ : 0x%.2x\n" - "Partition CHS start\n" - " Cylinder ___________ : 0x%.4x (%u)\n" - " Head _______________ : 0x%.2x (%u)\n" - " Sector _____________ : 0x%.2x (%u)\n" - "Partition type _______ : 0x%.2x\n" - "Partition CHS end\n" - " Cylinder ___________ : 0x%.4x (%u)\n" - " Head _______________ : 0x%.2x (%u)\n" - " Sector _____________ : 0x%.2x (%u)\n" - "Partition LBA start __ : 0x%.8x (%u)\n" - "Partition LBA count __ : 0x%.8x (%u)\n" - "-------------------------------\n", - part->active_flag, - chs_cylinder(part->start), - chs_cylinder(part->start), - chs_head(part->start), - chs_head(part->start), - chs_sector(part->start), - chs_sector(part->start), - part->ostype, - chs_cylinder(part->end), - chs_cylinder(part->end), - chs_head(part->end), - chs_head(part->end), - chs_sector(part->end), - chs_sector(part->end), - part->start_lba, part->start_lba, part->length, part->length); -} - -/* A DOS MBR */ -struct mbr { - char code[440]; - uint32_t disk_sig; - char pad[2]; - struct part_entry table[4]; - uint16_t sig; -} __attribute__ ((packed)); -static const uint16_t mbr_sig_magic = 0xAA55; - -/* Search for a specific drive, based on the MBR signature; bytes 440-443 */ -static int find_disk(uint32_t mbr_sig) -{ - int drive; - bool is_me; - struct mbr *mbr; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - if (!(mbr = read_sectors(0, 1))) - continue; /* Cannot read sector */ - is_me = (mbr->disk_sig == mbr_sig); - free(mbr); - if (is_me) - return drive; - } - return -1; -} - -/* Forward declaration */ -struct disk_part_iter; - -/* Partition-/scheme-specific routine returning the next partition */ -typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter * - part); - -/* Contains details for a partition under examination */ -struct disk_part_iter { - /* The block holding the table we are part of */ - char *block; - /* The LBA for the beginning of data */ - uint64_t lba_data; - /* The partition number, as determined by our heuristic */ - int index; - /* The DOS partition record to pass, if applicable */ - const struct part_entry *record; - /* Function returning the next available partition */ - disk_part_iter_func next; - /* Partition-/scheme-specific details */ - union { - /* MBR specifics */ - int mbr_index; - /* EBR specifics */ - struct { - /* The first extended partition's start LBA */ - uint64_t lba_extended; - /* Any applicable parent, or NULL */ - struct disk_part_iter *parent; - /* The parent extended partition index */ - int parent_index; - } ebr; - /* GPT specifics */ - struct { - /* Real (not effective) index in the partition table */ - int index; - /* Current partition GUID */ - const struct guid *part_guid; - /* Current partition label */ - const char *part_label; - /* Count of entries in GPT */ - int parts; - /* Partition record size */ - uint32_t size; - } gpt; - } private; -}; - -static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part) -{ - const struct part_entry *ebr_table; - const struct part_entry *parent_table = - ((const struct mbr *)part->private.ebr.parent->block)->table; - static const struct part_entry phony = {.start_lba = 0 }; - uint64_t ebr_lba; - - /* Don't look for a "next EBR" the first time around */ - if (part->private.ebr.parent_index >= 0) - /* Look at the linked list */ - ebr_table = ((const struct mbr *)part->block)->table + 1; - /* Do we need to look for an extended partition? */ - if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) { - /* Start looking for an extended partition in the MBR */ - while (++part->private.ebr.parent_index < 4) { - uint8_t type = parent_table[part->private.ebr.parent_index].ostype; - - if ((type == 0x05) || (type == 0x0F) || (type == 0x85)) - break; - } - if (part->private.ebr.parent_index == 4) - /* No extended partitions found */ - goto out_finished; - part->private.ebr.lba_extended = - parent_table[part->private.ebr.parent_index].start_lba; - ebr_table = &phony; - } - /* Load next EBR */ - ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended; - free(part->block); - part->block = read_sectors(ebr_lba, 1); - if (!part->block) { - error("Could not load EBR!\n"); - goto err_ebr; - } - ebr_table = ((const struct mbr *)part->block)->table; - dprintf("next_ebr_part:\n"); - mbr_part_dump(ebr_table); - - /* - * Sanity check entry: must not extend outside the - * extended partition. This is necessary since some OSes - * put crap in some entries. - */ - { - const struct mbr *mbr = - (const struct mbr *)part->private.ebr.parent->block; - const struct part_entry *extended = - mbr->table + part->private.ebr.parent_index; - - if (ebr_table[0].start_lba >= extended->start_lba + extended->length) { - dprintf("Insane logical partition!\n"); - goto err_insane; - } - } - /* Success */ - part->lba_data = ebr_table[0].start_lba + ebr_lba; - dprintf("Partition %d logical lba %"PRIu64"\n", - part->index, part->lba_data); - part->index++; - part->record = ebr_table; - return part; - -err_insane: - - free(part->block); - part->block = NULL; -err_ebr: - -out_finished: - free(part->private.ebr.parent->block); - free(part->private.ebr.parent); - free(part->block); - free(part); - return NULL; -} - -static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part) -{ - struct disk_part_iter *ebr_part; - /* Look at the partition table */ - struct part_entry *table = ((struct mbr *)part->block)->table; - - /* Look for data partitions */ - while (++part->private.mbr_index < 4) { - uint8_t type = table[part->private.mbr_index].ostype; - - if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85) - /* Skip empty or extended partitions */ - continue; - if (!table[part->private.mbr_index].length) - /* Empty */ - continue; - break; - } - /* If we're currently the last partition, it's time for EBR processing */ - if (part->private.mbr_index == 4) { - /* Allocate another iterator for extended partitions */ - ebr_part = malloc(sizeof(*ebr_part)); - if (!ebr_part) { - error("Could not allocate extended partition iterator!\n"); - goto err_alloc; - } - /* Setup EBR iterator parameters */ - ebr_part->block = NULL; - ebr_part->index = 4; - ebr_part->record = NULL; - ebr_part->next = next_ebr_part; - ebr_part->private.ebr.parent = part; - /* Trigger an initial EBR load */ - ebr_part->private.ebr.parent_index = -1; - /* The EBR iterator is responsible for freeing us */ - return next_ebr_part(ebr_part); - } - dprintf("next_mbr_part:\n"); - mbr_part_dump(table + part->private.mbr_index); - - /* Update parameters to reflect this new partition. Re-use iterator */ - part->lba_data = table[part->private.mbr_index].start_lba; - dprintf("Partition %d primary lba %"PRIu64"\n", - part->private.mbr_index, part->lba_data); - part->index = part->private.mbr_index + 1; - part->record = table + part->private.mbr_index; - return part; - - free(ebr_part); -err_alloc: - - free(part->block); - free(part); - return NULL; -} - -/* - * GUID - * Be careful with endianness, you must adjust it yourself - * iff you are directly using the fourth data chunk - */ -struct guid { - uint32_t data1; - uint16_t data2; - uint16_t data3; - uint64_t data4; -} __attribute__ ((packed)); - - /* - * This walk-map effectively reverses the little-endian - * portions of the GUID in the output text - */ -static const char guid_le_walk_map[] = { - 3, -1, -1, -1, 0, - 5, -1, 0, - 3, -1, 0, - 2, 1, 0, - 1, 1, 1, 1, 1, 1 -}; - -#if DEBUG -/* - * Fill a buffer with a textual GUID representation. - * The buffer must be >= char[37] and will be populated - * with an ASCII NUL C string terminator. - * Example: 11111111-2222-3333-4444-444444444444 - * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB - */ -static void guid_to_str(char *buf, const struct guid *id) -{ - unsigned int i = 0; - const char *walker = (const char *)id; - - while (i < sizeof(guid_le_walk_map)) { - walker += guid_le_walk_map[i]; - if (!guid_le_walk_map[i]) - *buf = '-'; - else { - *buf = ((*walker & 0xF0) >> 4) + '0'; - if (*buf > '9') - *buf += 'A' - '9' - 1; - buf++; - *buf = (*walker & 0x0F) + '0'; - if (*buf > '9') - *buf += 'A' - '9' - 1; - } - buf++; - i++; - } - *buf = 0; -} -#endif - -/* - * Create a GUID structure from a textual GUID representation. - * The input buffer must be >= 32 hexadecimal chars and be - * terminated with an ASCII NUL. Returns non-zero on failure. - * Example: 11111111-2222-3333-4444-444444444444 - * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB - */ -static int str_to_guid(const char *buf, struct guid *id) -{ - char guid_seq[sizeof(struct guid) * 2]; - unsigned int i = 0; - char *walker = (char *)id; - - while (*buf && i < sizeof(guid_seq)) { - switch (*buf) { - /* Skip these three characters */ - case '{': - case '}': - case '-': - break; - default: - /* Copy something useful to the temp. sequence */ - if ((*buf >= '0') && (*buf <= '9')) - guid_seq[i] = *buf - '0'; - else if ((*buf >= 'A') && (*buf <= 'F')) - guid_seq[i] = *buf - 'A' + 10; - else if ((*buf >= 'a') && (*buf <= 'f')) - guid_seq[i] = *buf - 'a' + 10; - else { - /* Or not */ - error("Illegal character in GUID!\n"); - return -1; - } - i++; - } - buf++; - } - /* Check for insufficient valid characters */ - if (i < sizeof(guid_seq)) { - error("Too few GUID characters!\n"); - return -1; - } - buf = guid_seq; - i = 0; - while (i < sizeof(guid_le_walk_map)) { - if (!guid_le_walk_map[i]) - i++; - walker += guid_le_walk_map[i]; - *walker = *buf << 4; - buf++; - *walker |= *buf; - buf++; - i++; - } - return 0; -} - -/* A GPT partition */ -struct gpt_part { - struct guid type; - struct guid uid; - uint64_t lba_first; - uint64_t lba_last; - uint64_t attribs; - char name[72]; -} __attribute__ ((packed)); - -static void gpt_part_dump(const struct gpt_part *gpt_part) -{ -#ifdef DEBUG - unsigned int i; - char guid_text[37]; - - dprintf("----------------------------------\n" - "GPT part. LBA first __ : 0x%.16llx\n" - "GPT part. LBA last ___ : 0x%.16llx\n" - "GPT part. attribs ____ : 0x%.16llx\n" - "GPT part. name _______ : '", - gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs); - for (i = 0; i < sizeof(gpt_part->name); i++) { - if (gpt_part->name[i]) - dprintf("%c", gpt_part->name[i]); - } - dprintf("'"); - guid_to_str(guid_text, &gpt_part->type); - dprintf("GPT part. type GUID __ : {%s}\n", guid_text); - guid_to_str(guid_text, &gpt_part->uid); - dprintf("GPT part. unique ID __ : {%s}\n", guid_text); -#endif - (void)gpt_part; -} - -/* A GPT header */ -struct gpt { - char sig[8]; - union { - struct { - uint16_t minor; - uint16_t major; - } fields __attribute__ ((packed)); - uint32_t uint32; - char raw[4]; - } rev __attribute__ ((packed)); - uint32_t hdr_size; - uint32_t chksum; - char reserved1[4]; - uint64_t lba_cur; - uint64_t lba_alt; - uint64_t lba_first_usable; - uint64_t lba_last_usable; - struct guid disk_guid; - uint64_t lba_table; - uint32_t part_count; - uint32_t part_size; - uint32_t table_chksum; - char reserved2[1]; -} __attribute__ ((packed)); -static const char gpt_sig_magic[] = "EFI PART"; - -#if DEBUG -static void gpt_dump(const struct gpt *gpt) -{ - char guid_text[37]; - - printf("GPT sig ______________ : '%8.8s'\n" - "GPT major revision ___ : 0x%.4x\n" - "GPT minor revision ___ : 0x%.4x\n" - "GPT header size ______ : 0x%.8x\n" - "GPT header checksum __ : 0x%.8x\n" - "GPT reserved _________ : '%4.4s'\n" - "GPT LBA current ______ : 0x%.16llx\n" - "GPT LBA alternative __ : 0x%.16llx\n" - "GPT LBA first usable _ : 0x%.16llx\n" - "GPT LBA last usable __ : 0x%.16llx\n" - "GPT LBA part. table __ : 0x%.16llx\n" - "GPT partition count __ : 0x%.8x\n" - "GPT partition size ___ : 0x%.8x\n" - "GPT part. table chksum : 0x%.8x\n", - gpt->sig, - gpt->rev.fields.major, - gpt->rev.fields.minor, - gpt->hdr_size, - gpt->chksum, - gpt->reserved1, - gpt->lba_cur, - gpt->lba_alt, - gpt->lba_first_usable, - gpt->lba_last_usable, - gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum); - guid_to_str(guid_text, &gpt->disk_guid); - printf("GPT disk GUID ________ : {%s}\n", guid_text); -} -#endif - -static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part) -{ - const struct gpt_part *gpt_part = NULL; - - while (++part->private.gpt.index < part->private.gpt.parts) { - gpt_part = - (const struct gpt_part *)(part->block + - (part->private.gpt.index * - part->private.gpt.size)); - if (!gpt_part->lba_first) - continue; - break; - } - /* Were we the last partition? */ - if (part->private.gpt.index == part->private.gpt.parts) { - goto err_last; - } - part->lba_data = gpt_part->lba_first; - part->private.gpt.part_guid = &gpt_part->uid; - part->private.gpt.part_label = gpt_part->name; - /* Update our index */ - part->index = part->private.gpt.index + 1; - gpt_part_dump(gpt_part); - - /* In a GPT scheme, we re-use the iterator */ - return part; - -err_last: - free(part->block); - free(part); - - return NULL; -} - -static struct disk_part_iter *get_first_partition(struct disk_part_iter *part) -{ - const struct gpt *gpt_candidate; - - /* - * Ignore any passed partition iterator. The caller should - * have passed NULL. Allocate a new partition iterator - */ - part = malloc(sizeof(*part)); - if (!part) { - error("Count not allocate partition iterator!\n"); - goto err_alloc_iter; - } - /* Read MBR */ - part->block = read_sectors(0, 2); - if (!part->block) { - error("Could not read two sectors!\n"); - goto err_read_mbr; - } - /* Check for an MBR */ - if (((struct mbr *)part->block)->sig != mbr_sig_magic) { - error("No MBR magic!\n"); - goto err_mbr; - } - /* Establish a pseudo-partition for the MBR (index 0) */ - part->index = 0; - part->record = NULL; - part->private.mbr_index = -1; - part->next = next_mbr_part; - /* Check for a GPT disk */ - gpt_candidate = (const struct gpt *)(part->block + SECTOR); - if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) { - /* LBA for partition table */ - uint64_t lba_table; - - /* It looks like one */ - /* TODO: Check checksum. Possibly try alternative GPT */ -#if DEBUG - puts("Looks like a GPT disk."); - gpt_dump(gpt_candidate); -#endif - /* TODO: Check table checksum (maybe) */ - /* Note relevant GPT details */ - part->next = next_gpt_part; - part->private.gpt.index = -1; - part->private.gpt.parts = gpt_candidate->part_count; - part->private.gpt.size = gpt_candidate->part_size; - lba_table = gpt_candidate->lba_table; - gpt_candidate = NULL; - /* Load the partition table */ - free(part->block); - part->block = - read_sectors(lba_table, - ((part->private.gpt.size * part->private.gpt.parts) + - SECTOR - 1) / SECTOR); - if (!part->block) { - error("Could not read GPT partition list!\n"); - goto err_gpt_table; - } - } - /* Return the pseudo-partition's next partition, which is real */ - return part->next(part); - -err_gpt_table: - -err_mbr: - - free(part->block); - part->block = NULL; -err_read_mbr: - - free(part); -err_alloc_iter: - - return NULL; -} - -/* - * Search for a specific drive/partition, based on the GPT GUID. - * We return the disk drive number if found, as well as populating the - * boot_part pointer with the matching partition, if applicable. - * If no matching partition is found or the GUID is a disk GUID, - * boot_part will be populated with NULL. If not matching disk is - * found, we return -1. - */ -static int find_by_guid(const struct guid *gpt_guid, - struct disk_part_iter **boot_part) -{ - int drive; - bool is_me; - struct gpt *header; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - if (!(header = read_sectors(1, 1))) - continue; /* Cannot read sector */ - if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) { - /* Not a GPT disk */ - free(header); - continue; - } -#if DEBUG - gpt_dump(header); -#endif - is_me = !memcmp(&header->disk_guid, gpt_guid, sizeof(*gpt_guid)); - free(header); - if (!is_me) { - /* Check for a matching partition */ - boot_part[0] = get_first_partition(NULL); - while (boot_part[0]) { - is_me = - !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid, - sizeof(*gpt_guid)); - if (is_me) - break; - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - } else - boot_part[0] = NULL; - if (is_me) - return drive; - } - return -1; -} - -/* - * Search for a specific partition, based on the GPT label. - * We return the disk drive number if found, as well as populating the - * boot_part pointer with the matching partition, if applicable. - * If no matching partition is found, boot_part will be populated with - * NULL and we return -1. - */ -static int find_by_label(const char *label, struct disk_part_iter **boot_part) -{ - int drive; - bool is_me; - - for (drive = 0x80; drive <= 0xff; drive++) { - if (get_disk_params(drive)) - continue; /* Drive doesn't exist */ - /* Check for a GPT disk */ - boot_part[0] = get_first_partition(NULL); - if (!(boot_part[0]->next == next_gpt_part)) { - /* Not a GPT disk */ - while (boot_part[0]) { - /* Run through until the end */ - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - continue; - } - /* Check for a matching partition */ - while (boot_part[0]) { - char gpt_label[sizeof(((struct gpt_part *) NULL)->name)]; - const char *gpt_label_scanner = - boot_part[0]->private.gpt.part_label; - int j = 0; - - /* Re-write the GPT partition label as ASCII */ - while (gpt_label_scanner < - boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) { - if ((gpt_label[j] = *gpt_label_scanner)) - j++; - gpt_label_scanner++; - } - if ((is_me = !strcmp(label, gpt_label))) - break; - boot_part[0] = boot_part[0]->next(boot_part[0]); - } - if (is_me) - return drive; - } - - return -1; -} - -static void do_boot(struct data_area *data, int ndata, - struct syslinux_rm_regs *regs) -{ - uint16_t *const bios_fbm = (uint16_t *) 0x413; - addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ - struct syslinux_memmap *mmap; - struct syslinux_movelist *mlist = NULL; - addr_t endimage; - uint8_t driveno = regs->edx.b[0]; - uint8_t swapdrive = driveno & 0x80; - int i; - - mmap = syslinux_memory_map(); - - if (!mmap) { - error("Cannot read system memory map\n"); - return; - } - - endimage = 0; - for (i = 0; i < ndata; i++) { - if (data[i].base + data[i].size > endimage) - endimage = data[i].base + data[i].size; - } - if (endimage > dosmem) - goto too_big; - - for (i = 0; i < ndata; i++) { - if (syslinux_add_movelist(&mlist, data[i].base, - (addr_t) data[i].data, data[i].size)) - goto enomem; - } - - if (opt.swap && driveno != swapdrive) { - static const uint8_t swapstub_master[] = { - /* The actual swap code */ - 0x53, /* 00: push bx */ - 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */ - 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */ - 0x5b, /* 08: pop bx */ - 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */ - 0x90, 0x90, /* 0E: nop; nop */ - /* Code to install this in the right location */ - /* Entry with DS = CS; ES = SI = 0; CX = 256 */ - 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */ - 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */ - 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */ - 0x4f, /* 1F: dec di */ - 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */ - 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */ - 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */ - 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */ - 0x8e, 0xc7, /* 32: mov es,di */ - 0x31, 0xff, /* 34: xor di,di */ - 0xf3, 0x66, 0xa5, /* 36: rep movsd */ - 0xbe, 0, 0, /* 39: mov si,0 */ - 0xbf, 0, 0, /* 3C: mov di,0 */ - 0x8e, 0xde, /* 3F: mov ds,si */ - 0x8e, 0xc7, /* 41: mov es,di */ - 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */ - 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */ - 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */ - 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */ - /* pad out to segment boundary */ - 0x90, 0x90, /* 5A: ... */ - 0x90, 0x90, 0x90, 0x90, /* 5C: ... */ - }; - static uint8_t swapstub[1024]; - uint8_t *p; - - /* Note: we can't rely on either INT 13h nor the dosmem - vector to be correct at this stage, so we have to use an - installer stub to put things in the right place. - Round the installer location to a 1K boundary so the only - possible overlap is the identity mapping. */ - endimage = (endimage + 1023) & ~1023; - - /* Create swap stub */ - memcpy(swapstub, swapstub_master, sizeof swapstub_master); - *(uint16_t *) & swapstub[0x3a] = regs->ds; - *(uint16_t *) & swapstub[0x3d] = regs->es; - *(uint32_t *) & swapstub[0x45] = regs->ecx.l; - *(uint32_t *) & swapstub[0x4b] = regs->esi.l; - *(uint32_t *) & swapstub[0x51] = regs->edi.l; - *(uint16_t *) & swapstub[0x56] = regs->ip; - *(uint16_t *) & swapstub[0x58] = regs->cs; - p = &swapstub[sizeof swapstub_master]; - - /* Mapping table; start out with identity mapping everything */ - for (i = 0; i < 256; i++) - p[i] = i; - - /* And the actual swap */ - p[driveno] = swapdrive; - p[swapdrive] = driveno; - - /* Adjust registers */ - regs->ds = regs->cs = endimage >> 4; - regs->es = regs->esi.l = 0; - regs->ecx.l = sizeof swapstub >> 2; - regs->ip = 0x10; /* Installer offset */ - regs->ebx.b[0] = regs->edx.b[0] = swapdrive; - - if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub, - sizeof swapstub)) - goto enomem; - - endimage += sizeof swapstub; - } - - /* Tell the shuffler not to muck with this area... */ - syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); - - /* Force text mode */ - syslinux_force_text_mode(); - - fputs("Booting...\n", stdout); - syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs); - error("Chainboot failed!\n"); - return; - -too_big: - error("Loader file too large\n"); - return; - -enomem: - error("Out of memory\n"); - return; -} - -static int hide_unhide(struct mbr *mbr, int part) -{ - int i; - struct part_entry *pt; - const uint16_t mask = - (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1 - << - 0x0c) - | (1 << 0x0e); - uint8_t t; - bool write_back = false; - - for (i = 1; i <= 4; i++) { - pt = mbr->table + i - 1; - t = pt->ostype; - if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) { - /* It's a hideable partition type */ - if (i == part) - t &= ~0x10; /* unhide */ - else - t |= 0x10; /* hide */ - } - if (t != pt->ostype) { - write_back = true; - pt->ostype = t; - } - } - - if (write_back) - return write_verify_sector(0, mbr); - - return 0; /* ok */ -} - -static uint32_t get_file_lba(const char *filename) -{ - com32sys_t inregs; - uint32_t lba; - - /* Start with clean registers */ - memset(&inregs, 0, sizeof(com32sys_t)); - - /* Put the filename in the bounce buffer */ - strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); - - /* Call comapi_open() which returns a structure pointer in SI - * to a structure whose first member happens to be the LBA. - */ - inregs.eax.w[0] = 0x0006; - inregs.esi.w[0] = OFFS(__com32.cs_bounce); - inregs.es = SEG(__com32.cs_bounce); - __com32.cs_intcall(0x22, &inregs, &inregs); - - if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) { - return 0; /* Filename not found */ - } - - /* Since the first member is the LBA, we simply cast */ - lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0])); - - /* Clean the registers for the next call */ - memset(&inregs, 0, sizeof(com32sys_t)); - - /* Put the filename in the bounce buffer */ - strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size); - - /* Call comapi_close() to free the structure */ - inregs.eax.w[0] = 0x0008; - inregs.esi.w[0] = OFFS(__com32.cs_bounce); - inregs.es = SEG(__com32.cs_bounce); - __com32.cs_intcall(0x22, &inregs, &inregs); - - return lba; -} - -static void usage(void) -{ - static const char usage[] = "\ -Usage: chain.c32 [options]\n\ - chain.c32 hd<disk#> [<partition>] [options]\n\ - chain.c32 fd<disk#> [options]\n\ - chain.c32 mbr:<id> [<partition>] [options]\n\ - chain.c32 guid:<guid> [<partition>] [options]\n\ - chain.c32 label:<label> [<partition>] [options]\n\ - chain.c32 boot [<partition>] [options]\n\ - chain.c32 fs [options]\n\ -Options: file=<loader> Load and execute file, instead of boot sector\n\ - isolinux=<loader> Load another version of ISOLINUX\n\ - ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\ - cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\ - freedos=<loader> Load FreeDOS KERNEL.SYS\n\ - freeldr=<loader> Load ReactOS' FREELDR.SYS\n\ - msdos=<loader> Load MS-DOS IO.SYS\n\ - pcdos=<loader> Load PC-DOS IBMBIO.COM\n\ - drmk=<loader> Load DRMK DELLBIO.BIN\n\ - grub=<loader> Load GRUB Legacy stage2\n\ - grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\ - grldr=<loader> Load GRUB4DOS grldr\n\ - seg=<seg> Jump to <seg>:0000, instead of 0000:7C00\n\ - seg=<seg>[:<offs>][{+@}<entry>] also specified offset and entrypoint\n\ - swap Swap drive numbers, if bootdisk is not fd0/hd0\n\ - hide Hide primary partitions, except selected partition\n\ - sethidden Set the FAT/NTFS hidden sectors field\n\ - keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\ -See syslinux/com32/modules/chain.c for more information\n"; - error(usage); -} - -int main(int argc, char *argv[]) -{ - struct mbr *mbr = NULL; - char *p; - struct disk_part_iter *cur_part = NULL; - struct syslinux_rm_regs regs; - char *drivename, *partition; - int hd, drive, whichpart = 0; /* MBR by default */ - int i; - uint64_t fs_lba = 0; /* Syslinux partition */ - uint32_t file_lba = 0; - struct guid gpt_guid; - unsigned char *isolinux_bin; - uint32_t *checksum, *chkhead, *chktail; - struct data_area data[3]; - int ndata = 0; - addr_t load_base; - static const char cmldr_signature[8] = "cmdcons"; - - openconsole(&dev_null_r, &dev_stdcon_w); - - drivename = "boot"; - partition = NULL; - - /* Prepare the register set */ - memset(®s, 0, sizeof regs); - - opt.seg = 0; - opt.offs = 0x7c00; - opt.entry = 0x7c00; - - for (i = 1; i < argc; i++) { - if (!strncmp(argv[i], "file=", 5)) { - opt.loadfile = argv[i] + 5; - } else if (!strncmp(argv[i], "seg=", 4)) { - uint32_t v; - bool add_entry = true; - char *ep, *p = argv[i] + 4; - - v = strtoul(p, &ep, 0); - if (ep == p || v < 0x50 || v > 0x9f000) { - error("seg: Invalid segment\n"); - goto bail; - } - opt.seg = v; - opt.offs = opt.entry = 0; - if (*ep == ':') { - p = ep+1; - v = strtoul(p, &ep, 0); - if (ep == p) { - error("seg: Invalid offset\n"); - goto bail; - } - opt.offs = v; - } - if (*ep == '@' || *ep == '+') { - add_entry = (*ep == '+'); - p = ep+1; - v = strtoul(p, &ep, 0); - if (ep == p) { - error("seg: Invalid entry point\n"); - goto bail; - } - opt.entry = v; - } - if (add_entry) - opt.entry += opt.offs; - } else if (!strncmp(argv[i], "isolinux=", 9)) { - opt.loadfile = argv[i] + 9; - opt.isolinux = true; - } else if (!strncmp(argv[i], "ntldr=", 6)) { - opt.seg = 0x2000; /* NTLDR wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "cmldr=", 6)) { - opt.seg = 0x2000; /* CMLDR wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.cmldr = true; - opt.sethidden = true; - } else if (!strncmp(argv[i], "freedos=", 8)) { - opt.seg = 0x60; /* FREEDOS wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 8; - opt.sethidden = true; - } else if (!strncmp(argv[i], "freeldr=", 8)) { - opt.loadfile = argv[i] + 8; - opt.sethidden = true; - /* The FreeLdr PE wants to be at 0:8000 */ - opt.seg = 0; - opt.offs = 0x8000; - /* TODO: Properly parse the PE. Right now, this is hard-coded */ - opt.entry = 0x8100; - } else if (!strncmp(argv[i], "msdos=", 6) || - !strncmp(argv[i], "pcdos=", 6)) { - opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 6; - opt.sethidden = true; - } else if (!strncmp(argv[i], "drmk=", 5)) { - opt.seg = 0x70; /* DRMK wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 5; - opt.sethidden = true; - opt.drmk = true; - } else if (!strncmp(argv[i], "grub=", 5)) { - opt.seg = 0x800; /* stage2 wants this address */ - opt.offs = opt.entry = 0; - opt.loadfile = argv[i] + 5; - opt.grub = true; - } else if (!strncmp(argv[i], "grubcfg=", 8)) { - opt.grubcfg = argv[i] + 8; - } else if (!strncmp(argv[i], "grldr=", 6)) { - opt.loadfile = argv[i] + 6; - opt.grldr = true; - } else if (!strcmp(argv[i], "swap")) { - opt.swap = true; - } else if (!strcmp(argv[i], "noswap")) { - opt.swap = false; - } else if (!strcmp(argv[i], "hide")) { - opt.hide = true; - } else if (!strcmp(argv[i], "nohide")) { - opt.hide = false; - } else if (!strcmp(argv[i], "keeppxe")) { - opt.keeppxe = 3; - } else if (!strcmp(argv[i], "sethidden")) { - opt.sethidden = true; - } else if (!strcmp(argv[i], "nosethidden")) { - opt.sethidden = false; - } else if (((argv[i][0] == 'h' || argv[i][0] == 'f') - && argv[i][1] == 'd') - || !strncmp(argv[i], "mbr:", 4) - || !strncmp(argv[i], "mbr=", 4) - || !strncmp(argv[i], "guid:", 5) - || !strncmp(argv[i], "guid=", 5) - || !strncmp(argv[i], "uuid:", 5) - || !strncmp(argv[i], "uuid=", 5) - || !strncmp(argv[i], "label:", 6) - || !strncmp(argv[i], "label=", 6) - || !strcmp(argv[i], "boot") - || !strncmp(argv[i], "boot,", 5) - || !strcmp(argv[i], "fs")) { - drivename = argv[i]; - p = strchr(drivename, ','); - if (p) { - *p = '\0'; - partition = p + 1; - } else if (argv[i + 1] && argv[i + 1][0] >= '0' - && argv[i + 1][0] <= '9') { - partition = argv[++i]; - } - } else { - usage(); - goto bail; - } - } - - if (opt.grubcfg && !opt.grub) { - error("grubcfg=<filename> must be used together with grub=<loader>.\n"); - goto bail; - } - - /* - * Set up initial register values - */ - regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg; - regs.ip = opt.entry; - - /* - * For the special case of the standard 0:7C00 entry point, put - * the stack below; otherwise leave the stack pointer at the end - * of the segment (sp = 0). - */ - if (opt.seg == 0 && opt.offs == 0x7c00) - regs.esp.l = 0x7c00; - - - hd = 0; - if (!strncmp(drivename, "mbr", 3)) { - drive = find_disk(strtoul(drivename + 4, NULL, 0)); - if (drive == -1) { - error("Unable to find requested MBR signature\n"); - goto bail; - } - } else if (!strncmp(drivename, "guid", 4) || !strncmp(drivename, "uuid", 4)) { - if (str_to_guid(drivename + 5, &gpt_guid)) - goto bail; - drive = find_by_guid(&gpt_guid, &cur_part); - if (drive == -1) { - error("Unable to find requested GPT disk/partition\n"); - goto bail; - } - } else if (!strncmp(drivename, "label", 5)) { - if (!drivename[6]) { - error("No label specified.\n"); - goto bail; - } - drive = find_by_label(drivename + 6, &cur_part); - if (drive == -1) { - error("Unable to find requested partition by label\n"); - goto bail; - } - } else if ((drivename[0] == 'h' || drivename[0] == 'f') && - drivename[1] == 'd') { - hd = drivename[0] == 'h'; - drivename += 2; - drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0); - } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) { - const union syslinux_derivative_info *sdi; - - sdi = syslinux_derivative_info(); - if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) - drive = 0x80; /* Boot drive not available */ - else - drive = sdi->disk.drive_number; - if (!strcmp(drivename, "fs") - && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX - || sdi->c.filesystem == SYSLINUX_FS_EXTLINUX - || sdi->c.filesystem == SYSLINUX_FS_ISOLINUX)) - /* We should lookup the Syslinux partition number and use it */ - fs_lba = *sdi->disk.partoffset; - } else { - error("Unparsable drive specification\n"); - goto bail; - } - - /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ - regs.ebx.b[0] = regs.edx.b[0] = drive; - - /* Get the disk geometry and disk access setup */ - if (get_disk_params(drive)) { - error("Cannot get disk parameters\n"); - goto bail; - } - - /* Get MBR */ - if (!(mbr = read_sectors(0, 1))) { - error("Cannot read Master Boot Record or sector 0\n"); - goto bail; - } - - if (partition) - whichpart = strtoul(partition, NULL, 0); - /* "guid:" or "label:" might have specified a partition */ - if (cur_part) - whichpart = cur_part->index; - - /* Boot the MBR by default */ - if (!cur_part && (whichpart || fs_lba)) { - /* Boot a partition, possibly the Syslinux partition itself */ - cur_part = get_first_partition(NULL); - while (cur_part) { - if ((cur_part->index == whichpart) - || (cur_part->lba_data == fs_lba)) - /* Found the partition to boot */ - break; - cur_part = cur_part->next(cur_part); - } - if (!cur_part) { - error("Requested partition not found!\n"); - goto bail; - } - whichpart = cur_part->index; - } - - if (!(drive & 0x80) && whichpart) { - error("Warning: Partitions of floppy devices may not work\n"); - } - - /* - * GRLDR of GRUB4DOS wants the partition number in DH: - * -1: whole drive (default) - * 0-3: primary partitions - * 4-*: logical partitions - */ - if (opt.grldr) - regs.edx.b[1] = whichpart - 1; - - if (opt.hide) { - if (whichpart < 1 || whichpart > 4) - error("WARNING: hide specified without a non-primary partition\n"); - if (hide_unhide(mbr, whichpart)) - error("WARNING: failed to write MBR for 'hide'\n"); - } - - /* Do the actual chainloading */ - load_base = (opt.seg << 4) + opt.offs; - - if (opt.loadfile) { - fputs("Loading the boot file...\n", stdout); - if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) { - error("Failed to load the boot file\n"); - goto bail; - } - data[ndata].base = load_base; - load_base = 0x7c00; /* If we also load a boot sector */ - - /* Create boot info table: needed when you want to chainload - another version of ISOLINUX (or another bootlaoder that needs - the -boot-info-table switch of mkisofs) - (will only work when run from ISOLINUX) */ - if (opt.isolinux) { - const union syslinux_derivative_info *sdi; - sdi = syslinux_derivative_info(); - - if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { - /* Boot info table info (integers in little endian format) - - Offset Name Size Meaning - 8 bi_pvd 4 bytes LBA of primary volume descriptor - 12 bi_file 4 bytes LBA of boot file - 16 bi_length 4 bytes Boot file length in bytes - 20 bi_csum 4 bytes 32-bit checksum - 24 bi_reserved 40 bytes Reserved - - The 32-bit checksum is the sum of all the 32-bit words in the - boot file starting at byte offset 64. All linear block - addresses (LBAs) are given in CD sectors (normally 2048 bytes). - - LBA of primary volume descriptor should already be set to 16. - */ - - isolinux_bin = (unsigned char *)data[ndata].data; - - /* Get LBA address of bootfile */ - file_lba = get_file_lba(opt.loadfile); - - if (file_lba == 0) { - error("Failed to find LBA offset of the boot file\n"); - goto bail; - } - /* Set it */ - *((uint32_t *) & isolinux_bin[12]) = file_lba; - - /* Set boot file length */ - *((uint32_t *) & isolinux_bin[16]) = data[ndata].size; - - /* Calculate checksum */ - checksum = (uint32_t *) & isolinux_bin[20]; - chkhead = (uint32_t *) & isolinux_bin[64]; - chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3]; - *checksum = 0; - while (chkhead < chktail) - *checksum += *chkhead++; - - /* - * Deal with possible fractional dword at the end; - * this *should* never happen... - */ - if (data[ndata].size & 3) { - uint32_t xword = 0; - memcpy(&xword, chkhead, data[ndata].size & 3); - *checksum += xword; - } - } else { - error - ("The isolinux= option is only valid when run from ISOLINUX\n"); - goto bail; - } - } - - if (opt.grub) { - /* Layout of stage2 file (from byte 0x0 to 0x270) */ - struct grub_stage2_patch_area { - /* 0x0 to 0x205 */ - char unknown[0x206]; - /* 0x206: compatibility version number major */ - uint8_t compat_version_major; - /* 0x207: compatibility version number minor */ - uint8_t compat_version_minor; - - /* 0x208: install_partition variable */ - struct { - /* 0x208: sub-partition in sub-partition part2 */ - uint8_t part3; - /* 0x209: sub-partition in top-level partition */ - uint8_t part2; - /* 0x20a: top-level partiton number */ - uint8_t part1; - /* 0x20b: BIOS drive number (must be 0) */ - uint8_t drive; - } __attribute__ ((packed)) install_partition; - - /* 0x20c: deprecated (historical reason only) */ - uint32_t saved_entryno; - /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */ - uint8_t stage2_id; - /* 0x211: force LBA */ - uint8_t force_lba; - /* 0x212: version string (will probably be 0.97) */ - char version_string[5]; - /* 0x217: config filename */ - char config_file[89]; - /* 0x270: start of code (after jump from 0x200) */ - char codestart[1]; - } __attribute__ ((packed)) * stage2; - - if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) { - error - ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n"); - goto bail; - } - - stage2 = data[ndata].data; - - /* - * Check the compatibility version number to see if we loaded a real - * stage2 file or a stage2 file that we support. - */ - if (stage2->compat_version_major != 3 - || stage2->compat_version_minor != 2) { - error - ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n"); - goto bail; - } - - /* jump 0x200 bytes into the loadfile */ - regs.ip = 0x200; - - /* - * GRUB Legacy wants the partition number in the install_partition - * variable, located at offset 0x208 of stage2. - * When GRUB Legacy is loaded, it is located at memory address 0x8208. - * - * It looks very similar to the "boot information format" of the - * Multiboot specification: - * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format - * - * 0x208 = part3: sub-partition in sub-partition part2 - * 0x209 = part2: sub-partition in top-level partition - * 0x20a = part1: top-level partition number - * 0x20b = drive: BIOS drive number (must be 0) - * - * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at - * another location. - * - * Partition numbers always start from zero. - * Unused partition bytes must be set to 0xFF. - * - * We only care about top-level partition, so we only need to change - * "part1" to the appropriate value: - * -1: whole drive (default) (-1 = 0xFF) - * 0-3: primary partitions - * 4-*: logical partitions - */ - stage2->install_partition.part1 = whichpart - 1; - - /* - * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the - * config filename. The filename passed via grubcfg= will overwrite - * the default config filename "/boot/grub/menu.lst". - */ - if (opt.grubcfg) { - if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) { - error - ("The config filename length can't exceed 88 characters.\n"); - goto bail; - } - - strcpy((char *)stage2->config_file, opt.grubcfg); - } - } - - if (opt.drmk) { - /* DRMK entry is different than MS-DOS/PC-DOS */ - /* - * A new size, aligned to 16 bytes to ease use of ds:[bp+28]. - * We only really need 4 new, usable bytes at the end. - */ - int tsize = (data[ndata].size + 19) & 0xfffffff0; - const union syslinux_derivative_info *sdi; - - sdi = syslinux_derivative_info(); - /* We should lookup the Syslinux partition offset and use it */ - fs_lba = *sdi->disk.partoffset; - /* - * fs_lba should be verified against the disk as some DRMK - * variants will check and fail if it does not match - */ - dprintf(" fs_lba offset is %"PRIu64"\n", fs_lba); - /* DRMK only uses a DWORD */ - if (fs_lba > 0xffffffff) { - error - ("LBA very large; Only using lower 32 bits; DRMK will probably fail\n"); - } - regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */ - if (!realloc(data[ndata].data, tsize)) { - error("Failed to realloc for DRMK\n"); - goto bail; /* We'll never make it */ - } - data[ndata].size = tsize; - /* ds:bp is assumed by DRMK to be the boot sector */ - /* offset 28 is the FAT HiddenSectors value */ - regs.ds = (tsize >> 4) + (opt.seg - 2); - /* "Patch" into tail of the new space */ - *(int *)(data[ndata].data + tsize - 4) = (int)(fs_lba & 0xffffffff); - } - - ndata++; - } - - if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) { - /* Actually read the boot sector */ - if (!cur_part) { - data[ndata].data = mbr; - } else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) { - error("Cannot read boot sector\n"); - goto bail; - } - data[ndata].size = SECTOR; - data[ndata].base = load_base; - - if (!opt.loadfile) { - const struct mbr *br = - (const struct mbr *)((char *)data[ndata].data + - data[ndata].size - sizeof(struct mbr)); - if (br->sig != mbr_sig_magic) { - error - ("Boot sector signature not found (unbootable disk/partition?)\n"); - goto bail; - } - } - /* - * To boot the Recovery Console of Windows NT/2K/XP we need to write - * the string "cmdcons\0" to memory location 0000:7C03. - * Memory location 0000:7C00 contains the bootsector of the partition. - */ - if (cur_part && opt.cmldr) { - memcpy((char *)data[ndata].data + 3, cmldr_signature, - sizeof cmldr_signature); - } - - /* - * Modify the hidden sectors (partition offset) copy in memory; - * this modifies the field used by FAT and NTFS filesystems, and - * possibly other boot loaders which use the same format. - */ - if (cur_part && opt.sethidden) { - *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data; - } - - ndata++; - } - - if (cur_part) { - if (cur_part->next == next_gpt_part) { - /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */ - struct part_entry *record; - /* Look at the GPT partition */ - const struct gpt_part *gp = (const struct gpt_part *) - (cur_part->block + - (cur_part->private.gpt.size * cur_part->private.gpt.index)); - /* Note the partition length */ - uint64_t lba_count = gp->lba_last - gp->lba_first + 1; - /* The length of the hand-over */ - int synth_size = - sizeof(struct part_entry) + sizeof(uint32_t) + - cur_part->private.gpt.size; - /* Will point to the partition record length in the hand-over */ - uint32_t *plen; - - /* Allocate the hand-over record */ - record = malloc(synth_size); - if (!record) { - error("Could not build GPT hand-over record!\n"); - goto bail; - } - /* Synthesize the record */ - memset(record, 0, synth_size); - record->active_flag = 0x80; - record->ostype = 0xED; - /* All bits set by default */ - record->start_lba = ~(uint32_t) 0; - record->length = ~(uint32_t) 0; - /* If these fit the precision, pass them on */ - if (cur_part->lba_data < record->start_lba) - record->start_lba = cur_part->lba_data; - if (lba_count < record->length) - record->length = lba_count; - /* Next comes the GPT partition record length */ - plen = (uint32_t *) (record + 1); - plen[0] = cur_part->private.gpt.size; - /* Next comes the GPT partition record copy */ - memcpy(plen + 1, gp, plen[0]); - cur_part->record = record; - - regs.eax.l = 0x54504721; /* '!GPT' */ - data[ndata].base = 0x7be; - data[ndata].size = synth_size; - data[ndata].data = (void *)record; - ndata++; - regs.esi.w[0] = 0x7be; - - dprintf("GPT handover:\n"); - mbr_part_dump(record); - gpt_part_dump((struct gpt_part *)(plen + 1)); - } else if (cur_part->record) { - /* MBR handover protocol */ - static struct part_entry handover_record; - - handover_record = *cur_part->record; - handover_record.start_lba = cur_part->lba_data; - - data[ndata].base = 0x7be; - data[ndata].size = sizeof handover_record; - data[ndata].data = &handover_record; - ndata++; - regs.esi.w[0] = 0x7be; - - dprintf("MBR handover:\n"); - mbr_part_dump(&handover_record); - } - } - - do_boot(data, ndata, ®s); - -bail: - if (cur_part) { - free(cur_part->block); - free((void *)cur_part->record); - } - free(cur_part); - free(mbr); - return 255; -} diff --git a/core/bootsect.inc b/core/bootsect.inc index 6c204096..9e47e1a5 100644 --- a/core/bootsect.inc +++ b/core/bootsect.inc @@ -1,7 +1,7 @@ ;; ----------------------------------------------------------------------- ;; ;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved -;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; Copyright 2009-2012 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 @@ -38,6 +38,7 @@ SuperSize equ $+1 load_bootsec: mov edi,free_high_memory mov [trackbuf+4],edi ; Copy from this address + mov eax,0xA0000 ; Maximum load xor dx,dx ; No padding mov bx,abort_check ; Don't print dots, but allow abort call load_high @@ -45,6 +46,9 @@ load_bootsec: sub edi,free_high_memory mov [trackbuf+8],edi ; Save length + cmp edi,0xA0000-7C00h + ja bs_too_big + mov eax,7C00h ; Entry point mov [trackbuf],eax ; Copy to this address @@ -237,3 +241,13 @@ replace_stub: .csip equ $-4 section .text16 +bs_too_big: + call close + mov si,err_bs_too_big + jmp abort_load + + section .data16 +err_bs_too_big db "Too large for a bootstrap (need LINUX instead of KERNEL?)" + db CR, LF, 0 + + section .text16 diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c new file mode 100644 index 00000000..500d0fd3 --- /dev/null +++ b/core/fs/ntfs/ntfs.c @@ -0,0 +1,1388 @@ +/* + * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com> + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Note: No support for compressed files */ + +#include <dprintf.h> +#include <stdio.h> +#include <string.h> +#include <sys/dirent.h> +#include <cache.h> +#include <core.h> +#include <disk.h> +#include <fs.h> +#include <ilog2.h> +#include <klibc/compiler.h> +#include <ctype.h> + +#include "codepage.h" +#include "ntfs.h" +#include "runlist.h" + +static struct ntfs_readdir_state *readdir_state; + +/*** Function declarations */ +static f_mft_record_lookup ntfs_mft_record_lookup_3_0; +static f_mft_record_lookup ntfs_mft_record_lookup_3_1; + +/*** Function definitions */ + +/* Check if there are specific zero fields in an NTFS boot sector */ +static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb) +{ + return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] && + !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 && + !sb->zero_3; +} + +static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb) +{ + return ntfs_check_zero_fields(sb) && + (!memcmp(sb->oem_name, "NTFS ", 8) || + !memcmp(sb->oem_name, "MSWIN4.0", 8) || + !memcmp(sb->oem_name, "MSWIN4.1", 8)); +} + +static inline struct inode *new_ntfs_inode(struct fs_info *fs) +{ + struct inode *inode; + + inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode)); + if (!inode) + malloc_error("inode structure"); + + return inode; +} + +static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec) +{ + uint16_t *usa; + uint16_t usa_no; + uint16_t usa_count; + uint16_t *blk; + + dprintf("in %s()\n", __func__); + + if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX) + return; + + /* get the Update Sequence Array offset */ + usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs); + /* get the Update Sequence Array Number and skip it */ + usa_no = *usa++; + /* get the Update Sequene Array count */ + usa_count = nrec->usa_count - 1; /* exclude the USA number */ + /* make it to point to the last two bytes of the RECORD's first sector */ + blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2); + + while (usa_count--) { + if (*blk != usa_no) + break; + + *blk = *usa++; + blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs)); + } +} + +/* read content from cache */ +static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count, + block_t *blk, uint64_t *blk_offset, + uint64_t *blk_next_offset, uint64_t *lcn) +{ + uint8_t *data; + uint64_t offset = *blk_offset; + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint64_t bytes; + uint64_t lbytes; + uint64_t loffset; + uint64_t k; + + dprintf("in %s()\n", __func__); + + if (count > len) + goto out; + + data = (uint8_t *)get_cache(fs->fs_dev, *blk); + if (!data) + goto out; + + if (!offset) + offset = (*lcn << clust_byte_shift) % blk_size; + + dprintf("LCN: 0x%X\n", *lcn); + dprintf("offset: 0x%X\n", offset); + + bytes = count; /* bytes to copy */ + lbytes = blk_size - offset; /* bytes left to copy */ + if (lbytes >= bytes) { + /* so there's room enough, then copy the whole content */ + memcpy(buf, data + offset, bytes); + loffset = offset; + offset += count; + } else { + dprintf("bytes: %u\n", bytes); + dprintf("bytes left: %u\n", lbytes); + /* otherwise, let's copy it partially... */ + k = 0; + while (bytes) { + memcpy(buf + k, data + offset, lbytes); + bytes -= lbytes; + loffset = offset; + offset += lbytes; + k += lbytes; + if (offset >= blk_size) { + /* then fetch a new FS block */ + data = (uint8_t *)get_cache(fs->fs_dev, ++*blk); + if (!data) + goto out; + + lbytes = bytes; + loffset = offset; + offset = 0; + } + } + } + + if (loffset >= blk_size) + loffset = 0; /* it must be aligned on a block boundary */ + + *blk_offset = loffset; + + if (blk_next_offset) + *blk_next_offset = offset; + + *lcn += blk_size / count; /* update LCN */ + + return 0; + +out: + return -1; +} + +static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs, + uint32_t file, block_t *blk) +{ + const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size; + uint8_t *buf; + const block_t mft_blk = NTFS_SB(fs)->mft_blk; + block_t cur_blk; + block_t right_blk; + uint64_t offset; + uint64_t next_offset; + const uint32_t mft_record_shift = ilog2(mft_record_size); + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + uint64_t lcn; + int err; + struct ntfs_mft_record *mrec; + + dprintf("in %s()\n", __func__); + + buf = (uint8_t *)malloc(mft_record_size); + if (!buf) + malloc_error("uint8_t *"); + + /* determine MFT record's LCN and block number */ + lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift); + cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk; + offset = (file << mft_record_shift) % BLOCK_SIZE(fs); + for (;;) { + right_blk = cur_blk + mft_blk; + err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk, + &offset, &next_offset, &lcn); + if (err) { + printf("Error while reading from cache.\n"); + break; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)buf); + + mrec = (struct ntfs_mft_record *)buf; + /* check if it has a valid magic number */ + if (mrec->magic == NTFS_MAGIC_FILE) { + if (blk) + *blk = cur_blk; /* update record starting block */ + + return mrec; /* found MFT record */ + } + + if (next_offset >= BLOCK_SIZE(fs)) { + /* try the next FS block */ + offset = 0; + cur_blk = right_blk - mft_blk + 1; + } else { + /* there's still content to fetch in the current block */ + cur_blk = right_blk - mft_blk; + offset = next_offset; /* update FS block offset */ + } + } + + free(buf); + + return NULL; +} + +static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs, + uint32_t file, block_t *blk) +{ + const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size; + uint8_t *buf; + const block_t mft_blk = NTFS_SB(fs)->mft_blk; + block_t cur_blk; + block_t right_blk; + uint64_t offset; + uint64_t next_offset; + const uint32_t mft_record_shift = ilog2(mft_record_size); + const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift; + uint64_t lcn; + int err; + struct ntfs_mft_record *mrec; + + dprintf("in %s()\n", __func__); + + buf = (uint8_t *)malloc(mft_record_size); + if (!buf) + malloc_error("uint8_t *"); + + lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift); + cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk; + offset = (file << mft_record_shift) % BLOCK_SIZE(fs); + for (;;) { + right_blk = cur_blk + NTFS_SB(fs)->mft_blk; + err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk, + &offset, &next_offset, &lcn); + if (err) { + printf("Error while reading from cache.\n"); + break; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)buf); + + mrec = (struct ntfs_mft_record *)buf; + /* Check if the NTFS 3.1 MFT record number matches */ + if (mrec->magic == NTFS_MAGIC_FILE && mrec->mft_record_no == file) { + if (blk) + *blk = cur_blk; /* update record starting block */ + + return mrec; /* found MFT record */ + } + + if (next_offset >= BLOCK_SIZE(fs)) { + /* try the next FS block */ + offset = 0; + cur_blk = right_blk - NTFS_SB(fs)->mft_blk + 1; + } else { + /* there's still content to fetch in the current block */ + cur_blk = right_blk - NTFS_SB(fs)->mft_blk; + offset = next_offset; /* update FS block offset */ + } + } + + free(buf); + + return NULL; +} + +static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie) +{ + const uint16_t *entry_fn; + uint8_t entry_fn_len; + unsigned i; + + dprintf("in %s()\n", __func__); + + entry_fn = ie->key.file_name.file_name; + entry_fn_len = ie->key.file_name.file_name_len; + + if (strlen(dname) != entry_fn_len) + return false; + + /* Do case-sensitive compares for Posix file names */ + if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) { + for (i = 0; i < entry_fn_len; i++) + if (entry_fn[i] != dname[i]) + return false; + } else { + for (i = 0; i < entry_fn_len; i++) + if (tolower(entry_fn[i]) != tolower(dname[i])) + return false; + } + + return true; +} + +static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr, + struct mapping_chunk *chunk, + uint32_t *offset) +{ + memset(chunk, 0, sizeof *chunk); + *offset = 0U; + + return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset; +} + +/* Parse data runs. + * + * return 0 on success or -1 on failure. + */ +static int parse_data_run(const void *stream, uint32_t *offset, + uint8_t *attr_len, struct mapping_chunk *chunk) +{ + uint8_t *buf; /* Pointer to the zero-terminated byte stream */ + uint8_t count; /* The count byte */ + uint8_t v, l; /* v is the number of changed low-order VCN bytes; + * l is the number of changed low-order LCN bytes + */ + uint8_t *byte; + int byte_shift = 8; + int mask; + uint8_t val; + int64_t res; + + (void)attr_len; + + dprintf("in %s()\n", __func__); + + chunk->flags &= ~MAP_MASK; + + buf = (uint8_t *)stream + *offset; + if (buf > attr_len || !*buf) { + chunk->flags |= MAP_END; /* we're done */ + return 0; + } + + if (!*offset) + chunk->flags |= MAP_START; /* initial chunk */ + + count = *buf; + v = count & 0x0F; + l = count >> 4; + + if (v > 8 || l > 8) /* more than 8 bytes ? */ + goto out; + + byte = (uint8_t *)buf + v; + count = v; + + res = 0LL; + while (count--) { + val = *byte--; + mask = val >> (byte_shift - 1); + res = (res << byte_shift) | ((val + mask) ^ mask); + } + + chunk->len = res; /* get length data */ + + byte = (uint8_t *)buf + v + l; + count = l; + + mask = 0xFFFFFFFF; + res = 0LL; + if (*byte & 0x80) + res |= (int64_t)mask; /* sign-extend it */ + + while (count--) + res = (res << byte_shift) | *byte--; + + chunk->lcn += res; + /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */ + if (!chunk->lcn) + chunk->flags |= MAP_UNALLOCATED; + else + chunk->flags |= MAP_ALLOCATED; + + *offset += v + l + 1; + + return 0; + +out: + return -1; +} + +static struct ntfs_mft_record * +ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr, + uint32_t type, struct ntfs_mft_record *mrec) +{ + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + uint8_t *stream; + int err; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint8_t buf[blk_size]; + uint64_t blk_offset; + int64_t vcn; + int64_t lcn; + int64_t last_lcn; + block_t blk; + struct ntfs_attr_list_entry *attr_entry; + uint32_t len = 0; + struct ntfs_mft_record *retval; + uint64_t start_blk = 0; + + dprintf("in %s()\n", __func__); + + if (attr->non_resident) + goto handle_non_resident_attr; + + attr_entry = (struct ntfs_attr_list_entry *) + ((uint8_t *)attr + attr->data.resident.value_offset); + len = attr->data.resident.value_len; + for (; (uint8_t *)attr_entry < (uint8_t *)attr + len; + attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry + + attr_entry->length)) { + dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n", + attr_entry->type); + if (attr_entry->type == type) + goto found; /* We got the attribute! :-) */ + } + + printf("No attribute found.\n"); + goto out; + +handle_non_resident_attr: + attr_len = (uint8_t *)attr + attr->len; + stream = mapping_chunk_init(attr, &chunk, &offset); + do { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("parse_data_run()\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + continue; + if (chunk.flags & MAP_END) + break; + if (chunk.flags & MAP_ALLOCATED) { + vcn = 0; + lcn = chunk.lcn; + while (vcn < chunk.len) { + blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >> + BLOCK_SHIFT(fs); + blk_offset = 0; + last_lcn = lcn; + lcn += vcn; + err = ntfs_read(fs, buf, blk_size, blk_size, &blk, + &blk_offset, NULL, (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto out; + } + + attr_entry = (struct ntfs_attr_list_entry *)&buf; + len = attr->data.non_resident.data_size; + for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len; + attr_entry = (struct ntfs_attr_list_entry *) + ((uint8_t *)attr_entry + attr_entry->length)) { + dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n", + attr_entry->type); + if (attr_entry->type == type) + goto found; /* We got the attribute! :-) */ + } + + lcn = last_lcn; /* restore original LCN */ + /* go to the next VCN */ + vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift)); + } + } + } while (!(chunk.flags & MAP_END)); + + printf("No attribute found.\n"); + +out: + return NULL; + +found: + /* At this point we have the attribute we were looking for. Now we + * will look for the MFT record that stores information about this + * attribute. + */ + + /* Check if the attribute type we're looking for is in the same + * MFT record. If so, we do not need to look it up again - return it. + */ + if (mrec->mft_record_no == attr_entry->mft_ref) + return mrec; + + retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref, + &start_blk); + if (!retval) { + printf("No MFT record found!\n"); + goto out; + } + + /* return the found MFT record */ + return retval; +} + +static struct ntfs_attr_record * +__ntfs_attr_lookup(struct fs_info *fs, uint32_t type, + struct ntfs_mft_record **mrec) +{ + struct ntfs_mft_record *_mrec = *mrec; + struct ntfs_attr_record *attr; + struct ntfs_attr_record *attr_list_attr; + + dprintf("in %s()\n", __func__); + + if (!_mrec || type == NTFS_AT_END) + goto out; + +again: + attr_list_attr = NULL; + + attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset); + /* walk through the file attribute records */ + for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) { + if (attr->type == NTFS_AT_END) + break; + + if (attr->type == NTFS_AT_ATTR_LIST) { + dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n", + _mrec->mft_record_no); + attr_list_attr = attr; + continue; + } + + if (attr->type == type) + break; + } + + /* if the record has an $ATTRIBUTE_LIST attribute associated + * with it, then we need to look for the wanted attribute in + * it as well. + */ + if (attr->type == NTFS_AT_END && attr_list_attr) { + struct ntfs_mft_record *retval; + + retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec); + if (!retval) + goto out; + + _mrec = retval; + goto again; + } else if (attr->type == NTFS_AT_END && !attr_list_attr) { + attr = NULL; + } + + return attr; + +out: + return NULL; +} + +static inline struct ntfs_attr_record * +ntfs_attr_lookup(struct fs_info *fs, uint32_t type, + struct ntfs_mft_record **mmrec, + struct ntfs_mft_record *mrec) +{ + struct ntfs_mft_record *_mrec = mrec; + struct ntfs_mft_record *other = *mmrec; + struct ntfs_attr_record *retval = NULL; + + if (mrec == other) + return __ntfs_attr_lookup(fs, type, &other); + + retval = __ntfs_attr_lookup(fs, type, &_mrec); + if (!retval) { + _mrec = other; + retval = __ntfs_attr_lookup(fs, type, &other); + if (!retval) + other = _mrec; + } else if (retval && (_mrec != mrec)) { + other = _mrec; + } + + return retval; +} + +static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec) +{ + return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG; +} + +static int index_inode_setup(struct fs_info *fs, unsigned long mft_no, + struct inode *inode) +{ + uint64_t start_blk = 0; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + enum dirent_type d_type; + uint8_t *attr_len; + struct mapping_chunk chunk; + int err; + uint8_t *stream; + uint32_t offset; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + + NTFS_PVT(inode)->mft_no = mft_no; + NTFS_PVT(inode)->seq_no = mrec->seq_no; + + NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift; + NTFS_PVT(inode)->here = start_blk; + + d_type = get_inode_mode(mrec); + if (d_type == DT_DIR) { /* directory stuff */ + dprintf("Got a directory.\n"); + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + /* check if we have a previous allocated state structure */ + if (readdir_state) { + free(readdir_state); + readdir_state = NULL; + } + + /* allocate our state structure */ + readdir_state = malloc(sizeof *readdir_state); + if (!readdir_state) + malloc_error("ntfs_readdir_state structure"); + + readdir_state->mft_no = mft_no; + /* obviously, the ntfs_readdir() caller will start from INDEX root */ + readdir_state->in_idx_root = true; + } else if (d_type == DT_REG) { /* file stuff */ + dprintf("Got a file.\n"); + attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + NTFS_PVT(inode)->non_resident = attr->non_resident; + NTFS_PVT(inode)->type = attr->type; + + if (!attr->non_resident) { + NTFS_PVT(inode)->data.resident.offset = + (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset); + inode->size = attr->data.resident.value_len; + } else { + attr_len = (uint8_t *)attr + attr->len; + + stream = mapping_chunk_init(attr, &chunk, &offset); + NTFS_PVT(inode)->data.non_resident.rlist = NULL; + for (;;) { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("parse_data_run()\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + continue; + if (chunk.flags & MAP_END) + break; + if (chunk.flags & MAP_ALLOCATED) { + /* append new run to the runlist */ + runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist, + (struct runlist_element *)&chunk); + /* update for next VCN */ + chunk.vcn += chunk.len; + } + } + + if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) { + printf("No mapping found\n"); + goto out; + } + + inode->size = attr->data.non_resident.initialized_size; + } + } + + inode->mode = d_type; + + free(mrec); + + return 0; + +out: + free(mrec); + + return -1; +} + +static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir) +{ + struct fs_info *fs = dir->fs; + struct ntfs_mft_record *mrec, *lmrec; + block_t blk; + uint64_t blk_offset; + struct ntfs_attr_record *attr; + struct ntfs_idx_root *ir; + struct ntfs_idx_entry *ie; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + uint8_t buf[blk_size]; + struct ntfs_idx_allocation *iblk; + int err; + uint8_t *stream; + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + int64_t vcn; + int64_t lcn; + int64_t last_lcn; + struct inode *inode; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + ir = (struct ntfs_idx_root *)((uint8_t *)attr + + attr->data.resident.value_offset); + ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index + + ir->index.entries_offset); + for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)mrec || + (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&ir->index + ir->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&ir->index + ir->index.index_len) + goto index_err; + + /* last entry cannot contain a key. it can however contain + * a pointer to a child node in the B+ tree so we just break out + */ + if (ie->flags & INDEX_ENTRY_END) + break; + + if (ntfs_filename_cmp(dname, ie)) + goto found; + } + + /* check for the presence of a child node */ + if (!(ie->flags & INDEX_ENTRY_NODE)) { + printf("No child node, aborting...\n"); + goto out; + } + + /* then descend into child node */ + + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + if (!attr->non_resident) { + printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n"); + goto out; + } + + attr_len = (uint8_t *)attr + attr->len; + stream = mapping_chunk_init(attr, &chunk, &offset); + do { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) + break; + + if (chunk.flags & MAP_UNALLOCATED) + continue; + + if (chunk.flags & MAP_ALLOCATED) { + dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len, + chunk.lcn); + + vcn = 0; + lcn = chunk.lcn; + while (vcn < chunk.len) { + blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << + SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs); + + blk_offset = 0; + last_lcn = lcn; + lcn += vcn; + err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, + &blk_offset, NULL, (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto not_found; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf); + + iblk = (struct ntfs_idx_allocation *)&buf; + if (iblk->magic != NTFS_MAGIC_INDX) { + printf("Not a valid INDX record.\n"); + goto not_found; + } + + ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index + + iblk->index.entries_offset); + for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie + + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&iblk->index + iblk->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&iblk->index + iblk->index.index_len) + goto index_err; + + /* last entry cannot contain a key */ + if (ie->flags & INDEX_ENTRY_END) + break; + + if (ntfs_filename_cmp(dname, ie)) + goto found; + } + + lcn = last_lcn; /* restore the original LCN */ + /* go to the next VCN */ + vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift)); + } + } + } while (!(chunk.flags & MAP_END)); + +not_found: + dprintf("Index not found\n"); + +out: + free(mrec); + + return NULL; + +found: + dprintf("Index found\n"); + inode = new_ntfs_inode(fs); + err = index_inode_setup(fs, ie->data.dir.indexed_file, inode); + if (err) { + printf("Error in index_inode_setup()\n"); + free(inode); + goto out; + } + + free(mrec); + + return inode; + +index_err: + printf("Corrupt index. Aborting lookup...\n"); + goto out; +} + +/* Convert an UTF-16LE LFN to OEM LFN */ +static uint8_t ntfs_cvt_filename(char *filename, + const struct ntfs_idx_entry *ie) +{ + const uint16_t *entry_fn; + uint8_t entry_fn_len; + unsigned i; + + entry_fn = ie->key.file_name.file_name; + entry_fn_len = ie->key.file_name.file_name_len; + + for (i = 0; i < entry_fn_len; i++) + filename[i] = (char)entry_fn[i]; + + filename[i] = '\0'; + + return entry_fn_len; +} + +static int ntfs_next_extent(struct inode *inode, uint32_t lstart) +{ + struct fs_info *fs = inode->fs; + struct ntfs_sb_info *sbi = NTFS_SB(fs); + sector_t pstart = 0; + struct runlist *rlist; + struct runlist *ret; + const uint32_t sec_size = SECTOR_SIZE(fs); + const uint32_t sec_shift = SECTOR_SHIFT(fs); + + dprintf("in %s()\n", __func__); + + if (!NTFS_PVT(inode)->non_resident) { + pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >> + sec_shift; + inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift; + } else { + rlist = NTFS_PVT(inode)->data.non_resident.rlist; + + if (!lstart || lstart >= NTFS_PVT(inode)->here) { + if (runlist_is_empty(rlist)) + goto out; /* nothing to do ;-) */ + + ret = runlist_remove(&rlist); + + NTFS_PVT(inode)->here = + ((ret->run.len << sbi->clust_byte_shift) >> sec_shift); + + pstart = ret->run.lcn << sbi->clust_shift; + inode->next_extent.len = + ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >> + sec_shift; + + NTFS_PVT(inode)->data.non_resident.rlist = rlist; + + free(ret); + ret = NULL; + } + } + + inode->next_extent.pstart = pstart; + + return 0; + +out: + return -1; +} + +static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors, + bool *have_more) +{ + uint8_t non_resident; + uint32_t ret; + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + char *p; + + dprintf("in %s()\n", __func__); + + non_resident = NTFS_PVT(inode)->non_resident; + + ret = generic_getfssec(file, buf, sectors, have_more); + if (!ret) + return ret; + + if (!non_resident) { + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, + NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + p = (char *)((uint8_t *)attr + attr->data.resident.value_offset); + + /* p now points to the data offset, so let's copy it into buf */ + memcpy(buf, p, inode->size); + + ret = inode->size; + + free(mrec); + } + + return ret; + +out: + free(mrec); + + return 0; +} + +static inline bool is_filename_printable(const char *s) +{ + return s && (*s != '.' && *s != '$'); +} + +static int ntfs_readdir(struct file *file, struct dirent *dirent) +{ + struct fs_info *fs = file->fs; + struct inode *inode = file->inode; + struct ntfs_mft_record *mrec, *lmrec; + block_t blk; + uint64_t blk_offset; + const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs); + struct ntfs_attr_record *attr; + struct ntfs_idx_root *ir; + uint32_t count; + int len; + struct ntfs_idx_entry *ie = NULL; + uint8_t buf[BLOCK_SIZE(fs)]; + struct ntfs_idx_allocation *iblk; + int err; + uint8_t *stream; + uint8_t *attr_len; + struct mapping_chunk chunk; + uint32_t offset; + int64_t vcn; + int64_t lcn; + char filename[NTFS_MAX_FILE_NAME_LEN + 1]; + + dprintf("in %s()\n", __func__); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + lmrec = mrec; + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec); + if (!attr) { + printf("No attribute found.\n"); + goto out; + } + + ir = (struct ntfs_idx_root *)((uint8_t *)attr + + attr->data.resident.value_offset); + + if (!file->offset && readdir_state->in_idx_root) { + file->offset = (uint32_t)((uint8_t *)&ir->index + + ir->index.entries_offset); + } + +idx_root_next_entry: + if (readdir_state->in_idx_root) { + ie = (struct ntfs_idx_entry *)(uint8_t *)file->offset; + if (ie->flags & INDEX_ENTRY_END) { + file->offset = 0; + readdir_state->in_idx_root = false; + readdir_state->idx_blks_count = 1; + readdir_state->entries_count = 0; + readdir_state->last_vcn = 0; + goto descend_into_child_node; + } + + file->offset = (uint32_t)((uint8_t *)ie + ie->len); + len = ntfs_cvt_filename(filename, ie); + if (!is_filename_printable(filename)) + goto idx_root_next_entry; + + goto done; + } + +descend_into_child_node: + if (!(ie->flags & INDEX_ENTRY_NODE)) + goto out; + + attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec); + if (!attr) + goto out; + + if (!attr->non_resident) { + printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n"); + goto out; + } + + attr_len = (uint8_t *)attr + attr->len; + +next_run: + stream = mapping_chunk_init(attr, &chunk, &offset); + count = readdir_state->idx_blks_count; + while (count--) { + err = parse_data_run(stream, &offset, attr_len, &chunk); + if (err) { + printf("Error while parsing data runs.\n"); + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) + break; + if (chunk.flags & MAP_END) + goto out; + } + + if (chunk.flags & MAP_UNALLOCATED) { + readdir_state->idx_blks_count++; + goto next_run; + } + +next_vcn: + vcn = readdir_state->last_vcn; + if (vcn >= chunk.len) { + readdir_state->last_vcn = 0; + readdir_state->idx_blks_count++; + goto next_run; + } + + lcn = chunk.lcn; + blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >> + BLOCK_SHIFT(fs); + + blk_offset = 0; + err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL, + (uint64_t *)&lcn); + if (err) { + printf("Error while reading from cache.\n"); + goto not_found; + } + + ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf); + + iblk = (struct ntfs_idx_allocation *)&buf; + if (iblk->magic != NTFS_MAGIC_INDX) { + printf("Not a valid INDX record.\n"); + goto not_found; + } + +idx_block_next_entry: + ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index + + iblk->index.entries_offset); + count = readdir_state->entries_count; + for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) { + /* bounds checks */ + if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie + + sizeof(struct ntfs_idx_entry_header) > + (uint8_t *)&iblk->index + iblk->index.index_len || + (uint8_t *)ie + ie->len > + (uint8_t *)&iblk->index + iblk->index.index_len) + goto index_err; + + /* last entry cannot contain a key */ + if (ie->flags & INDEX_ENTRY_END) { + /* go to the next VCN */ + readdir_state->last_vcn += (blk_size / (1 << + NTFS_SB(fs)->clust_byte_shift)); + readdir_state->entries_count = 0; + goto next_vcn; + } + } + + readdir_state->entries_count++; + + /* Need to check if this entry has INDEX_ENTRY_END flag set. If + * so, then it won't contain a indexed_file file, so continue the + * lookup on the next VCN/LCN (if any). + */ + if (ie->flags & INDEX_ENTRY_END) + goto next_vcn; + + len = ntfs_cvt_filename(filename, ie); + if (!is_filename_printable(filename)) + goto idx_block_next_entry; + + goto done; + +out: + readdir_state->in_idx_root = true; + + free(mrec); + + return -1; + +done: + dirent->d_ino = ie->data.dir.indexed_file; + dirent->d_off = file->offset; + dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1; + + free(mrec); + + mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL); + if (!mrec) { + printf("No MFT record found.\n"); + goto out; + } + + dirent->d_type = get_inode_mode(mrec); + memcpy(dirent->d_name, filename, len + 1); + + free(mrec); + + return 0; + +not_found: + printf("Index not found\n"); + goto out; + +index_err: + printf("Corrupt index. Aborting lookup...\n"); + goto out; +} + +static inline struct inode *ntfs_iget(const char *dname, struct inode *parent) +{ + return ntfs_index_lookup(dname, parent); +} + +static struct inode *ntfs_iget_root(struct fs_info *fs) +{ + uint64_t start_blk; + struct ntfs_mft_record *mrec, *lmrec; + struct ntfs_attr_record *attr; + struct ntfs_vol_info *vol_info; + struct inode *inode; + int err; + + dprintf("in %s()\n", __func__); + + /* Fetch the $Volume MFT record */ + start_blk = 0; + mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk); + if (!mrec) { + printf("Could not fetch $Volume MFT record!\n"); + goto err_mrec; + } + + lmrec = mrec; + + /* Fetch the volume information attribute */ + attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec); + if (!attr) { + printf("Could not find volume info attribute!\n"); + goto err_attr; + } + + /* Note NTFS version and choose version-dependent functions */ + vol_info = (void *)((char *)attr + attr->data.resident.value_offset); + NTFS_SB(fs)->major_ver = vol_info->major_ver; + NTFS_SB(fs)->minor_ver = vol_info->minor_ver; + if (vol_info->major_ver == 3 && vol_info->minor_ver == 0) + NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0; + else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 && + mrec->mft_record_no == FILE_Volume) + NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1; + + /* Free MFT record */ + free(mrec); + mrec = NULL; + + inode = new_ntfs_inode(fs); + inode->fs = fs; + + err = index_inode_setup(fs, FILE_root, inode); + if (err) + goto err_setup; + + NTFS_PVT(inode)->start = NTFS_PVT(inode)->here; + + return inode; + +err_setup: + + free(inode); +err_attr: + + free(mrec); +err_mrec: + + return NULL; +} + +/* Initialize the filesystem metadata and return blk size in bits */ +static int ntfs_fs_init(struct fs_info *fs) +{ + int read_count; + struct ntfs_bpb ntfs; + struct ntfs_sb_info *sbi; + struct disk *disk = fs->fs_dev->disk; + uint8_t mft_record_shift; + + dprintf("in %s()\n", __func__); + + read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0); + if (!read_count) + return -1; + + if (!ntfs_check_sb_fields(&ntfs)) + return -1; + + SECTOR_SHIFT(fs) = disk->sector_shift; + + /* Note: ntfs.clust_per_mft_record can be a negative number. + * If negative, it represents a shift count, else it represents + * a multiplier for the cluster size. + */ + mft_record_shift = ntfs.clust_per_mft_record < 0 ? + -ntfs.clust_per_mft_record : + ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) + + ilog2(ntfs.clust_per_mft_record); + + SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs); + + sbi = malloc(sizeof *sbi); + if (!sbi) + malloc_error("ntfs_sb_info structure"); + + fs->fs_info = sbi; + + sbi->clust_shift = ilog2(ntfs.sec_per_clust); + sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs); + sbi->clust_mask = ntfs.sec_per_clust - 1; + sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs); + sbi->mft_record_size = 1 << mft_record_shift; + sbi->clust_per_idx_record = ntfs.clust_per_idx_record; + + BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift; + BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs); + + sbi->mft_lcn = ntfs.mft_lclust; + sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >> + BLOCK_SHIFT(fs); + /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */ + sbi->mft_size = mft_record_shift << sbi->clust_shift << 4; + + sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift; + if (sbi->clusters > 0xFFFFFFFFFFF4ULL) + sbi->clusters = 0xFFFFFFFFFFF4ULL; + + /* + * Assume NTFS version 3.0 to begin with. If we find that the + * volume is a different version later on, we will adjust at + * that time. + */ + sbi->major_ver = 3; + sbi->minor_ver = 0; + sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0; + + /* Initialize the cache */ + cache_init(fs->fs_dev, BLOCK_SHIFT(fs)); + + return BLOCK_SHIFT(fs); +} + +const struct fs_ops ntfs_fs_ops = { + .fs_name = "ntfs", + .fs_flags = FS_USEMEM | FS_THISIND, + .fs_init = ntfs_fs_init, + .searchdir = NULL, + .getfssec = ntfs_getfssec, + .close_file = generic_close_file, + .mangle_name = generic_mangle_name, + .load_config = generic_load_config, + .readdir = ntfs_readdir, + .iget_root = ntfs_iget_root, + .iget = ntfs_iget, + .next_extent = ntfs_next_extent, +}; diff --git a/core/fs/ntfs/ntfs.h b/core/fs/ntfs/ntfs.h new file mode 100644 index 00000000..721a78d7 --- /dev/null +++ b/core/fs/ntfs/ntfs.h @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com> + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "runlist.h" + +#ifndef _NTFS_H_ +#define _NTFS_H_ + +struct ntfs_bpb { + uint8_t jmp_boot[3]; + char oem_name[8]; + uint16_t sector_size; + uint8_t sec_per_clust; + uint16_t res_sectors; + uint8_t zero_0[3]; + uint16_t zero_1; + uint8_t media; + uint16_t zero_2; + uint16_t unused_0; + uint16_t unused_1; + uint32_t unused_2; + uint32_t zero_3; + uint32_t unused_3; + uint64_t total_sectors; + uint64_t mft_lclust; + uint64_t mft_mirr_lclust; + int8_t clust_per_mft_record; + uint8_t unused_4[3]; + uint8_t clust_per_idx_record; + uint8_t unused_5[3]; + uint64_t vol_serial; + uint32_t unused_6; + + uint8_t pad[428]; /* padding to a sector boundary (512 bytes) */ +} __attribute__((__packed__)); + +/* Function type for an NTFS-version-dependent MFT record lookup */ +struct ntfs_mft_record; +typedef struct ntfs_mft_record *f_mft_record_lookup(struct fs_info *, + uint32_t, block_t *); + +struct ntfs_sb_info { + block_t mft_blk; /* The first MFT record block */ + uint64_t mft_lcn; /* LCN of the first MFT record */ + unsigned mft_size; /* The MFT size in sectors */ + uint64_t mft_record_size; /* MFT record size in bytes */ + + uint8_t clust_per_idx_record; /* Clusters per Index Record */ + + unsigned long long clusters; /* Total number of clusters */ + + unsigned clust_shift; /* Based on sectors */ + unsigned clust_byte_shift; /* Based on bytes */ + unsigned clust_mask; + unsigned clust_size; + + uint8_t major_ver; /* Major version from $Volume */ + uint8_t minor_ver; /* Minor version from $Volume */ + + /* NTFS-version-dependent MFT record lookup function to use */ + f_mft_record_lookup *mft_record_lookup; +} __attribute__((__packed__)); + +/* The NTFS in-memory inode structure */ +struct ntfs_inode { + int64_t initialized_size; + int64_t allocated_size; + unsigned long mft_no; /* Number of the mft record / inode */ + uint16_t seq_no; /* Sequence number of the mft record */ + uint32_t type; /* Attribute type of this inode */ + uint8_t non_resident; + union { /* Non-resident $DATA attribute */ + struct { /* Used only if non_resident flags isn't set */ + uint32_t offset; /* Data offset */ + } resident; + struct { /* Used only if non_resident is set */ + struct runlist *rlist; + } non_resident; + } data; + uint32_t start_cluster; /* Starting cluster address */ + sector_t start; /* Starting sector */ + sector_t offset; /* Current sector offset */ + sector_t here; /* Sector corresponding to offset */ +}; + +/* This is structure is used to keep a state for ntfs_readdir() callers. + * As NTFS stores directory entries in a complex way, this is structure + * ends up saving a state required to find out where we must start from + * for the next ntfs_readdir() call. + */ +struct ntfs_readdir_state { + unsigned long mft_no; /* MFT record number */ + bool in_idx_root; /* It's true if we're still in the INDEX root */ + uint32_t idx_blks_count; /* Number of read INDX blocks */ + uint32_t entries_count; /* Number of read INDEX entries */ + int64_t last_vcn; /* Last VCN of the INDX block */ +}; + +enum { + MAP_UNSPEC, + MAP_START = 1 << 0, + MAP_END = 1 << 1, + MAP_ALLOCATED = 1 << 2, + MAP_UNALLOCATED = 1 << 3, + MAP_MASK = 0x0000000F, +}; + +struct mapping_chunk { + uint64_t vcn; + int64_t lcn; + uint64_t len; + uint32_t flags; +}; + +/* System defined attributes (32-bit) + * Each attribute type has a corresponding attribute name (in Unicode) + */ +enum { + NTFS_AT_UNUSED = 0x00, + NTFS_AT_STANDARD_INFORMATION = 0x10, + NTFS_AT_ATTR_LIST = 0x20, + NTFS_AT_FILENAME = 0x30, + NTFS_AT_OBJ_ID = 0x40, + NTFS_AT_SECURITY_DESCP = 0x50, + NTFS_AT_VOL_NAME = 0x60, + NTFS_AT_VOL_INFO = 0x70, + NTFS_AT_DATA = 0x80, + NTFS_AT_INDEX_ROOT = 0x90, + NTFS_AT_INDEX_ALLOCATION = 0xA0, + NTFS_AT_BITMAP = 0xB0, + NTFS_AT_REPARSE_POINT = 0xC0, + NTFS_AT_EA_INFO = 0xD0, + NTFS_AT_EA = 0xE0, + NTFS_AT_PROPERTY_SET = 0xF0, + NTFS_AT_LOGGED_UTIL_STREAM = 0x100, + NTFS_AT_FIRST_USER_DEFINED_ATTR = 0x1000, + NTFS_AT_END = 0xFFFFFFFF, +}; + +/* NTFS File Permissions (also called attributes in DOS terminology) */ +enum { + NTFS_FILE_ATTR_READONLY = 0x00000001, + NTFS_FILE_ATTR_HIDDEN = 0x00000002, + NTFS_FILE_ATTR_SYSTEM = 0x00000004, + NTFS_FILE_ATTR_DIRECTORY = 0x00000010, + NTFS_FILE_ATTR_ARCHIVE = 0x00000020, + NTFS_FILE_ATTR_DEVICE = 0x00000040, + NTFS_FILE_ATTR_NORMAL = 0x00000080, + NTFS_FILE_ATTR_TEMPORARY = 0x00000100, + NTFS_FILE_ATTR_SPARSE_FILE = 0x00000200, + NTFS_FILE_ATTR_REPARSE_POINT = 0x00000400, + NTFS_FILE_ATTR_COMPRESSED = 0x00000800, + NTFS_FILE_ATTR_OFFLINE = 0x00001000, + NTFS_FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000, + NTFS_FILE_ATTR_ENCRYPTED = 0x00004000, + NTFS_FILE_ATTR_VALID_FLAGS = 0x00007FB7, + NTFS_FILE_ATTR_VALID_SET_FLAGS = 0x000031A7, + NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT = 0x10000000, + NTFS_FILE_ATTR_DUP_VIEW_INDEX_PRESENT = 0x20000000, +}; + +/* + * Magic identifiers present at the beginning of all ntfs record containing + * records (like mft records for example). + */ +enum { + /* Found in $MFT/$DATA */ + NTFS_MAGIC_FILE = 0x454C4946, /* MFT entry */ + NTFS_MAGIC_INDX = 0x58444E49, /* Index buffer */ + NTFS_MAGIC_HOLE = 0x454C4F48, + + /* Found in $LogFile/$DATA */ + NTFS_MAGIC_RSTR = 0x52545352, + NTFS_MAGIC_RCRD = 0x44524352, + /* Found in $LogFile/$DATA (May be found in $MFT/$DATA, also ?) */ + NTFS_MAGIC_CHKDSK = 0x444B4843, + /* Found in all ntfs record containing records. */ + NTFS_MAGIC_BAAD = 0x44414142, + NTFS_MAGIC_EMPTY = 0xFFFFFFFF, /* Record is empty */ +}; + +struct ntfs_record { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; +} __attribute__((__packed__)) NTFS_RECORD; + +/* The $MFT metadata file types */ +enum ntfs_system_file { + FILE_MFT = 0, + FILE_MFTMirr = 1, + FILE_LogFile = 2, + FILE_Volume = 3, + FILE_AttrDef = 4, + FILE_root = 5, + FILE_Bitmap = 6, + FILE_Boot = 7, + FILE_BadClus = 8, + FILE_Secure = 9, + FILE_UpCase = 10, + FILE_Extend = 11, + FILE_reserved12 = 12, + FILE_reserved13 = 13, + FILE_reserved14 = 14, + FILE_reserved15 = 15, + FILE_reserved16 = 16, +}; + +enum { + MFT_RECORD_IN_USE = 0x0001, + MFT_RECORD_IS_DIRECTORY = 0x0002, +} __attribute__((__packed__)); + +struct ntfs_mft_record { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; + uint64_t lsn; + uint16_t seq_no; + uint16_t link_count; + uint16_t attrs_offset; + uint16_t flags; /* MFT record flags */ + uint32_t bytes_in_use; + uint32_t bytes_allocated; + uint64_t base_mft_record; + uint16_t next_attr_instance; + uint16_t reserved; + uint32_t mft_record_no; +} __attribute__((__packed__)); /* 48 bytes */ + +/* This is the version without the NTFS 3.1+ specific fields */ +struct ntfs_mft_record_old { + uint32_t magic; + uint16_t usa_ofs; + uint16_t usa_count; + uint64_t lsn; + uint16_t seq_no; + uint16_t link_count; + uint16_t attrs_offset; + uint16_t flags; /* MFT record flags */ + uint32_t bytes_in_use; + uint32_t bytes_allocated; + uint64_t base_mft_record; + uint16_t next_attr_instance; +} __attribute__((__packed__)); /* 42 bytes */ + +enum { + ATTR_DEF_INDEXABLE = 0x02, + ATTR_DEF_MULTIPLE = 0x04, + ATTR_DEF_NOT_ZERO = 0x08, + ATTR_DEF_INDEXED_UNIQUE = 0x10, + ATTR_DEF_NAMED_UNIQUE = 0x20, + ATTR_DEF_RESIDENT = 0x40, + ATTR_DEF_ALWAYS_LOG = 0x80, +}; + +struct ntfs_attr_record { + uint32_t type; /* Attr. type code */ + uint32_t len; + uint8_t non_resident; + uint8_t name_len; + uint16_t name_offset; + uint16_t flags; /* Attr. flags */ + uint16_t instance; + union { + struct { /* Resident attribute */ + uint32_t value_len; + uint16_t value_offset; + uint8_t flags; /* Flags of resident attributes */ + int8_t reserved; + } __attribute__((__packed__)) resident; + struct { /* Non-resident attributes */ + uint64_t lowest_vcn; + uint64_t highest_vcn; + uint16_t mapping_pairs_offset; + uint8_t compression_unit; + uint8_t reserved[5]; + int64_t allocated_size; + int64_t data_size; /* Byte size of the attribute value. + * Note: it can be larger than + * allocated_size if attribute value is + * compressed or sparse. + */ + int64_t initialized_size; + int64_t compressed_size; + } __attribute__((__packed__)) non_resident; + } __attribute__((__packed__)) data; +} __attribute__((__packed__)); + +/* Attribute: Attribute List (0x20) + * Note: it can be either resident or non-resident + */ +struct ntfs_attr_list_entry { + uint32_t type; + uint16_t length; + uint8_t name_length; + uint8_t name_offset; + uint64_t lowest_vcn; + uint64_t mft_ref; + uint16_t instance; + uint16_t name[0]; +} __attribute__((__packed__)); + +#define NTFS_MAX_FILE_NAME_LEN 255 + +/* Possible namespaces for filenames in ntfs (8-bit) */ +enum { + FILE_NAME_POSIX = 0x00, + FILE_NAME_WIN32 = 0x01, + FILE_NAME_DOS = 0x02, + FILE_NAME_WIN32_AND_DOS = 0x03, +} __attribute__((__packed__)); + +/* Attribute: Filename (0x30) + * Note: always resident + */ +struct ntfs_filename_attr { + uint64_t parent_directory; + int64_t ctime; + int64_t atime; + int64_t mtime; + int64_t rtime; + uint64_t allocated_size; + uint64_t data_size; + uint32_t file_attrs; + union { + struct { + uint16_t packed_ea_size; + uint16_t reserved; /* reserved for alignment */ + } __attribute__((__packed__)) ea; + struct { + uint32_t reparse_point_tag; + } __attribute__((__packed__)) rp; + } __attribute__((__packed__)) type; + uint8_t file_name_len; + uint8_t file_name_type; + uint16_t file_name[0]; /* File name in Unicode */ +} __attribute__((__packed__)); + +/* Attribute: Volume Name (0x60) + * Note: always resident + * Note: Present only in FILE_volume + */ +struct ntfs_vol_name { + uint16_t name[0]; /* The name of the volume in Unicode */ +} __attribute__((__packed__)); + +/* Attribute: Volume Information (0x70) + * Note: always resident + * Note: present only in FILE_Volume + */ +struct ntfs_vol_info { + uint64_t reserved; + uint8_t major_ver; + uint8_t minor_ver; + uint16_t flags; /* Volume flags */ +} __attribute__((__packed__)); + +/* Attribute: Data attribute (0x80) + * Note: can be either resident or non-resident + */ +struct ntfs_data_attr { + uint8_t data[0]; +} __attribute__((__packed__)); + +/* Index header flags (8-bit) */ +enum { + SMALL_INDEX = 0, + LARGE_INDEX = 1, + LEAF_NODE = 0, + INDEX_NODE = 1, + NODE_MASK = 1, +} __attribute__((__packed__)); + +/* Header for the indexes, describing the INDEX_ENTRY records, which + * follow the struct ntfs_idx_header. + */ +struct ntfs_idx_header { + uint32_t entries_offset; + uint32_t index_len; + uint32_t allocated_size; + uint8_t flags; /* Index header flags */ + uint8_t reserved[3]; /* Align to 8-byte boundary */ +} __attribute__((__packed__)); + +/* Attribute: Index Root (0x90) + * Note: always resident + */ +struct ntfs_idx_root { + uint32_t type; /* It is $FILE_NAME for directories, zero for view indexes. + * No other values allowed. + */ + uint32_t collation_rule; + uint32_t index_block_size; + uint8_t clust_per_index_block; + uint8_t reserved[3]; + struct ntfs_idx_header index; +} __attribute__((__packed__)); + +/* Attribute: Index allocation (0xA0) + * Note: always non-resident, of course! :-) + */ +struct ntfs_idx_allocation { + uint32_t magic; + uint16_t usa_ofs; /* Update Sequence Array offsets */ + uint16_t usa_count; /* Update Sequence Array number in bytes */ + int64_t lsn; + int64_t index_block_vcn; /* Virtual cluster number of the index block */ + struct ntfs_idx_header index; +} __attribute__((__packed__)); + +enum { + INDEX_ENTRY_NODE = 1, + INDEX_ENTRY_END = 2, + /* force enum bit width to 16-bit */ + INDEX_ENTRY_SPACE_FILTER = 0xFFFF, +} __attribute__((__packed__)); + +struct ntfs_idx_entry_header { + union { + struct { /* Only valid when INDEX_ENTRY_END is not set */ + uint64_t indexed_file; + } __attribute__((__packed__)) dir; + struct { /* Used for views/indexes to find the entry's data */ + uint16_t data_offset; + uint16_t data_len; + uint32_t reservedV; + } __attribute__((__packed__)) vi; + } __attribute__((__packed__)) data; + uint16_t len; + uint16_t key_len; + uint16_t flags; /* Index entry flags */ + uint16_t reserved; /* Align to 8-byte boundary */ +} __attribute__((__packed__)); + +struct ntfs_idx_entry { + union { + struct { /* Only valid when INDEX_ENTRY_END is not set */ + uint64_t indexed_file; + } __attribute__((__packed__)) dir; + struct { /* Used for views/indexes to find the entry's data */ + uint16_t data_offset; + uint16_t data_len; + uint32_t reservedV; + } __attribute__((__packed__)) vi; + } __attribute__((__packed__)) data; + uint16_t len; + uint16_t key_len; + uint16_t flags; /* Index entry flags */ + uint16_t reserved; /* Align to 8-byte boundary */ + union { + struct ntfs_filename_attr file_name; + //SII_INDEX_KEY sii; + //SDH_INDEX_KEY sdh; + //GUID object_id; + //REPARSE_INDEX_KEY reparse; + //SID sid; + uint32_t owner_id; + } __attribute__((__packed__)) key; +} __attribute__((__packed__)); + +static inline struct ntfs_sb_info *NTFS_SB(struct fs_info *fs) +{ + return fs->fs_info; +} + +#define NTFS_PVT(i) ((struct ntfs_inode *)((i)->pvt)) + +#endif /* _NTFS_H_ */ diff --git a/core/fs/ntfs/runlist.h b/core/fs/ntfs/runlist.h new file mode 100644 index 00000000..4938696b --- /dev/null +++ b/core/fs/ntfs/runlist.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _RUNLIST_H_ +#define _RUNLIST_H_ + +struct runlist_element { + uint64_t vcn; + int64_t lcn; + uint64_t len; +}; + +struct runlist { + struct runlist_element run; + struct runlist *next; +}; + +static struct runlist *tail; + +static inline bool runlist_is_empty(struct runlist *rlist) +{ + return !rlist; +} + +static inline struct runlist *runlist_alloc(void) +{ + struct runlist *rlist; + + rlist = malloc(sizeof *rlist); + if (!rlist) + malloc_error("runlist structure"); + + rlist->next = NULL; + + return rlist; +} + +static inline void runlist_append(struct runlist **rlist, + struct runlist_element *elem) +{ + struct runlist *n = runlist_alloc(); + + n->run = *elem; + + if (runlist_is_empty(*rlist)) { + *rlist = n; + tail = n; + } else { + tail->next = n; + tail = n; + } +} + +static inline struct runlist *runlist_remove(struct runlist **rlist) +{ + struct runlist *ret; + + if (runlist_is_empty(*rlist)) + return NULL; + + ret = *rlist; + *rlist = ret->next; + + return ret; +} + +#endif /* _RUNLIST_H_ */ diff --git a/core/ldlinux.asm b/core/ldlinux.asm index f62f55b2..a2f859d0 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -37,6 +37,8 @@ ROOT_FS_OPS: dd vfat_fs_ops extern ext2_fs_ops dd ext2_fs_ops + extern ntfs_fs_ops + dd ntfs_fs_ops extern btrfs_fs_ops dd btrfs_fs_ops dd 0 diff --git a/doc/chain.txt b/doc/chain.txt new file mode 100644 index 00000000..6dd0632d --- /dev/null +++ b/doc/chain.txt @@ -0,0 +1,327 @@ + chain.c32 documentation + +Although syslinux is capable of (very simple) native chainloading (through .bss +and .bs options - see doc/syslinux.txt), it also features a very roboust and +rich com32 module designed for such purpose. + +Chain module can perform few basic tasks: + +- load and jump to a sector +- load and jump to a file (also loading a sector for other purposes) +- prepare handover data to use by a file / boot sector +- fix different options in a file / sector / partition entries +- perform a "service-only" run + +It can chainload data from both GPT and DOS partitions, as well as boot the +first sector from a raw disk. + +In more details, the rough overview of code is as follows: + +1. Parse arguments. +2. Find drive and/or partition to boot from. +3. Perform partition-level patching - for example hiding, unhiding, fixing chs values, etc. +4. Load a file to boot from. +5. Load a sector to boot from, if it doesn't conflict with #5. +6. Prepare handover area, if it doesn't conflict with #5 & #6. +7. Prepare registers. +8. Patch loaded file if necessary. +9. Patch loaded sector if necessary. +10. Chainload. + +In most basic form, syslinux loads specified boot sector (or mbr, if not +specified) at 0:0x7c00, prepares handover area as a standard mbr would do, and +jumps to 0:0x7c00. + +A "service-only" run is possible when either: + +- 'break' is in effect + +or + +- 'nofile' and 'nomaps' (or 'nosect') are in effect + +This is useful for invocations such as: + +chain.c32 hdN M setbpb save break +chain.c32 hdN fixchs break +chain.c32 hdN unhideall break + +Please see respective options for more details. + + +Module invocation: + +chain [drive/partition] [options] + + DRIVE / PARTITION SPECIFICATION + +Drive can be specified as 'hd#', 'fd#', 'boot', 'mbr', or 'guid'. + +- 'mbr' will select a drive by a signature. +- 'guid' will select a drive by a guid +- 'boot' is the drive syslinux was booted from. This is the default value, if + nothing else is specified. +- 'hd#' and 'fd#' are standard ways to specify drive number as seen by bios, + starting from 0. + +Option 'guid' is shared with partition selection (see below). If you happened +to have non-unique guids, they are searched in disk0, partitions of disk0, +disk1 ... order. + +The priority of those options are the same as in the above list. + +If you specify the same value more than once, the last value will be used. + +'mbr' and 'guid' take extra parameter - you should use ':' or '=' as a +delimiter. + + +Partition can be specified as '#', 'guid', 'label' or 'fs'. + +- 'guid' option will select a partition by a guid (not a type guid !) +- 'label' will select a partition by a label (searching is done in + disk order) +- 'fs' will select a partition from which syslinux was executed +- '#' is the standard method. Partitions 1-4 are primary, 5+ logical, 0 = boot + MBR (default). + +The priority of those options are the same as in the above list. + +If you use a number to select a partition it should be specified after a drive +using space or comma as delimiters (after 'hd#', 'fd#', 'mbr', 'guid' or 'boot'). + + OPTIONS + file=<file> + *nofile + +It's often convenient to load a file directly and transfer control to it, +instead of the sector from the disk. Note, that the <file> must reside on +syslinux partition. + +If you choose this option without specifying any addresses explicitly (see +options 'sect=' and 'seg='), the file will cause sector to not be loaded at all +(as their memory placement would overlap). + + seg=<segment>:<offset>:<ip> + *seg=0:0x7c00:0x7c00 + +This triplet lets you alter the addresses a file will use. It's loaded at +<segment:offset>, the entry point is at <segment:ip>. When you chainload some +other bootloader or kernel, it's almost always mandatory. + +The defaults, if option is not specified, are 0:0x7c00:0x7c00 +If any of the fields are ommited (e.g. 0x2000::), they default to 0. + + sect=<segment>:<offset>:<ip> + nosect + *sect=0:0x7c00:0x7c00 + nosect sets: nomaps + +This triplet lets you alter the addresses a sector will use. It's loaded at +<segment:offset>, the entry point is at <segment:ip>. This option is mostly +used in tandem with 'file=' and 'seg=' options, as some loaders/kernels will +expect relocated sector at some particular address (e.g. DRKM). + +'nosect' will cause sector to not be loaded at all. In plenty cases, when a file +is being chainloaded, sector is not necessary. + +The defaults if option is not specified, are 0:0x7c00:0x7c00. +If some of the fields are ommited (e.g. 0x2000::), they default to 0. + + *maps + nomaps + +In some cases, it's useful to fix BPB values in NTFS/FATxx bootsectors and +evntually write them back, but otherwise boot sector itself is not necessary to +continue booting. 'nomaps' allows that - a sector will be loaded, but won't be +mmapped into real memory. Any overlap tests (vs. handover or file areas) are +not performed, being meaningless in such case. + + setbpb + *nosetbpb + +Microsoft side of the world is paritculary sensitive to certain BPB values. +Depending on the system and chainloading method (sector or file), some or all +of those fields must match reality - and after e.g. drive clonning or +when using usb stick in different computers - that is often not the case. + +The "reality" means: + +"hidden sectors" - valid offset of the partition from the beginning of the disk +"geometry" - valid disk geometry as reported by BIOS +"drive" - valid drive number + +This option will automatically determine the type of BPB and fix what is possible +to fix, relatively to detected BPB. If it's impossible to detect BPB, function +will do nothing. + + filebpb + *nofilebpb + +Chainloaded file can simply be an image of a sector. In such case, it could be +useful to also fix its BPB values. + + save + *nosave + +Fixing BPB values only in memory might not be enough. This option allows +writing of the corrected sector. You will probably want to use this option +together with 'setbpb'. + +- this option never applies to a loaded file +- chain module will not save anything to disk by default (besides options such + as hide or fixchs - so options related directly to partition entries) +- writing is only performed, if the values actually changed + + *hand + nohand + +By default, a handover area is always prepared if possible - meaning it doesn't +overlap with other areas. It's often not necessary though - usually, a +chainloaded file or kernel don't care about it anymore, so a user can disable +it explicitly with this option. + + hptr + *nohptr + +In case when both file and sector are loaded, ds:si and ds:bp will point to +sector address before the chainloading. This option lets user force those +registers to point to handover area. This is useful when both the file and the +sector are actually a sector's image and the sector is mmapped. + + swap + *noswap + +This option will install a tiny stub code used to swap drive numbers, if the +drive we use during chainloading is not fd0 or hd0. + + hide[all] + unhide[all] + *nohide + +In certain situations it's useful to hide partitions - for example to make sure +DOS gets C:. 'hide' will hide hidable primary partitions, except the one we're +booting from. Similary, 'hideall' will hide all hidable partitions, except the +one we're booting from. Hiding is performed only on the selected drive. Options +starting with 'un' will simply unhide every partition (primary ones or all). +Writing is only performed, if the os type values actually changed. + + fixchs + *nofixchs + +If you want to make a drive you're booting from totally compatible with current +BIOS, you can use this to fix all partitions' CHS numbers. Good to silence e.g. +FreeDOS complainig about 'logical CHS differs from physical' of sfdisk about +'found (...) expected (...). Functionally seems to be mostly cosmetic, as +Microsoft world - in cases it cares about geometry - generally sticks to values +written in bootsectors. And the rest of the world generally doesn't care about +them at all. Writing is only performed, if the values actually got changed. + + keepexe + *nokeepexe + +If you're booting over a network using pxelinux - this lets you keep UNDI +stacks in memory (pxelinux only). + + warn + *nowarn + +This option will wait for a keypress right before continuing the chainloading. +Useful to see warnings emited by the chain module. + + *nobreak + break + break sets: nofile nomaps nohand + +It is possible to trigger a "service-only" run - The chain module will do +everything requested as usual, but it will not perform the actual chainloading. +'break' option disables handover, file loading and sector mapping, as these +are pointless in such scenario (although file might be reenabled in some future +version, if writing to actual files becomes possible). Mainly useful for +options 'fixchs', '[un]hide[all]' and setbpb. + + isolinux=<file> + sets: file=<file> nohand nosect isolinux + +Chainload another version/build of the ISOLINUX bootloader and patch the loader +with appropriate parameters in memory. This avoids the need for the +-eltorito-alt-boot parameter of mkisofs, when you want more than one ISOLINUX +per CD/DVD. + + ntldr=<file> + sets: file=<file> seg=0x2000 setbpb nohand + +Prepares to load ntldr directly. You might want to add 'save' option to store +corrected BPB values. + + cmldr=<file> + sets: file=<file> seg=0x2000 setbpb nohand cmldr + +Prepares to load recovery console directly. In-memory copy of bootsector is +patched with "cmdcons\0". Remarks the same as in 'ntldr='. + + reactos=<file> + sets: file=<file> seg=0:0x8000:0x8100 setbpb nohand + +Prepares to load ReactOS's freeldr directly. You might want to add 'save' +option to store corrected BPB values. + + freedos=<file> + sets: file=<file> seg=0x60 sect=0x1FE0 setbpb nohand + +Prepares to load freedos kernel directly. You will likely want to add 'save' +option, as those kernels seem to require proper geometry written back to disk. +Sector address is chosen based on where freedos' bootsectors relocate themselves, +although it seems the kernel doesn't rely on it. + +You might also want to employ 'hide' option, if you have problems with properly +assigned C: drive. + + pcdos=<file> + msdos=<file> + sets: file=<file> seg=0x70 sect=0x8000 setbpb nohand + +Similary to 'freedos=', This prepares to load MSDOS 2.00 - 6.xx or derivatives. +Sector address is chosen arbitrarily. Otherwise comments as above. + + msdos7=<file> + sets: file=<file> seg=0x70::0x200 sect=0x8000 setbpb nohand + +Only for MSDOS 7+ versions (98se ~ 7.xx, Me ~ 8.xx). Comments as above. +TODO/TEST + + drmk=<file> + sets: file=<file> seg=0x70 sect=0x2000:0:0 setbpb nohand + +This is used for loading of *only* Dell's DOS derivatives. It does require boot +sector at 0x2000 and overall valid BPB values. As in other DOS-ish cases, +likely candidates for use are 'save' and 'hide'. + + grub=<file> [grubcfg=<config>] + sets: file=<file> seg=0x800::0x200 nohand nosect grub + +Chainloads grub legacy's stage2, performing additional corrections on the file +in memory. Additionally, alternate config file can be specified through +'grubcfg=' option + + grldr=<file> + sets: file=<file> nohand nosect grldr + +Chainloads GRUB4DOS grldr, performing additional corrections on the file +in memory. + + bss=<file> + sets: file=<file> nomaps setbpb bss + +This emulates syslinux's native BSS option. This loads both the file and the +sector, adjusts BPB values in the loaded sector, then copies all possible BPB +fields to the loaded file. Everything is made with reference to the selected +disk/partition. + + bs=<file> + sets: file=<file> nosect filebpb + +This emulates syslinux's native BS option. This loads the file and if possible +- adjusts its BPB values. Everything is made with reference to the selected +disk/partition. + diff --git a/doc/gpt.txt b/doc/gpt.txt index 09099320..2ef387a9 100644 --- a/doc/gpt.txt +++ b/doc/gpt.txt @@ -51,7 +51,7 @@ form: 4 1 0xED (partition type: synthetic) 5 3 CHS of partition end 8 4 Partition start LBA - 12 4 Partition end LBA + 12 4 Partition length in sectors 16 4 Length of the GPT entry 20 varies GPT partition entry diff --git a/doc/logo/LICENSE b/doc/logo/LICENSE new file mode 100644 index 00000000..e753087f --- /dev/null +++ b/doc/logo/LICENSE @@ -0,0 +1,5 @@ +The Syslinux logo is licensed under the Creative Commons +Attribution-ShareAlike 3.0 Unported License. To view a copy of this +license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send +a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain +View, California, 94041, USA. diff --git a/dos/Makefile b/dos/Makefile index 5e5fc63f..f9420084 100644 --- a/dos/Makefile +++ b/dos/Makefile @@ -27,7 +27,7 @@ INCLUDES = -include code16.h -nostdinc -iwithprefix include \ -I. -I.. -I../libfat -I ../libinstaller -I ../libinstaller/getopt SRCS = syslinux.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/dos/syslinux.c b/dos/syslinux.c index b5fdfc52..fa4bf387 100644 --- a/dos/syslinux.c +++ b/dos/syslinux.c @@ -31,6 +31,7 @@ #include "sysexits.h" #include "syslxopt.h" #include "syslxint.h" +#include "syslxfs.h" char *program = "syslinux.com"; /* Name of program */ uint16_t dos_version; @@ -638,7 +639,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) { unlock_device(0); puts(errmsg); putchar('\n'); @@ -749,7 +750,7 @@ int main(int argc, char *argv[]) read_device(dev_fd, sectbuf, 1, 0); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, VFAT); /* Write new boot sector */ if (opt.bootsecfile) { diff --git a/dosutil/Makefile b/dosutil/Makefile index 5746e86b..6bce6248 100644 --- a/dosutil/Makefile +++ b/dosutil/Makefile @@ -11,7 +11,7 @@ WCLOPT = -6 -osx -mt -bt=DOS -l=COM UPX = upx NASM = nasm -NASMOPT = -O9999 +NASMOPT = -Ox WCTARGETS = mdiskchk.com NSTARGETS = eltorito.sys copybs.com diff --git a/extlinux/Makefile b/extlinux/Makefile index 12213d88..865c7a64 100644 --- a/extlinux/Makefile +++ b/extlinux/Makefile @@ -11,7 +11,7 @@ ## ----------------------------------------------------------------------- ## -## Linux vfat, ext2/ext3/ext4 and btrfs installer +## Linux vfat, ntfs, ext2/ext3/ext4 and btrfs installer ## topdir = .. diff --git a/extlinux/main.c b/extlinux/main.c index e574051b..5da89e2d 100755..100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Copyright 2009-2012 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 @@ -14,7 +14,7 @@ /* * extlinux.c * - * Install the syslinux boot block on an fat, ext2/3/4 and btrfs filesystem + * Install the syslinux boot block on an fat, ntfs, ext2/3/4 and btrfs filesystem */ #define _GNU_SOURCE /* Enable everything */ @@ -45,9 +45,11 @@ #include "btrfs.h" #include "fat.h" +#include "ntfs.h" #include "../version.h" #include "syslxint.h" #include "syslxcom.h" /* common functions shared with extlinux and syslinux */ +#include "syslxfs.h" #include "setadv.h" #include "syslxopt.h" /* unified options */ @@ -214,7 +216,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd) sector_t *sectp; uint64_t totalbytes, totalsectors; int nsect; - struct boot_sector *sbs; + struct fat_boot_sector *sbs; char *dirpath, *subpath, *xdirpath; int rv; @@ -271,7 +273,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd) early bootstrap share code with the FAT version. */ dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors); - sbs = (struct boot_sector *)syslinux_bootsect; + sbs = (struct fat_boot_sector *)syslinux_bootsect; totalsectors = totalbytes >> SECTOR_SHIFT; if (totalsectors >= 65536) { @@ -292,7 +294,7 @@ 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) { + if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) { if (sectmap(fd, sectp, nsect)) { perror("bmap"); exit(1); @@ -323,7 +325,8 @@ int install_bootblock(int fd, const char *device) { struct ext2_super_block sb; struct btrfs_super_block sb2; - struct boot_sector sb3; + struct fat_boot_sector sb3; + struct ntfs_boot_sector sb4; bool ok = false; if (fs_type == EXT2) { @@ -348,20 +351,39 @@ int install_bootblock(int fd, const char *device) } if (fat_check_sb_fields(&sb3)) ok = true; + } else if (fs_type == NTFS) { + if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) { + perror("reading ntfs superblock"); + return 1; + } + + if (ntfs_check_sb_fields(&sb4)) + ok = true; } if (!ok) { - fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n", + fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n", device); return 1; } if (fs_type == VFAT) { - struct boot_sector *sbs = (struct boot_sector *)syslinux_bootsect; - if (xpwrite(fd, &sbs->bsHead, bsHeadLen, 0) != bsHeadLen || - xpwrite(fd, &sbs->bsCode, bsCodeLen, - offsetof(struct boot_sector, bsCode)) != bsCodeLen) { + struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect; + if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen || + xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen, + offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) { perror("writing fat bootblock"); return 1; } + } else if (fs_type == NTFS) { + struct ntfs_boot_sector *sbs = + (struct ntfs_boot_sector *)syslinux_bootsect; + if (xpwrite(fd, &sbs->NTFS_bsHead, + NTFS_bsHeadLen, 0) != NTFS_bsHeadLen || + xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen, + offsetof(struct ntfs_boot_sector, + NTFS_bsCode)) != NTFS_bsCodeLen) { + perror("writing ntfs bootblock"); + return 1; + } } else { if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0) != syslinux_bootsect_len) { @@ -754,7 +776,7 @@ static char * get_default_subvol(char * rootdir, char * subvol) int install_file(const char *path, int devfd, struct stat *rst) { - if (fs_type == EXT2 || fs_type == VFAT) + 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); @@ -803,37 +825,46 @@ static const char *find_device(const char *mtab_file, dev_t dev) /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */ switch (fs_type) { case BTRFS: - if (!strcmp(mnt->mnt_type, "btrfs") && - !stat(mnt->mnt_dir, &dst) && - dst.st_dev == dev) { - if (!subvol[0]) { - get_default_subvol(mnt->mnt_dir, subvol); - } - done = true; - } - break; + if (!strcmp(mnt->mnt_type, "btrfs") && + !stat(mnt->mnt_dir, &dst) && + dst.st_dev == dev) { + if (!subvol[0]) + get_default_subvol(mnt->mnt_dir, subvol); + done = true; + } + break; case EXT2: - if ((!strcmp(mnt->mnt_type, "ext2") || - !strcmp(mnt->mnt_type, "ext3") || - !strcmp(mnt->mnt_type, "ext4")) && - !stat(mnt->mnt_fsname, &dst) && - dst.st_rdev == dev) { - done = true; - break; - } + if ((!strcmp(mnt->mnt_type, "ext2") || + !strcmp(mnt->mnt_type, "ext3") || + !strcmp(mnt->mnt_type, "ext4")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } case VFAT: - if ((!strcmp(mnt->mnt_type, "vfat")) && - !stat(mnt->mnt_fsname, &dst) && - dst.st_rdev == dev) { - done = true; - break; - } + if ((!strcmp(mnt->mnt_type, "vfat")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } + case NTFS: + if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ || + !strcmp(mnt->mnt_type, "ntfs")) && + !stat(mnt->mnt_fsname, &dst) && + dst.st_rdev == dev) { + done = true; + break; + } + + break; case NONE: break; } if (done) { - devname = strdup(mnt->mnt_fsname); - break; + devname = strdup(mnt->mnt_fsname); + break; } } endmntent(mtab); @@ -842,6 +873,54 @@ static const char *find_device(const char *mtab_file, dev_t dev) } #endif +/* + * On newer Linux kernels we can use sysfs to get a backwards mapping + * from device names to standard filenames + */ +static const char *find_device_sysfs(dev_t dev) +{ + char sysname[64]; + char linkname[PATH_MAX]; + ssize_t llen; + char *p, *q; + char *buf = NULL; + struct stat st; + + snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u", + major(dev), minor(dev)); + + llen = readlink(sysname, linkname, sizeof linkname); + if (llen < 0 || llen >= sizeof linkname) + goto err; + + linkname[llen] = '\0'; + + p = strrchr(linkname, '/'); + p = p ? p+1 : linkname; /* Leave basename */ + + buf = q = malloc(strlen(p) + 6); + if (!buf) + goto err; + + memcpy(q, "/dev/", 5); + q += 5; + + while (*p) { + *q++ = (*p == '!') ? '/' : *p; + p++; + } + + *q = '\0'; + + if (!stat(buf, &st) && st.st_dev == dev) + return buf; /* Found it! */ + +err: + if (buf) + free(buf); + return NULL; +} + static const char *get_devname(const char *path) { const char *devname = NULL; @@ -856,21 +935,26 @@ static const char *get_devname(const char *path) fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno)); return devname; } + #ifdef __KLIBC__ - /* klibc doesn't have getmntent and friends; instead, just create - a new device with the appropriate device type */ - snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u", - major(st.st_dev), minor(st.st_dev)); + devname = find_device_sysfs(st.st_dev); - if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) { - fprintf(stderr, "%s: cannot create device %s\n", program, devname); - return devname; + if (!devname) { + /* klibc doesn't have getmntent and friends; instead, just create + a new device with the appropriate device type */ + snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u", + major(st.st_dev), minor(st.st_dev)); + + if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) { + fprintf(stderr, "%s: cannot create device %s\n", program, devname); + return devname; + } + + atexit(device_cleanup); /* unlink the device node on exit */ + devname = devname_buf; } - atexit(device_cleanup); /* unlink the device node on exit */ - devname = devname_buf; - #else devname = find_device("/proc/mounts", st.st_dev); @@ -879,11 +963,14 @@ static const char *get_devname(const char *path) devname = find_device("/etc/mtab", st.st_dev); } if (!devname) { + devname = find_device_sysfs(st.st_dev); + fprintf(stderr, "%s: cannot find device for path %s\n", program, path); return devname; } fprintf(stderr, "%s is device %s\n", path, devname); + #endif return devname; } @@ -910,9 +997,12 @@ static int open_device(const char *path, struct stat *st, const char **_devname) fs_type = BTRFS; else if (sfs.f_type == MSDOS_SUPER_MAGIC) fs_type = VFAT; + else if (sfs.f_type == NTFS_SB_MAGIC || + sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */) + fs_type = NTFS; if (!fs_type) { - fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n", + fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n", program, path); return -1; } @@ -959,7 +1049,7 @@ static int ext_read_adv(const char *path, int devfd, const char **namep) if (err == 2) /* ldlinux.sys does not exist */ err = read_adv(path, name = "extlinux.sys"); if (namep) - *namep = name; + *namep = name; return err; } } diff --git a/extlinux/ntfs.h b/extlinux/ntfs.h new file mode 100644 index 00000000..d907d452 --- /dev/null +++ b/extlinux/ntfs.h @@ -0,0 +1,19 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef _NTFS_H_ +#define _NTFS_H_ + +#define NTFS_SB_MAGIC 0x5346544E +#define FUSE_SUPER_MAGIC 0x65735546 + +#endif /* _NTFS_H_ */ diff --git a/libinstaller/ext2fs/ext2_fs.h b/libinstaller/ext2fs/ext2_fs.h new file mode 100644 index 00000000..e52c9e1f --- /dev/null +++ b/libinstaller/ext2fs/ext2_fs.h @@ -0,0 +1,856 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _EXT2FS_EXT2_FS_H +#define _EXT2FS_EXT2_FS_H + +#include <linux/types.h> + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT2_JOURNAL_INO 8 /* Journal inode */ +#define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ +#define EXT4_REPLICA_INO 10 /* Used by non-upstream feature */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 65000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ +#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ +#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) +#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) +#ifdef __KERNEL__ +#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) +#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) +#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) +#else +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) +#endif +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) + +/* + * Macro-instructions used to manage allocation clusters + */ +#define EXT2_MIN_CLUSTER_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_MAX_CLUSTER_LOG_SIZE 29 /* 512MB */ +#define EXT2_MIN_CLUSTER_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_CLUSTER_SIZE (1 << EXT2_MAX_CLUSTER_LOG_SIZE) +#define EXT2_CLUSTER_SIZE(s) (EXT2_MIN_BLOCK_SIZE << \ + (s)->s_log_cluster_size) +#define EXT2_CLUSTER_SIZE_BITS(s) ((s)->s_log_cluster_size + 10) + +/* + * Macro-instructions used to manage fragments + * + * Note: for backwards compatibility only, for the dump program. + * Ext2/3/4 will never support fragments.... + */ +#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE +#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE +#define EXT2_FRAG_SIZE(s) EXT2_BLOCK_SIZE(s) +#define EXT2_FRAGS_PER_BLOCK(s) 1 + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; + __u32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __u32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */ + __u16 bg_itable_unused; /* Unused inodes count */ + __u16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __u32 bg_inode_table_hi; /* Inodes table block MSB */ + __u16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __u16 bg_used_dirs_count_hi; /* Directories count MSB */ + __u16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __u32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */ + __u16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */ + __u16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */ + __u32 bg_reserved; +}; + +#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ +#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ +#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ + +/* + * Data structures used by the directory indexing feature + * + * Note: all of the multibyte integer fields are little endian. + */ + +/* + * Note: dx_root_info is laid out so that if it should somehow get + * overlaid by a dirent the two low bits of the hash version will be + * zero. Therefore, the hash version mod 4 should never be 0. + * Sincerely, the paranoia department. + */ +struct ext2_dx_root_info { + __u32 reserved_zero; + __u8 hash_version; /* 0 now, 1 at release */ + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; +}; + +#define EXT2_HASH_LEGACY 0 +#define EXT2_HASH_HALF_MD4 1 +#define EXT2_HASH_TEA 2 +#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ +#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ +#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ + +#define EXT2_HASH_FLAG_INCOMPAT 0x1 + +struct ext2_dx_entry { + __u32 hash; + __u32 block; +}; + +struct ext2_dx_countlimit { + __u16 limit; + __u16 count; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_MIN_DESC_SIZE 32 +#define EXT2_MIN_DESC_SIZE_64BIT 64 +#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE +#define EXT2_DESC_SIZE(s) \ + ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \ + (s)->s_desc_size : EXT2_MIN_DESC_SIZE) + +#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) +#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) +#define EXT2_CLUSTERS_PER_GROUP(s) (EXT2_SB(s)->s_clusters_per_group) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) +/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ +#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((((unsigned) 1 << 16) - 8) * \ + (EXT2_CLUSTER_SIZE(s) / \ + EXT2_BLOCK_SIZE(s))) +#define EXT2_MAX_CLUSTERS_PER_GROUP(s) (((unsigned) 1 << 16) - 8) +#define EXT2_MAX_INODES_PER_GROUP(s) (((unsigned) 1 << 16) - \ + EXT2_INODES_PER_BLOCK(s)) +#ifdef __KERNEL__ +#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) +#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) +#else +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 +#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +/* EXT4_EOFBLOCKS_FL 0x00400000 was here */ +#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ +#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ +#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ + +/* + * ioctl commands + */ + +/* Used for online resize */ +struct ext2_new_group_input { + __u32 group; /* Group number for this data */ + __u32 block_bitmap; /* Absolute block number of block bitmap */ + __u32 inode_bitmap; /* Absolute block number of inode bitmap */ + __u32 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; /* Number of reserved GDT blocks in group */ +}; + +struct ext4_new_group_input { + __u32 group; /* Group number for this data */ + __u64 block_bitmap; /* Absolute block number of block bitmap */ + __u64 inode_bitmap; /* Absolute block number of inode bitmap */ + __u64 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +#ifdef __GNU__ /* Needed for the Hurd */ +#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) +#endif + +#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) +#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) +#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) +#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) +#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) +#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */ + __u16 l_i_reserved; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ +}; + +/* + * Permanent part of an large inode on the disk + */ +struct ext2_inode_large { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Inode Change time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_version; /* was l_i_reserved1 */ + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u16 l_i_blocks_hi; + __u16 l_i_file_acl_high; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */ + __u16 l_i_reserved; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + } osd2; /* OS dependent 2 */ + __u16 i_extra_isize; + __u16 i_checksum_hi; /* crc32c(uuid+inum+inode) */ + __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ + __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __u32 i_crtime; /* File creation time */ + __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ + __u32 i_version_hi; /* high 32 bits for 64-bit version */ +}; + +#define i_dir_acl i_size_high + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#else +#if defined(__GNU__) + +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author + +#endif /* __GNU__ */ +#endif /* defined(__KERNEL__) || defined(__linux__) */ + +#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) +#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) +#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x)) +#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x)) + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ +#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ + +/* + * Misc. filesystem flags + */ +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ +#define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ +#define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ +#define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +#if (__GNUC__ >= 4) +#define ext4_offsetof(TYPE,MEMBER) __builtin_offsetof(TYPE,MEMBER) +#else +#define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __u32 s_log_cluster_size; /* Allocation cluster size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_clusters_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; /* Default type of journal backup */ + __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock group */ + __u32 s_mkfs_time; /* When the filesystem was created */ + __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ + __u32 s_blocks_count_hi; /* Blocks count high 32bits */ + __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ + __u32 s_free_blocks_hi; /* Free blocks count */ + __u16 s_min_extra_isize; /* All inodes have at least # bytes */ + __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __u32 s_flags; /* Miscellaneous flags */ + __u16 s_raid_stride; /* RAID stride */ + __u16 s_mmp_update_interval; /* # seconds to wait in MMP checking */ + __u64 s_mmp_block; /* Block for multi-mount protection */ + __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_reserved_char_pad; + __u16 s_reserved_pad; /* Padding to next 32bits */ + __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __u32 s_snapshot_inum; /* Inode number of active snapshot */ + __u32 s_snapshot_id; /* sequential ID of active snapshot */ + __u64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __u32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ +#define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count) + __u32 s_error_count; /* number of fs errors */ + __u32 s_first_error_time; /* first time an error happened */ + __u32 s_first_error_ino; /* inode involved in first error */ + __u64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __u32 s_first_error_line; /* line number where error happened */ + __u32 s_last_error_time; /* most recent time of an error */ + __u32 s_last_error_ino; /* inode involved in last error */ + __u32 s_last_error_line; /* line number where error happened */ + __u64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __u32 s_usr_quota_inum; /* inode number of user quota file */ + __u32 s_grp_quota_inum; /* inode number of group quota file */ + __u32 s_overhead_blocks; /* overhead blocks/clusters in fs */ + __u32 s_reserved[108]; /* Padding to the end of the block */ + __u32 s_checksum; /* crc32c(superblock) */ +}; + +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OBSO_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Journal inode backup types + */ +#define EXT3_JNL_BACKUP_BLOCKS 1 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 +/* #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 not used, legacy */ +#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP 0x0100 + + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 +#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_MMP) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Default mount options + */ +#define EXT2_DEFM_DEBUG 0x0001 +#define EXT2_DEFM_BSDGROUPS 0x0002 +#define EXT2_DEFM_XATTR_USER 0x0004 +#define EXT2_DEFM_ACL 0x0008 +#define EXT2_DEFM_UID16 0x0010 +#define EXT3_DEFM_JMODE 0x0060 +#define EXT3_DEFM_JMODE_DATA 0x0020 +#define EXT3_DEFM_JMODE_ORDERED 0x0040 +#define EXT3_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + +/* + * This structure is used for multiple mount protection. It is written + * into the block number saved in the s_mmp_block field in the superblock. + * Programs that check MMP should assume that if SEQ_FSCK (or any unknown + * code above SEQ_MAX) is present then it is NOT safe to use the filesystem, + * regardless of how old the timestamp is. + * + * The timestamp in the MMP structure will be updated by e2fsck at some + * arbitary intervals (start of passes, after every few groups of inodes + * in pass1 and pass1b). There is no guarantee that e2fsck is updating + * the MMP block in a timely manner, and the updates it does are purely + * for the convenience of the sysadmin and not for automatic validation. + * + * Note: Only the mmp_seq value is used to determine whether the MMP block + * is being updated. The mmp_time, mmp_nodename, and mmp_bdevname + * fields are only for informational purposes for the administrator, + * due to clock skew between nodes and hostname HA service takeover. + */ +#define EXT4_MMP_MAGIC 0x004D4D50U /* ASCII for MMP */ +#define EXT4_MMP_SEQ_CLEAN 0xFF4D4D50U /* mmp_seq value for clean unmount */ +#define EXT4_MMP_SEQ_FSCK 0xE24D4D50U /* mmp_seq value when being fscked */ +#define EXT4_MMP_SEQ_MAX 0xE24D4D4FU /* maximum valid mmp_seq value */ + +struct mmp_struct { + __u32 mmp_magic; /* Magic number for MMP */ + __u32 mmp_seq; /* Sequence no. updated periodically */ + __u64 mmp_time; /* Time last updated */ + char mmp_nodename[64]; /* Node which last updated MMP block */ + char mmp_bdevname[32]; /* Bdev which last updated MMP block */ + __u16 mmp_check_interval; /* Changed mmp_check_interval */ + __u16 mmp_pad1; + __u32 mmp_pad2[227]; +}; + +/* + * Default interval for MMP update in seconds. + */ +#define EXT4_MMP_UPDATE_INTERVAL 5 + +/* + * Maximum interval for MMP update in seconds. + */ +#define EXT4_MMP_MAX_UPDATE_INTERVAL 300 + +/* + * Minimum interval for MMP checking in seconds. + */ +#define EXT4_MMP_MIN_CHECK_INTERVAL 5 + +#endif /* _EXT2FS_EXT2_FS_H */ diff --git a/libinstaller/fat.c b/libinstaller/fs.c index 9cde00c2..179629e9 100644 --- a/libinstaller/fat.c +++ b/libinstaller/fs.c @@ -1,7 +1,8 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved - * Copyright 2009-2010 Intel Corporation; author H. Peter Anvin + * Copyright 1998-2011 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2011 Intel Corporation; author H. Peter Anvin + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> * * 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 @@ -12,7 +13,7 @@ * ----------------------------------------------------------------------- */ /* - * fat.c - Initial sanity check for FAT-based installers + * fs.c - Generic sanity check for FAT/NTFS-based installers */ #define _XOPEN_SOURCE 500 /* Required on glibc 2.x */ @@ -25,45 +26,41 @@ #include "syslinux.h" #include "syslxint.h" +#include "syslxcom.h" +#include "syslxfs.h" -void syslinux_make_bootsect(void *bs) +void syslinux_make_bootsect(void *bs, int fs_type) { - struct boot_sector *bootsect = bs; - const struct boot_sector *sbs = - (const struct boot_sector *)boot_sector; - - memcpy(&bootsect->bsHead, &sbs->bsHead, bsHeadLen); - memcpy(&bootsect->bsCode, &sbs->bsCode, bsCodeLen); + if (fs_type == VFAT) { + struct fat_boot_sector *bootsect = bs; + const struct fat_boot_sector *sbs = + (const struct fat_boot_sector *)boot_sector; + + memcpy(&bootsect->FAT_bsHead, &sbs->FAT_bsHead, FAT_bsHeadLen); + memcpy(&bootsect->FAT_bsCode, &sbs->FAT_bsCode, FAT_bsCodeLen); + } else if (fs_type == NTFS) { + struct ntfs_boot_sector *bootsect = bs; + const struct ntfs_boot_sector *sbs = + (const struct ntfs_boot_sector *)boot_sector; + + memcpy(&bootsect->NTFS_bsHead, &sbs->NTFS_bsHead, NTFS_bsHeadLen); + memcpy(&bootsect->NTFS_bsCode, &sbs->NTFS_bsCode, NTFS_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; - */ -const char *syslinux_check_bootsect(const void *bs) +static const char *check_fat_bootsect(const void *bs, int *fs_type) { int sectorsize; + const struct fat_boot_sector *sectbuf = bs; long long sectors, fatsectors, dsectors; long long clusters; int rootdirents, clustersize; - const struct boot_sector *sectbuf = bs; - - /* 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"; + return "impossible cluster size on an FAT volume"; sectors = get_16(§buf->bsSectors); sectors = sectors ? sectors : get_32(§buf->bsHugeSectors); @@ -79,16 +76,19 @@ const char *syslinux_check_bootsect(const void *bs) dsectors -= (rootdirents + sectorsize / 32 - 1) / sectorsize; if (dsectors < 0) - return "negative number of data sectors"; - - if (fatsectors == 0) - return "zero FAT sectors"; + return "negative number of data sectors on an FAT volume"; clusters = dsectors / clustersize; + fatsectors = get_16(§buf->bsFATsecs); + fatsectors = fatsectors ? fatsectors : get_32(§buf->bs32.FATSz32); + fatsectors *= get_8(§buf->bsFATs); + + if (!fatsectors) + return "zero FAT sectors"; + if (clusters < 0xFFF5) { /* FAT12 or FAT16 */ - if (!get_16(§buf->bsFATsecs)) return "zero FAT sectors (FAT12/16)"; @@ -100,10 +100,10 @@ const char *syslinux_check_bootsect(const void *bs) 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"; + return "less than 65525 clusters but claims FAT32"; } else if (memcmp(§buf->bs16.FileSysType, "FAT ", 8)) { - static char fserr[] = - "filesystem type \"????????\" not supported"; + static char fserr[] = "filesystem type \"????????\" not " + "supported"; memcpy(fserr + 17, §buf->bs16.FileSysType, 8); return fserr; } @@ -119,8 +119,54 @@ const char *syslinux_check_bootsect(const void *bs) memcmp(§buf->bs32.FileSysType, "FAT32 ", 8)) return "missing FAT32 signature"; } else { - return "impossibly large number of clusters"; + return "impossibly large number of clusters on an FAT volume"; } + if (fs_type) + *fs_type = VFAT; + + return NULL; +} + +static const char *check_ntfs_bootsect(const void *bs, int *fs_type) +{ + const struct ntfs_boot_sector *sectbuf = bs; + + if (memcmp(§buf->bsOemName, "NTFS ", 8) && + memcmp(§buf->bsOemName, "MSWIN4.0", 8) && + memcmp(§buf->bsOemName, "MSWIN4.1", 8)) + return "unknown OEM name but claims NTFS"; + + if (fs_type) + *fs_type = NTFS; + return NULL; } + +const char *syslinux_check_bootsect(const void *bs, int *fs_type) +{ + uint8_t media_sig; + int sectorsize; + const struct fat_boot_sector *sectbuf = bs; + const char *retval; + + media_sig = get_8(§buf->bsMedia); + /* Must be 0xF0 or 0xF8-0xFF for FAT/NTFS volumes */ + if (media_sig != 0xF0 && media_sig < 0xF8) + return "invalid media signature (not an FAT/NTFS volume?)"; + + 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"; + + if (ntfs_check_zero_fields((struct ntfs_boot_sector *)bs)) + retval = check_ntfs_bootsect(bs, fs_type); + else + retval = check_fat_bootsect(bs, fs_type); + + return retval; +} diff --git a/libinstaller/linuxioctl.h b/libinstaller/linuxioctl.h index 7ef919a3..e2731c7c 100644 --- a/libinstaller/linuxioctl.h +++ b/libinstaller/linuxioctl.h @@ -9,22 +9,36 @@ #include <sys/ioctl.h> +#ifdef __linux__ + #define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */ #include <linux/fd.h> /* Floppy geometry */ #include <linux/hdreg.h> /* Hard disk geometry */ -#include <linux/fs.h> /* FIGETBSZ, FIBMAP, FS_IOC_FIEMAP */ -#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */ +#include <linux/fs.h> /* FIGETBSZ, FIBMAP, FS_IOC_* */ #undef SECTOR_SIZE /* Defined in msdos_fs.h for no good reason */ #undef SECTOR_BITS -#include <linux/ext2_fs.h> /* EXT2_IOC_* */ + +#ifndef FS_IOC_GETFLAGS +/* Old kernel headers, these were once ext2-specific... */ +# include <linux/ext2_fs.h> /* EXT2_IOC_* */ + +# define FS_IOC_GETFLAGS EXT2_IOC_GETFLAGS +# define FS_IOC_SETFLAGS EXT2_IOC_SETFLAGS + +# define FS_IMMUTABLE_FL EXT2_IMMUTABLE_FL + +#else + +# include <ext2fs/ext2_fs.h> + +#endif #ifndef FAT_IOCTL_GET_ATTRIBUTES # define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) #endif - #ifndef FAT_IOCTL_SET_ATTRIBUTES # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) #endif @@ -37,11 +51,13 @@ #undef statfs -#if defined(__linux__) && !defined(BLKGETSIZE64) +#ifndef BLKGETSIZE64 /* This takes a u64, but the size field says size_t. Someone screwed big. */ # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif #include <linux/loop.h> +#endif /* __linux__ */ + #endif /* LIBINSTALLER_LINUXIOCTL_H */ diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c index 9cf6f188..214f7fc1 100644 --- a/libinstaller/setadv.c +++ b/libinstaller/setadv.c @@ -30,6 +30,7 @@ #include <errno.h> #include "syslxint.h" #include "syslxcom.h" +#include "syslxfs.h" unsigned char syslinux_adv[2 * ADV_SIZE]; diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h index 710d30e5..8b86f881 100644 --- a/libinstaller/syslinux.h +++ b/libinstaller/syslinux.h @@ -40,10 +40,10 @@ extern const int syslinux_mbr_mtime; #define SECTOR_SIZE (1 << SECTOR_SHIFT) /* This takes a boot sector and merges in the syslinux fields */ -void syslinux_make_bootsect(void *); +void syslinux_make_bootsect(void *bs, int fs_type); /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ -const char *syslinux_check_bootsect(const void *bs); +const char *syslinux_check_bootsect(const void *bs, int *fs_type); /* This patches the boot sector and ldlinux.sys based on a sector map */ typedef uint64_t sector_t; diff --git a/libinstaller/syslxcom.c b/libinstaller/syslxcom.c index 1de85aa5..57f13cda 100644 --- a/libinstaller/syslxcom.c +++ b/libinstaller/syslxcom.c @@ -30,8 +30,10 @@ #include <sys/types.h> #include <sys/mount.h> #include <sys/vfs.h> + #include "linuxioctl.h" #include "syslxcom.h" +#include "syslxfs.h" const char *program; @@ -121,9 +123,9 @@ void clear_attributes(int fd) { int flags; - if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { - flags &= ~EXT2_IMMUTABLE_FL; - ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + if (!ioctl(fd, FS_IOC_GETFLAGS, &flags)) { + flags &= ~FS_IMMUTABLE_FL; + ioctl(fd, FS_IOC_SETFLAGS, &flags); } break; } @@ -133,6 +135,8 @@ void clear_attributes(int fd) ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); break; } + case NTFS: + break; default: break; } @@ -151,9 +155,9 @@ void set_attributes(int fd) { int flags; - if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { - flags |= EXT2_IMMUTABLE_FL; - ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + if (st.st_uid == 0 && !ioctl(fd, FS_IOC_GETFLAGS, &flags)) { + flags |= FS_IMMUTABLE_FL; + ioctl(fd, FS_IOC_SETFLAGS, &flags); } break; } @@ -163,6 +167,8 @@ void set_attributes(int fd) ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); break; } + case NTFS: + break; default: break; } diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h index bf186ca6..8b3b4614 100644 --- a/libinstaller/syslxcom.h +++ b/libinstaller/syslxcom.h @@ -3,15 +3,6 @@ #include "syslinux.h" -/* Global fs_type for handling fat, ext2/3/4 and btrfs */ -enum filesystem { - NONE, - EXT2, - BTRFS, - VFAT, -}; - -extern int fs_type; extern const char *program; 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); diff --git a/libinstaller/syslxfs.h b/libinstaller/syslxfs.h new file mode 100644 index 00000000..7a231461 --- /dev/null +++ b/libinstaller/syslxfs.h @@ -0,0 +1,26 @@ +/* + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLXFS_H_ +#define _SYSLXFS_H_ + +/* Global fs_type for handling fat, ntfs, ext2/3/4 and btrfs */ +enum filesystem { + NONE, + EXT2, + BTRFS, + VFAT, + NTFS, +}; + +extern int fs_type; + +#endif /* _SYSLXFS_H_ */ diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h index 7c9da516..2e317d0e 100644 --- a/libinstaller/syslxint.h +++ b/libinstaller/syslxint.h @@ -2,6 +2,7 @@ * * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin + * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com> * * 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 @@ -192,7 +193,7 @@ struct syslinux_extent { } __attribute__((packed)); /* FAT bootsector format, also used by other disk-based derivatives */ -struct boot_sector { +struct fat_boot_sector { uint8_t bsJump[3]; char bsOemName[8]; uint16_t bsBytesPerSec; @@ -241,13 +242,68 @@ struct boot_sector { uint16_t bsSignature; } __attribute__ ((packed)); -#define bsHead bsJump -#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)) +/* NTFS bootsector format */ +struct ntfs_boot_sector { + uint8_t bsJump[3]; + char bsOemName[8]; + uint16_t bsBytesPerSec; + uint8_t bsSecPerClust; + uint16_t bsResSectors; + uint8_t bsZeroed_0[3]; + uint16_t bsZeroed_1; + uint8_t bsMedia; + uint16_t bsZeroed_2; + uint16_t bsUnused_0; + uint16_t bsUnused_1; + uint32_t bsUnused_2; + uint32_t bsZeroed_3; + uint32_t bsUnused_3; + uint64_t bsTotalSectors; + uint64_t bsMFTLogicalClustNr; + uint64_t bsMFTMirrLogicalClustNr; + uint8_t bsClustPerMFTrecord; + uint8_t bsUnused_4[3]; + uint8_t bsClustPerIdxBuf; + uint8_t bsUnused_5[3]; + uint64_t bsVolSerialNr; + uint32_t bsUnused_6; + + uint8_t Code[420]; + + uint32_t bsMagic; + uint16_t bsForwardPtr; + uint16_t bsSignature; +} __attribute__((packed)); + +#define FAT_bsHead bsJump +#define FAT_bsHeadLen offsetof(struct fat_boot_sector, bsBytesPerSec) +#define FAT_bsCode bs32.Code /* The common safe choice */ +#define FAT_bsCodeLen (offsetof(struct fat_boot_sector, bsSignature) - \ + offsetof(struct fat_boot_sector, FAT_bsCode)) + +#define NTFS_bsHead bsJump +#define NTFS_bsHeadLen offsetof(struct ntfs_boot_sector, bsOemName) +#define NTFS_bsCode Code +#define NTFS_bsCodeLen (offsetof(struct ntfs_boot_sector, bsSignature) - \ + offsetof(struct ntfs_boot_sector, NTFS_bsCode)) + +/* Check if there are specific zero fields in an NTFS boot sector */ +static inline int ntfs_check_zero_fields(const struct ntfs_boot_sector *sb) +{ + return !sb->bsResSectors && (!sb->bsZeroed_0[0] && !sb->bsZeroed_0[1] && + !sb->bsZeroed_0[2]) && !sb->bsZeroed_1 && !sb->bsZeroed_2 && + !sb->bsZeroed_3; +} + +static inline int ntfs_check_sb_fields(const struct ntfs_boot_sector *sb) +{ + return ntfs_check_zero_fields(sb) && + (!memcmp(sb->bsOemName, "NTFS ", 8) || + !memcmp(sb->bsOemName, "MSWIN4.0", 8) || + !memcmp(sb->bsOemName, "MSWIN4.1", 8)); +} -static inline int fat_check_sb_fields(const struct boot_sector *sb) +static inline int fat_check_sb_fields(const struct fat_boot_sector *sb) { return sb->bsResSectors && sb->bsFATs && (!memcmp(sb->bs16.FileSysType, "FAT12 ", 8) || diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c index 8847b736..24368459 100644 --- a/libinstaller/syslxmod.c +++ b/libinstaller/syslxmod.c @@ -109,7 +109,7 @@ int syslinux_patch(const sector_t *sectp, int nsectors, int nsect = ((boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT) + 2; uint32_t csum; int i, dw, nptrs; - struct boot_sector *sbs = (struct boot_sector *)boot_sector; + struct fat_boot_sector *sbs = (struct fat_boot_sector *)boot_sector; uint64_t *advptrs; if (nsectors < nsect) diff --git a/libinstaller/syslxopt.c b/libinstaller/syslxopt.c index 7ceb3ba2..e081a00e 100644 --- a/libinstaller/syslxopt.c +++ b/libinstaller/syslxopt.c @@ -25,6 +25,7 @@ #include <sysexits.h> #include "../version.h" #include "syslxcom.h" +#include "syslxfs.h" #include "syslxopt.h" /* These are the options we can set their values */ diff --git a/linux/Makefile b/linux/Makefile index 82bf1118..08a3ed49 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -11,7 +11,7 @@ ## ----------------------------------------------------------------------- ## -## Linux FAT installer +## Linux FAT/NTFS installer ## topdir = .. @@ -28,7 +28,7 @@ SRCS = syslinux.c \ ../libinstaller/syslxcom.c \ ../libinstaller/setadv.c \ ../libinstaller/advio.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/bootsect_bin.c \ ../libinstaller/ldlinux_bin.c diff --git a/linux/syslinux.c b/linux/syslinux.c index c7a9ecc4..4b13b7fe 100755 --- a/linux/syslinux.c +++ b/linux/syslinux.c @@ -69,6 +69,7 @@ #include <getopt.h> #include <sysexits.h> #include "syslxcom.h" +#include "syslxfs.h" #include "setadv.h" #include "syslxopt.h" /* unified options */ @@ -294,14 +295,14 @@ int main(int argc, char *argv[]) die("can't combine an offset with a block device"); } - fs_type = VFAT; xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); fsync(dev_fd); /* - * Check to see that what we got was indeed an MS-DOS boot sector/superblock + * Check to see that what we got was indeed an FAT/NTFS + * boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { fprintf(stderr, "%s: %s\n", opt.device, errmsg); exit(1); } @@ -357,10 +358,17 @@ int main(int argc, char *argv[]) mntpath = mntname; } - if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && - do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { - rmdir(mntpath); - die("mount failed"); + if (fs_type == VFAT) { + if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && + do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { + rmdir(mntpath); + die("failed on mounting fat volume"); + } + } else if (fs_type == NTFS) { + if (do_mount(dev_fd, &mnt_cookie, mntpath, "ntfs-3g")) { + rmdir(mntpath); + die("failed on mounting ntfs volume"); + } } ldlinux_path = alloca(strlen(mntpath) + strlen(subdir) + 1); @@ -474,7 +482,7 @@ umount: xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, fs_type); /* Write new boot sector */ xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); diff --git a/mbr/altmbr.S b/mbr/altmbr.S index 1b609051..c66b4ddc 100644 --- a/mbr/altmbr.S +++ b/mbr/altmbr.S @@ -204,7 +204,7 @@ scan_partition_table: 5: decb (partition) jz boot - addw $16, %bx + addw $16, %si loopw 5b popw %cx /* %cx <- 4 */ diff --git a/memdisk/Makefile b/memdisk/Makefile index 5475b44d..b5cd52ce 100644 --- a/memdisk/Makefile +++ b/memdisk/Makefile @@ -20,7 +20,7 @@ INCLUDES = -I$(topdir)/com32/include CFLAGS += -D__MEMDISK__ -DDATE='"$(DATE)"' LDFLAGS = $(GCCOPT) -g NASM = nasm -NASMOPT = -O9999 +NASMOPT = -Ox NFLAGS = -dDATE='"$(DATE)"' NINCLUDE = diff --git a/mtools/Makefile b/mtools/Makefile index 3e172fd6..78cea1e2 100755 --- a/mtools/Makefile +++ b/mtools/Makefile @@ -8,7 +8,7 @@ CFLAGS = $(GCCWARN) -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES) LDFLAGS = SRCS = syslinux.c \ - ../libinstaller/fat.c \ + ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/mtools/syslinux.c b/mtools/syslinux.c index ac189c61..c65021bb 100755 --- a/mtools/syslinux.c +++ b/mtools/syslinux.c @@ -41,6 +41,7 @@ #include "libfat.h" #include "setadv.h" #include "syslxopt.h" +#include "syslxfs.h" char *program; /* Name of program */ pid_t mypid; @@ -197,7 +198,7 @@ int main(int argc, char *argv[]) /* * Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) { die(errmsg); } @@ -356,7 +357,7 @@ int main(int argc, char *argv[]) xpread(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); /* Copy the syslinux code into the boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, VFAT); /* Write new boot sector */ xpwrite(dev_fd, sectbuf, SECTOR_SIZE, opt.offset); diff --git a/utils/isohybrid.c b/utils/isohybrid.c index 1dcbaa11..ac04bfd4 100644 --- a/utils/isohybrid.c +++ b/utils/isohybrid.c @@ -54,7 +54,7 @@ enum { VERBOSE = 1 , EFI = 2 , MAC = 4}; uint16_t head = 64; /* 1 <= head <= 256 */ uint8_t sector = 32; /* 1 <= sector <= 63 */ -uint8_t entry = 1; /* partition number: 1 <= entry <= 4 */ +uint8_t entry = 0; /* partition number: 1 <= entry <= 4 */ uint8_t offset = 0; /* partition offset: 0 <= offset <= 64 */ uint16_t type = 0x17; /* partition type: 0 <= type <= 255 */ uint32_t id = 0; /* MBR: 0 <= id <= 0xFFFFFFFF(4294967296) */ @@ -300,6 +300,8 @@ check_option(int argc, char *argv[]) entry = strtoul(optarg, &err, 0); if (entry < 1 || entry > 4) errx(1, "invalid entry: `%s', 1 <= entry <= 4", optarg); + if (mode & MAC || mode & EFI) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'o': @@ -334,10 +336,14 @@ check_option(int argc, char *argv[]) case 'u': mode |= EFI; + if (entry) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'm': mode |= MAC; + if (entry) + errx(1, "setting an entry is unsupported with EFI or Mac"); break; case 'v': @@ -581,6 +587,12 @@ initialise_mbr(uint8_t *mbr) memcpy(mbr, afp_header, sizeof(afp_header)); } + if (!entry) + entry = 1; + + if (mode & EFI) + type = 0; + mbr += MBRSIZE; /* offset 432 */ tmp = lendian_int(de_lba * 4); @@ -633,6 +645,40 @@ initialise_mbr(uint8_t *mbr) tmp = lendian_int(psize); memcpy(&mbr[12], &tmp, sizeof(tmp)); } + if (i == 2 && (mode & EFI)) + { + mbr[0] = 0x0; + mbr[1] = 0xfe; + mbr[2] = 0xff; + mbr[3] = 0xff; + mbr[4] = 0xef; + mbr[5] = 0xfe; + mbr[6] = 0xff; + mbr[7] = 0xff; + + tmp = lendian_int(efi_lba * 4); + memcpy(&mbr[8], &tmp, sizeof(tmp)); + + tmp = lendian_int(efi_count); + memcpy(&mbr[12], &tmp, sizeof(tmp)); + } + if (i == 3 && (mode & MAC)) + { + mbr[0] = 0x0; + mbr[1] = 0xfe; + mbr[2] = 0xff; + mbr[3] = 0xff; + mbr[4] = 0x0; + mbr[5] = 0xfe; + mbr[6] = 0xff; + mbr[7] = 0xff; + + tmp = lendian_int(mac_lba * 4); + memcpy(&mbr[8], &tmp, sizeof(tmp)); + + tmp = lendian_int(mac_count); + memcpy(&mbr[12], &tmp, sizeof(tmp)); + } mbr += 16; } mbr[0] = 0x55; @@ -908,8 +954,7 @@ main(int argc, char *argv[]) if (!read_efi_section(buf)) { buf += 32; if (!read_efi_catalogue(buf, &efi_count, &efi_lba) && efi_lba) { - offset = 1; - type = 0xee; + offset = 0; } else { errx(1, "%s: invalid efi catalogue", argv[0]); } @@ -925,8 +970,7 @@ main(int argc, char *argv[]) if (!read_efi_section(buf)) { buf += 32; if (!read_efi_catalogue(buf, &mac_count, &mac_lba) && mac_lba) { - offset = 1; - type = 0xee; + offset = 0; } else { errx(1, "%s: invalid efi catalogue", argv[0]); } @@ -1 +1 @@ -4.10 2011 +4.10 2012 diff --git a/win/ntfssect.c b/win/ntfssect.c new file mode 100644 index 00000000..8c2bccaa --- /dev/null +++ b/win/ntfssect.c @@ -0,0 +1,355 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - All Rights Reserved + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +/**** + * ntfssect.c + * + * Fetch NTFS file cluster & sector information via Windows + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ + +#include <windows.h> +#include <winioctl.h> +#include <stddef.h> +#include <string.h> + +#include "ntfssect.h" + +/*** Macros */ +#define M_ERR(msg) (NtfsSectLastErrorMessage = (msg)) + +/*** Function declarations */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo); + +/*** Objects */ +CHAR * NtfsSectLastErrorMessage; + +/*** Function definitions */ +DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent( + HANDLE File, + LARGE_INTEGER * Vcn, + S_NTFSSECT_EXTENT * Extent + ) { + BOOL bad, ok; + DWORD output_size, rc; + STARTING_VCN_INPUT_BUFFER input; + RETRIEVAL_POINTERS_BUFFER output; + + bad = ( + File == INVALID_HANDLE_VALUE || + !Vcn || + Vcn->QuadPart < 0 || + !Extent + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + input.StartingVcn = *Vcn; + ok = DeviceIoControl( + File, + FSCTL_GET_RETRIEVAL_POINTERS, + &input, + sizeof input, + &output, + sizeof output, + &output_size, + NULL + ); + rc = GetLastError(); + switch (rc) { + case NO_ERROR: + case ERROR_MORE_DATA: + Extent->FirstVcn = output.StartingVcn; + Extent->NextVcn = output.Extents[0].NextVcn; + Extent->FirstLcn = output.Extents[0].Lcn; + return ERROR_SUCCESS; + + case ERROR_HANDLE_EOF: + break; + + default: + M_ERR("NtfsSectGetFileVcnExtent(): Unknown status!"); + } + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumeHandle( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + #define M_VOL_PREFIX "\\\\.\\" + CHAR volname[sizeof M_VOL_PREFIX - 1 + MAX_PATH + 1] = M_VOL_PREFIX; + CHAR * const volname_short = volname + sizeof M_VOL_PREFIX - 1; + CHAR * c; + DWORD rc; + + /* Prefix "\\.\" onto the passed volume name */ + strcpy(volname + sizeof M_VOL_PREFIX - 1, VolumeName); + + /* Find the last non-null character */ + for (c = volname_short; *c; ++c) + ; + + /* Remove trailing back-slash */ + if (c[-1] == '\\') + c[-1] = 0; + + /* Open the volume */ + VolumeInfo->Handle = CreateFile( + volname, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + rc = GetLastError(); + if (VolumeInfo->Handle == INVALID_HANDLE_VALUE) { + M_ERR("Unable to open volume handle!"); + goto err_handle; + } + + return ERROR_SUCCESS; + + CloseHandle(VolumeInfo->Handle); + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc, free_clusts, total_clusts; + BOOL ok; + + if (!VolumeName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectGetVolumeHandle(VolumeName, VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_handle; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) + goto err_xp_funcs; + + ok = xp_funcs.GetDiskFreeSpace( + VolumeName, + &VolumeInfo->SectorsPerCluster, + &VolumeInfo->BytesPerSector, + &free_clusts, + &total_clusts + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetDiskFreeSpace() failed!"); + goto err_freespace; + } + + rc = NtfsSectGetVolumePartitionLba(VolumeInfo); + if (rc != ERROR_SUCCESS) + goto err_lba; + + VolumeInfo->Size = sizeof *VolumeInfo; + rc = ERROR_SUCCESS; + + err_lba: + + err_freespace: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + if (rc != ERROR_SUCCESS) { + CloseHandle(VolumeInfo->Handle); + VolumeInfo->Handle = INVALID_HANDLE_VALUE; + } + err_handle: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName( + CHAR * FileName, + S_NTFSSECT_VOLINFO * VolumeInfo + ) { + S_NTFSSECT_XPFUNCS xp_funcs; + DWORD rc; + CHAR volname[MAX_PATH + 1]; + BOOL ok; + + if (!FileName || !VolumeInfo) + return ERROR_INVALID_PARAMETER; + + rc = NtfsSectLoadXpFuncs(&xp_funcs); + if (rc != ERROR_SUCCESS) { + goto err_xp_funcs; + } + + ok = xp_funcs.GetVolumePathName( + FileName, + volname, + sizeof volname + ); + rc = GetLastError(); + if (!ok) { + M_ERR("GetVolumePathName() failed!"); + goto err_volname; + } + + rc = NtfsSectGetVolumeInfo(volname, VolumeInfo); + + err_volname: + + NtfsSectUnloadXpFuncs(&xp_funcs); + err_xp_funcs: + + return rc; + } + +/* Internal use only */ +static DWORD NtfsSectGetVolumePartitionLba(S_NTFSSECT_VOLINFO * VolumeInfo) { + BOOL ok; + VOLUME_DISK_EXTENTS vol_disk_extents; + DWORD output_size, rc; + + ok = DeviceIoControl( + VolumeInfo->Handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, + 0, + &vol_disk_extents, + sizeof vol_disk_extents, + &output_size, + NULL + ); + rc = GetLastError(); + if (!ok) { + M_ERR("Couldn't fetch volume disk extent(s)!"); + goto err_vol_disk_extents; + } + + if (vol_disk_extents.NumberOfDiskExtents != 1) { + M_ERR("Unsupported number of volume disk extents!"); + goto err_num_of_extents; + } + + VolumeInfo->PartitionLba.QuadPart = ( + vol_disk_extents.Extents[0].StartingOffset.QuadPart / + VolumeInfo->BytesPerSector + ); + + return ERROR_SUCCESS; + + err_num_of_extents: + + err_vol_disk_extents: + + return rc; + } + +DWORD M_NTFSSECT_API NtfsSectLcnToLba( + const S_NTFSSECT_VOLINFO * VolumeInfo, + const LARGE_INTEGER * Lcn, + LARGE_INTEGER * Lba + ) { + BOOL bad; + bad = ( + !VolumeInfo || + !VolumeInfo->BytesPerSector || + !VolumeInfo->SectorsPerCluster || + !Lcn || + Lcn->QuadPart < 0 || + !Lba + ); + if (bad) + return ERROR_INVALID_PARAMETER; + + Lba->QuadPart = ( + VolumeInfo->PartitionLba.QuadPart + + Lcn->QuadPart * + VolumeInfo->SectorsPerCluster + ); + return ERROR_SUCCESS; + } + +DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + DWORD rc; + + if (!XpFuncs) + return ERROR_INVALID_PARAMETER; + + XpFuncs->Size = sizeof *XpFuncs; + + XpFuncs->Kernel32 = LoadLibrary("kernel32.dll"); + rc = GetLastError(); + if (!XpFuncs->Kernel32) { + M_ERR("KERNEL32.DLL not found!"); + goto err; + } + + XpFuncs->GetVolumePathName = (F_KERNEL32_GETVOLUMEPATHNAME *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetVolumePathNameA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetVolumePathName) { + M_ERR("GetVolumePathName() not found in KERNEL32.DLL!"); + goto err; + } + + XpFuncs->GetDiskFreeSpace = (F_KERNEL32_GETDISKFREESPACE *) ( + GetProcAddress( + XpFuncs->Kernel32, + "GetDiskFreeSpaceA" + ) + ); + rc = GetLastError(); + if (!XpFuncs->GetDiskFreeSpace) { + M_ERR("GetDiskFreeSpace() not found in KERNEL32.DLL!"); + goto err; + } + + return ERROR_SUCCESS; + + err: + NtfsSectUnloadXpFuncs(XpFuncs); + return rc; + } + +VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs) { + if (!XpFuncs) + return; + + XpFuncs->GetDiskFreeSpace = NULL; + XpFuncs->GetVolumePathName = NULL; + if (XpFuncs->Kernel32) + FreeLibrary(XpFuncs->Kernel32); + XpFuncs->Kernel32 = NULL; + XpFuncs->Size = 0; + return; + } + diff --git a/win/ntfssect.h b/win/ntfssect.h new file mode 100644 index 00000000..246c26da --- /dev/null +++ b/win/ntfssect.h @@ -0,0 +1,152 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - All Rights Reserved + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ +#ifndef M_NTFSSECT_H_ + +/**** + * ntfssect.h + * + * Fetch NTFS file cluster & sector information via Windows + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ + +/*** Macros */ +#define M_NTFSSECT_H_ +#define M_NTFSSECT_API + +/*** Object types */ + +/* An "extent;" a contiguous range of file data */ +typedef struct S_NTFSSECT_EXTENT_ S_NTFSSECT_EXTENT; + +/* Volume info relevant to file cluster & sector info */ +typedef struct S_NTFSSECT_VOLINFO_ S_NTFSSECT_VOLINFO; + +/* Stores function pointers to some Windows functions */ +typedef struct S_NTFSSECT_XPFUNCS_ S_NTFSSECT_XPFUNCS; + +/*** Function types */ + +/* The function type for Kernel32.dll's GetDiskFreeSpace() */ +typedef BOOL WINAPI F_KERNEL32_GETDISKFREESPACE( + LPCTSTR, + LPDWORD, + LPDWORD, + LPDWORD, + LPDWORD + ); + +/* The function type for Kernel32.dll's GetVolumePathName() */ +typedef BOOL WINAPI F_KERNEL32_GETVOLUMEPATHNAME(LPCTSTR, LPCTSTR, DWORD); + +/*** Function declarations */ + +/** + * Fetch the extent containing a particular VCN + * + * @v File + * @v Vcn + * @v Extent + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetFileVcnExtent( + HANDLE File, + LARGE_INTEGER * Vcn, + S_NTFSSECT_EXTENT * Extent + ); + +/** + * Populate a volume info object + * + * @v VolumeName + * @v VolumeInfo + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfo( + CHAR * VolumeName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); + +/** + * Populate a volume info object + * + * @v FileName + * @v VolumeInfo + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectGetVolumeInfoFromFileName( + CHAR * FileName, + S_NTFSSECT_VOLINFO * VolumeInfo + ); + +/** + * Convert a volume LCN to an absolute disk LBA + * + * @v VolumeInfo + * @v Lcn + * @v Lba + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectLcnToLba( + const S_NTFSSECT_VOLINFO * VolumeInfo, + const LARGE_INTEGER * Lcn, + LARGE_INTEGER * Lba + ); + +/** + * Load some helper XP functions + * + * @v XpFuncs + * @ret DWORD + */ +DWORD M_NTFSSECT_API NtfsSectLoadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs); + +/** + * Unload some helper XP functions + * + * @v XpFuncs + * @ret DWORD + */ +VOID M_NTFSSECT_API NtfsSectUnloadXpFuncs(S_NTFSSECT_XPFUNCS * XpFuncs); + +/*** Object declarations */ + +/** + * The last error message set by one of our functions. + * Obviously not per-thread + */ +extern CHAR * NtfsSectLastErrorMessage; + +/*** Struct/union definitions */ +struct S_NTFSSECT_EXTENT_ { + LARGE_INTEGER FirstVcn; + LARGE_INTEGER NextVcn; + LARGE_INTEGER FirstLcn; + }; + +struct S_NTFSSECT_VOLINFO_ { + DWORD Size; + HANDLE Handle; + DWORD BytesPerSector; + DWORD SectorsPerCluster; + LARGE_INTEGER PartitionLba; + }; + +struct S_NTFSSECT_XPFUNCS_ { + DWORD Size; + HMODULE Kernel32; + F_KERNEL32_GETVOLUMEPATHNAME * GetVolumePathName; + F_KERNEL32_GETDISKFREESPACE * GetDiskFreeSpace; + }; + +#endif /* M_NTFSSECT_H_ */ diff --git a/win/ntfstest.c b/win/ntfstest.c new file mode 100644 index 00000000..1fc27182 --- /dev/null +++ b/win/ntfstest.c @@ -0,0 +1,163 @@ +/* -------------------------------------------------------------------------- * + * + * Copyright 2011 Shao Miller - All Rights Reserved + * + * 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 + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +/**** + * ntfstest.c + * + * (C) Shao Miller, 2011 + * + * Tests ntfssect.c functions + * + * With special thanks to Mark Roddy for his article: + * http://www.wd-3.com/archive/luserland.htm + */ +#include <windows.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ntfssect.h" + +/*** Object types */ + +/*** Function declarations */ +static void show_usage(void); +static void show_last_err(void); +static void show_err(DWORD); + +/*** Struct/union definitions */ + +/*** Function definitions */ + +/** Program entry-point */ +int main(int argc, char ** argv) { + int rc; + DWORD err; + S_NTFSSECT_VOLINFO vol_info; + HANDLE file; + LARGE_INTEGER vcn, lba; + S_NTFSSECT_EXTENT extent; + LONGLONG len; + BOOL ok; + + if (argc != 2) { + rc = EXIT_FAILURE; + show_usage(); + goto err_args; + } + + /* Get volume info */ + err = NtfsSectGetVolumeInfoFromFileName(argv[1], &vol_info); + if (err != ERROR_SUCCESS) { + show_err(err); + goto err_vol_info; + } + printf( + "Volume has %d bytes per sector, %d sectors per cluster\n", + vol_info.BytesPerSector, + vol_info.SectorsPerCluster + ); + + /* Open the file for reading */ + file = CreateFile( + argv[1], + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + if (file == INVALID_HANDLE_VALUE) { + rc = EXIT_FAILURE; + show_last_err(); + goto err_file; + } + + /* For each extent */ + for ( + vcn.QuadPart = 0; + NtfsSectGetFileVcnExtent(file, &vcn, &extent) == ERROR_SUCCESS; + vcn = extent.NextVcn + ) { + len = extent.NextVcn.QuadPart - extent.FirstVcn.QuadPart; + printf("Extent @ VCN #%lld,", extent.FirstVcn.QuadPart); + printf(" %lld clusters long:\n", len); + printf(" VCN #%lld -", extent.FirstVcn.QuadPart); + printf(" #%lld\n", extent.FirstVcn.QuadPart + len - 1); + printf(" LCN #%lld -", extent.FirstLcn.QuadPart); + printf(" #%lld\n", extent.FirstLcn.QuadPart + len - 1); + err = NtfsSectLcnToLba( + &vol_info, + &extent.FirstLcn, + &lba + ); + if (err == ERROR_SUCCESS) { + printf(" LBA #%lld -", lba.QuadPart); + printf( + " #%lld\n", + lba.QuadPart + len * vol_info.SectorsPerCluster + ); + } + continue; + } + + rc = EXIT_SUCCESS; + + CloseHandle(file); + err_file: + + CloseHandle(vol_info.Handle); + err_vol_info: + + err_args: + + return rc; + } + +/** Display usage */ +static void show_usage(void) { + static const char usage_text[] = "\ + File sector info . . . . . . . . . . . . . . . . . . . . Shao Miller, 2011\n\ +\n\ + Usage: NTFSTEST.EXE <filename>\n\ +\n\ + Attempts to dump cluster and sector info for <filename>.\n"; + + printf(usage_text); + return; + } + +static void show_last_err(void) { + show_err(GetLastError()); + return; + } + +/** Display an error */ +static void show_err(DWORD err_code) { + void * buf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &buf, + 0, + NULL + ); + fprintf(stderr, "Error: %s\n", buf); + LocalFree(buf); + return; + } + diff --git a/win/ntfstest.rc b/win/ntfstest.rc new file mode 100644 index 00000000..462d21f1 --- /dev/null +++ b/win/ntfstest.rc @@ -0,0 +1,26 @@ + +1 VERSIONINFO +FILEVERSION 0,0,0,1 +PRODUCTVERSION 0,0,0,1 +FILEOS 0x40004 +FILETYPE 0x1 + { + BLOCK "StringFileInfo" + { + BLOCK "040904B0" + { + VALUE "CompanyName", "Shao Miller" + VALUE "FileDescription", "NTFS File Sector Info" + VALUE "FileVersion", "0.0.0.1 (Aug-12-2011)" + VALUE "InternalName", "NTFSTest" + VALUE "LegalCopyright", "© 2011 Shao Miller. All rights reserved." + VALUE "OriginalFilename", "NTFSTEST.EXE" + VALUE "ProductName", "Utilities" + VALUE "ProductVersion", "0.0.0.1" + } + } + } + +#if 0 +1 ICON "ntfstest.ico" +#endif diff --git a/win/syslinux.c b/win/syslinux.c index 0e833d8d..26e5a278 100644 --- a/win/syslinux.c +++ b/win/syslinux.c @@ -27,6 +27,8 @@ #include "setadv.h" #include "sysexits.h" #include "syslxopt.h" +#include "syslxfs.h" +#include "ntfssect.h" #ifdef __GNUC__ # define noreturn void __attribute__((noreturn)) @@ -254,6 +256,7 @@ int main(int argc, char *argv[]) int ldlinux_sectors; uint32_t ldlinux_cluster; int nsectors; + int fs_type; if (!checkver()) { fprintf(stderr, @@ -326,8 +329,10 @@ int main(int argc, char *argv[]) exit(1); } - /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ - if ((errmsg = syslinux_check_bootsect(sectbuf))) { + /* Check to see that what we got was indeed an FAT/NTFS + * boot sector/superblock + */ + if ((errmsg = syslinux_check_bootsect(sectbuf, &fs_type))) { fprintf(stderr, "%s\n", errmsg); exit(1); } @@ -379,6 +384,38 @@ int main(int argc, char *argv[]) ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE + SECTOR_SIZE - 1) >> SECTOR_SHIFT; sectors = calloc(ldlinux_sectors, sizeof *sectors); + if (fs_type == NTFS) { + DWORD err; + S_NTFSSECT_VOLINFO vol_info; + LARGE_INTEGER vcn, lba, len; + S_NTFSSECT_EXTENT extent; + + err = NtfsSectGetVolumeInfo(drive_name + 4, &vol_info); + if (err != ERROR_SUCCESS) { + error("Could not fetch NTFS volume info"); + exit(1); + } + secp = sectors; + nsectors = 0; + for (vcn.QuadPart = 0; + NtfsSectGetFileVcnExtent(f_handle, &vcn, &extent) == ERROR_SUCCESS; + vcn = extent.NextVcn) { + err = NtfsSectLcnToLba(&vol_info, &extent.FirstLcn, &lba); + if (err != ERROR_SUCCESS) { + error("Could not translate LDLINUX.SYS LCN to disk LBA"); + exit(1); + } + lba.QuadPart -= vol_info.PartitionLba.QuadPart; + len.QuadPart = ((extent.NextVcn.QuadPart - + extent.FirstVcn.QuadPart) * + vol_info.SectorsPerCluster); + while (len.QuadPart-- && nsectors < ldlinux_sectors) { + *secp++ = lba.QuadPart++; + nsectors++; + } + } + goto map_done; + } fs = libfat_open(libfat_readfile, (intptr_t) d_handle); ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); secp = sectors; @@ -390,6 +427,7 @@ int main(int argc, char *argv[]) s = libfat_nextsector(fs, s); } libfat_close(fs); +map_done: /* * Patch ldlinux.sys and the boot sector @@ -472,7 +510,7 @@ int main(int argc, char *argv[]) } /* Make the syslinux boot sector */ - syslinux_make_bootsect(sectbuf); + syslinux_make_bootsect(sectbuf, fs_type); /* Write the syslinux boot sector into the boot sector */ if (opt.bootsecfile) { diff --git a/win32/Makefile b/win32/Makefile index d4133ff8..f960998a 100644 --- a/win32/Makefile +++ b/win32/Makefile @@ -47,9 +47,9 @@ WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ .SUFFIXES: .c .obj .lib .exe .i .s .S -SRCS = ../win/syslinux.c +SRCS = ../win/syslinux.c ../win/ntfssect.c OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) -LIBSRC = ../libinstaller/fat.c \ +LIBSRC = ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/win32/find-mingw32.sh b/win32/find-mingw32.sh index f79949c7..51dcdd7e 100755 --- a/win32/find-mingw32.sh +++ b/win32/find-mingw32.sh @@ -20,7 +20,8 @@ for prefix in \ i386-mingw32msvc- \ i486-mingw32msvc- \ i586-mingw32msvc- \ - i686-mingw32msvc-; do + i686-mingw32msvc- \ + i686-w64-mingw32-; do if "${prefix}${cc}" -v > /dev/null 2>&1; then echo "$prefix" exit 0 diff --git a/win32/ntfstest/Makefile b/win32/ntfstest/Makefile new file mode 100644 index 00000000..00e89cf9 --- /dev/null +++ b/win32/ntfstest/Makefile @@ -0,0 +1,87 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-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 +## the Free Software Foundation, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for Win32 NTFS file cluster test +# +# This is separated out mostly so we can have a different set of Makefile +# variables. +# + +OSTYPE = $(shell uname -msr) +ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN) +## Compiling on Cygwin +WINPREFIX := +WINCFLAGS := -mno-cygwin $(GCCWARN) -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -mno-cygwin -Os -s +else +## Compiling on some variant of MinGW +ifeq ($(findstring MINGW32,$(OSTYPE)),MINGW32) +WINPREFIX := +else +WINPREFIX := $(shell ../find-mingw32.sh gcc) +endif +WINCFLAGS := $(GCCWARN) -Wno-sign-compare -Os -fomit-frame-pointer \ + -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -Os -s +endif +WINCFLAGS += -I. -I../../win + +WINCC := $(WINPREFIX)gcc +WINAR := $(WINPREFIX)ar +WINRANLIB := $(WINPREFIX)ranlib +WINDRES := $(WINPREFIX)windres + +WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ + -o hello.exe ../../win/hello.c >/dev/null 2>&1 ; echo $$?) + +.SUFFIXES: .c .obj .lib .exe .i .s .S .rc .res + +SRCS = ../../win/ntfstest.c ../../win/ntfssect.c +RCS = ../../win/ntfstest.rc +OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) +RESS = $(patsubst %.rc,%.res,$(notdir $(RCS))) + +VPATH = .:../../win + +TARGETS = ntfstest.exe + +ifeq ($(WINCC_IS_GOOD),0) +all: $(TARGETS) +else +all: + rm -f $(TARGETS) +endif + +tidy dist: + -rm -f *.o *.obj *.lib *.i *.s *.a .*.d *.tmp *_bin.c hello.exe + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +ntfstest.exe: $(OBJS) $(RESS) + $(WINCC) $(WINLDFLAGS) -o $@ $^ + + +%.obj: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -c -o $@ $< +%.i: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -E -o $@ $< +%.s: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -S -o $@ $< +%.res: %.rc + $(WINDRES) -O COFF $< $@ + +-include .*.d *.tmp diff --git a/win64/Makefile b/win64/Makefile index 0bc746d5..fe60793c 100644 --- a/win64/Makefile +++ b/win64/Makefile @@ -37,9 +37,9 @@ WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ .SUFFIXES: .c .obj .lib .exe .i .s .S -SRCS = ../win/syslinux.c +SRCS = ../win/syslinux.c ../win/ntfssect.c OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) -LIBSRC = ../libinstaller/fat.c \ +LIBSRC = ../libinstaller/fs.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/setadv.c \ diff --git a/win64/find-mingw64.sh b/win64/find-mingw64.sh index c45db562..c2e42cd7 100755 --- a/win64/find-mingw64.sh +++ b/win64/find-mingw64.sh @@ -12,6 +12,8 @@ for prefix in \ x86_64-mingw64msvc- \ x86_64-mingw32- \ x86_64-mingw32msvc- \ + x86_64-w64-mingw32- \ + x86_64-w64-mingw32msvc- \ amd64-pc-mingw64- \ amd64-pc-mingw64msvc- \ amd64-pc-mingw32- \ @@ -20,6 +22,8 @@ for prefix in \ amd64-mingw64msvc- \ amd64-mingw32- \ amd64-mingw32msvc- \ + amd64-w64-mingw32- \ + amd64-w64-mingw32msvc- \ ; do if "${prefix}${cc}" -v > /dev/null 2>&1; then echo "$prefix" diff --git a/win64/ntfstest/Makefile b/win64/ntfstest/Makefile new file mode 100644 index 00000000..5b975be5 --- /dev/null +++ b/win64/ntfstest/Makefile @@ -0,0 +1,76 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-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 +## the Free Software Foundation, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for Win64 NTFS file cluster test +# +# This is separated out mostly so we can have a different set of Makefile +# variables. +# + +OSTYPE = $(shell uname -msr) +# Don't know how to do a native compile here... +WINPREFIX := $(shell ../find-mingw64.sh gcc) +WINCFLAGS := $(GCCWARN) -Wno-sign-compare -Os -fomit-frame-pointer \ + -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -Os -s +WINCFLAGS += -I. -I../../win + +WINCC := $(WINPREFIX)gcc +WINAR := $(WINPREFIX)ar +WINRANLIB := $(WINPREFIX)ranlib +WINDRES := $(WINPREFIX)windres + +WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) \ + -o hello.exe ../../win/hello.c >/dev/null 2>&1 ; echo $$?) + +.SUFFIXES: .c .obj .lib .exe .i .s .S .rc .res + +SRCS = ../../win/ntfstest.c ../../win/ntfssect.c +RCS = ../../win/ntfstest.rc +OBJS = $(patsubst %.c,%.obj,$(notdir $(SRCS))) +RESS = $(patsubst %.rc,%.res,$(notdir $(RCS))) + +VPATH = .:../../win + +TARGETS = ntfstest64.exe + +ifeq ($(WINCC_IS_GOOD),0) +all: $(TARGETS) +else +all: + rm -f $(TARGETS) +endif + +tidy dist: + -rm -f *.o *.obj *.lib *.i *.s *.a .*.d *.tmp *_bin.c hello.exe + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +ntfstest64.exe: $(OBJS) $(RESS) + $(WINCC) $(WINLDFLAGS) -o $@ $^ + + +%.obj: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -c -o $@ $< +%.i: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -E -o $@ $< +%.s: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -S -o $@ $< +%.res: %.rc + $(WINDRES) -O COFF $< $@ + +-include .*.d *.tmp |