Merge branch 'chaindev'
diff --git a/Makefile.private b/Makefile.private
index 972abc2..92127e9 100644
--- a/Makefile.private
+++ b/Makefile.private
@@ -100,7 +100,3 @@
 unprerel:
 	echo $(LATEST_PRERELNO) > $(PRERELDIR)/.prerel
 	@echo Next release will be $(LATEST_PREREL)
-
-preupload:
-	scp $(PRERELDIR)/$(LATEST_PREREL).* $(UPLOAD)/Testing
-	git push --tags
diff --git a/NEWS b/NEWS
index 88adba1..a84b890 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@
 or EXTLINUX apply to that specific program only; other changes apply
 to all derivatives.
 
+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.
@@ -9,6 +12,12 @@
 	  on virtually all systems since the beginning, and has been
 	  totally broken since 4.00 at least.  Use MEMDISK instead.
 	* chain.c32: Support chaining ReactOS' FreeLdr (Shao Miller)
+	* isohybrid: -m option to add support for Mac EFI booting.
+        * ifmemdsk.c32: Choose boot option based on presence of
+	  MEMDISK.
+	* Remove bogus distributed mk-lba-img binary.
+	* The Syslinux project has a new, cool logo by Abi
+	  "ixxvil" Rasheed (doc/logo/*).
 
 Changes in 4.04:
 	* PXELINUX: Fix handling of unqualified DNS names.
diff --git a/com32/hdt/.gitignore b/com32/hdt/.gitignore
index 9892794..82d5b47 100644
--- a/com32/hdt/.gitignore
+++ b/com32/hdt/.gitignore
@@ -5,3 +5,4 @@
 *.iso
 iso/
 *gz
+hdt*checksums
diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile
index f187346..17fa1ab 100644
--- a/com32/hdt/Makefile
+++ b/com32/hdt/Makefile
@@ -110,10 +110,16 @@
 	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 aac50eb..8e9a9e6 100644
--- a/com32/hdt/hdt-common.c
+++ b/com32/hdt/hdt-common.c
@@ -106,12 +106,36 @@
 	    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 @@
     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 d37fcc8..8c85260 100644
--- a/com32/hdt/hdt-common.h
+++ b/com32/hdt/hdt-common.h
@@ -214,10 +214,12 @@
     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 dcbcaa9..ff744b3 100644
--- a/com32/hdt/hdt-dump-disks.c
+++ b/com32/hdt/hdt-dump-disks.c
@@ -42,6 +42,7 @@
     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 @@
 
     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 @@
 	    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 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 @@
 	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 8c22140..b963e19 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) {
+struct dump_option {
+    char *flag;
+    char *item;
+};
 
-   snprintf(filename,size,"%s/",hardware->dump_path);
+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;
 
-    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));
+    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;
+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)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 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 @@
     /* 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 ad87c29..cef7e69 100644
--- a/com32/hdt/hdt-menu-summary.c
+++ b/com32/hdt/hdt-menu-summary.c
@@ -52,8 +52,7 @@
     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 a1e3923..851b046 100644
--- a/com32/hdt/hdt.c
+++ b/com32/hdt/hdt.c
@@ -74,14 +74,21 @@
 
     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 7b35236..9b9e8a1 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 af9792b..d847440 100644
--- a/com32/include/string.h
+++ b/com32/include/string.h
@@ -42,5 +42,6 @@
 __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/lib/Makefile b/com32/lib/Makefile
index b9ff822..62a322a 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -32,6 +32,7 @@
 	chrreplace.o							\
 	bufprintf.o							\
 	inet.o								\
+	strreplace.o								\
 	\
 	lmalloc.o lstrdup.o						\
 	\
diff --git a/com32/lib/pci/scan.c b/com32/lib/pci/scan.c
index e0974f9..fe00fc2 100644
--- a/com32/lib/pci/scan.c
+++ b/com32/lib/pci/scan.c
@@ -579,14 +579,14 @@
 				    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 0000000..d59efe0
--- /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/modules/ifmemdsk.c b/com32/modules/ifmemdsk.c
new file mode 100644
index 0000000..cfed87f
--- /dev/null
+++ b/com32/modules/ifmemdsk.c
@@ -0,0 +1,392 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   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., 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/****
+ * @file ifmemdsk.c
+ *
+ * This COM32 module detects if there are MEMDISKs established.
+ */
+
+static const char usage_text[] = "\
+Usage:\n\
+  ifmemdsk.c32 [<option> [...]] --info [<option> [...]]\n\
+  ifmemdsk.c32 [<option> [...]] [<detected_cmd>] -- [<not_detected_cmd>]\n\
+\n\
+Options:\n\
+  --info  . . . . . Displays info about MEMDISK(s)\n\
+  --safe-hooks . .  Will scan INT 13h \"safe hook\" chain\n\
+  --mbfts . . . . . Will scan memory for MEMDISK mBFTs\n\
+  --no-sequential   Suppresses probing all drive numbers\n\
+\n\
+If a MEMDISK is found, or if a particular MEMDISK is sought by the options\n\
+and is found, then the 'detected_cmd' action will be taken, else the\n\
+'not_detected_cmd' action will be taken.\n\
+\n";
+
+#include <stdio.h>
+#include <string.h>
+#include <alloca.h>
+#include <com32.h>
+#include <console.h>
+#include <syslinux/boot.h>
+
+/* Pull in MEMDISK common structures */
+#include "../../memdisk/mstructs.h"
+
+/*** Macros */
+#define M_GET_DRIVE_PARAMS (0x08)
+#define M_SEGOFFTOPTR(seg, off) (((seg) << 4) + (off))
+#define M_INT13H M_SEGOFFTOPTR(0x0000, 0x0013 * 4)
+#define M_FREEBASEMEM M_SEGOFFTOPTR(0x0040, 0x0013)
+#define M_TOP M_SEGOFFTOPTR(0x9FFF, 0x0000)
+
+/*** Object types */
+typedef struct mdi s_mdi;
+typedef real_addr_t u_segoff;
+typedef struct safe_hook s_safe_hook;
+typedef struct mBFT s_mbft;
+
+/*** Function types */
+typedef int f_find(void);
+
+/*** Function declarations */
+static const s_mdi * installation_check(int);
+static f_find scan_drives;
+static f_find walk_safe_hooks;
+static const s_safe_hook * is_safe_hook(const void *);
+static const s_mdi * is_memdisk_hook(const s_safe_hook *);
+static f_find scan_mbfts;
+static const s_mbft * is_mbft(const void *);
+static f_find do_nothing;
+static void memdisk_info(const s_mdi *);
+static void boot_args(char **);
+static const char * bootloadername(uint8_t);
+
+/*** Structure/union definitions */
+
+/*** Objects */
+static int show_info = 0;
+
+/*** Function definitions */
+
+int main(int argc, char ** argv) {
+    static f_find * do_scan_drives = scan_drives;
+    static f_find * do_walk_safe_hooks = do_nothing;
+    static f_find * do_scan_mbfts = do_nothing;
+    char ** detected_cmd;
+    char ** not_detected_cmd;
+    char ** cmd;
+    char ** cur_arg;
+    int show_usage;
+    int found;
+
+    (void) argc;
+
+    openconsole(&dev_null_r, &dev_stdcon_w);
+
+    detected_cmd = NULL;
+    not_detected_cmd = NULL;
+    show_usage = 1;
+    for (cur_arg = argv + 1; *cur_arg; ++cur_arg) {
+        /* Check for command divider */
+        if (!strcmp(*cur_arg, "--")) {
+            show_usage = 0;
+            *cur_arg = NULL;
+            not_detected_cmd = cur_arg + 1;
+            break;
+          }
+
+        /* Check for '--info' */
+        if (!strcmp(*cur_arg, "--info")) {
+            show_usage = 0;
+            show_info = 1;
+            continue;
+          }
+
+        /* Other options */
+        if (!strcmp(*cur_arg, "--no-sequential")) {
+            do_scan_drives = do_nothing;
+            continue;
+          }
+
+        if (!strcmp(*cur_arg, "--safe-hooks")) {
+            do_walk_safe_hooks = walk_safe_hooks;
+            continue;
+          }
+
+        if (!strcmp(*cur_arg, "--mbfts")) {
+            do_scan_mbfts = scan_mbfts;
+            continue;
+          }
+
+        /* Check for invalid option */
+        if (!memcmp(*cur_arg, "--", sizeof "--" - 1)) {
+            puts("Invalid option!");
+            show_usage = 1;
+            break;
+          }
+
+        /* Set 'detected_cmd' if it's null */
+        if (!detected_cmd)
+          detected_cmd = cur_arg;
+
+        continue;
+      }
+
+    if (show_usage) {
+        fprintf(stderr, usage_text);
+        return 1;
+      }
+
+    found = 0;
+    found += do_walk_safe_hooks();
+    found += do_scan_mbfts();
+    found += do_scan_drives();
+
+    cmd = found ? detected_cmd : not_detected_cmd;
+    if (cmd && *cmd)
+      boot_args(cmd);
+
+    return 0;
+  }
+
+static const s_mdi * installation_check(int drive) {
+    com32sys_t params, results;
+    int found;
+
+    /* Set parameters for INT 0x13 call */
+    memset(&params, 0, sizeof params);
+    params.eax.w[0] = M_GET_DRIVE_PARAMS << 8;
+    params.edx.w[0] = drive;
+    /* 'ME' 'MD' 'IS' 'K?' */
+    params.eax.w[1] = 0x454D;
+    params.ecx.w[1] = 0x444D;
+    params.edx.w[1] = 0x5349;
+    params.ebx.w[1] = 0x3F4B;
+
+    /* Perform the call */
+    __intcall(0x13, &params, &results);
+
+    /* Check result */
+    found = (
+        /* '!M' 'EM' 'DI' 'SK' */
+        results.eax.w[1] == 0x4D21 &&
+        results.ecx.w[1] == 0x4D45 &&
+        results.edx.w[1] == 0x4944 &&
+        results.ebx.w[1] == 0x4B53
+      );
+
+    if (found)
+      return MK_PTR(results.es, results.edi.w[0]);
+
+    return NULL;
+  }
+
+static int scan_drives(void) {
+    int found, drive;
+    const s_mdi * mdi;
+
+    for (found = drive = 0; drive <= 0xFF; ++drive) {
+        mdi = installation_check(drive);
+        if (!mdi)
+          continue;
+
+        memdisk_info(mdi);
+        ++found;
+        continue;
+      }
+
+    return found;
+  }
+
+static int walk_safe_hooks(void) {
+    static const u_segoff * const int13 = (void *) M_INT13H;
+    const void * addr;
+    int found;
+    const s_safe_hook * hook;
+    const s_mdi * mdi;
+
+    /* INT 0x13 vector */
+    addr = MK_PTR(int13->seg_off.segment, int13->seg_off.offset);
+    found = 0;
+    while (addr) {
+        hook = is_safe_hook(addr);
+        if (!hook)
+          break;
+
+        mdi = is_memdisk_hook(hook);
+        if (mdi) {
+            memdisk_info(mdi);
+            ++found;
+          }
+
+        addr = MK_PTR(
+            hook->old_hook.seg_off.segment,
+            hook->old_hook.seg_off.offset
+          );
+        continue;
+      }
+    return found;
+  }
+
+static const s_safe_hook * is_safe_hook(const void * addr) {
+    static const char magic[] = "$INT13SF";
+    const s_safe_hook * const test = addr;
+
+    if (memcmp(test->signature, magic, sizeof magic - 1))
+      return NULL;
+
+    return test;
+  }
+
+static const s_mdi * is_memdisk_hook(const s_safe_hook * hook) {
+    static const char magic[] = "MEMDISK";
+    const s_mbft * mbft;
+
+    if (memcmp(hook->vendor, magic, sizeof magic - 1))
+      return NULL;
+
+    /* An mBFT is always aligned */
+    mbft = MK_PTR(hook->mbft >> 4, 0);
+    return &mbft->mdi;
+  }
+
+static int scan_mbfts(void) {
+    static const uint16_t * const free_base_mem = (void *) M_FREEBASEMEM;
+    static const void * const top = (void *) M_TOP;
+    const void * addr;
+    const s_mbft * mbft;
+    int found;
+
+    found = 0;
+    for (addr = MK_PTR(*free_base_mem << 4, 0); addr < top; addr += 1 << 4) {
+        if (!(mbft = is_mbft(addr)))
+          continue;
+
+        memdisk_info(&mbft->mdi);
+        ++found;
+        continue;
+      }
+
+    return found;
+  }
+
+static const s_mbft * is_mbft(const void * addr) {
+    static const char magic[] = "mBFT";
+    const s_mbft * const test = addr;
+    const uint8_t * ptr, * end;
+    uint8_t chksum;
+
+    if (memcmp(test->acpi.signature, magic, sizeof magic - 1))
+      return NULL;
+
+    if (test->acpi.length != sizeof *test)
+      return NULL;
+
+    end = (void *) (test + 1);
+    chksum = 0;
+    for (ptr = addr; ptr < end; ++ptr)
+      chksum += *ptr;
+    if (chksum)
+      return NULL;
+
+    /* Looks like it's an mBFT! */
+    return test;
+  }
+
+static int do_nothing(void) {
+    return 0;
+  }
+
+static void memdisk_info(const s_mdi * mdi) {
+    const char * cmdline;
+
+    if (!show_info)
+      return;
+
+    cmdline = MK_PTR(
+        mdi->cmdline.seg_off.segment,
+        mdi->cmdline.seg_off.offset
+      );
+    printf(
+        "Found MEMDISK version %u.%02u:\n"
+        "  diskbuf == 0x%08X, disksize == %u sectors\n"
+        "  bootloaderid == 0x%02X (%s),\n"
+        "  cmdline: %s\n",
+        mdi->version_major,
+        mdi->version_minor,
+        mdi->diskbuf,
+        mdi->disksize,
+        mdi->bootloaderid,
+        bootloadername(mdi->bootloaderid),
+        cmdline
+      );
+    return;
+  }
+
+/* This function copyright H. Peter Anvin */
+static void boot_args(char **args)
+{
+    int len = 0, a = 0;
+    char **pp;
+    const char *p;
+    char c, *q, *str;
+
+    for (pp = args; *pp; pp++)
+	len += strlen(*pp) + 1;
+
+    q = str = alloca(len);
+    for (pp = args; *pp; pp++) {
+	p = *pp;
+	while ((c = *p++))
+	    *q++ = c;
+	*q++ = ' ';
+	a = 1;
+    }
+    q -= a;
+    *q = '\0';
+
+    if (!str[0])
+	syslinux_run_default();
+    else
+	syslinux_run_command(str);
+}
+
+/* This function copyright H. Peter Anvin */
+static const char *bootloadername(uint8_t id)
+{
+    static const struct {
+	uint8_t id, mask;
+	const char *name;
+    } *lp, list[] = {
+	{0x00, 0xf0, "LILO"}, 
+	{0x10, 0xf0, "LOADLIN"},
+	{0x31, 0xff, "SYSLINUX"},
+	{0x32, 0xff, "PXELINUX"},
+	{0x33, 0xff, "ISOLINUX"},
+	{0x34, 0xff, "EXTLINUX"},
+	{0x30, 0xf0, "Syslinux family"},
+	{0x40, 0xf0, "Etherboot"},
+	{0x50, 0xf0, "ELILO"},
+	{0x70, 0xf0, "GrUB"},
+	{0x80, 0xf0, "U-Boot"},
+	{0xA0, 0xf0, "Gujin"},
+	{0xB0, 0xf0, "Qemu"},
+	{0x00, 0x00, "unknown"}
+    };
+
+    for (lp = list;; lp++) {
+	if (((id ^ lp->id) & lp->mask) == 0)
+	    return lp->name;
+    }
+}
+
diff --git a/core/bootsect.inc b/core/bootsect.inc
index 6c20409..9e47e1a 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 @@
 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 @@
 		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 @@
 .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 0000000..500d0fd
--- /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 0000000..721a78d
--- /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 0000000..4938696
--- /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 f62f55b..a2f859d 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -37,6 +37,8 @@
 		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/diag/geodsp/Makefile b/diag/geodsp/Makefile
index bf26104..119ccbc 100644
--- a/diag/geodsp/Makefile
+++ b/diag/geodsp/Makefile
@@ -47,9 +47,9 @@
 
 tidy dist:
 	rm -Rf *.lst *.img
+	rm -f mk-lba-img
 
 clean: tidy
-	rm -f mk-lba-img
 
 spotless: clean
 	rm -f $(BTARGET)
diff --git a/doc/logo/LICENSE b/doc/logo/LICENSE
new file mode 100644
index 0000000..e753087
--- /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/doc/logo/syslinux-100.png b/doc/logo/syslinux-100.png
new file mode 100644
index 0000000..647635a
--- /dev/null
+++ b/doc/logo/syslinux-100.png
Binary files differ
diff --git a/dos/Makefile b/dos/Makefile
index 5e5fc63..f942008 100644
--- a/dos/Makefile
+++ b/dos/Makefile
@@ -27,7 +27,7 @@
 	   -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 b5fdfc5..fa4bf38 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 @@
     /*
      * 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 @@
     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/extlinux/Makefile b/extlinux/Makefile
index 12213d8..865c7a6 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
old mode 100755
new mode 100644
index e574051..5da89e2
--- 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 @@
     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 @@
        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 @@
     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 @@
 {
     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 @@
 	}
 	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 @@
 
 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 @@
 	/* 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 @@
 }
 #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 @@
 	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 @@
         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 @@
 	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 @@
 	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 0000000..d907d45
--- /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/fat.c b/libinstaller/fat.c
deleted file mode 100644
index 9cde00c..0000000
--- a/libinstaller/fat.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
- *   Copyright 2009-2010 Intel Corporation; author H. Peter Anvin
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   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.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * fat.c - Initial sanity check for FAT-based installers
- */
-
-#define _XOPEN_SOURCE 500	/* Required on glibc 2.x */
-#define _BSD_SOURCE
-#include <stdio.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-#include "syslinux.h"
-#include "syslxint.h"
-
-void syslinux_make_bootsect(void *bs)
-{
-    struct boot_sector *bootsect = bs;
-    const struct boot_sector *sbs =
-	(const struct boot_sector *)boot_sector;
-
-    memcpy(&bootsect->bsHead, &sbs->bsHead, bsHeadLen);
-    memcpy(&bootsect->bsCode, &sbs->bsCode, bsCodeLen);
-}
-
-/*
- * Check to see that what we got was indeed an MS-DOS boot sector/superblock;
- * Return NULL if OK and otherwise an error message;
- */
-const char *syslinux_check_bootsect(const void *bs)
-{
-    int sectorsize;
-    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";
-
-    sectors = get_16(&sectbuf->bsSectors);
-    sectors = sectors ? sectors : get_32(&sectbuf->bsHugeSectors);
-
-    dsectors = sectors - get_16(&sectbuf->bsResSectors);
-
-    fatsectors = get_16(&sectbuf->bsFATsecs);
-    fatsectors = fatsectors ? fatsectors : get_32(&sectbuf->bs32.FATSz32);
-    fatsectors *= get_8(&sectbuf->bsFATs);
-    dsectors -= fatsectors;
-
-    rootdirents = get_16(&sectbuf->bsRootDirEnts);
-    dsectors -= (rootdirents + sectorsize / 32 - 1) / sectorsize;
-
-    if (dsectors < 0)
-	return "negative number of data sectors";
-
-    if (fatsectors == 0)
-	return "zero FAT sectors";
-
-    clusters = dsectors / clustersize;
-
-    if (clusters < 0xFFF5) {
-	/* FAT12 or FAT16 */
-
-	if (!get_16(&sectbuf->bsFATsecs))
-	    return "zero FAT sectors (FAT12/16)";
-
-	if (get_8(&sectbuf->bs16.BootSignature) == 0x29) {
-	    if (!memcmp(&sectbuf->bs16.FileSysType, "FAT12   ", 8)) {
-		if (clusters >= 0xFF5)
-		    return "more than 4084 clusters but claims FAT12";
-	    } else if (!memcmp(&sectbuf->bs16.FileSysType, "FAT16   ", 8)) {
-		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";
-	    } else if (memcmp(&sectbuf->bs16.FileSysType, "FAT     ", 8)) {
-		static char fserr[] =
-		    "filesystem type \"????????\" not supported";
-		memcpy(fserr + 17, &sectbuf->bs16.FileSysType, 8);
-		return fserr;
-	    }
-	}
-    } else if (clusters < 0x0FFFFFF5) {
-	/*
-	 * FAT32...
-	 *
-	 * Moving the FileSysType and BootSignature was a lovely stroke
-	 * of M$ idiocy...
-	 */
-	if (get_8(&sectbuf->bs32.BootSignature) != 0x29 ||
-	    memcmp(&sectbuf->bs32.FileSysType, "FAT32   ", 8))
-	    return "missing FAT32 signature";
-    } else {
-	return "impossibly large number of clusters";
-    }
-
-    return NULL;
-}
diff --git a/libinstaller/fs.c b/libinstaller/fs.c
new file mode 100644
index 0000000..179629e
--- /dev/null
+++ b/libinstaller/fs.c
@@ -0,0 +1,172 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   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
+ *   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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * fs.c - Generic sanity check for FAT/NTFS-based installers
+ */
+
+#define _XOPEN_SOURCE 500	/* Required on glibc 2.x */
+#define _BSD_SOURCE
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "syslinux.h"
+#include "syslxint.h"
+#include "syslxcom.h"
+#include "syslxfs.h"
+
+void syslinux_make_bootsect(void *bs, int fs_type)
+{
+    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);
+    }
+}
+
+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;
+
+    sectorsize = get_16(&sectbuf->bsBytesPerSec);
+
+    clustersize = get_8(&sectbuf->bsSecPerClust);
+    if (clustersize == 0 || (clustersize & (clustersize - 1)))
+	return "impossible cluster size on an FAT volume";
+
+    sectors = get_16(&sectbuf->bsSectors);
+    sectors = sectors ? sectors : get_32(&sectbuf->bsHugeSectors);
+
+    dsectors = sectors - get_16(&sectbuf->bsResSectors);
+
+    fatsectors = get_16(&sectbuf->bsFATsecs);
+    fatsectors = fatsectors ? fatsectors : get_32(&sectbuf->bs32.FATSz32);
+    fatsectors *= get_8(&sectbuf->bsFATs);
+    dsectors -= fatsectors;
+
+    rootdirents = get_16(&sectbuf->bsRootDirEnts);
+    dsectors -= (rootdirents + sectorsize / 32 - 1) / sectorsize;
+
+    if (dsectors < 0)
+	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)";
+
+	if (get_8(&sectbuf->bs16.BootSignature) == 0x29) {
+	    if (!memcmp(&sectbuf->bs16.FileSysType, "FAT12   ", 8)) {
+		if (clusters >= 0xFF5)
+		    return "more than 4084 clusters but claims FAT12";
+	    } else if (!memcmp(&sectbuf->bs16.FileSysType, "FAT16   ", 8)) {
+		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";
+	    } else if (memcmp(&sectbuf->bs16.FileSysType, "FAT     ", 8)) {
+		static char fserr[] = "filesystem type \"????????\" not "
+		    "supported";
+		memcpy(fserr + 17, &sectbuf->bs16.FileSysType, 8);
+		return fserr;
+	    }
+	}
+    } else if (clusters < 0x0FFFFFF5) {
+	/*
+	 * FAT32...
+	 *
+	 * Moving the FileSysType and BootSignature was a lovely stroke
+	 * of M$ idiocy...
+	 */
+	if (get_8(&sectbuf->bs32.BootSignature) != 0x29 ||
+	    memcmp(&sectbuf->bs32.FileSysType, "FAT32   ", 8))
+	    return "missing FAT32 signature";
+    } else {
+	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 7ef919a..c339480 100644
--- a/libinstaller/linuxioctl.h
+++ b/libinstaller/linuxioctl.h
@@ -15,7 +15,6 @@
 #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 */
 
 #undef SECTOR_SIZE		/* Defined in msdos_fs.h for no good reason */
 #undef SECTOR_BITS
@@ -25,9 +24,7 @@
 # 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
+#define FAT_IOCTL_SET_ATTRIBUTES	_IOW('r', 0x11, __u32)
 
 #include <linux/fiemap.h>	/* FIEMAP definitions */
 
diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c
index 9cf6f18..214f7fc 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 710d30e..8b86f88 100644
--- a/libinstaller/syslinux.h
+++ b/libinstaller/syslinux.h
@@ -40,10 +40,10 @@
 #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 1de85aa..651f982 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;
 
@@ -133,6 +135,8 @@
 	    ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
 	    break;
 	}
+    case NTFS:
+        break;
 	default:
 	    break;
 	}
@@ -163,6 +167,8 @@
 	    ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
 	    break;
 	}
+    case NTFS:
+        break;
 	default:
 	    break;
 	}
diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h
index bf186ca..8b3b461 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 0000000..7a23146
--- /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 7c9da51..2e317d0 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 @@
 } __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 @@
     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;
 
-static inline int fat_check_sb_fields(const struct boot_sector *sb)
+    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 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 8847b73..2436845 100644
--- a/libinstaller/syslxmod.c
+++ b/libinstaller/syslxmod.c
@@ -109,7 +109,7 @@
     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 7ceb3ba..e081a00 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 82bf111..08a3ed4 100644
--- a/linux/Makefile
+++ b/linux/Makefile
@@ -11,7 +11,7 @@
 ## -----------------------------------------------------------------------
 
 ##
-## Linux FAT installer
+## Linux FAT/NTFS installer
 ##
 
 topdir = ..
@@ -28,7 +28,7 @@
 	   ../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 c7a9ecc..4b13b7f 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 @@
 	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 @@
 	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 @@
     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 1b60905..c66b4dd 100644
--- a/mbr/altmbr.S
+++ b/mbr/altmbr.S
@@ -204,7 +204,7 @@
 5:
 	decb	(partition)
 	jz	boot
-	addw	$16, %bx
+	addw	$16, %si
 	loopw	5b
 
 	popw	%cx			/* %cx <- 4    */
diff --git a/mbr/isohdpfx.S b/mbr/isohdpfx.S
index 2784fb8..17e1efe 100644
--- a/mbr/isohdpfx.S
+++ b/mbr/isohdpfx.S
@@ -66,6 +66,37 @@
 	.globl	_start
 _start:
 	.byte	0x33, 0xed	/* xorw	%bp, %bp */
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	.byte	0x33, 0xed	/* xorw	%bp, %bp */
 	cli
 	movw	%bp, %ss
 	movw	$stack, %sp
diff --git a/mtools/Makefile b/mtools/Makefile
index 3e172fd..78cea1e 100755
--- a/mtools/Makefile
+++ b/mtools/Makefile
@@ -8,7 +8,7 @@
 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 ac189c6..c65021b 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 @@
     /*
      * 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 @@
     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/Makefile b/utils/Makefile
index acda8c0..44cb54f 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -51,7 +51,7 @@
 	$(PERL) isohdpfxarray.pl $(ISOHDPFX) > $@
 
 isohybrid: isohybrid.o isohdpfx.o
-	$(CC) $(LDFLAGS) -o $@ $^
+	$(CC) $(LDFLAGS) -luuid -o $@ $^
 
 gethostip: gethostip.o
 	$(CC) $(LDFLAGS) -o $@ $^
diff --git a/utils/isohybrid.c b/utils/isohybrid.c
index 8a60531..ac04bfd 100644
--- a/utils/isohybrid.c
+++ b/utils/isohybrid.c
@@ -36,20 +36,25 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <inttypes.h>
+#include <uuid/uuid.h>
 
 #include "isohybrid.h"
 
 char *prog = NULL;
 extern int opterr, optind;
+struct stat isostat;
+unsigned int padding = 0;
+
+uuid_t disk_uuid, part_uuid, iso_uuid;
 
 uint8_t mode = 0;
-enum { VERBOSE = 1 };
+enum { VERBOSE = 1 , EFI = 2 , MAC = 4};
 
 /* user options */
 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) */
@@ -61,10 +66,150 @@
 uint32_t catoffset = 0;
 uint32_t c = 0, cc = 0, cs = 0;
 
+uint32_t psize = 0, isosize = 0;
+
 /* boot catalogue parameters */
 uint32_t de_lba = 0;
 uint16_t de_seg = 0, de_count = 0, de_mbz2 = 0;
 uint8_t de_boot = 0, de_media = 0, de_sys = 0, de_mbz1 = 0;
+uint32_t efi_lba = 0, mac_lba = 0;
+uint16_t efi_count = 0, mac_count = 0;
+uint8_t efi_boot = 0, efi_media = 0, efi_sys = 0;
+
+int apm_parts = 3;
+
+uint8_t afp_header[] = { 0x45, 0x52, 0x08, 0x00, 0x00, 0x00, 0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+uuid_t efi_system_partition = {0xC1, 0x2A, 0x73, 0x28, 0xF8, 0x1F, 0x11, 0xD2, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B};
+uuid_t basic_partition = {0xEB,0xD0,0xA0,0xA2,0xB9,0xE5,0x44,0x33,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7};
+uuid_t hfs_partition = {0x48, 0x46, 0x53, 0x00, 0x00, 0x00, 0x11, 0xAA, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC};
+
+uint32_t crc_tab[256] =
+{
+    0, 0x77073096, 0xEE0E612C, 0x990951BA,
+    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+struct iso_primary_descriptor {
+    uint8_t ignore [80];
+    uint32_t size;
+    uint8_t ignore2 [44];
+    uint16_t block_size;
+};
+
+struct gpt_header {
+    uint64_t signature;
+    uint32_t revision;
+    uint32_t headerSize;
+    uint32_t headerCRC;
+    uint32_t reserved;
+    uint64_t currentLBA;
+    uint64_t backupLBA;
+    uint64_t firstUsableLBA;
+    uint64_t lastUsableLBA;
+    uuid_t diskGUID;
+    uint64_t partitionEntriesLBA;
+    uint32_t numParts;
+    uint32_t sizeOfPartitionEntries;
+    uint32_t partitionEntriesCRC;
+    uint8_t reserved2[420];
+};
+
+struct gpt_part_header {
+    uuid_t partTypeGUID;
+    uuid_t partGUID;
+    uint64_t firstLBA;
+    uint64_t lastLBA;
+    uint64_t attributes;
+    uint16_t name[36];
+};
+
+#define APM_OFFSET 2048
+
+struct apple_part_header {
+    uint16_t        signature;      /* expected to be MAC_PARTITION_MAGIC */
+    uint16_t        res1;
+    uint32_t        map_count;      /* # blocks in partition map */
+    uint32_t        start_block;    /* absolute starting block # of partition */
+    uint32_t        block_count;    /* number of blocks in partition */
+    char            name[32];       /* partition name */
+    char            type[32];       /* string type description */
+    uint32_t        data_start;     /* rel block # of first data block */
+    uint32_t        data_count;     /* number of data blocks */
+    uint32_t        status;         /* partition status bits */
+    uint32_t        boot_start;
+    uint32_t        boot_count;
+    uint32_t        boot_load;
+    uint32_t        boot_load2;
+    uint32_t        boot_entry;
+    uint32_t        boot_entry2;
+    uint32_t        boot_cksum;
+    char            processor[16];  /* Contains 680x0, x=0,2,3,4; or empty */
+    uint32_t        driver_sig;
+    char            _padding[372];
+};
 
 
 void
@@ -89,6 +234,8 @@
     printf(FMT, "   -o --offset", "Specify partition offset (default 0)");
     printf(FMT, "   -t --type", "Specify partition type (default 0x17)");
     printf(FMT, "   -i --id", "Specify MBR ID (default random)");
+    printf(FMT, "   -u --uefi", "Build EFI bootable image");
+    printf(FMT, "   -m --mac", "Add AFP table support");
 
     printf("\n");
     printf(FMT, "   --forcehd0", "Assume we are loaded as disk ID 0");
@@ -122,6 +269,8 @@
         { "forcehd0", no_argument, NULL, 'f' },
         { "ctrlhd0", no_argument, NULL, 'c' },
         { "partok", no_argument, NULL, 'p'},
+	{ "uefi", no_argument, NULL, 'u'},
+	{ "mac", no_argument, NULL, 'm'},
 
         { "help", no_argument, NULL, '?' },
         { "verbose", no_argument, NULL, 'v' },
@@ -151,6 +300,8 @@
             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':
@@ -183,6 +334,18 @@
             partok = 1;
             break;
 
+	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':
             mode |= VERBOSE;
             break;
@@ -207,6 +370,33 @@
     return optind;
 }
 
+uint16_t
+bendian_short(const uint16_t s)
+{
+    uint16_t r = 1;
+
+    if (!*(uint8_t *)&r)
+        return s;
+
+    r = (s & 0x00FF) << 8 | (s & 0xFF00) >> 8;
+
+    return r;
+}
+
+
+uint32_t
+bendian_int(const uint32_t s)
+{
+    uint32_t r = 1;
+
+    if (!*(uint8_t *)&r)
+        return s;
+
+    r = (s & 0x000000FF) << 24 | (s & 0xFF000000) >> 24
+        | (s & 0x0000FF00) << 8 | (s & 0x00FF0000) >> 8;
+
+    return r;
+}
 
 uint16_t
 lendian_short(const uint16_t s)
@@ -236,6 +426,22 @@
     return r;
 }
 
+uint64_t
+lendian_64(const uint64_t s)
+{
+	uint64_t r = 1;
+
+	if (*(uint8_t *)&r)
+		return s;
+
+	r = (s & 0x00000000000000FF) << 56 | (s & 0xFF00000000000000) >> 56
+	     | (s & 0x000000000000FF00) << 40 | (s & 0x00FF000000000000) >> 40
+	     | (s & 0x0000000000FF0000) << 24 | (s & 0x0000FF0000000000) >> 24
+	     | (s & 0x00000000FF000000) << 8 | (s & 0x000000FF00000000) >> 8;
+
+	return r;
+}
+
 
 int
 check_banner(const uint8_t *buf)
@@ -314,6 +520,43 @@
 }
 
 
+int
+read_efi_section(const uint8_t *buf)
+{
+	unsigned char header_indicator;
+	unsigned char platform_id;
+	short count;
+
+	memcpy(&header_indicator, buf++, 1);
+	memcpy(&platform_id, buf++, 1);
+
+	memcpy(&count, buf, 2);
+	count = lendian_short(count);
+	buf += 2;
+
+	if (platform_id == 0xef)
+		return 0;
+
+	return 1;
+}
+
+int
+read_efi_catalogue(const uint8_t *buf, uint16_t *count, uint32_t *lba)
+{
+    buf += 6;
+
+    memcpy(count, buf, 2);
+    *count = lendian_short(*count);
+    buf += 2;
+
+    memcpy(lba, buf, 4);
+    *lba = lendian_int(*lba);
+    buf += 6;
+
+    return 0;
+}
+
+
 void
 display_catalogue(void)
 {
@@ -327,12 +570,11 @@
     printf("de_mbz2: %hu\n", de_mbz2);
 }
 
-
 int
 initialise_mbr(uint8_t *mbr)
 {
     int i = 0;
-    uint32_t psize = 0, tmp = 0;
+    uint32_t tmp = 0;
     uint8_t ptype = 0, *rbm = mbr;
     uint8_t bhead = 0, bsect = 0, bcyle = 0;
     uint8_t ehead = 0, esect = 0, ecyle = 0;
@@ -340,6 +582,17 @@
     extern unsigned char isohdpfx[][MBRSIZE];
 
     memcpy(mbr, &isohdpfx[hd0 + 3 * partok], MBRSIZE);
+
+    if (mode & MAC) {
+	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);
@@ -392,6 +645,40 @@
             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;
@@ -401,7 +688,6 @@
     return mbr - rbm;
 }
 
-
 void
 display_mbr(const uint8_t *mbr, size_t len)
 {
@@ -431,14 +717,179 @@
 }
 
 
+uint32_t chksum_crc32 (unsigned char *block, unsigned int length)
+{
+	register unsigned long crc;
+	unsigned long i;
+
+	crc = 0xFFFFFFFF;
+	for (i = 0; i < length; i++)
+	{
+		crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
+	}
+	return (crc ^ 0xFFFFFFFF);
+}
+
+void
+reverse_uuid(uuid_t uuid)
+{
+	uint8_t t, *p = (uint8_t *)uuid;
+
+	t = p[0]; p[0] = p[3]; p[3] = t;
+	t = p[1]; p[1] = p[2]; p[2] = t;
+	t = p[4]; p[4] = p[5]; p[5] = t;
+	t = p[6]; p[6] = p[7]; p[7] = t;
+}
+
+void
+initialise_gpt(uint8_t *gpt, uint32_t current, uint32_t alternate, int primary)
+{
+    struct gpt_header *header = (struct gpt_header *)gpt;
+    struct gpt_part_header *part;
+    int hole = 0;
+    int gptsize = 128 / 4 + 2;
+
+    if (mac_lba) {
+	/* 2048 bytes per partition, plus round to 2048 boundary */
+	hole = (apm_parts * 4) + 2;
+    }
+
+    if (primary) {
+	uuid_generate(disk_uuid);
+	reverse_uuid(disk_uuid);
+    }
+
+    header->signature = lendian_64(0x5452415020494645);
+    header->revision = lendian_int(0x010000);
+    header->headerSize = lendian_int(0x5c);
+    header->currentLBA = lendian_64(current);
+    header->backupLBA = lendian_64(alternate);
+    header->firstUsableLBA = lendian_64(gptsize + hole);
+    header->lastUsableLBA = lendian_64((isostat.st_size + padding)/512 -
+				       gptsize);
+    if (primary)
+	header->partitionEntriesLBA = lendian_64(0x02 + hole);
+    else
+	header->partitionEntriesLBA = lendian_64(current - (128 / 4));
+    header->numParts = lendian_int(0x80);
+    header->sizeOfPartitionEntries = lendian_int(0x80);
+    memcpy(header->diskGUID, disk_uuid, sizeof(uuid_t));
+
+    if (primary)
+	gpt += sizeof(struct gpt_header) + hole * 512;
+    else
+	gpt -= header->sizeOfPartitionEntries * header->numParts;
+
+    part = (struct gpt_part_header *)gpt;
+    if (primary) {
+	uuid_generate(part_uuid);
+	uuid_generate(iso_uuid);
+	reverse_uuid(part_uuid);
+	reverse_uuid(iso_uuid);
+    }
+
+    memcpy(part->partGUID, iso_uuid, sizeof(uuid_t));
+    memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
+    part->firstLBA = lendian_64(0);
+    part->lastLBA = lendian_64(psize);
+    memcpy(part->name, "ISOHybrid ISO", 28);
+
+    gpt += sizeof(struct gpt_part_header);
+    part++;
+
+    memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
+    memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
+    part->firstLBA = lendian_64(efi_lba * 4);
+    part->lastLBA = lendian_64(part->firstLBA + efi_count - 1);
+    memcpy(part->name, "ISOHybrid", 20);
+
+    gpt += sizeof(struct gpt_part_header);
+
+    if (mac_lba) {
+	gpt += sizeof(struct gpt_part_header);
+
+	part++;
+
+	memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
+	memcpy(part->partTypeGUID, hfs_partition, sizeof(uuid_t));
+	part->firstLBA = lendian_64(mac_lba * 4);
+	part->lastLBA = lendian_64(part->firstLBA + mac_count - 1);
+	memcpy(part->name, "ISOHybrid", 20);
+
+	part--;
+    }
+
+    part--;
+
+    header->partitionEntriesCRC = lendian_int (chksum_crc32((uint8_t *)part,
+			   header->numParts * header->sizeOfPartitionEntries));
+
+    header->headerCRC = lendian_int(chksum_crc32((uint8_t *)header,
+						 header->headerSize));
+}
+
+void
+initialise_apm(uint8_t *gpt, uint32_t start)
+{
+    struct apple_part_header *part = (struct apple_part_header *)gpt;
+
+    part->signature = bendian_short(0x504d);
+    part->map_count = bendian_int(apm_parts);
+    part->start_block = bendian_int(1);
+    part->block_count = bendian_int(0x10);
+    strcpy(part->name, "Apple");
+    strcpy(part->type, "Apple_partition_map");
+    part->data_start = bendian_int(0);
+    part->data_count = bendian_int(10);
+    part->status = bendian_int(0x03);
+
+    part = (struct apple_part_header *)(gpt + 2048);
+
+    part->signature = bendian_short(0x504d);
+    part->map_count = bendian_int(3);
+    part->start_block = bendian_int(efi_lba);
+    part->block_count = bendian_int(efi_count);
+    strcpy(part->name, "EFI");
+    strcpy(part->type, "Apple_HFS");
+    part->data_start = bendian_int(0);
+    part->data_count = bendian_int(efi_count);
+    part->status = bendian_int(0x33);
+
+    part = (struct apple_part_header *)(gpt + 4096);
+
+    if (mac_lba)
+    {
+	part->signature = bendian_short(0x504d);
+	part->map_count = bendian_int(3);
+	part->start_block = bendian_int(mac_lba);
+	part->block_count = bendian_int(mac_count);
+	strcpy(part->name, "EFI");
+	strcpy(part->type, "Apple_HFS");
+	part->data_start = bendian_int(0);
+	part->data_count = bendian_int(mac_count);
+	part->status = bendian_int(0x33);
+    } else {
+	part->signature = bendian_short(0x504d);
+	part->map_count = bendian_int(3);
+	part->start_block = bendian_int((start/2048) + 10);
+	part->block_count = bendian_int(efi_lba - start/2048 - 10);
+	strcpy(part->name, "ISO");
+	strcpy(part->type, "Apple_Free");
+	part->data_start = bendian_int(0);
+	part->data_count = bendian_int(efi_lba - start/2048 - 10);
+	part->status = bendian_int(0x01);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
     int i = 0;
     FILE *fp = NULL;
-    struct stat isostat;
     uint8_t *buf = NULL, *bufz = NULL;
-    int cylsize = 0, frac = 0, padding = 0;
+    int cylsize = 0, frac = 0;
+    size_t orig_gpt_size, free_space, gpt_size;
+    struct iso_primary_descriptor descriptor;
 
     prog = strcpy(alloca(strlen(argv[0]) + 1), argv[0]);
     i = check_option(argc, argv);
@@ -450,10 +901,21 @@
         usage();
         return 1;
     }
+
+    if ((mode & EFI) && offset)
+	errx(1, "%s: --offset is invalid with UEFI images\n", argv[0]);
+
     srand(time(NULL) << (getppid() << getpid()));
 
     if (!(fp = fopen(argv[0], "r+")))
         err(1, "could not open file `%s'", argv[0]);
+
+    if (fseek(fp, (16 << 11), SEEK_SET))
+        err(1, "%s: seek error - 0", argv[0]);
+
+    if (fread(&descriptor, sizeof(char), sizeof(descriptor), fp) != sizeof(descriptor))
+        err(1, "%s: read error - 0", argv[0]);
+
     if (fseek(fp, 17 * 2048, SEEK_SET))
         err(1, "%s: seek error - 1", argv[0]);
 
@@ -485,6 +947,38 @@
     if (mode & VERBOSE)
         display_catalogue();
 
+    buf += 32;
+
+    if (mode & EFI)
+    {
+	if (!read_efi_section(buf)) {
+	    buf += 32;
+	    if (!read_efi_catalogue(buf, &efi_count, &efi_lba) && efi_lba) {
+		offset = 0;
+	    } else {
+		errx(1, "%s: invalid efi catalogue", argv[0]);
+	    }
+	} else {
+	    errx(1, "%s: unable to find efi image", argv[0]);
+	}
+    }
+
+    buf += 32;
+
+    if (mode & MAC)
+    {
+	if (!read_efi_section(buf)) {
+	    buf += 32;
+	    if (!read_efi_catalogue(buf, &mac_count, &mac_lba) && mac_lba) {
+		offset = 0;
+	    } else {
+		errx(1, "%s: invalid efi catalogue", argv[0]);
+	    }
+	} else {
+	    errx(1, "%s: unable to find mac efi image", argv[0]);
+	}
+    }
+
     if (fseek(fp, (de_lba * 2048 + 0x40), SEEK_SET))
         err(1, "%s: seek error - 3", argv[0]);
 
@@ -501,6 +995,9 @@
     if (stat(argv[0], &isostat))
         err(1, "%s", argv[0]);
 
+    isosize = lendian_int(descriptor.size) * lendian_short(descriptor.block_size);
+    free_space = isostat.st_size - isosize;
+
     cylsize = head * sector * 512;
     frac = isostat.st_size % cylsize;
     padding = (frac > 0) ? cylsize - frac : 0;
@@ -508,7 +1005,7 @@
     if (mode & VERBOSE)
         printf("imgsize: %zu, padding: %d\n", (size_t)isostat.st_size, padding);
 
-    cc = c = (isostat.st_size + padding) / cylsize;
+    cc = c = ( isostat.st_size + padding) / cylsize;
     if (c > 1024)
     {
         warnx("Warning: more than 1024 cylinders: %d", c);
@@ -548,6 +1045,62 @@
     if (fwrite(buf, sizeof(char), i, fp) != (size_t)i)
         err(1, "%s: write error - 1", argv[0]);
 
+    if (efi_lba) {
+	reverse_uuid(basic_partition);
+	reverse_uuid(hfs_partition);
+
+	/* 512 byte header, 128 entries of 128 bytes */
+	orig_gpt_size = gpt_size = 512 + (128 * 128);
+
+	/* Leave space for the APM if necessary */
+	if (mac_lba)
+	    gpt_size += (4 * 2048);
+
+	buf = calloc(gpt_size, sizeof(char));
+	memset(buf, 0, gpt_size);
+
+	/*
+	 * We need to ensure that we have enough space for the secondary GPT.
+	 * Unlike the primary, this doesn't need a hole for the APM. We still
+	 * want to be 1MB aligned so just bump the padding by a megabyte.
+	 */
+	if (free_space < orig_gpt_size && padding < orig_gpt_size) {
+	    padding += 1024 * 1024;
+	}
+
+	/*
+	 * Determine the size of the ISO filesystem. This will define the size
+	 * of the partition that covers it.
+	 */
+	psize = isosize / 512;
+
+	/*
+	 * Primary GPT starts at sector 1, secondary GPT starts at 1 sector
+	 * before the end of the image
+	 */
+	initialise_gpt(buf, 1, (isostat.st_size + padding - 1024) / 512, 1);
+
+	if (fseek(fp, 512, SEEK_SET))
+	    err(1, "%s: seek error - 6", argv[0]);
+
+	if (fwrite(buf, sizeof(char), gpt_size, fp) != (size_t)gpt_size)
+	    err(1, "%s: write error - 2", argv[0]);
+    }
+
+    if (mac_lba)
+    {
+	/* Apple partition entries filling 2048 bytes each */
+	int apm_size = apm_parts * 2048;
+
+	buf = realloc(buf, apm_size);
+	memset(buf, 0, apm_size);
+
+	initialise_apm(buf, APM_OFFSET);
+
+	fseek(fp, APM_OFFSET, SEEK_SET);
+	fwrite(buf, sizeof(char), apm_size, fp);
+    }
+
     if (padding)
     {
         if (fsync(fileno(fp)))
@@ -557,6 +1110,30 @@
             err(1, "%s: could not add padding bytes", argv[0]);
     }
 
+    if (efi_lba) {
+	buf = realloc(buf, orig_gpt_size);
+	memset(buf, 0, orig_gpt_size);
+
+	buf += orig_gpt_size - sizeof(struct gpt_header);
+
+	initialise_gpt(buf, (isostat.st_size + padding - 1024) / 512, 1, 0);
+
+	/* Shift back far enough to write the 128 GPT entries */
+	buf -= 128 * sizeof(struct gpt_part_header);
+
+	/*
+	 * Seek far enough back that the gpt header is 512 bytes before the
+	 * end of the image
+	 */
+
+	if (fseek(fp, (isostat.st_size + padding) - orig_gpt_size - 512,
+		  SEEK_SET))
+	    err(1, "%s: seek error - 8", argv[0]);
+
+	if (fwrite(buf, sizeof(char), orig_gpt_size, fp) != orig_gpt_size)
+	    err(1, "%s: write error - 4", argv[0]);
+    }
+
     free(buf);
     fclose(fp);
 
diff --git a/version b/version
index c85bb01..822ae5a 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-4.05 2011
+4.06 2011
diff --git a/win/ntfssect.c b/win/ntfssect.c
new file mode 100644
index 0000000..8c2bcca
--- /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 0000000..246c26d
--- /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 0000000..1fc2718
--- /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 0000000..462d21f
--- /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 0e833d8..26e5a27 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 ldlinux_sectors;
     uint32_t ldlinux_cluster;
     int nsectors;
+    int fs_type;
 
     if (!checkver()) {
 	fprintf(stderr,
@@ -326,8 +329,10 @@
 	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 @@
     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 @@
 	s = libfat_nextsector(fs, s);
     }
     libfat_close(fs);
+map_done:
 
     /*
      * Patch ldlinux.sys and the boot sector
@@ -472,7 +510,7 @@
     }
 
     /* 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 d4133ff..f960998 100644
--- a/win32/Makefile
+++ b/win32/Makefile
@@ -47,9 +47,9 @@
 
 .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 f79949c..51dcdd7 100755
--- a/win32/find-mingw32.sh
+++ b/win32/find-mingw32.sh
@@ -20,7 +20,8 @@
     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 0000000..00e89cf
--- /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 0bc746d..fe60793 100644
--- a/win64/Makefile
+++ b/win64/Makefile
@@ -37,9 +37,9 @@
 
 .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 c45db56..c2e42cd 100755
--- a/win64/find-mingw64.sh
+++ b/win64/find-mingw64.sh
@@ -12,6 +12,8 @@
     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 @@
     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 0000000..5b975be
--- /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