summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2012-03-30 17:40:50 -0700
committerH. Peter Anvin <hpa@zytor.com>2012-03-30 17:40:50 -0700
commit86a2df0467466d3e197837964db38b312b89ac0b (patch)
tree30828febadd5e0dd25e949819ccd93c803f14f27
parent73caae75a315c3de65ac25c9d5f521c1a4d3847f (diff)
parenta1006762fa6f98750bb77d76dd992cb8ea9f9c99 (diff)
downloadsyslinux-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>
-rw-r--r--Makefile2
-rw-r--r--NEWS3
-rw-r--r--com32/Makefile2
-rw-r--r--com32/chain/Makefile42
-rw-r--r--com32/chain/chain.c658
-rw-r--r--com32/chain/chain.h14
-rw-r--r--com32/chain/common.h9
-rw-r--r--com32/chain/mangle.c618
-rw-r--r--com32/chain/mangle.h32
-rw-r--r--com32/chain/options.c376
-rw-r--r--com32/chain/options.h47
-rw-r--r--com32/chain/partiter.c805
-rw-r--r--com32/chain/partiter.h107
-rw-r--r--com32/chain/utility.c214
-rw-r--r--com32/chain/utility.h30
-rw-r--r--com32/hdt/.gitignore1
-rw-r--r--com32/hdt/Makefile8
-rw-r--r--com32/hdt/hdt-common.c27
-rw-r--r--com32/hdt/hdt-common.h2
-rw-r--r--com32/hdt/hdt-dump-disks.c12
-rw-r--r--com32/hdt/hdt-dump.c192
-rw-r--r--com32/hdt/hdt-menu-summary.c3
-rw-r--r--com32/hdt/hdt.c15
-rw-r--r--com32/hdt/hdt.h4
-rw-r--r--com32/include/string.h1
-rw-r--r--com32/include/syslinux/disk.h180
-rw-r--r--com32/lib/Makefile5
-rw-r--r--com32/lib/pci/scan.c6
-rw-r--r--com32/lib/strreplace.c58
-rw-r--r--com32/lib/syslinux/disk.c534
-rw-r--r--com32/modules/Makefile2
-rw-r--r--com32/modules/chain.c1932
-rw-r--r--core/bootsect.inc16
-rw-r--r--core/fs/ntfs/ntfs.c1388
-rw-r--r--core/fs/ntfs/ntfs.h485
-rw-r--r--core/fs/ntfs/runlist.h83
-rw-r--r--core/ldlinux.asm2
-rw-r--r--doc/chain.txt327
-rw-r--r--doc/gpt.txt2
-rw-r--r--doc/logo/LICENSE5
-rw-r--r--dos/Makefile2
-rw-r--r--dos/syslinux.c5
-rw-r--r--dosutil/Makefile2
-rw-r--r--extlinux/Makefile2
-rw-r--r--[-rwxr-xr-x]extlinux/main.c188
-rw-r--r--extlinux/ntfs.h19
-rw-r--r--libinstaller/ext2fs/ext2_fs.h856
-rw-r--r--libinstaller/fs.c (renamed from libinstaller/fat.c)120
-rw-r--r--libinstaller/linuxioctl.h26
-rw-r--r--libinstaller/setadv.c1
-rw-r--r--libinstaller/syslinux.h4
-rw-r--r--libinstaller/syslxcom.c18
-rw-r--r--libinstaller/syslxcom.h9
-rw-r--r--libinstaller/syslxfs.h26
-rw-r--r--libinstaller/syslxint.h70
-rw-r--r--libinstaller/syslxmod.c2
-rw-r--r--libinstaller/syslxopt.c1
-rw-r--r--linux/Makefile4
-rwxr-xr-xlinux/syslinux.c24
-rw-r--r--mbr/altmbr.S2
-rw-r--r--memdisk/Makefile2
-rwxr-xr-xmtools/Makefile2
-rwxr-xr-xmtools/syslinux.c5
-rw-r--r--utils/isohybrid.c54
-rw-r--r--version2
-rw-r--r--win/ntfssect.c355
-rw-r--r--win/ntfssect.h152
-rw-r--r--win/ntfstest.c163
-rw-r--r--win/ntfstest.rc26
-rw-r--r--win/syslinux.c44
-rw-r--r--win32/Makefile4
-rwxr-xr-xwin32/find-mingw32.sh3
-rw-r--r--win32/ntfstest/Makefile87
-rw-r--r--win64/Makefile4
-rwxr-xr-xwin64/find-mingw64.sh4
-rw-r--r--win64/ntfstest/Makefile76
76 files changed, 8445 insertions, 2168 deletions
diff --git a/Makefile b/Makefile
index 48b34201..1b419aea 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/NEWS b/NEWS
index 4652e583..5eb66b92 100644
--- a/NEWS
+++ b/NEWS
@@ -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(&regs, 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, &regs);
-
-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(&sectbuf->bsMedia) != 0xF0 && get_8(&sectbuf->bsMedia) < 0xF8)
- return "invalid media signature (not a FAT filesystem?)";
sectorsize = get_16(&sectbuf->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(&sectbuf->bsSecPerClust);
if (clustersize == 0 || (clustersize & (clustersize - 1)))
- return "impossible cluster size";
+ return "impossible cluster size on an FAT volume";
sectors = get_16(&sectbuf->bsSectors);
sectors = sectors ? sectors : get_32(&sectbuf->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(&sectbuf->bsFATsecs);
+ fatsectors = fatsectors ? fatsectors : get_32(&sectbuf->bs32.FATSz32);
+ fatsectors *= get_8(&sectbuf->bsFATs);
+
+ if (!fatsectors)
+ return "zero FAT sectors";
+
if (clusters < 0xFFF5) {
/* FAT12 or FAT16 */
-
if (!get_16(&sectbuf->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(&sectbuf->bs16.FileSysType, "FAT32 ", 8)) {
- return "less than 65525 clusters but claims FAT32";
+ return "less than 65525 clusters but claims FAT32";
} else if (memcmp(&sectbuf->bs16.FileSysType, "FAT ", 8)) {
- static char fserr[] =
- "filesystem type \"????????\" not supported";
+ static char fserr[] = "filesystem type \"????????\" not "
+ "supported";
memcpy(fserr + 17, &sectbuf->bs16.FileSysType, 8);
return fserr;
}
@@ -119,8 +119,54 @@ const char *syslinux_check_bootsect(const void *bs)
memcmp(&sectbuf->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(&sectbuf->bsOemName, "NTFS ", 8) &&
+ memcmp(&sectbuf->bsOemName, "MSWIN4.0", 8) &&
+ memcmp(&sectbuf->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(&sectbuf->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(&sectbuf->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]);
}
diff --git a/version b/version
index ae8aecb6..fa556550 100644
--- a/version
+++ b/version
@@ -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