Merge remote-tracking branch 'zytor/master' into merge/elflink/master
authorMatt Fleming <matt.fleming@intel.com>
Thu, 31 May 2012 11:16:22 +0000 (12:16 +0100)
committerMatt Fleming <matt.fleming@intel.com>
Thu, 31 May 2012 12:45:42 +0000 (13:45 +0100)
A lot of development has gone on in the 'master' branch since the last
time we merged; new features, bug fixes, etc, etc.

Conflicts:
Makefile
com32/Makefile
com32/lib/Makefile
com32/lib/syslinux/load_linux.c
com32/modules/Makefile
com32/modules/chain.c
core/bootsect.inc
core/init.inc
version

14 files changed:
1  2 
Makefile
com32/Makefile
com32/chain/Makefile
com32/hdt/Makefile
com32/hdt/hdt-common.h
com32/lib/Makefile
com32/lua/src/Makefile
com32/modules/Makefile
core/fs/ntfs/ntfs.c
core/init.c
core/syslinux.ld
libinstaller/syslxmod.c
mk/elf.mk
mk/syslinux.mk

diff --cc Makefile
+++ b/Makefile
@@@ -34,8 -34,7 +34,8 @@@ include $(MAKEDIR)/syslinux.m
  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
++      com32/sysdump/*.c32 com32/lua/src/*.c32 com32/chain/.*c32 \
 +      com32/lib/*.c32 com32/libutil/*.c32 com32/gpllib/*.c32
  
  # syslinux.exe is BTARGET so as to not require everyone to have the
  # mingw suite installed
diff --cc com32/Makefile
@@@ -1,4 -1,5 +1,5 @@@
 -SUBDIRS = libupload tools lib gpllib libutil modules mboot menu samples rosh cmenu \
 -        hdt gfxboot sysdump lua/src chain
 +SUBDIRS = libupload tools lib elflink/ldlinux gpllib libutil modules mboot \
-         menu samples elflink rosh cmenu hdt gfxboot sysdump lua/src
++        menu samples elflink rosh cmenu hdt gfxboot sysdump lua/src chain
  all tidy dist clean spotless install:
        set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
index 0000000,9d398a8..9a298fa
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,42 +1,42 @@@
 -include $(MAKEDIR)/com32.mk
+ ## -----------------------------------------------------------------------
+ ##
+ ##   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
 -chain.elf: $(OBJS) $(LIBS) $(C_LIBS)
++include $(MAKEDIR)/elf.mk
+ OBJS = chain.o partiter.o utility.o options.o mangle.o
+ all: chain.c32
++chain.c32: $(OBJS) $(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
Simple merge
Simple merge
@@@ -108,67 -67,84 +108,68 @@@ LIBCONSOLE_OBJS = 
        \
        sys/ansi.o                                                      \
        \
 -      sys/libansi.o                                                   \
 -      \
 -      sys/gpxe.o                                                      \
 -      \
 -      sys/ansicon_write.o sys/ansiserial_write.o                      \
 -      \
 -      sys/vesacon_write.o sys/vesaserial_write.o                      \
 -      sys/vesa/initvesa.o sys/vesa/drawtxt.o  sys/vesa/background.o   \
 -      sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o    \
 -      sys/vesa/i915resolution.o                                       \
 -      \
 -      pci/cfgtype.o pci/scan.o pci/bios.o                             \
 -      pci/readb.o pci/readw.o pci/readl.o                             \
 -      pci/writeb.o pci/writew.o pci/writel.o                          \
 -      \
 -      zlib/adler32.o zlib/compress.o zlib/crc32.o                     \
 -      zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o         \
 -      zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o    \
 -      \
 -      libpng/png.o libpng/pngset.o libpng/pngget.o libpng/pngrutil.o  \
 -      libpng/pngtrans.o libpng/pngwutil.o libpng/pngread.o            \
 -      libpng/pngrio.o libpng/pngwio.o libpng/pngwrite.o               \
 -      libpng/pngrtran.o libpng/pngwtran.o libpng/pngmem.o             \
 -      libpng/pngerror.o libpng/pngpread.o                             \
 -      \
 -      jpeg/tinyjpeg.o jpeg/jidctflt.o jpeg/decode1.o jpeg/decode3.o   \
 -      jpeg/grey.o jpeg/yuv420p.o                                      \
 -      jpeg/rgb24.o jpeg/bgr24.o                                       \
 -      jpeg/rgba32.o jpeg/bgra32.o                                     \
 -      \
 -      sys/x86_init_fpu.o math/pow.o math/strtod.o                     \
 +      sys/ansicon_write.o sys/ansiserial_write.o      \
        \
 -      syslinux/idle.o syslinux/reboot.o                               \
 -      syslinux/features.o syslinux/config.o syslinux/serial.o         \
 -      syslinux/ipappend.o syslinux/dsinfo.o syslinux/version.o        \
 -      syslinux/keyboard.o                                             \
 +      syslinux/serial.o
 +
 +LIBOTHER_OBJS = \
 +      atoi.o atol.o atoll.o calloc.o creat.o          \
 +      fgets.o fprintf.o fputc.o       \
 +      putchar.o                               \
 +      getopt.o getopt_long.o                                          \
 +      lrand48.o stack.o memccpy.o memchr.o            \
 +      mempcpy.o memmem.o memmove.o memswap.o  \
 +      perror.o qsort.o seed48.o \
 +      srand48.o sscanf.o strcasecmp.o strcat.o        \
 +      strerror.o              \
 +      strnlen.o                                                       \
 +      strncat.o strndup.o             \
 +      stpncpy.o                                               \
 +      strntoimax.o strntoumax.o strrchr.o strsep.o strspn.o strstr.o  \
 +      strtoimax.o strtok.o strtol.o strtoll.o strtoul.o strtoull.o    \
 +      strtoumax.o vprintf.o vsprintf.o                \
 +      asprintf.o vasprintf.o                  \
 +      vsscanf.o                                                       \
 +      skipspace.o                                                     \
 +      chrreplace.o                                                    \
 +      bufprintf.o                                                     \
 +      inet.o                                                          \
-       \
++      strreplace.o                                                    \
 +      lstrdup.o                                               \
        \
 -      syslinux/memscan.o                                              \
 +      suffix_number.o                                                 \
        \
 -      syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o        \
 -      syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o    \
 -      syslinux/shuffle_rm.o syslinux/zonelist.o                       \
 -      syslinux/dump_mmap.o syslinux/dump_movelist.o                   \
 +      getcwd.o fdopendir.o    \
        \
 -      syslinux/run_default.o syslinux/run_command.o                   \
 -      syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o     \
 +      sys/line_input.o                                \
 +      sys/colortable.o sys/screensize.o                               \
        \
 -      syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o   \
 +      sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o          \
 +      sys/rawcon_write.o              \
 +      sys/null_read.o sys/null_write.o sys/serial_write.o             \
        \
 -      syslinux/load_linux.o syslinux/initramfs.o                      \
 -      syslinux/initramfs_file.o syslinux/initramfs_loadfile.o         \
 -      syslinux/initramfs_archive.o                                    \
 +      sys/xserial_write.o                                             \
        \
 -      syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o                \
 -      syslinux/pxe_dns.o                                              \
 +      sys/ansi.o                                                      \
        \
 -      syslinux/adv.o syslinux/advwrite.o syslinux/getadv.o            \
 -      syslinux/setadv.o                                               \
 +      sys/ansicon_write.o sys/ansiserial_write.o                      \
        \
 -      syslinux/video/fontquery.o syslinux/video/forcetext.o           \
 -      syslinux/video/reportmode.o                                     \
 +      pci/cfgtype.o pci/scan.o pci/bios.o                                     \
 +      pci/readb.o pci/readw.o pci/readl.o                     \
 +      pci/writeb.o pci/writew.o pci/writel.o  \
        \
 +      sys/x86_init_fpu.o math/pow.o math/strtod.o                     \
+       syslinux/disk.o
  
 -# These are the objects which are also imported into the core
 -LIBCOREOBJS =         \
 -      memcpy.o mempcpy.o memset.o memcmp.o memmove.o                  \
 -      strlen.o stpcpy.o strcpy.o strcmp.o strlcpy.o strlcat.o         \
 -      strchr.o strncmp.o strncpy.o                                    \
 -      \
 -      snprintf.o sprintf.o vsnprintf.o                                \
 -      \
 -      dprintf.o vdprintf.o                                            \
 -      \
 -      zalloc.o strdup.o                                               \
 -      \
 -      sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o       \
 -      \
 +CORELIBOBJS = \
 +      memcpy.o memset.o memcmp.o printf.o strncmp.o vfprintf.o        \
 +      strlen.o vsnprintf.o snprintf.o stpcpy.o strcmp.o strdup.o      \
 +      strcpy.o strncpy.o setjmp.o fopen.o fread.o fread2.o puts.o     \
 +      sprintf.o strlcat.o strchr.o strlcpy.o strncasecmp.o ctypes.o   \
 +      fputs.o fwrite2.o fwrite.o fgetc.o fclose.o errno.o lmalloc.o   \
 +      sys/err_read.o sys/err_write.o sys/null_read.o                  \
 +      sys/stdcon_write.o sys/openconsole.o                            \
 +      syslinux/memscan.o                                              \
        libgcc/__ashldi3.o libgcc/__udivdi3.o                           \
        libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o         \
        libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o      \
Simple merge
  
  topdir = ../..
  MAKEDIR = $(topdir)/mk
 -include $(MAKEDIR)/com32.mk
 +include $(MAKEDIR)/elf.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 \
-           ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 whichsys.c32 \
-           hello.c32
+           ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \
 -          whichsys.c32 pxechn.c32
++          whichsys.c32 hello.c32 pxechn.c32
  
  TESTFILES =
  
index 0000000,500d0fd..f54df7e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1388 +1,1388 @@@
 -    .load_config    = generic_load_config,
+ /*
+  * 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,
++    .open_config    = generic_open_config,
+     .readdir        = ntfs_readdir,
+     .iget_root      = ntfs_iget_root,
+     .iget           = ntfs_iget,
+     .next_extent    = ntfs_next_extent,
+ };
diff --cc core/init.c
index 19db1e2,0000000..01319f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,83 -1,0 +1,84 @@@
-                                "It appears you computer has less than "
-                                "%dK of low (DOS)\nRAM.  Syslinux "
-                                "needs at least this amount to boot.  "
-                                "If you get\nthis message in error, "
-                                "hold down the Ctrl key while\nbooting, "
-                                "and I will take your word for it.\n",
-                                mem);
 +#include <core.h>
 +#include <com32.h>
 +#include <sys/io.h>
 +#include <fs.h>
 +#include <bios.h>
 +
 +static uint32_t min_lowmem_heap = 65536;
 +extern char __lowmem_heap[];
 +uint8_t KbdFlags;             /* Check for keyboard escapes */
 +
 +static inline void check_escapes(void)
 +{
 +      com32sys_t ireg, oreg;
 +
 +      ireg.eax.b[1] = 0x02;   /* Check keyboard flags */
 +      __intcall(0x16, &ireg, &oreg);
 +
 +      KbdFlags = oreg.eax.b[0];
 +
 +      /* Ctrl->skip 386 check */
 +      if (oreg.eax.b[0] & 0x04) {
 +              /*
 +               * Now check that there is sufficient low (DOS) memory
 +               *
 +               * NOTE: Linux doesn't use all of real_mode_seg, but we use
 +               * the same segment for COMBOOT images, which can use all 64K.
 +               */
 +              uint16_t mem;
 +
 +              __intcall(0x12, &ireg, &oreg);
 +
 +              mem = ((uint32_t)__lowmem_heap) + min_lowmem_heap + 1023;
 +              mem = mem >> 10;
 +
 +              if (mem < oreg.eax.w[0]) {
 +                      char buf[256];
 +
 +                      snprintf(buf, sizeof(buf),
++                               "It appears your computer has only "
++                               "%dK of low (\"DOS\") RAM.\n"
++                               "This version of Syslinux needs "
++                               "%dK to boot.  "
++                               "If you get this\nmessage in error, "
++                               "hold down the Ctrl key while booting, "
++                               "and I\nwill take your word for it.\n",
++                               oreg.eax.w[0], mem);
 +                      writestr(buf);
 +                      kaboom();
 +              }
 +      }
 +}
 +
 +extern uint32_t BIOS_timer_next;
 +extern uint32_t timer_irq;
 +static inline void bios_timer_init(void)
 +{
 +      unsigned long next;
 +      uint32_t *hook = (uint32_t *)BIOS_timer_hook;
 +
 +      next = *hook;
 +      BIOS_timer_next = next;
 +      *hook = (uint32_t)&timer_irq;
 +}
 +
 +extern void printf_init(void);
 +void init(com32sys_t *regs __unused)
 +{
 +      int i;
 +
 +      /* Initialize timer */
 +      bios_timer_init();
 +
 +      for (i = 0; i < 256; i++)
 +              KbdMap[i] = i;
 +
 +      adjust_screen();
 +      printf_init();
 +
 +      /* Init the memory subsystem */
 +      mem_init();
 +
 +      /* CPU-dependent initialization and related checks. */
 +      check_escapes();
 +}
Simple merge
Simple merge
diff --cc mk/elf.mk
index c5d5055,0000000..5242b2d
mode 100644,000000..100644
--- /dev/null
+++ b/mk/elf.mk
@@@ -1,84 -1,0 +1,85 @@@
-            -I../../core/include
 +## -*- makefile -*- -------------------------------------------------------
 +##   
 +##   Copyright 2008 H. Peter Anvin - 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., 51 Franklin St, Fifth Floor,
 +##   Boston MA 02110-1301, USA; either version 2 of the License, or
 +##   (at your option) any later version; incorporated herein by reference.
 +##
 +## -----------------------------------------------------------------------
 +
 +##
 +## ELF common configurables
 +##
 +
 +include $(MAKEDIR)/syslinux.mk
 +
 +GCCOPT := $(call gcc_ok,-std=gnu99,)
 +GCCOPT += $(call gcc_ok,-m32,)
 +GCCOPT += $(call gcc_ok,-fno-stack-protector,)
 +GCCOPT += $(call gcc_ok,-fwrapv,)
 +GCCOPT += $(call gcc_ok,-freg-struct-return,)
 +GCCOPT += -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3
 +GCCOPT += $(call gcc_ok,-fno-exceptions,)
 +GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
 +GCCOPT += $(call gcc_ok,-fPIE,-fPIC)
 +GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
 +GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
 +GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
 +GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
 +GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
 +
 +com32 = $(topdir)/com32
++core = $(topdir)/core
 +
 +ifneq ($(NOGPL),1)
 +GPLLIB     = $(com32)/gpllib/libcom32gpl.c32
 +GPLINCLUDE = -I$(com32)/gplinclude
 +else
 +GPLLIB     =
 +GPLINCLUDE =
 +endif
 +
 +CFLAGS     = $(GCCOPT) -W -Wall -march=i386 \
 +           -fomit-frame-pointer -D__COM32__ -DDYNAMIC_MODULE \
 +           -nostdinc -iwithprefix include \
 +           -I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE) \
++           -I$(core)/include
 +SFLAGS     = $(GCCOPT) -D__COM32__ -march=i386
 +LDFLAGS    = -m elf_i386 -shared --hash-style=gnu -T $(com32)/lib/elf32.ld
 +
 +LNXCFLAGS  = -I$(com32)/libutil/include -W -Wall -O -g -D_GNU_SOURCE
 +LNXSFLAGS  = -g
 +LNXLDFLAGS = -g
 +
 +C_LIBS           = $(com32)/libutil/libutil_com.c32 $(GPLLIB) \
 +           $(com32)/lib/libcom32.c32
 +C_LNXLIBS  = $(com32)/libutil/libutil_lnx.a \
 +           $(com32)/elflink/ldlinux/ldlinux_lnx.a
 +
 +.SUFFIXES: .lss .c .o
 +
 +.PRECIOUS: %.o
 +%.o: %.S
 +      $(CC) $(SFLAGS) -c -o $@ $<
 +
 +.PRECIOUS: %.o
 +%.o: %.c
 +      $(CC) $(CFLAGS) -c -o $@ $<
 +
 +.PRECIOUS: %.lo
 +%.lo: %.S
 +      $(CC) $(LNXSFLAGS) -c -o $@ $<
 +
 +.PRECIOUS: %.lo
 +%.lo: %.c
 +      $(CC) $(LNXCFLAGS) -c -o $@ $<
 +
 +.PRECIOUS: %.lnx
 +%.lnx: %.lo $(LNXLIBS) $(C_LNXLIBS)
 +      $(CC) $(LNXCFLAGS) -o $@ $^
 +
 +%.c32: %.o
 +      $(LD) $(LDFLAGS) -o $@ $^
diff --cc mk/syslinux.mk
Simple merge