summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/Makefile5
-rw-r--r--core/bootsect.inc16
-rw-r--r--core/comboot.inc15
-rw-r--r--core/diskboot.inc2
-rw-r--r--core/diskfs.inc4
-rw-r--r--core/diskstart.inc40
-rw-r--r--core/fs/btrfs/btrfs.c5
-rw-r--r--core/fs/cache.c4
-rw-r--r--core/fs/chdir.c110
-rw-r--r--core/fs/ext2/ext2.c3
-rw-r--r--core/fs/fat/fat.c35
-rw-r--r--core/fs/fs.c13
-rw-r--r--core/fs/lib/searchconfig.c3
-rw-r--r--core/fs/ntfs/ntfs.c1388
-rw-r--r--core/fs/ntfs/ntfs.h485
-rw-r--r--core/fs/ntfs/runlist.h83
-rw-r--r--core/head.inc4
-rw-r--r--core/include/fs.h1
-rw-r--r--core/init.inc24
-rw-r--r--core/isolinux.asm173
-rw-r--r--core/ldlinux.asm2
-rw-r--r--core/syslinux.ld9
-rw-r--r--core/ui.inc7
23 files changed, 2144 insertions, 287 deletions
diff --git a/core/Makefile b/core/Makefile
index 33ad7e95..112fe3a8 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -20,7 +20,8 @@ MAKEFLAGS += -r
MAKE += -r
topdir = ..
-include $(topdir)/MCONFIG.embedded
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/embedded.mk
-include $(topdir)/version.mk
OPTFLAGS =
@@ -109,7 +110,7 @@ ldlinux.bss: ldlinux.bin
dd if=$< of=$@ bs=512 count=1
ldlinux.sys: ldlinux.bin
- dd if=$< of=$@ bs=512 skip=1
+ dd if=$< of=$@ bs=512 skip=2
codepage.cp: ../codepage/$(CODEPAGE).cp
cp -f $< $@
diff --git a/core/bootsect.inc b/core/bootsect.inc
index 6c204096..9e47e1a5 100644
--- a/core/bootsect.inc
+++ b/core/bootsect.inc
@@ -1,7 +1,7 @@
;; -----------------------------------------------------------------------
;;
;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;; Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ SuperSize equ $+1
load_bootsec:
mov edi,free_high_memory
mov [trackbuf+4],edi ; Copy from this address
+ mov eax,0xA0000 ; Maximum load
xor dx,dx ; No padding
mov bx,abort_check ; Don't print dots, but allow abort
call load_high
@@ -45,6 +46,9 @@ load_bootsec:
sub edi,free_high_memory
mov [trackbuf+8],edi ; Save length
+ cmp edi,0xA0000-7C00h
+ ja bs_too_big
+
mov eax,7C00h ; Entry point
mov [trackbuf],eax ; Copy to this address
@@ -237,3 +241,13 @@ replace_stub:
.csip equ $-4
section .text16
+bs_too_big:
+ call close
+ mov si,err_bs_too_big
+ jmp abort_load
+
+ section .data16
+err_bs_too_big db "Too large for a bootstrap (need LINUX instead of KERNEL?)"
+ db CR, LF, 0
+
+ section .text16
diff --git a/core/comboot.inc b/core/comboot.inc
index d6f670c9..61c8a3b7 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -651,21 +651,8 @@ comapi_cleanup:
ret
;
-; INT 22h AX=000Dh Clean up then replace bootstrap
+; INT 22h AX=000Dh Obsolete
;
-comapi_chainboot:
- call comapi_cleanup
- mov eax,P_EDI
- mov [trackbuf+4],eax ; Copy from
- mov eax,P_ECX
- mov [trackbuf+8],eax ; Total bytes
- mov eax,7C00h
- mov [trackbuf],eax ; Copy to
- push eax ; Entry point on stack
- mov esi,P_ESI
- mov edx,P_EBX
- mov bx,P_DS
- jmp replace_bootstrap_one
;
; INT 22h AX=000Eh Get configuration file name
diff --git a/core/diskboot.inc b/core/diskboot.inc
index 68672e4a..141986e8 100644
--- a/core/diskboot.inc
+++ b/core/diskboot.inc
@@ -278,7 +278,7 @@ Sect1Ptr1 equ $-4
cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
jne kaboom
- ; Go for it! This also normalizes CS:IP.
+ ; Go for it!
jmp ldlinux_ent
;
diff --git a/core/diskfs.inc b/core/diskfs.inc
index fc80a153..41391e7f 100644
--- a/core/diskfs.inc
+++ b/core/diskfs.inc
@@ -30,8 +30,8 @@ LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
; This indicates the general format of the last few bytes in the boot sector
BS_MAGIC_VER equ 0x1b << 9
-SECTOR_SHIFT equ 9
-SECTOR_SIZE equ (1 << SECTOR_SHIFT)
+MIN_SECTOR_SHIFT equ 9
+MIN_SECTOR_SIZE equ (1 << MIN_SECTOR_SHIFT)
;
; The following structure is used for "virtual kernels"; i.e. LILO-style
diff --git a/core/diskstart.inc b/core/diskstart.inc
index 02505a6b..b2ef2b63 100644
--- a/core/diskstart.inc
+++ b/core/diskstart.inc
@@ -23,6 +23,14 @@ Sect1Ptr1_VAL equ 0xfeedface
%include "diskboot.inc"
; ===========================================================================
+; Padding after the (minimum) 512-byte boot sector so that the rest of
+; the file has aligned sectors, even if they are larger than 512 bytes.
+; ===========================================================================
+
+ section .init
+align_pad zb 512
+
+; ===========================================================================
; Start of LDLINUX.SYS
; ===========================================================================
@@ -110,13 +118,15 @@ ldlinux_ent:
; Checksum data thus far
;
mov si,ldlinux_sys
- mov cx,SECTOR_SIZE >> 2
+ mov cx,[bsBytesPerSec]
+ shr cx,2
mov edx,-LDLINUX_MAGIC
.checksum:
lodsd
add edx,eax
loop .checksum
mov [CheckSum],edx ; Save intermediate result
+ movzx ebx,si ; Start of the next sector
;
; Tell the user if we're using EBIOS or CBIOS
@@ -132,6 +142,7 @@ print_bios:
call writestr_early
section .earlybss
+ alignb 2
%define HAVE_BIOSNAME 1
BIOSName resw 1
@@ -140,8 +151,9 @@ BIOSName resw 1
; Now we read the rest of LDLINUX.SYS.
;
load_rest:
+ push bx ; LSW of load address
+
lea esi,[SectorPtrs]
- mov ebx,TEXT_START+2*SECTOR_SIZE ; Where we start loading
mov cx,[DataSectors]
dec cx ; Minus this sector
@@ -157,7 +169,7 @@ load_rest:
xor bx,bx
call getlinsec
pop ebx
- shl ebp,SECTOR_SHIFT
+ imul bp,[bsBytesPerSec] ; Will be < 64K
add ebx,ebp
add si,10
jmp .get_chunk
@@ -170,9 +182,11 @@ load_rest:
; by the time we get to the end it should all cancel out.
;
verify_checksum:
- mov si,ldlinux_sys + SECTOR_SIZE
- mov ecx,[LDLDwords]
- sub ecx,SECTOR_SIZE >> 2
+ pop si ; LSW of load address
+ movzx eax,word [bsBytesPerSec]
+ shr ax,2
+ mov ecx,[LDLDwords] ; Total dwords
+ sub ecx,eax ; ... minus one sector
mov eax,[CheckSum]
.checksum:
add eax,[si]
@@ -260,7 +274,7 @@ getlinsec_ebios:
add eax,edi ; Advance sector pointer
adc edx,0
sub bp,di ; Sectors left
- shl di,SECTOR_SHIFT ; 512-byte sectors
+ imul di,[bsBytesPerSec]
add bx,di ; Advance buffer pointer
and bp,bp
jnz .loop
@@ -350,7 +364,7 @@ getlinsec_cbios:
jc .error
.resume:
movzx ecx,al ; ECX <- sectors transferred
- shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
+ imul ax,[bsBytesPerSec] ; Convert sectors in AL to bytes in AX
pop bx
add bx,ax
pop bp
@@ -418,10 +432,10 @@ safedumpregs:
rl_checkpt equ $ ; Must be <= 8000h
-rl_checkpt_off equ ($-$$)
+rl_checkpt_off equ $-ldlinux_sys
%ifndef DEPEND
- %if rl_checkpt_off > 3F6h ; Need one extent
- %assign rl_checkpt_overflow rl_checkpt_off - 3F6h
+ %if rl_checkpt_off > 512-10 ; Need minimum one extent
+ %assign rl_checkpt_overflow rl_checkpt_off - (512-10)
%error Sector 1 overflow by rl_checkpt_overflow bytes
%endif
%endif
@@ -434,8 +448,8 @@ rl_checkpt_off equ ($-$$)
;
alignz 2
MaxInitDataSize equ 96 << 10
-MaxLMA equ TEXT_START+SECTOR_SIZE+MaxInitDataSize
-SectorPtrs zb 10*(MaxInitDataSize >> SECTOR_SHIFT)
+MaxLMA equ LDLINUX_SYS+MaxInitDataSize
+SectorPtrs zb 10*(MaxInitDataSize >> MIN_SECTOR_SHIFT)
SectorPtrsEnd equ $
; ----------------------------------------------------------------------------
diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c
index b6a14e3b..aeb7614a 100644
--- a/core/fs/btrfs/btrfs.c
+++ b/core/fs/btrfs/btrfs.c
@@ -602,12 +602,15 @@ static void btrfs_get_fs_tree(struct fs_info *fs)
do {
do {
struct btrfs_root_ref *ref;
+ int pathlen;
if (btrfs_comp_keys_type(&search_key,
&path.item.key))
break;
ref = (struct btrfs_root_ref *)path.data;
- if (!strcmp((char*)(ref + 1), SubvolName)) {
+ pathlen = path.item.size - sizeof(struct btrfs_root_ref);
+
+ if (!strncmp((char*)(ref + 1), SubvolName, pathlen)) {
subvol_ok = true;
break;
}
diff --git a/core/fs/cache.c b/core/fs/cache.c
index 0d7891be..3b21fc26 100644
--- a/core/fs/cache.c
+++ b/core/fs/cache.c
@@ -37,10 +37,10 @@ void cache_init(struct device *dev, int block_size_shift)
dev->cache_head = head = (struct cache *)
(data + (dev->cache_entries << block_size_shift));
- cache = dev->cache_head + 1; /* First cache descriptor */
+ cache = head + 1; /* First cache descriptor */
head->prev = &cache[dev->cache_entries-1];
- head->next->prev = dev->cache_head;
+ head->prev->next = head;
head->block = -1;
head->data = NULL;
diff --git a/core/fs/chdir.c b/core/fs/chdir.c
index 9e8dfd2e..903cabce 100644
--- a/core/fs/chdir.c
+++ b/core/fs/chdir.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
+#include <dprintf.h>
#include "fs.h"
#include "cache.h"
@@ -16,57 +17,70 @@ void pm_realpath(com32sys_t *regs)
realpath(dst, src, FILENAME_MAX);
}
-#define EMIT(x) \
-do { \
- if (++n < bufsize) \
- *q++ = (x); \
-} while (0)
-
-static size_t join_paths(char *dst, size_t bufsize,
- const char *s1, const char *s2)
+static size_t copy_string(char *buf, size_t ix, size_t bufsize, const char *src)
{
- const char *list[2];
- int i;
char c;
- const char *p;
- char *q = dst;
- size_t n = 0;
- bool slash = false;
-
- list[0] = s1;
- list[1] = s2;
-
- for (i = 0; i < 2; i++) {
- p = list[i];
-
- while ((c = *p++)) {
- if (c == '/') {
- if (!slash)
- EMIT(c);
- slash = true;
- } else {
- EMIT(c);
- slash = false;
- }
- }
+
+ while ((c = *src++)) {
+ if (ix+1 < bufsize)
+ buf[ix] = c;
+ ix++;
}
- if (bufsize)
- *q = '\0';
+ if (ix < bufsize)
+ buf[ix] = '\0';
- return n;
+ return ix;
+}
+
+static size_t generic_inode_to_path(struct inode *inode, char *dst, size_t bufsize)
+{
+ size_t s = 0;
+
+ dprintf("inode %p name %s\n", inode, inode->name);
+
+ if (inode->parent) {
+ if (!inode->name) /* Only the root should have no name */
+ return -1;
+
+ s = generic_inode_to_path(inode->parent, dst, bufsize);
+ if (s == (size_t)-1)
+ return s; /* Error! */
+
+ s = copy_string(dst, s, bufsize, "/");
+ s = copy_string(dst, s, bufsize, inode->name);
+ }
+
+ return s;
}
size_t realpath(char *dst, const char *src, size_t bufsize)
{
+ int rv;
+ struct file *file;
+ size_t s;
+
+ dprintf("realpath: input: %s\n", src);
+
if (this_fs->fs_ops->realpath) {
- return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
+ s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
} else {
- /* Filesystems with "common" pathname resolution */
- return join_paths(dst, bufsize,
- src[0] == '/' ? "" : this_fs->cwd_name,
- src);
+ rv = searchdir(src);
+ if (rv < 0) {
+ dprintf("realpath: searchpath failure\n");
+ return -1;
+ }
+
+ file = handle_to_file(rv);
+ s = generic_inode_to_path(file->inode, dst, bufsize);
+ if (s == 0)
+ s = copy_string(dst, 0, bufsize, "/");
+
+ _close_file(file);
}
+
+ dprintf("realpath: output: %s\n", dst);
+ return s;
}
int chdir(const char *src)
@@ -74,6 +88,10 @@ int chdir(const char *src)
int rv;
struct file *file;
char cwd_buf[CURRENTDIR_MAX];
+ size_t s;
+
+ dprintf("chdir: from %s (inode %p) add %s\n",
+ this_fs->cwd_name, this_fs->cwd, src);
if (this_fs->fs_ops->chdir)
return this_fs->fs_ops->chdir(this_fs, src);
@@ -94,10 +112,20 @@ int chdir(const char *src)
_close_file(file);
/* Save the current working directory */
- realpath(cwd_buf, src, CURRENTDIR_MAX);
+ s = generic_inode_to_path(this_fs->cwd, cwd_buf, CURRENTDIR_MAX-1);
/* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */
- join_paths(this_fs->cwd_name, CURRENTDIR_MAX, cwd_buf, "/");
+ if (s < 1 || cwd_buf[s-1] != '/')
+ cwd_buf[s++] = '/';
+
+ if (s >= CURRENTDIR_MAX)
+ s = CURRENTDIR_MAX - 1;
+
+ cwd_buf[s++] = '\0';
+ memcpy(this_fs->cwd_name, cwd_buf, s);
+
+ dprintf("chdir: final %s (inode %p)\n",
+ this_fs->cwd_name, this_fs->cwd);
return 0;
}
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
index 716670c6..7988faaf 100644
--- a/core/fs/ext2/ext2.c
+++ b/core/fs/ext2/ext2.c
@@ -164,6 +164,9 @@ static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr)
struct inode *inode;
e_inode = ext2_get_inode(fs, inr);
+ if (!e_inode)
+ return NULL;
+
if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode))))
return NULL;
fill_inode(inode, e_inode);
diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c
index d3079269..b08923cf 100644
--- a/core/fs/fat/fat.c
+++ b/core/fs/fat/fat.c
@@ -220,24 +220,30 @@ static sector_t next_sector(struct file *file)
return sector;
}
-/*
- * Mangle a filename pointed to by src into a buffer pointed to by dst;
- * ends on encountering any whitespace.
+/**
+ * mangle_name:
+ *
+ * Mangle a filename pointed to by src into a buffer pointed
+ * to by dst; ends on encountering any whitespace.
+ * dst is preserved.
+ *
+ * This verifies that a filename is < FILENAME_MAX characters,
+ * doesn't contain whitespace, zero-pads the output buffer,
+ * and removes redundant slashes.
+ *
+ * Unlike the generic version, this also converts backslashes to
+ * forward slashes.
*
*/
static void vfat_mangle_name(char *dst, const char *src)
{
char *p = dst;
+ int i = FILENAME_MAX-1;
char c;
- int i = FILENAME_MAX -1;
- /*
- * Copy the filename, converting backslash to slash and
- * collapsing duplicate separators.
- */
while (not_whitespace(c = *src)) {
- if (c == '\\')
- c = '/';
+ if (c == '\\')
+ c = '/';
if (c == '/') {
if (src[1] == '/' || src[1] == '\\') {
@@ -250,16 +256,13 @@ static void vfat_mangle_name(char *dst, const char *src)
*dst++ = *src++;
}
- /* Strip terminal slashes or whitespace */
while (1) {
if (dst == p)
break;
- if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */
- break;
- if (dst-2 == p && *(dst-2) == '.' && *(dst-1) == '.' ) /* the '..' case */
- break;
- if ((*(dst-1) != '/') && (*(dst-1) != '.'))
+ if (dst[-1] != '/')
break;
+ if ((dst[-1] == '/') && ((dst - 1) == p))
+ break;
dst--;
i++;
diff --git a/core/fs/fs.c b/core/fs/fs.c
index ad2fb370..21f5dba0 100644
--- a/core/fs/fs.c
+++ b/core/fs/fs.c
@@ -37,6 +37,8 @@ void put_inode(struct inode *inode)
while (inode && --inode->refcnt == 0) {
struct inode *dead = inode;
inode = inode->parent;
+ if (dead->name)
+ free((char *)dead->name);
free(dead);
}
}
@@ -207,6 +209,9 @@ int searchdir(const char *name)
char *part, *p, echar;
int symlink_count = MAX_SYMLINK_CNT;
+ dprintf("searchdir: %s root: %p cwd: %p\n",
+ name, this_fs->root, this_fs->cwd);
+
if (!(file = alloc_file()))
goto err_no_close;
file->fs = this_fs;
@@ -305,6 +310,9 @@ int searchdir(const char *name)
goto got_link;
}
+ inode->name = strdup(part);
+ dprintf("path component: %s\n", inode->name);
+
inode->parent = parent;
parent = NULL;
@@ -349,6 +357,8 @@ int open_file(const char *name, struct com32_filedata *filedata)
struct file *file;
char mangled_name[FILENAME_MAX];
+ dprintf("open_file %s\n", name);
+
mangle_name(mangled_name, name);
rv = searchdir(mangled_name);
@@ -376,6 +386,8 @@ void pm_open_file(com32sys_t *regs)
const char *name = MK_PTR(regs->es, regs->esi.w[0]);
char mangled_name[FILENAME_MAX];
+ dprintf("pm_open_file %s\n", name);
+
mangle_name(mangled_name, name);
rv = searchdir(mangled_name);
if (rv < 0) {
@@ -470,6 +482,7 @@ void fs_init(com32sys_t *regs)
if (fs.fs_ops->iget_root) {
fs.root = fs.fs_ops->iget_root(&fs);
fs.cwd = get_inode(fs.root);
+ dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd);
}
SectorShift = fs.sector_shift;
diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c
index 24bfde31..f18836a8 100644
--- a/core/fs/lib/searchconfig.c
+++ b/core/fs/lib/searchconfig.c
@@ -25,7 +25,8 @@ int search_config(const char *search_directories[], const char *filenames[])
"%s%s%s",
sd, (*sd && sd[strlen(sd)-1] == '/') ? "" : "/",
sf);
- realpath(ConfigName, confignamebuf, FILENAME_MAX);
+ if (realpath(ConfigName, confignamebuf, FILENAME_MAX) == (size_t)-1)
+ continue;
regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
dprintf("Config search: %s\n", ConfigName);
call16(core_open, &regs, &regs);
diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
new file mode 100644
index 00000000..500d0fd3
--- /dev/null
+++ b/core/fs/ntfs/ntfs.c
@@ -0,0 +1,1388 @@
+/*
+ * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* Note: No support for compressed files */
+
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.h>
+#include <ilog2.h>
+#include <klibc/compiler.h>
+#include <ctype.h>
+
+#include "codepage.h"
+#include "ntfs.h"
+#include "runlist.h"
+
+static struct ntfs_readdir_state *readdir_state;
+
+/*** Function declarations */
+static f_mft_record_lookup ntfs_mft_record_lookup_3_0;
+static f_mft_record_lookup ntfs_mft_record_lookup_3_1;
+
+/*** Function definitions */
+
+/* Check if there are specific zero fields in an NTFS boot sector */
+static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
+{
+ return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
+ !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
+ !sb->zero_3;
+}
+
+static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
+{
+ return ntfs_check_zero_fields(sb) &&
+ (!memcmp(sb->oem_name, "NTFS ", 8) ||
+ !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
+ !memcmp(sb->oem_name, "MSWIN4.1", 8));
+}
+
+static inline struct inode *new_ntfs_inode(struct fs_info *fs)
+{
+ struct inode *inode;
+
+ inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
+ if (!inode)
+ malloc_error("inode structure");
+
+ return inode;
+}
+
+static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec)
+{
+ uint16_t *usa;
+ uint16_t usa_no;
+ uint16_t usa_count;
+ uint16_t *blk;
+
+ dprintf("in %s()\n", __func__);
+
+ if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
+ return;
+
+ /* get the Update Sequence Array offset */
+ usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
+ /* get the Update Sequence Array Number and skip it */
+ usa_no = *usa++;
+ /* get the Update Sequene Array count */
+ usa_count = nrec->usa_count - 1; /* exclude the USA number */
+ /* make it to point to the last two bytes of the RECORD's first sector */
+ blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
+
+ while (usa_count--) {
+ if (*blk != usa_no)
+ break;
+
+ *blk = *usa++;
+ blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
+ }
+}
+
+/* read content from cache */
+static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
+ block_t *blk, uint64_t *blk_offset,
+ uint64_t *blk_next_offset, uint64_t *lcn)
+{
+ uint8_t *data;
+ uint64_t offset = *blk_offset;
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint64_t bytes;
+ uint64_t lbytes;
+ uint64_t loffset;
+ uint64_t k;
+
+ dprintf("in %s()\n", __func__);
+
+ if (count > len)
+ goto out;
+
+ data = (uint8_t *)get_cache(fs->fs_dev, *blk);
+ if (!data)
+ goto out;
+
+ if (!offset)
+ offset = (*lcn << clust_byte_shift) % blk_size;
+
+ dprintf("LCN: 0x%X\n", *lcn);
+ dprintf("offset: 0x%X\n", offset);
+
+ bytes = count; /* bytes to copy */
+ lbytes = blk_size - offset; /* bytes left to copy */
+ if (lbytes >= bytes) {
+ /* so there's room enough, then copy the whole content */
+ memcpy(buf, data + offset, bytes);
+ loffset = offset;
+ offset += count;
+ } else {
+ dprintf("bytes: %u\n", bytes);
+ dprintf("bytes left: %u\n", lbytes);
+ /* otherwise, let's copy it partially... */
+ k = 0;
+ while (bytes) {
+ memcpy(buf + k, data + offset, lbytes);
+ bytes -= lbytes;
+ loffset = offset;
+ offset += lbytes;
+ k += lbytes;
+ if (offset >= blk_size) {
+ /* then fetch a new FS block */
+ data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
+ if (!data)
+ goto out;
+
+ lbytes = bytes;
+ loffset = offset;
+ offset = 0;
+ }
+ }
+ }
+
+ if (loffset >= blk_size)
+ loffset = 0; /* it must be aligned on a block boundary */
+
+ *blk_offset = loffset;
+
+ if (blk_next_offset)
+ *blk_next_offset = offset;
+
+ *lcn += blk_size / count; /* update LCN */
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
+ uint32_t file, block_t *blk)
+{
+ const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
+ uint8_t *buf;
+ const block_t mft_blk = NTFS_SB(fs)->mft_blk;
+ block_t cur_blk;
+ block_t right_blk;
+ uint64_t offset;
+ uint64_t next_offset;
+ const uint32_t mft_record_shift = ilog2(mft_record_size);
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ uint64_t lcn;
+ int err;
+ struct ntfs_mft_record *mrec;
+
+ dprintf("in %s()\n", __func__);
+
+ buf = (uint8_t *)malloc(mft_record_size);
+ if (!buf)
+ malloc_error("uint8_t *");
+
+ /* determine MFT record's LCN and block number */
+ lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
+ cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
+ offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
+ for (;;) {
+ right_blk = cur_blk + mft_blk;
+ err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
+ &offset, &next_offset, &lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ break;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+
+ mrec = (struct ntfs_mft_record *)buf;
+ /* check if it has a valid magic number */
+ if (mrec->magic == NTFS_MAGIC_FILE) {
+ if (blk)
+ *blk = cur_blk; /* update record starting block */
+
+ return mrec; /* found MFT record */
+ }
+
+ if (next_offset >= BLOCK_SIZE(fs)) {
+ /* try the next FS block */
+ offset = 0;
+ cur_blk = right_blk - mft_blk + 1;
+ } else {
+ /* there's still content to fetch in the current block */
+ cur_blk = right_blk - mft_blk;
+ offset = next_offset; /* update FS block offset */
+ }
+ }
+
+ free(buf);
+
+ return NULL;
+}
+
+static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
+ uint32_t file, block_t *blk)
+{
+ const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
+ uint8_t *buf;
+ const block_t mft_blk = NTFS_SB(fs)->mft_blk;
+ block_t cur_blk;
+ block_t right_blk;
+ uint64_t offset;
+ uint64_t next_offset;
+ const uint32_t mft_record_shift = ilog2(mft_record_size);
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ uint64_t lcn;
+ int err;
+ struct ntfs_mft_record *mrec;
+
+ dprintf("in %s()\n", __func__);
+
+ buf = (uint8_t *)malloc(mft_record_size);
+ if (!buf)
+ malloc_error("uint8_t *");
+
+ lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
+ cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
+ offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
+ for (;;) {
+ right_blk = cur_blk + NTFS_SB(fs)->mft_blk;
+ err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
+ &offset, &next_offset, &lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ break;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+
+ mrec = (struct ntfs_mft_record *)buf;
+ /* Check if the NTFS 3.1 MFT record number matches */
+ if (mrec->magic == NTFS_MAGIC_FILE && mrec->mft_record_no == file) {
+ if (blk)
+ *blk = cur_blk; /* update record starting block */
+
+ return mrec; /* found MFT record */
+ }
+
+ if (next_offset >= BLOCK_SIZE(fs)) {
+ /* try the next FS block */
+ offset = 0;
+ cur_blk = right_blk - NTFS_SB(fs)->mft_blk + 1;
+ } else {
+ /* there's still content to fetch in the current block */
+ cur_blk = right_blk - NTFS_SB(fs)->mft_blk;
+ offset = next_offset; /* update FS block offset */
+ }
+ }
+
+ free(buf);
+
+ return NULL;
+}
+
+static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
+{
+ const uint16_t *entry_fn;
+ uint8_t entry_fn_len;
+ unsigned i;
+
+ dprintf("in %s()\n", __func__);
+
+ entry_fn = ie->key.file_name.file_name;
+ entry_fn_len = ie->key.file_name.file_name_len;
+
+ if (strlen(dname) != entry_fn_len)
+ return false;
+
+ /* Do case-sensitive compares for Posix file names */
+ if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
+ for (i = 0; i < entry_fn_len; i++)
+ if (entry_fn[i] != dname[i])
+ return false;
+ } else {
+ for (i = 0; i < entry_fn_len; i++)
+ if (tolower(entry_fn[i]) != tolower(dname[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,
+ struct mapping_chunk *chunk,
+ uint32_t *offset)
+{
+ memset(chunk, 0, sizeof *chunk);
+ *offset = 0U;
+
+ return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
+}
+
+/* Parse data runs.
+ *
+ * return 0 on success or -1 on failure.
+ */
+static int parse_data_run(const void *stream, uint32_t *offset,
+ uint8_t *attr_len, struct mapping_chunk *chunk)
+{
+ uint8_t *buf; /* Pointer to the zero-terminated byte stream */
+ uint8_t count; /* The count byte */
+ uint8_t v, l; /* v is the number of changed low-order VCN bytes;
+ * l is the number of changed low-order LCN bytes
+ */
+ uint8_t *byte;
+ int byte_shift = 8;
+ int mask;
+ uint8_t val;
+ int64_t res;
+
+ (void)attr_len;
+
+ dprintf("in %s()\n", __func__);
+
+ chunk->flags &= ~MAP_MASK;
+
+ buf = (uint8_t *)stream + *offset;
+ if (buf > attr_len || !*buf) {
+ chunk->flags |= MAP_END; /* we're done */
+ return 0;
+ }
+
+ if (!*offset)
+ chunk->flags |= MAP_START; /* initial chunk */
+
+ count = *buf;
+ v = count & 0x0F;
+ l = count >> 4;
+
+ if (v > 8 || l > 8) /* more than 8 bytes ? */
+ goto out;
+
+ byte = (uint8_t *)buf + v;
+ count = v;
+
+ res = 0LL;
+ while (count--) {
+ val = *byte--;
+ mask = val >> (byte_shift - 1);
+ res = (res << byte_shift) | ((val + mask) ^ mask);
+ }
+
+ chunk->len = res; /* get length data */
+
+ byte = (uint8_t *)buf + v + l;
+ count = l;
+
+ mask = 0xFFFFFFFF;
+ res = 0LL;
+ if (*byte & 0x80)
+ res |= (int64_t)mask; /* sign-extend it */
+
+ while (count--)
+ res = (res << byte_shift) | *byte--;
+
+ chunk->lcn += res;
+ /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
+ if (!chunk->lcn)
+ chunk->flags |= MAP_UNALLOCATED;
+ else
+ chunk->flags |= MAP_ALLOCATED;
+
+ *offset += v + l + 1;
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static struct ntfs_mft_record *
+ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
+ uint32_t type, struct ntfs_mft_record *mrec)
+{
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ uint8_t *stream;
+ int err;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint8_t buf[blk_size];
+ uint64_t blk_offset;
+ int64_t vcn;
+ int64_t lcn;
+ int64_t last_lcn;
+ block_t blk;
+ struct ntfs_attr_list_entry *attr_entry;
+ uint32_t len = 0;
+ struct ntfs_mft_record *retval;
+ uint64_t start_blk = 0;
+
+ dprintf("in %s()\n", __func__);
+
+ if (attr->non_resident)
+ goto handle_non_resident_attr;
+
+ attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr + attr->data.resident.value_offset);
+ len = attr->data.resident.value_len;
+ for (; (uint8_t *)attr_entry < (uint8_t *)attr + len;
+ attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
+ attr_entry->length)) {
+ dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
+ attr_entry->type);
+ if (attr_entry->type == type)
+ goto found; /* We got the attribute! :-) */
+ }
+
+ printf("No attribute found.\n");
+ goto out;
+
+handle_non_resident_attr:
+ attr_len = (uint8_t *)attr + attr->len;
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ do {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("parse_data_run()\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+ if (chunk.flags & MAP_END)
+ break;
+ if (chunk.flags & MAP_ALLOCATED) {
+ vcn = 0;
+ lcn = chunk.lcn;
+ while (vcn < chunk.len) {
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
+ BLOCK_SHIFT(fs);
+ blk_offset = 0;
+ last_lcn = lcn;
+ lcn += vcn;
+ err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
+ &blk_offset, NULL, (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto out;
+ }
+
+ attr_entry = (struct ntfs_attr_list_entry *)&buf;
+ len = attr->data.non_resident.data_size;
+ for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len;
+ attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr_entry + attr_entry->length)) {
+ dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
+ attr_entry->type);
+ if (attr_entry->type == type)
+ goto found; /* We got the attribute! :-) */
+ }
+
+ lcn = last_lcn; /* restore original LCN */
+ /* go to the next VCN */
+ vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
+ }
+ }
+ } while (!(chunk.flags & MAP_END));
+
+ printf("No attribute found.\n");
+
+out:
+ return NULL;
+
+found:
+ /* At this point we have the attribute we were looking for. Now we
+ * will look for the MFT record that stores information about this
+ * attribute.
+ */
+
+ /* Check if the attribute type we're looking for is in the same
+ * MFT record. If so, we do not need to look it up again - return it.
+ */
+ if (mrec->mft_record_no == attr_entry->mft_ref)
+ return mrec;
+
+ retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref,
+ &start_blk);
+ if (!retval) {
+ printf("No MFT record found!\n");
+ goto out;
+ }
+
+ /* return the found MFT record */
+ return retval;
+}
+
+static struct ntfs_attr_record *
+__ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
+ struct ntfs_mft_record **mrec)
+{
+ struct ntfs_mft_record *_mrec = *mrec;
+ struct ntfs_attr_record *attr;
+ struct ntfs_attr_record *attr_list_attr;
+
+ dprintf("in %s()\n", __func__);
+
+ if (!_mrec || type == NTFS_AT_END)
+ goto out;
+
+again:
+ attr_list_attr = NULL;
+
+ attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset);
+ /* walk through the file attribute records */
+ for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
+ if (attr->type == NTFS_AT_END)
+ break;
+
+ if (attr->type == NTFS_AT_ATTR_LIST) {
+ dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
+ _mrec->mft_record_no);
+ attr_list_attr = attr;
+ continue;
+ }
+
+ if (attr->type == type)
+ break;
+ }
+
+ /* if the record has an $ATTRIBUTE_LIST attribute associated
+ * with it, then we need to look for the wanted attribute in
+ * it as well.
+ */
+ if (attr->type == NTFS_AT_END && attr_list_attr) {
+ struct ntfs_mft_record *retval;
+
+ retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec);
+ if (!retval)
+ goto out;
+
+ _mrec = retval;
+ goto again;
+ } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
+ attr = NULL;
+ }
+
+ return attr;
+
+out:
+ return NULL;
+}
+
+static inline struct ntfs_attr_record *
+ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
+ struct ntfs_mft_record **mmrec,
+ struct ntfs_mft_record *mrec)
+{
+ struct ntfs_mft_record *_mrec = mrec;
+ struct ntfs_mft_record *other = *mmrec;
+ struct ntfs_attr_record *retval = NULL;
+
+ if (mrec == other)
+ return __ntfs_attr_lookup(fs, type, &other);
+
+ retval = __ntfs_attr_lookup(fs, type, &_mrec);
+ if (!retval) {
+ _mrec = other;
+ retval = __ntfs_attr_lookup(fs, type, &other);
+ if (!retval)
+ other = _mrec;
+ } else if (retval && (_mrec != mrec)) {
+ other = _mrec;
+ }
+
+ return retval;
+}
+
+static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
+{
+ return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
+}
+
+static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
+ struct inode *inode)
+{
+ uint64_t start_blk = 0;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ enum dirent_type d_type;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ int err;
+ uint8_t *stream;
+ uint32_t offset;
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+
+ NTFS_PVT(inode)->mft_no = mft_no;
+ NTFS_PVT(inode)->seq_no = mrec->seq_no;
+
+ NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
+ NTFS_PVT(inode)->here = start_blk;
+
+ d_type = get_inode_mode(mrec);
+ if (d_type == DT_DIR) { /* directory stuff */
+ dprintf("Got a directory.\n");
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ /* check if we have a previous allocated state structure */
+ if (readdir_state) {
+ free(readdir_state);
+ readdir_state = NULL;
+ }
+
+ /* allocate our state structure */
+ readdir_state = malloc(sizeof *readdir_state);
+ if (!readdir_state)
+ malloc_error("ntfs_readdir_state structure");
+
+ readdir_state->mft_no = mft_no;
+ /* obviously, the ntfs_readdir() caller will start from INDEX root */
+ readdir_state->in_idx_root = true;
+ } else if (d_type == DT_REG) { /* file stuff */
+ dprintf("Got a file.\n");
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ NTFS_PVT(inode)->non_resident = attr->non_resident;
+ NTFS_PVT(inode)->type = attr->type;
+
+ if (!attr->non_resident) {
+ NTFS_PVT(inode)->data.resident.offset =
+ (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
+ inode->size = attr->data.resident.value_len;
+ } else {
+ attr_len = (uint8_t *)attr + attr->len;
+
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ NTFS_PVT(inode)->data.non_resident.rlist = NULL;
+ for (;;) {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("parse_data_run()\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+ if (chunk.flags & MAP_END)
+ break;
+ if (chunk.flags & MAP_ALLOCATED) {
+ /* append new run to the runlist */
+ runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
+ (struct runlist_element *)&chunk);
+ /* update for next VCN */
+ chunk.vcn += chunk.len;
+ }
+ }
+
+ if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
+ printf("No mapping found\n");
+ goto out;
+ }
+
+ inode->size = attr->data.non_resident.initialized_size;
+ }
+ }
+
+ inode->mode = d_type;
+
+ free(mrec);
+
+ return 0;
+
+out:
+ free(mrec);
+
+ return -1;
+}
+
+static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
+{
+ struct fs_info *fs = dir->fs;
+ struct ntfs_mft_record *mrec, *lmrec;
+ block_t blk;
+ uint64_t blk_offset;
+ struct ntfs_attr_record *attr;
+ struct ntfs_idx_root *ir;
+ struct ntfs_idx_entry *ie;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint8_t buf[blk_size];
+ struct ntfs_idx_allocation *iblk;
+ int err;
+ uint8_t *stream;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ int64_t vcn;
+ int64_t lcn;
+ int64_t last_lcn;
+ struct inode *inode;
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ ir = (struct ntfs_idx_root *)((uint8_t *)attr +
+ attr->data.resident.value_offset);
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index +
+ ir->index.entries_offset);
+ for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)mrec ||
+ (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&ir->index + ir->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&ir->index + ir->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key. it can however contain
+ * a pointer to a child node in the B+ tree so we just break out
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+
+ if (ntfs_filename_cmp(dname, ie))
+ goto found;
+ }
+
+ /* check for the presence of a child node */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ printf("No child node, aborting...\n");
+ goto out;
+ }
+
+ /* then descend into child node */
+
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ if (!attr->non_resident) {
+ printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
+ goto out;
+ }
+
+ attr_len = (uint8_t *)attr + attr->len;
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ do {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err)
+ break;
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+
+ if (chunk.flags & MAP_ALLOCATED) {
+ dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
+ chunk.lcn);
+
+ vcn = 0;
+ lcn = chunk.lcn;
+ while (vcn < chunk.len) {
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
+ SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
+
+ blk_offset = 0;
+ last_lcn = lcn;
+ lcn += vcn;
+ err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
+ &blk_offset, NULL, (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto not_found;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
+
+ iblk = (struct ntfs_idx_allocation *)&buf;
+ if (iblk->magic != NTFS_MAGIC_INDX) {
+ printf("Not a valid INDX record.\n");
+ goto not_found;
+ }
+
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
+ iblk->index.entries_offset);
+ for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie +
+ ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
+ sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&iblk->index + iblk->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&iblk->index + iblk->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+
+ if (ntfs_filename_cmp(dname, ie))
+ goto found;
+ }
+
+ lcn = last_lcn; /* restore the original LCN */
+ /* go to the next VCN */
+ vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
+ }
+ }
+ } while (!(chunk.flags & MAP_END));
+
+not_found:
+ dprintf("Index not found\n");
+
+out:
+ free(mrec);
+
+ return NULL;
+
+found:
+ dprintf("Index found\n");
+ inode = new_ntfs_inode(fs);
+ err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
+ if (err) {
+ printf("Error in index_inode_setup()\n");
+ free(inode);
+ goto out;
+ }
+
+ free(mrec);
+
+ return inode;
+
+index_err:
+ printf("Corrupt index. Aborting lookup...\n");
+ goto out;
+}
+
+/* Convert an UTF-16LE LFN to OEM LFN */
+static uint8_t ntfs_cvt_filename(char *filename,
+ const struct ntfs_idx_entry *ie)
+{
+ const uint16_t *entry_fn;
+ uint8_t entry_fn_len;
+ unsigned i;
+
+ entry_fn = ie->key.file_name.file_name;
+ entry_fn_len = ie->key.file_name.file_name_len;
+
+ for (i = 0; i < entry_fn_len; i++)
+ filename[i] = (char)entry_fn[i];
+
+ filename[i] = '\0';
+
+ return entry_fn_len;
+}
+
+static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct fs_info *fs = inode->fs;
+ struct ntfs_sb_info *sbi = NTFS_SB(fs);
+ sector_t pstart = 0;
+ struct runlist *rlist;
+ struct runlist *ret;
+ const uint32_t sec_size = SECTOR_SIZE(fs);
+ const uint32_t sec_shift = SECTOR_SHIFT(fs);
+
+ dprintf("in %s()\n", __func__);
+
+ if (!NTFS_PVT(inode)->non_resident) {
+ pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
+ sec_shift;
+ inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
+ } else {
+ rlist = NTFS_PVT(inode)->data.non_resident.rlist;
+
+ if (!lstart || lstart >= NTFS_PVT(inode)->here) {
+ if (runlist_is_empty(rlist))
+ goto out; /* nothing to do ;-) */
+
+ ret = runlist_remove(&rlist);
+
+ NTFS_PVT(inode)->here =
+ ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
+
+ pstart = ret->run.lcn << sbi->clust_shift;
+ inode->next_extent.len =
+ ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
+ sec_shift;
+
+ NTFS_PVT(inode)->data.non_resident.rlist = rlist;
+
+ free(ret);
+ ret = NULL;
+ }
+ }
+
+ inode->next_extent.pstart = pstart;
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
+ bool *have_more)
+{
+ uint8_t non_resident;
+ uint32_t ret;
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ char *p;
+
+ dprintf("in %s()\n", __func__);
+
+ non_resident = NTFS_PVT(inode)->non_resident;
+
+ ret = generic_getfssec(file, buf, sectors, have_more);
+ if (!ret)
+ return ret;
+
+ if (!non_resident) {
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
+ NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
+
+ /* p now points to the data offset, so let's copy it into buf */
+ memcpy(buf, p, inode->size);
+
+ ret = inode->size;
+
+ free(mrec);
+ }
+
+ return ret;
+
+out:
+ free(mrec);
+
+ return 0;
+}
+
+static inline bool is_filename_printable(const char *s)
+{
+ return s && (*s != '.' && *s != '$');
+}
+
+static int ntfs_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ struct ntfs_mft_record *mrec, *lmrec;
+ block_t blk;
+ uint64_t blk_offset;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ struct ntfs_attr_record *attr;
+ struct ntfs_idx_root *ir;
+ uint32_t count;
+ int len;
+ struct ntfs_idx_entry *ie = NULL;
+ uint8_t buf[BLOCK_SIZE(fs)];
+ struct ntfs_idx_allocation *iblk;
+ int err;
+ uint8_t *stream;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ int64_t vcn;
+ int64_t lcn;
+ char filename[NTFS_MAX_FILE_NAME_LEN + 1];
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ ir = (struct ntfs_idx_root *)((uint8_t *)attr +
+ attr->data.resident.value_offset);
+
+ if (!file->offset && readdir_state->in_idx_root) {
+ file->offset = (uint32_t)((uint8_t *)&ir->index +
+ ir->index.entries_offset);
+ }
+
+idx_root_next_entry:
+ if (readdir_state->in_idx_root) {
+ ie = (struct ntfs_idx_entry *)(uint8_t *)file->offset;
+ if (ie->flags & INDEX_ENTRY_END) {
+ file->offset = 0;
+ readdir_state->in_idx_root = false;
+ readdir_state->idx_blks_count = 1;
+ readdir_state->entries_count = 0;
+ readdir_state->last_vcn = 0;
+ goto descend_into_child_node;
+ }
+
+ file->offset = (uint32_t)((uint8_t *)ie + ie->len);
+ len = ntfs_cvt_filename(filename, ie);
+ if (!is_filename_printable(filename))
+ goto idx_root_next_entry;
+
+ goto done;
+ }
+
+descend_into_child_node:
+ if (!(ie->flags & INDEX_ENTRY_NODE))
+ goto out;
+
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
+ if (!attr)
+ goto out;
+
+ if (!attr->non_resident) {
+ printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
+ goto out;
+ }
+
+ attr_len = (uint8_t *)attr + attr->len;
+
+next_run:
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ count = readdir_state->idx_blks_count;
+ while (count--) {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("Error while parsing data runs.\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ break;
+ if (chunk.flags & MAP_END)
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED) {
+ readdir_state->idx_blks_count++;
+ goto next_run;
+ }
+
+next_vcn:
+ vcn = readdir_state->last_vcn;
+ if (vcn >= chunk.len) {
+ readdir_state->last_vcn = 0;
+ readdir_state->idx_blks_count++;
+ goto next_run;
+ }
+
+ lcn = chunk.lcn;
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
+ BLOCK_SHIFT(fs);
+
+ blk_offset = 0;
+ err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
+ (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto not_found;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
+
+ iblk = (struct ntfs_idx_allocation *)&buf;
+ if (iblk->magic != NTFS_MAGIC_INDX) {
+ printf("Not a valid INDX record.\n");
+ goto not_found;
+ }
+
+idx_block_next_entry:
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
+ iblk->index.entries_offset);
+ count = readdir_state->entries_count;
+ for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
+ sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&iblk->index + iblk->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&iblk->index + iblk->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key */
+ if (ie->flags & INDEX_ENTRY_END) {
+ /* go to the next VCN */
+ readdir_state->last_vcn += (blk_size / (1 <<
+ NTFS_SB(fs)->clust_byte_shift));
+ readdir_state->entries_count = 0;
+ goto next_vcn;
+ }
+ }
+
+ readdir_state->entries_count++;
+
+ /* Need to check if this entry has INDEX_ENTRY_END flag set. If
+ * so, then it won't contain a indexed_file file, so continue the
+ * lookup on the next VCN/LCN (if any).
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ goto next_vcn;
+
+ len = ntfs_cvt_filename(filename, ie);
+ if (!is_filename_printable(filename))
+ goto idx_block_next_entry;
+
+ goto done;
+
+out:
+ readdir_state->in_idx_root = true;
+
+ free(mrec);
+
+ return -1;
+
+done:
+ dirent->d_ino = ie->data.dir.indexed_file;
+ dirent->d_off = file->offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
+
+ free(mrec);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ dirent->d_type = get_inode_mode(mrec);
+ memcpy(dirent->d_name, filename, len + 1);
+
+ free(mrec);
+
+ return 0;
+
+not_found:
+ printf("Index not found\n");
+ goto out;
+
+index_err:
+ printf("Corrupt index. Aborting lookup...\n");
+ goto out;
+}
+
+static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
+{
+ return ntfs_index_lookup(dname, parent);
+}
+
+static struct inode *ntfs_iget_root(struct fs_info *fs)
+{
+ uint64_t start_blk;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ struct ntfs_vol_info *vol_info;
+ struct inode *inode;
+ int err;
+
+ dprintf("in %s()\n", __func__);
+
+ /* Fetch the $Volume MFT record */
+ start_blk = 0;
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk);
+ if (!mrec) {
+ printf("Could not fetch $Volume MFT record!\n");
+ goto err_mrec;
+ }
+
+ lmrec = mrec;
+
+ /* Fetch the volume information attribute */
+ attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec);
+ if (!attr) {
+ printf("Could not find volume info attribute!\n");
+ goto err_attr;
+ }
+
+ /* Note NTFS version and choose version-dependent functions */
+ vol_info = (void *)((char *)attr + attr->data.resident.value_offset);
+ NTFS_SB(fs)->major_ver = vol_info->major_ver;
+ NTFS_SB(fs)->minor_ver = vol_info->minor_ver;
+ if (vol_info->major_ver == 3 && vol_info->minor_ver == 0)
+ NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0;
+ else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 &&
+ mrec->mft_record_no == FILE_Volume)
+ NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1;
+
+ /* Free MFT record */
+ free(mrec);
+ mrec = NULL;
+
+ inode = new_ntfs_inode(fs);
+ inode->fs = fs;
+
+ err = index_inode_setup(fs, FILE_root, inode);
+ if (err)
+ goto err_setup;
+
+ NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
+
+ return inode;
+
+err_setup:
+
+ free(inode);
+err_attr:
+
+ free(mrec);
+err_mrec:
+
+ return NULL;
+}
+
+/* Initialize the filesystem metadata and return blk size in bits */
+static int ntfs_fs_init(struct fs_info *fs)
+{
+ int read_count;
+ struct ntfs_bpb ntfs;
+ struct ntfs_sb_info *sbi;
+ struct disk *disk = fs->fs_dev->disk;
+ uint8_t mft_record_shift;
+
+ dprintf("in %s()\n", __func__);
+
+ read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
+ if (!read_count)
+ return -1;
+
+ if (!ntfs_check_sb_fields(&ntfs))
+ return -1;
+
+ SECTOR_SHIFT(fs) = disk->sector_shift;
+
+ /* Note: ntfs.clust_per_mft_record can be a negative number.
+ * If negative, it represents a shift count, else it represents
+ * a multiplier for the cluster size.
+ */
+ mft_record_shift = ntfs.clust_per_mft_record < 0 ?
+ -ntfs.clust_per_mft_record :
+ ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
+ ilog2(ntfs.clust_per_mft_record);
+
+ SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
+
+ sbi = malloc(sizeof *sbi);
+ if (!sbi)
+ malloc_error("ntfs_sb_info structure");
+
+ fs->fs_info = sbi;
+
+ sbi->clust_shift = ilog2(ntfs.sec_per_clust);
+ sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs);
+ sbi->clust_mask = ntfs.sec_per_clust - 1;
+ sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
+ sbi->mft_record_size = 1 << mft_record_shift;
+ sbi->clust_per_idx_record = ntfs.clust_per_idx_record;
+
+ BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
+ BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
+
+ sbi->mft_lcn = ntfs.mft_lclust;
+ sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >>
+ BLOCK_SHIFT(fs);
+ /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
+ sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
+
+ sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
+ if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
+ sbi->clusters = 0xFFFFFFFFFFF4ULL;
+
+ /*
+ * Assume NTFS version 3.0 to begin with. If we find that the
+ * volume is a different version later on, we will adjust at
+ * that time.
+ */
+ sbi->major_ver = 3;
+ sbi->minor_ver = 0;
+ sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0;
+
+ /* Initialize the cache */
+ cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
+
+ return BLOCK_SHIFT(fs);
+}
+
+const struct fs_ops ntfs_fs_ops = {
+ .fs_name = "ntfs",
+ .fs_flags = FS_USEMEM | FS_THISIND,
+ .fs_init = ntfs_fs_init,
+ .searchdir = NULL,
+ .getfssec = ntfs_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = generic_mangle_name,
+ .load_config = generic_load_config,
+ .readdir = ntfs_readdir,
+ .iget_root = ntfs_iget_root,
+ .iget = ntfs_iget,
+ .next_extent = ntfs_next_extent,
+};
diff --git a/core/fs/ntfs/ntfs.h b/core/fs/ntfs/ntfs.h
new file mode 100644
index 00000000..721a78d7
--- /dev/null
+++ b/core/fs/ntfs/ntfs.h
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "runlist.h"
+
+#ifndef _NTFS_H_
+#define _NTFS_H_
+
+struct ntfs_bpb {
+ uint8_t jmp_boot[3];
+ char oem_name[8];
+ uint16_t sector_size;
+ uint8_t sec_per_clust;
+ uint16_t res_sectors;
+ uint8_t zero_0[3];
+ uint16_t zero_1;
+ uint8_t media;
+ uint16_t zero_2;
+ uint16_t unused_0;
+ uint16_t unused_1;
+ uint32_t unused_2;
+ uint32_t zero_3;
+ uint32_t unused_3;
+ uint64_t total_sectors;
+ uint64_t mft_lclust;
+ uint64_t mft_mirr_lclust;
+ int8_t clust_per_mft_record;
+ uint8_t unused_4[3];
+ uint8_t clust_per_idx_record;
+ uint8_t unused_5[3];
+ uint64_t vol_serial;
+ uint32_t unused_6;
+
+ uint8_t pad[428]; /* padding to a sector boundary (512 bytes) */
+} __attribute__((__packed__));
+
+/* Function type for an NTFS-version-dependent MFT record lookup */
+struct ntfs_mft_record;
+typedef struct ntfs_mft_record *f_mft_record_lookup(struct fs_info *,
+ uint32_t, block_t *);
+
+struct ntfs_sb_info {
+ block_t mft_blk; /* The first MFT record block */
+ uint64_t mft_lcn; /* LCN of the first MFT record */
+ unsigned mft_size; /* The MFT size in sectors */
+ uint64_t mft_record_size; /* MFT record size in bytes */
+
+ uint8_t clust_per_idx_record; /* Clusters per Index Record */
+
+ unsigned long long clusters; /* Total number of clusters */
+
+ unsigned clust_shift; /* Based on sectors */
+ unsigned clust_byte_shift; /* Based on bytes */
+ unsigned clust_mask;
+ unsigned clust_size;
+
+ uint8_t major_ver; /* Major version from $Volume */
+ uint8_t minor_ver; /* Minor version from $Volume */
+
+ /* NTFS-version-dependent MFT record lookup function to use */
+ f_mft_record_lookup *mft_record_lookup;
+} __attribute__((__packed__));
+
+/* The NTFS in-memory inode structure */
+struct ntfs_inode {
+ int64_t initialized_size;
+ int64_t allocated_size;
+ unsigned long mft_no; /* Number of the mft record / inode */
+ uint16_t seq_no; /* Sequence number of the mft record */
+ uint32_t type; /* Attribute type of this inode */
+ uint8_t non_resident;
+ union { /* Non-resident $DATA attribute */
+ struct { /* Used only if non_resident flags isn't set */
+ uint32_t offset; /* Data offset */
+ } resident;
+ struct { /* Used only if non_resident is set */
+ struct runlist *rlist;
+ } non_resident;
+ } data;
+ uint32_t start_cluster; /* Starting cluster address */
+ sector_t start; /* Starting sector */
+ sector_t offset; /* Current sector offset */
+ sector_t here; /* Sector corresponding to offset */
+};
+
+/* This is structure is used to keep a state for ntfs_readdir() callers.
+ * As NTFS stores directory entries in a complex way, this is structure
+ * ends up saving a state required to find out where we must start from
+ * for the next ntfs_readdir() call.
+ */
+struct ntfs_readdir_state {
+ unsigned long mft_no; /* MFT record number */
+ bool in_idx_root; /* It's true if we're still in the INDEX root */
+ uint32_t idx_blks_count; /* Number of read INDX blocks */
+ uint32_t entries_count; /* Number of read INDEX entries */
+ int64_t last_vcn; /* Last VCN of the INDX block */
+};
+
+enum {
+ MAP_UNSPEC,
+ MAP_START = 1 << 0,
+ MAP_END = 1 << 1,
+ MAP_ALLOCATED = 1 << 2,
+ MAP_UNALLOCATED = 1 << 3,
+ MAP_MASK = 0x0000000F,
+};
+
+struct mapping_chunk {
+ uint64_t vcn;
+ int64_t lcn;
+ uint64_t len;
+ uint32_t flags;
+};
+
+/* System defined attributes (32-bit)
+ * Each attribute type has a corresponding attribute name (in Unicode)
+ */
+enum {
+ NTFS_AT_UNUSED = 0x00,
+ NTFS_AT_STANDARD_INFORMATION = 0x10,
+ NTFS_AT_ATTR_LIST = 0x20,
+ NTFS_AT_FILENAME = 0x30,
+ NTFS_AT_OBJ_ID = 0x40,
+ NTFS_AT_SECURITY_DESCP = 0x50,
+ NTFS_AT_VOL_NAME = 0x60,
+ NTFS_AT_VOL_INFO = 0x70,
+ NTFS_AT_DATA = 0x80,
+ NTFS_AT_INDEX_ROOT = 0x90,
+ NTFS_AT_INDEX_ALLOCATION = 0xA0,
+ NTFS_AT_BITMAP = 0xB0,
+ NTFS_AT_REPARSE_POINT = 0xC0,
+ NTFS_AT_EA_INFO = 0xD0,
+ NTFS_AT_EA = 0xE0,
+ NTFS_AT_PROPERTY_SET = 0xF0,
+ NTFS_AT_LOGGED_UTIL_STREAM = 0x100,
+ NTFS_AT_FIRST_USER_DEFINED_ATTR = 0x1000,
+ NTFS_AT_END = 0xFFFFFFFF,
+};
+
+/* NTFS File Permissions (also called attributes in DOS terminology) */
+enum {
+ NTFS_FILE_ATTR_READONLY = 0x00000001,
+ NTFS_FILE_ATTR_HIDDEN = 0x00000002,
+ NTFS_FILE_ATTR_SYSTEM = 0x00000004,
+ NTFS_FILE_ATTR_DIRECTORY = 0x00000010,
+ NTFS_FILE_ATTR_ARCHIVE = 0x00000020,
+ NTFS_FILE_ATTR_DEVICE = 0x00000040,
+ NTFS_FILE_ATTR_NORMAL = 0x00000080,
+ NTFS_FILE_ATTR_TEMPORARY = 0x00000100,
+ NTFS_FILE_ATTR_SPARSE_FILE = 0x00000200,
+ NTFS_FILE_ATTR_REPARSE_POINT = 0x00000400,
+ NTFS_FILE_ATTR_COMPRESSED = 0x00000800,
+ NTFS_FILE_ATTR_OFFLINE = 0x00001000,
+ NTFS_FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000,
+ NTFS_FILE_ATTR_ENCRYPTED = 0x00004000,
+ NTFS_FILE_ATTR_VALID_FLAGS = 0x00007FB7,
+ NTFS_FILE_ATTR_VALID_SET_FLAGS = 0x000031A7,
+ NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT = 0x10000000,
+ NTFS_FILE_ATTR_DUP_VIEW_INDEX_PRESENT = 0x20000000,
+};
+
+/*
+ * Magic identifiers present at the beginning of all ntfs record containing
+ * records (like mft records for example).
+ */
+enum {
+ /* Found in $MFT/$DATA */
+ NTFS_MAGIC_FILE = 0x454C4946, /* MFT entry */
+ NTFS_MAGIC_INDX = 0x58444E49, /* Index buffer */
+ NTFS_MAGIC_HOLE = 0x454C4F48,
+
+ /* Found in $LogFile/$DATA */
+ NTFS_MAGIC_RSTR = 0x52545352,
+ NTFS_MAGIC_RCRD = 0x44524352,
+ /* Found in $LogFile/$DATA (May be found in $MFT/$DATA, also ?) */
+ NTFS_MAGIC_CHKDSK = 0x444B4843,
+ /* Found in all ntfs record containing records. */
+ NTFS_MAGIC_BAAD = 0x44414142,
+ NTFS_MAGIC_EMPTY = 0xFFFFFFFF, /* Record is empty */
+};
+
+struct ntfs_record {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+} __attribute__((__packed__)) NTFS_RECORD;
+
+/* The $MFT metadata file types */
+enum ntfs_system_file {
+ FILE_MFT = 0,
+ FILE_MFTMirr = 1,
+ FILE_LogFile = 2,
+ FILE_Volume = 3,
+ FILE_AttrDef = 4,
+ FILE_root = 5,
+ FILE_Bitmap = 6,
+ FILE_Boot = 7,
+ FILE_BadClus = 8,
+ FILE_Secure = 9,
+ FILE_UpCase = 10,
+ FILE_Extend = 11,
+ FILE_reserved12 = 12,
+ FILE_reserved13 = 13,
+ FILE_reserved14 = 14,
+ FILE_reserved15 = 15,
+ FILE_reserved16 = 16,
+};
+
+enum {
+ MFT_RECORD_IN_USE = 0x0001,
+ MFT_RECORD_IS_DIRECTORY = 0x0002,
+} __attribute__((__packed__));
+
+struct ntfs_mft_record {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+ uint64_t lsn;
+ uint16_t seq_no;
+ uint16_t link_count;
+ uint16_t attrs_offset;
+ uint16_t flags; /* MFT record flags */
+ uint32_t bytes_in_use;
+ uint32_t bytes_allocated;
+ uint64_t base_mft_record;
+ uint16_t next_attr_instance;
+ uint16_t reserved;
+ uint32_t mft_record_no;
+} __attribute__((__packed__)); /* 48 bytes */
+
+/* This is the version without the NTFS 3.1+ specific fields */
+struct ntfs_mft_record_old {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+ uint64_t lsn;
+ uint16_t seq_no;
+ uint16_t link_count;
+ uint16_t attrs_offset;
+ uint16_t flags; /* MFT record flags */
+ uint32_t bytes_in_use;
+ uint32_t bytes_allocated;
+ uint64_t base_mft_record;
+ uint16_t next_attr_instance;
+} __attribute__((__packed__)); /* 42 bytes */
+
+enum {
+ ATTR_DEF_INDEXABLE = 0x02,
+ ATTR_DEF_MULTIPLE = 0x04,
+ ATTR_DEF_NOT_ZERO = 0x08,
+ ATTR_DEF_INDEXED_UNIQUE = 0x10,
+ ATTR_DEF_NAMED_UNIQUE = 0x20,
+ ATTR_DEF_RESIDENT = 0x40,
+ ATTR_DEF_ALWAYS_LOG = 0x80,
+};
+
+struct ntfs_attr_record {
+ uint32_t type; /* Attr. type code */
+ uint32_t len;
+ uint8_t non_resident;
+ uint8_t name_len;
+ uint16_t name_offset;
+ uint16_t flags; /* Attr. flags */
+ uint16_t instance;
+ union {
+ struct { /* Resident attribute */
+ uint32_t value_len;
+ uint16_t value_offset;
+ uint8_t flags; /* Flags of resident attributes */
+ int8_t reserved;
+ } __attribute__((__packed__)) resident;
+ struct { /* Non-resident attributes */
+ uint64_t lowest_vcn;
+ uint64_t highest_vcn;
+ uint16_t mapping_pairs_offset;
+ uint8_t compression_unit;
+ uint8_t reserved[5];
+ int64_t allocated_size;
+ int64_t data_size; /* Byte size of the attribute value.
+ * Note: it can be larger than
+ * allocated_size if attribute value is
+ * compressed or sparse.
+ */
+ int64_t initialized_size;
+ int64_t compressed_size;
+ } __attribute__((__packed__)) non_resident;
+ } __attribute__((__packed__)) data;
+} __attribute__((__packed__));
+
+/* Attribute: Attribute List (0x20)
+ * Note: it can be either resident or non-resident
+ */
+struct ntfs_attr_list_entry {
+ uint32_t type;
+ uint16_t length;
+ uint8_t name_length;
+ uint8_t name_offset;
+ uint64_t lowest_vcn;
+ uint64_t mft_ref;
+ uint16_t instance;
+ uint16_t name[0];
+} __attribute__((__packed__));
+
+#define NTFS_MAX_FILE_NAME_LEN 255
+
+/* Possible namespaces for filenames in ntfs (8-bit) */
+enum {
+ FILE_NAME_POSIX = 0x00,
+ FILE_NAME_WIN32 = 0x01,
+ FILE_NAME_DOS = 0x02,
+ FILE_NAME_WIN32_AND_DOS = 0x03,
+} __attribute__((__packed__));
+
+/* Attribute: Filename (0x30)
+ * Note: always resident
+ */
+struct ntfs_filename_attr {
+ uint64_t parent_directory;
+ int64_t ctime;
+ int64_t atime;
+ int64_t mtime;
+ int64_t rtime;
+ uint64_t allocated_size;
+ uint64_t data_size;
+ uint32_t file_attrs;
+ union {
+ struct {
+ uint16_t packed_ea_size;
+ uint16_t reserved; /* reserved for alignment */
+ } __attribute__((__packed__)) ea;
+ struct {
+ uint32_t reparse_point_tag;
+ } __attribute__((__packed__)) rp;
+ } __attribute__((__packed__)) type;
+ uint8_t file_name_len;
+ uint8_t file_name_type;
+ uint16_t file_name[0]; /* File name in Unicode */
+} __attribute__((__packed__));
+
+/* Attribute: Volume Name (0x60)
+ * Note: always resident
+ * Note: Present only in FILE_volume
+ */
+struct ntfs_vol_name {
+ uint16_t name[0]; /* The name of the volume in Unicode */
+} __attribute__((__packed__));
+
+/* Attribute: Volume Information (0x70)
+ * Note: always resident
+ * Note: present only in FILE_Volume
+ */
+struct ntfs_vol_info {
+ uint64_t reserved;
+ uint8_t major_ver;
+ uint8_t minor_ver;
+ uint16_t flags; /* Volume flags */
+} __attribute__((__packed__));
+
+/* Attribute: Data attribute (0x80)
+ * Note: can be either resident or non-resident
+ */
+struct ntfs_data_attr {
+ uint8_t data[0];
+} __attribute__((__packed__));
+
+/* Index header flags (8-bit) */
+enum {
+ SMALL_INDEX = 0,
+ LARGE_INDEX = 1,
+ LEAF_NODE = 0,
+ INDEX_NODE = 1,
+ NODE_MASK = 1,
+} __attribute__((__packed__));
+
+/* Header for the indexes, describing the INDEX_ENTRY records, which
+ * follow the struct ntfs_idx_header.
+ */
+struct ntfs_idx_header {
+ uint32_t entries_offset;
+ uint32_t index_len;
+ uint32_t allocated_size;
+ uint8_t flags; /* Index header flags */
+ uint8_t reserved[3]; /* Align to 8-byte boundary */
+} __attribute__((__packed__));
+
+/* Attribute: Index Root (0x90)
+ * Note: always resident
+ */
+struct ntfs_idx_root {
+ uint32_t type; /* It is $FILE_NAME for directories, zero for view indexes.
+ * No other values allowed.
+ */
+ uint32_t collation_rule;
+ uint32_t index_block_size;
+ uint8_t clust_per_index_block;
+ uint8_t reserved[3];
+ struct ntfs_idx_header index;
+} __attribute__((__packed__));
+
+/* Attribute: Index allocation (0xA0)
+ * Note: always non-resident, of course! :-)
+ */
+struct ntfs_idx_allocation {
+ uint32_t magic;
+ uint16_t usa_ofs; /* Update Sequence Array offsets */
+ uint16_t usa_count; /* Update Sequence Array number in bytes */
+ int64_t lsn;
+ int64_t index_block_vcn; /* Virtual cluster number of the index block */
+ struct ntfs_idx_header index;
+} __attribute__((__packed__));
+
+enum {
+ INDEX_ENTRY_NODE = 1,
+ INDEX_ENTRY_END = 2,
+ /* force enum bit width to 16-bit */
+ INDEX_ENTRY_SPACE_FILTER = 0xFFFF,
+} __attribute__((__packed__));
+
+struct ntfs_idx_entry_header {
+ union {
+ struct { /* Only valid when INDEX_ENTRY_END is not set */
+ uint64_t indexed_file;
+ } __attribute__((__packed__)) dir;
+ struct { /* Used for views/indexes to find the entry's data */
+ uint16_t data_offset;
+ uint16_t data_len;
+ uint32_t reservedV;
+ } __attribute__((__packed__)) vi;
+ } __attribute__((__packed__)) data;
+ uint16_t len;
+ uint16_t key_len;
+ uint16_t flags; /* Index entry flags */
+ uint16_t reserved; /* Align to 8-byte boundary */
+} __attribute__((__packed__));
+
+struct ntfs_idx_entry {
+ union {
+ struct { /* Only valid when INDEX_ENTRY_END is not set */
+ uint64_t indexed_file;
+ } __attribute__((__packed__)) dir;
+ struct { /* Used for views/indexes to find the entry's data */
+ uint16_t data_offset;
+ uint16_t data_len;
+ uint32_t reservedV;
+ } __attribute__((__packed__)) vi;
+ } __attribute__((__packed__)) data;
+ uint16_t len;
+ uint16_t key_len;
+ uint16_t flags; /* Index entry flags */
+ uint16_t reserved; /* Align to 8-byte boundary */
+ union {
+ struct ntfs_filename_attr file_name;
+ //SII_INDEX_KEY sii;
+ //SDH_INDEX_KEY sdh;
+ //GUID object_id;
+ //REPARSE_INDEX_KEY reparse;
+ //SID sid;
+ uint32_t owner_id;
+ } __attribute__((__packed__)) key;
+} __attribute__((__packed__));
+
+static inline struct ntfs_sb_info *NTFS_SB(struct fs_info *fs)
+{
+ return fs->fs_info;
+}
+
+#define NTFS_PVT(i) ((struct ntfs_inode *)((i)->pvt))
+
+#endif /* _NTFS_H_ */
diff --git a/core/fs/ntfs/runlist.h b/core/fs/ntfs/runlist.h
new file mode 100644
index 00000000..4938696b
--- /dev/null
+++ b/core/fs/ntfs/runlist.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _RUNLIST_H_
+#define _RUNLIST_H_
+
+struct runlist_element {
+ uint64_t vcn;
+ int64_t lcn;
+ uint64_t len;
+};
+
+struct runlist {
+ struct runlist_element run;
+ struct runlist *next;
+};
+
+static struct runlist *tail;
+
+static inline bool runlist_is_empty(struct runlist *rlist)
+{
+ return !rlist;
+}
+
+static inline struct runlist *runlist_alloc(void)
+{
+ struct runlist *rlist;
+
+ rlist = malloc(sizeof *rlist);
+ if (!rlist)
+ malloc_error("runlist structure");
+
+ rlist->next = NULL;
+
+ return rlist;
+}
+
+static inline void runlist_append(struct runlist **rlist,
+ struct runlist_element *elem)
+{
+ struct runlist *n = runlist_alloc();
+
+ n->run = *elem;
+
+ if (runlist_is_empty(*rlist)) {
+ *rlist = n;
+ tail = n;
+ } else {
+ tail->next = n;
+ tail = n;
+ }
+}
+
+static inline struct runlist *runlist_remove(struct runlist **rlist)
+{
+ struct runlist *ret;
+
+ if (runlist_is_empty(*rlist))
+ return NULL;
+
+ ret = *rlist;
+ *rlist = ret->next;
+
+ return ret;
+}
+
+#endif /* _RUNLIST_H_ */
diff --git a/core/head.inc b/core/head.inc
index 18ce132c..71eb5744 100644
--- a/core/head.inc
+++ b/core/head.inc
@@ -20,8 +20,8 @@
%ifndef _HEAD_INC
%define _HEAD_INC
-%if __NASM_MAJOR__ < 2
- %error "NASM 2.00 or later required to compile correctly"
+%if __NASM_MAJOR__ < 2 || (__NASM_MAJOR__ == 2 && __NASM_MINOR__ < 3)
+ %error "NASM 2.03 or later required to compile correctly"
%endif
%include "macros.inc"
diff --git a/core/include/fs.h b/core/include/fs.h
index ecd148da..e1f5733c 100644
--- a/core/include/fs.h
+++ b/core/include/fs.h
@@ -95,6 +95,7 @@ struct extent {
struct inode {
struct fs_info *fs; /* The filesystem this inode is associated with */
struct inode *parent; /* Parent directory, if any */
+ const char *name; /* Name, valid for generic path search only */
int refcnt;
int mode; /* FILE , DIR or SYMLINK */
uint32_t size;
diff --git a/core/init.inc b/core/init.inc
index e06ca96f..8c6a178f 100644
--- a/core/init.inc
+++ b/core/init.inc
@@ -69,9 +69,15 @@ check_escapes:
shr edx,10
cmp ax,dx
jae enough_ram
- mov ax,dx
mov si,err_noram
mov cl,10
+ push dx
+ div cl
+ add [si+err_noram.need-err_noram+2],ah
+ cbw
+ div cl
+ add [si+err_noram.need-err_noram],ax
+ pop ax
div cl
add [si+err_noram.size-err_noram+2],ah
cbw
@@ -83,15 +89,15 @@ enough_ram:
skip_checks:
section .data16
-err_noram db 'It appears your computer has less than '
+err_noram db 'It appears your computer has only '
.size db '000'
- db 'K of low ("DOS")'
- db CR, LF
- db 'RAM. Syslinux needs at least this amount to boot. If you get'
- db CR, LF
- db 'this message in error, hold down the Ctrl key while'
- db CR, LF
- db 'booting, and I will take your word for it.', CR, LF, 0
+ db 'K of low ("DOS") RAM.', CR, LF
+ db 'This version of Syslinux needs '
+.need db '000'
+ db 'K to boot. If you get this', CR, LF
+ db 'message in error, hold down the Ctrl key while'
+ db 'booting, and I', CR, LF
+ db 'will take your word for it.', CR, LF, 0
section .text16
;
diff --git a/core/isolinux.asm b/core/isolinux.asm
index ca8ee3a3..7a871f0e 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -52,22 +52,6 @@ vk_append: resb max_cmd_len+1 ; Command line
vk_end: equ $ ; Should be <= vk_size
endstruc
-;
-; File structure. This holds the information for each currently open file.
-;
- struc open_file_t
-file_sector resd 1 ; Sector pointer (0 = structure free)
-file_bytesleft resd 1 ; Number of bytes left
-file_left resd 1 ; Number of sectors left
- resd 1 ; Unused
- endstruc
-
-%ifndef DEPEND
-%if (open_file_t_size & (open_file_t_size-1))
-%error "open_file_t is not a power of 2"
-%endif
-%endif
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -1195,138 +1179,6 @@ ROOT_FS_OPS:
;
%include "ui.inc"
-;
-; Enable disk emulation. The kind of disk we emulate is dependent on the
-; size of the file: 1200K, 1440K or 2880K floppy, otherwise harddisk.
-;
-is_disk_image:
- TRACER CR
- TRACER LF
- TRACER 'D'
- TRACER ':'
-
- mov edx,eax ; File size
- mov di,img_table
- mov cx,img_table_count
- mov eax,[si+file_sector] ; Starting LBA of file
- mov [dsp_lba],eax ; Location of file
- mov byte [dsp_drive], 0 ; 00h floppy, 80h hard disk
-.search_table:
- TRACER 't'
- mov eax,[di+4]
- cmp edx,[di]
- je .type_found
- add di,8
- loop .search_table
-
- ; Hard disk image. Need to examine the partition table
- ; in order to deduce the C/H/S geometry. Sigh.
-.hard_disk_image:
- TRACER 'h'
- cmp edx,512
- jb .bad_image
-
- mov bx,trackbuf
- mov cx,1 ; Load 1 sector
- pm_call getfssec
-
- cmp word [trackbuf+510],0aa55h ; Boot signature
- jne .bad_image ; Image not bootable
-
- mov cx,4 ; 4 partition entries
- mov di,trackbuf+446 ; Start of partition table
-
- xor ax,ax ; Highest sector(al) head(ah)
-
-.part_scan:
- cmp byte [di+4], 0
- jz .part_loop
- lea si,[di+1]
- call .hs_check
- add si,byte 4
- call .hs_check
-.part_loop:
- add di,byte 16
- loop .part_scan
-
- push eax ; H/S
- push edx ; File size
- mov bl,ah
- xor bh,bh
- inc bx ; # of heads in BX
- xor ah,ah ; # of sectors in AX
- cwde ; EAX[31:16] <- 0
- mul bx
- shl eax,9 ; Convert to bytes
- ; Now eax contains the number of bytes per cylinder
- pop ebx ; File size
- xor edx,edx
- div ebx
- and edx,edx
- jz .no_remainder
- inc eax ; Fractional cylinder...
- ; Now (e)ax contains the number of cylinders
-.no_remainder: cmp eax,1024
- jna .ok_cyl
- mov ax,1024 ; Max possible #
-.ok_cyl: dec ax ; Convert to max cylinder no
- pop ebx ; S(bl) H(bh)
- shl ah,6
- or bl,ah
- xchg ax,bx
- shl eax,16
- mov ah,bl
- mov al,4 ; Hard disk boot
- mov byte [dsp_drive], 80h ; Drive 80h = hard disk
-
-.type_found:
- TRACER 'T'
- mov bl,[sp_media]
- and bl,0F0h ; Copy controller info bits
- or al,bl
- mov [dsp_media],al ; Emulation type
- shr eax,8
- mov [dsp_chs],eax ; C/H/S geometry
- mov ax,[sp_devspec] ; Copy device spec
- mov [dsp_devspec],ax
- mov al,[sp_controller] ; Copy controller index
- mov [dsp_controller],al
-
- TRACER 'V'
- call vgaclearmode ; Reset video
-
- mov ax,4C00h ; Enable emulation and boot
- mov si,dspec_packet
- mov dl,[DriveNumber]
- lss sp,[InitStack]
- TRACER 'X'
-
- call int13
-
- ; If this returns, we have problems
-.bad_image:
- mov si,err_disk_image
- call writestr
- jmp enter_command
-
-;
-; Look for the highest seen H/S geometry
-; We compute cylinders separately
-;
-.hs_check:
- mov bl,[si] ; Head #
- cmp bl,ah
- jna .done_track
- mov ah,bl ; New highest head #
-.done_track: mov bl,[si+1]
- and bl,3Fh ; Sector #
- cmp bl,al
- jna .done_sector
- mov al,bl
-.done_sector: ret
-
-
-
; -----------------------------------------------------------------------------
; Common modules
; -----------------------------------------------------------------------------
@@ -1352,33 +1204,8 @@ err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
;
alignz 4
exten_table: db '.cbt' ; COMBOOT (specific)
- db '.img' ; Disk image
db '.bin' ; CD boot sector
db '.com' ; COMBOOT (same as DOS)
db '.c32' ; COM32
exten_table_end:
dd 0, 0 ; Need 8 null bytes here
-
-;
-; Floppy image table
-;
- alignz 4
-img_table_count equ 3
-img_table:
- dd 1200*1024 ; 1200K floppy
- db 1 ; Emulation type
- db 80-1 ; Max cylinder
- db 15 ; Max sector
- db 2-1 ; Max head
-
- dd 1440*1024 ; 1440K floppy
- db 2 ; Emulation type
- db 80-1 ; Max cylinder
- db 18 ; Max sector
- db 2-1 ; Max head
-
- dd 2880*1024 ; 2880K floppy
- db 3 ; Emulation type
- db 80-1 ; Max cylinder
- db 36 ; Max sector
- db 2-1 ; Max head
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
index f62f55b2..a2f859d0 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -37,6 +37,8 @@ ROOT_FS_OPS:
dd vfat_fs_ops
extern ext2_fs_ops
dd ext2_fs_ops
+ extern ntfs_fs_ops
+ dd ntfs_fs_ops
extern btrfs_fs_ops
dd btrfs_fs_ops
dd 0
diff --git a/core/syslinux.ld b/core/syslinux.ld
index 40a01394..11adbcb8 100644
--- a/core/syslinux.ld
+++ b/core/syslinux.ld
@@ -280,8 +280,9 @@ SECTIONS
__ctors_lma = __ctors_vma + __text_lma - __text_vma;
.ctors : AT(__ctors_lma) {
__ctors_start = .;
- KEEP (*(SORT(.ctors.*)))
- KEEP (*(.ctors))
+ KEEP (*(SORT(.preinit_array*)))
+ KEEP (*(SORT(.init_array*)))
+ KEEP (*(SORT(.ctors*)))
__ctors_end = .;
}
@@ -289,8 +290,8 @@ SECTIONS
__dtors_lma = __dtors_vma + __text_lma - __text_vma;
.dtors : AT(__dtors_lma) {
__dtors_start = .;
- KEEP (*(SORT(.dtors.*)))
- KEEP (*(.dtors))
+ KEEP (*(SORT(.fini_array*)))
+ KEEP (*(SORT(.dtors*)))
__dtors_end = .;
}
diff --git a/core/ui.inc b/core/ui.inc
index 0a4bb569..631860f8 100644
--- a/core/ui.inc
+++ b/core/ui.inc
@@ -681,11 +681,8 @@ is_bad_image:
%else
is_bss_sector equ is_bad_image
%endif
-%if IS_ISOLINUX
- ; ok
-%else
-is_disk_image equ is_bad_image
-%endif
+
+is_disk_image equ is_bad_image ; No longer supported
section .data16
boot_prompt db 'boot: ', 0