aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2012-03-30 17:40:50 -0700
committerH. Peter Anvin <hpa@zytor.com>2012-03-30 17:40:50 -0700
commit86a2df0467466d3e197837964db38b312b89ac0b (patch)
tree30828febadd5e0dd25e949819ccd93c803f14f27 /core
parent73caae75a315c3de65ac25c9d5f521c1a4d3847f (diff)
parenta1006762fa6f98750bb77d76dd992cb8ea9f9c99 (diff)
downloadsyslinux-86a2df0467466d3e197837964db38b312b89ac0b.tar.gz
syslinux-86a2df0467466d3e197837964db38b312b89ac0b.tar.xz
syslinux-86a2df0467466d3e197837964db38b312b89ac0b.zip
Merge remote-tracking branch 'origin/master' into lwipsyslinux-4.10-pre18
Resolved Conflicts: NEWS version Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core')
-rw-r--r--core/bootsect.inc16
-rw-r--r--core/fs/ntfs/ntfs.c1388
-rw-r--r--core/fs/ntfs/ntfs.h485
-rw-r--r--core/fs/ntfs/runlist.h83
-rw-r--r--core/ldlinux.asm2
5 files changed, 1973 insertions, 1 deletions
diff --git a/core/bootsect.inc b/core/bootsect.inc
index 6c204096..9e47e1a5 100644
--- a/core/bootsect.inc
+++ b/core/bootsect.inc
@@ -1,7 +1,7 @@
;; -----------------------------------------------------------------------
;;
;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;; Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ SuperSize equ $+1
load_bootsec:
mov edi,free_high_memory
mov [trackbuf+4],edi ; Copy from this address
+ mov eax,0xA0000 ; Maximum load
xor dx,dx ; No padding
mov bx,abort_check ; Don't print dots, but allow abort
call load_high
@@ -45,6 +46,9 @@ load_bootsec:
sub edi,free_high_memory
mov [trackbuf+8],edi ; Save length
+ cmp edi,0xA0000-7C00h
+ ja bs_too_big
+
mov eax,7C00h ; Entry point
mov [trackbuf],eax ; Copy to this address
@@ -237,3 +241,13 @@ replace_stub:
.csip equ $-4
section .text16
+bs_too_big:
+ call close
+ mov si,err_bs_too_big
+ jmp abort_load
+
+ section .data16
+err_bs_too_big db "Too large for a bootstrap (need LINUX instead of KERNEL?)"
+ db CR, LF, 0
+
+ section .text16
diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
new file mode 100644
index 00000000..500d0fd3
--- /dev/null
+++ b/core/fs/ntfs/ntfs.c
@@ -0,0 +1,1388 @@
+/*
+ * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* Note: No support for compressed files */
+
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.h>
+#include <ilog2.h>
+#include <klibc/compiler.h>
+#include <ctype.h>
+
+#include "codepage.h"
+#include "ntfs.h"
+#include "runlist.h"
+
+static struct ntfs_readdir_state *readdir_state;
+
+/*** Function declarations */
+static f_mft_record_lookup ntfs_mft_record_lookup_3_0;
+static f_mft_record_lookup ntfs_mft_record_lookup_3_1;
+
+/*** Function definitions */
+
+/* Check if there are specific zero fields in an NTFS boot sector */
+static inline int ntfs_check_zero_fields(const struct ntfs_bpb *sb)
+{
+ return !sb->res_sectors && (!sb->zero_0[0] && !sb->zero_0[1] &&
+ !sb->zero_0[2]) && !sb->zero_1 && !sb->zero_2 &&
+ !sb->zero_3;
+}
+
+static inline int ntfs_check_sb_fields(const struct ntfs_bpb *sb)
+{
+ return ntfs_check_zero_fields(sb) &&
+ (!memcmp(sb->oem_name, "NTFS ", 8) ||
+ !memcmp(sb->oem_name, "MSWIN4.0", 8) ||
+ !memcmp(sb->oem_name, "MSWIN4.1", 8));
+}
+
+static inline struct inode *new_ntfs_inode(struct fs_info *fs)
+{
+ struct inode *inode;
+
+ inode = alloc_inode(fs, 0, sizeof(struct ntfs_inode));
+ if (!inode)
+ malloc_error("inode structure");
+
+ return inode;
+}
+
+static void ntfs_fixups_writeback(struct fs_info *fs, struct ntfs_record *nrec)
+{
+ uint16_t *usa;
+ uint16_t usa_no;
+ uint16_t usa_count;
+ uint16_t *blk;
+
+ dprintf("in %s()\n", __func__);
+
+ if (nrec->magic != NTFS_MAGIC_FILE && nrec->magic != NTFS_MAGIC_INDX)
+ return;
+
+ /* get the Update Sequence Array offset */
+ usa = (uint16_t *)((uint8_t *)nrec + nrec->usa_ofs);
+ /* get the Update Sequence Array Number and skip it */
+ usa_no = *usa++;
+ /* get the Update Sequene Array count */
+ usa_count = nrec->usa_count - 1; /* exclude the USA number */
+ /* make it to point to the last two bytes of the RECORD's first sector */
+ blk = (uint16_t *)((uint8_t *)nrec + SECTOR_SIZE(fs) - 2);
+
+ while (usa_count--) {
+ if (*blk != usa_no)
+ break;
+
+ *blk = *usa++;
+ blk = (uint16_t *)((uint8_t *)blk + SECTOR_SIZE(fs));
+ }
+}
+
+/* read content from cache */
+static int ntfs_read(struct fs_info *fs, void *buf, size_t len, uint64_t count,
+ block_t *blk, uint64_t *blk_offset,
+ uint64_t *blk_next_offset, uint64_t *lcn)
+{
+ uint8_t *data;
+ uint64_t offset = *blk_offset;
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint64_t bytes;
+ uint64_t lbytes;
+ uint64_t loffset;
+ uint64_t k;
+
+ dprintf("in %s()\n", __func__);
+
+ if (count > len)
+ goto out;
+
+ data = (uint8_t *)get_cache(fs->fs_dev, *blk);
+ if (!data)
+ goto out;
+
+ if (!offset)
+ offset = (*lcn << clust_byte_shift) % blk_size;
+
+ dprintf("LCN: 0x%X\n", *lcn);
+ dprintf("offset: 0x%X\n", offset);
+
+ bytes = count; /* bytes to copy */
+ lbytes = blk_size - offset; /* bytes left to copy */
+ if (lbytes >= bytes) {
+ /* so there's room enough, then copy the whole content */
+ memcpy(buf, data + offset, bytes);
+ loffset = offset;
+ offset += count;
+ } else {
+ dprintf("bytes: %u\n", bytes);
+ dprintf("bytes left: %u\n", lbytes);
+ /* otherwise, let's copy it partially... */
+ k = 0;
+ while (bytes) {
+ memcpy(buf + k, data + offset, lbytes);
+ bytes -= lbytes;
+ loffset = offset;
+ offset += lbytes;
+ k += lbytes;
+ if (offset >= blk_size) {
+ /* then fetch a new FS block */
+ data = (uint8_t *)get_cache(fs->fs_dev, ++*blk);
+ if (!data)
+ goto out;
+
+ lbytes = bytes;
+ loffset = offset;
+ offset = 0;
+ }
+ }
+ }
+
+ if (loffset >= blk_size)
+ loffset = 0; /* it must be aligned on a block boundary */
+
+ *blk_offset = loffset;
+
+ if (blk_next_offset)
+ *blk_next_offset = offset;
+
+ *lcn += blk_size / count; /* update LCN */
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
+ uint32_t file, block_t *blk)
+{
+ const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
+ uint8_t *buf;
+ const block_t mft_blk = NTFS_SB(fs)->mft_blk;
+ block_t cur_blk;
+ block_t right_blk;
+ uint64_t offset;
+ uint64_t next_offset;
+ const uint32_t mft_record_shift = ilog2(mft_record_size);
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ uint64_t lcn;
+ int err;
+ struct ntfs_mft_record *mrec;
+
+ dprintf("in %s()\n", __func__);
+
+ buf = (uint8_t *)malloc(mft_record_size);
+ if (!buf)
+ malloc_error("uint8_t *");
+
+ /* determine MFT record's LCN and block number */
+ lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
+ cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
+ offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
+ for (;;) {
+ right_blk = cur_blk + mft_blk;
+ err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
+ &offset, &next_offset, &lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ break;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+
+ mrec = (struct ntfs_mft_record *)buf;
+ /* check if it has a valid magic number */
+ if (mrec->magic == NTFS_MAGIC_FILE) {
+ if (blk)
+ *blk = cur_blk; /* update record starting block */
+
+ return mrec; /* found MFT record */
+ }
+
+ if (next_offset >= BLOCK_SIZE(fs)) {
+ /* try the next FS block */
+ offset = 0;
+ cur_blk = right_blk - mft_blk + 1;
+ } else {
+ /* there's still content to fetch in the current block */
+ cur_blk = right_blk - mft_blk;
+ offset = next_offset; /* update FS block offset */
+ }
+ }
+
+ free(buf);
+
+ return NULL;
+}
+
+static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
+ uint32_t file, block_t *blk)
+{
+ const uint64_t mft_record_size = NTFS_SB(fs)->mft_record_size;
+ uint8_t *buf;
+ const block_t mft_blk = NTFS_SB(fs)->mft_blk;
+ block_t cur_blk;
+ block_t right_blk;
+ uint64_t offset;
+ uint64_t next_offset;
+ const uint32_t mft_record_shift = ilog2(mft_record_size);
+ const uint32_t clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
+ uint64_t lcn;
+ int err;
+ struct ntfs_mft_record *mrec;
+
+ dprintf("in %s()\n", __func__);
+
+ buf = (uint8_t *)malloc(mft_record_size);
+ if (!buf)
+ malloc_error("uint8_t *");
+
+ lcn = NTFS_SB(fs)->mft_lcn + (file << mft_record_shift >> clust_byte_shift);
+ cur_blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs)) - mft_blk;
+ offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
+ for (;;) {
+ right_blk = cur_blk + NTFS_SB(fs)->mft_blk;
+ err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &right_blk,
+ &offset, &next_offset, &lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ break;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+
+ mrec = (struct ntfs_mft_record *)buf;
+ /* Check if the NTFS 3.1 MFT record number matches */
+ if (mrec->magic == NTFS_MAGIC_FILE && mrec->mft_record_no == file) {
+ if (blk)
+ *blk = cur_blk; /* update record starting block */
+
+ return mrec; /* found MFT record */
+ }
+
+ if (next_offset >= BLOCK_SIZE(fs)) {
+ /* try the next FS block */
+ offset = 0;
+ cur_blk = right_blk - NTFS_SB(fs)->mft_blk + 1;
+ } else {
+ /* there's still content to fetch in the current block */
+ cur_blk = right_blk - NTFS_SB(fs)->mft_blk;
+ offset = next_offset; /* update FS block offset */
+ }
+ }
+
+ free(buf);
+
+ return NULL;
+}
+
+static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)
+{
+ const uint16_t *entry_fn;
+ uint8_t entry_fn_len;
+ unsigned i;
+
+ dprintf("in %s()\n", __func__);
+
+ entry_fn = ie->key.file_name.file_name;
+ entry_fn_len = ie->key.file_name.file_name_len;
+
+ if (strlen(dname) != entry_fn_len)
+ return false;
+
+ /* Do case-sensitive compares for Posix file names */
+ if (ie->key.file_name.file_name_type == FILE_NAME_POSIX) {
+ for (i = 0; i < entry_fn_len; i++)
+ if (entry_fn[i] != dname[i])
+ return false;
+ } else {
+ for (i = 0; i < entry_fn_len; i++)
+ if (tolower(entry_fn[i]) != tolower(dname[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,
+ struct mapping_chunk *chunk,
+ uint32_t *offset)
+{
+ memset(chunk, 0, sizeof *chunk);
+ *offset = 0U;
+
+ return (uint8_t *)attr + attr->data.non_resident.mapping_pairs_offset;
+}
+
+/* Parse data runs.
+ *
+ * return 0 on success or -1 on failure.
+ */
+static int parse_data_run(const void *stream, uint32_t *offset,
+ uint8_t *attr_len, struct mapping_chunk *chunk)
+{
+ uint8_t *buf; /* Pointer to the zero-terminated byte stream */
+ uint8_t count; /* The count byte */
+ uint8_t v, l; /* v is the number of changed low-order VCN bytes;
+ * l is the number of changed low-order LCN bytes
+ */
+ uint8_t *byte;
+ int byte_shift = 8;
+ int mask;
+ uint8_t val;
+ int64_t res;
+
+ (void)attr_len;
+
+ dprintf("in %s()\n", __func__);
+
+ chunk->flags &= ~MAP_MASK;
+
+ buf = (uint8_t *)stream + *offset;
+ if (buf > attr_len || !*buf) {
+ chunk->flags |= MAP_END; /* we're done */
+ return 0;
+ }
+
+ if (!*offset)
+ chunk->flags |= MAP_START; /* initial chunk */
+
+ count = *buf;
+ v = count & 0x0F;
+ l = count >> 4;
+
+ if (v > 8 || l > 8) /* more than 8 bytes ? */
+ goto out;
+
+ byte = (uint8_t *)buf + v;
+ count = v;
+
+ res = 0LL;
+ while (count--) {
+ val = *byte--;
+ mask = val >> (byte_shift - 1);
+ res = (res << byte_shift) | ((val + mask) ^ mask);
+ }
+
+ chunk->len = res; /* get length data */
+
+ byte = (uint8_t *)buf + v + l;
+ count = l;
+
+ mask = 0xFFFFFFFF;
+ res = 0LL;
+ if (*byte & 0x80)
+ res |= (int64_t)mask; /* sign-extend it */
+
+ while (count--)
+ res = (res << byte_shift) | *byte--;
+
+ chunk->lcn += res;
+ /* are VCNS from cur_vcn to next_vcn - 1 unallocated ? */
+ if (!chunk->lcn)
+ chunk->flags |= MAP_UNALLOCATED;
+ else
+ chunk->flags |= MAP_ALLOCATED;
+
+ *offset += v + l + 1;
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static struct ntfs_mft_record *
+ntfs_attr_list_lookup(struct fs_info *fs, struct ntfs_attr_record *attr,
+ uint32_t type, struct ntfs_mft_record *mrec)
+{
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ uint8_t *stream;
+ int err;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint8_t buf[blk_size];
+ uint64_t blk_offset;
+ int64_t vcn;
+ int64_t lcn;
+ int64_t last_lcn;
+ block_t blk;
+ struct ntfs_attr_list_entry *attr_entry;
+ uint32_t len = 0;
+ struct ntfs_mft_record *retval;
+ uint64_t start_blk = 0;
+
+ dprintf("in %s()\n", __func__);
+
+ if (attr->non_resident)
+ goto handle_non_resident_attr;
+
+ attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr + attr->data.resident.value_offset);
+ len = attr->data.resident.value_len;
+ for (; (uint8_t *)attr_entry < (uint8_t *)attr + len;
+ attr_entry = (struct ntfs_attr_list_entry *)((uint8_t *)attr_entry +
+ attr_entry->length)) {
+ dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%X\n",
+ attr_entry->type);
+ if (attr_entry->type == type)
+ goto found; /* We got the attribute! :-) */
+ }
+
+ printf("No attribute found.\n");
+ goto out;
+
+handle_non_resident_attr:
+ attr_len = (uint8_t *)attr + attr->len;
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ do {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("parse_data_run()\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+ if (chunk.flags & MAP_END)
+ break;
+ if (chunk.flags & MAP_ALLOCATED) {
+ vcn = 0;
+ lcn = chunk.lcn;
+ while (vcn < chunk.len) {
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_byte_shift >>
+ BLOCK_SHIFT(fs);
+ blk_offset = 0;
+ last_lcn = lcn;
+ lcn += vcn;
+ err = ntfs_read(fs, buf, blk_size, blk_size, &blk,
+ &blk_offset, NULL, (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto out;
+ }
+
+ attr_entry = (struct ntfs_attr_list_entry *)&buf;
+ len = attr->data.non_resident.data_size;
+ for (; (uint8_t *)attr_entry < (uint8_t *)&buf[0] + len;
+ attr_entry = (struct ntfs_attr_list_entry *)
+ ((uint8_t *)attr_entry + attr_entry->length)) {
+ dprintf("<$ATTRIBUTE_LIST> Attribute type: 0x%x\n",
+ attr_entry->type);
+ if (attr_entry->type == type)
+ goto found; /* We got the attribute! :-) */
+ }
+
+ lcn = last_lcn; /* restore original LCN */
+ /* go to the next VCN */
+ vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
+ }
+ }
+ } while (!(chunk.flags & MAP_END));
+
+ printf("No attribute found.\n");
+
+out:
+ return NULL;
+
+found:
+ /* At this point we have the attribute we were looking for. Now we
+ * will look for the MFT record that stores information about this
+ * attribute.
+ */
+
+ /* Check if the attribute type we're looking for is in the same
+ * MFT record. If so, we do not need to look it up again - return it.
+ */
+ if (mrec->mft_record_no == attr_entry->mft_ref)
+ return mrec;
+
+ retval = NTFS_SB(fs)->mft_record_lookup(fs, attr_entry->mft_ref,
+ &start_blk);
+ if (!retval) {
+ printf("No MFT record found!\n");
+ goto out;
+ }
+
+ /* return the found MFT record */
+ return retval;
+}
+
+static struct ntfs_attr_record *
+__ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
+ struct ntfs_mft_record **mrec)
+{
+ struct ntfs_mft_record *_mrec = *mrec;
+ struct ntfs_attr_record *attr;
+ struct ntfs_attr_record *attr_list_attr;
+
+ dprintf("in %s()\n", __func__);
+
+ if (!_mrec || type == NTFS_AT_END)
+ goto out;
+
+again:
+ attr_list_attr = NULL;
+
+ attr = (struct ntfs_attr_record *)((uint8_t *)_mrec + _mrec->attrs_offset);
+ /* walk through the file attribute records */
+ for (;; attr = (struct ntfs_attr_record *)((uint8_t *)attr + attr->len)) {
+ if (attr->type == NTFS_AT_END)
+ break;
+
+ if (attr->type == NTFS_AT_ATTR_LIST) {
+ dprintf("MFT record #%lu has an $ATTRIBUTE_LIST attribute.\n",
+ _mrec->mft_record_no);
+ attr_list_attr = attr;
+ continue;
+ }
+
+ if (attr->type == type)
+ break;
+ }
+
+ /* if the record has an $ATTRIBUTE_LIST attribute associated
+ * with it, then we need to look for the wanted attribute in
+ * it as well.
+ */
+ if (attr->type == NTFS_AT_END && attr_list_attr) {
+ struct ntfs_mft_record *retval;
+
+ retval = ntfs_attr_list_lookup(fs, attr_list_attr, type, _mrec);
+ if (!retval)
+ goto out;
+
+ _mrec = retval;
+ goto again;
+ } else if (attr->type == NTFS_AT_END && !attr_list_attr) {
+ attr = NULL;
+ }
+
+ return attr;
+
+out:
+ return NULL;
+}
+
+static inline struct ntfs_attr_record *
+ntfs_attr_lookup(struct fs_info *fs, uint32_t type,
+ struct ntfs_mft_record **mmrec,
+ struct ntfs_mft_record *mrec)
+{
+ struct ntfs_mft_record *_mrec = mrec;
+ struct ntfs_mft_record *other = *mmrec;
+ struct ntfs_attr_record *retval = NULL;
+
+ if (mrec == other)
+ return __ntfs_attr_lookup(fs, type, &other);
+
+ retval = __ntfs_attr_lookup(fs, type, &_mrec);
+ if (!retval) {
+ _mrec = other;
+ retval = __ntfs_attr_lookup(fs, type, &other);
+ if (!retval)
+ other = _mrec;
+ } else if (retval && (_mrec != mrec)) {
+ other = _mrec;
+ }
+
+ return retval;
+}
+
+static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec)
+{
+ return mrec->flags & MFT_RECORD_IS_DIRECTORY ? DT_DIR : DT_REG;
+}
+
+static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
+ struct inode *inode)
+{
+ uint64_t start_blk = 0;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ enum dirent_type d_type;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ int err;
+ uint8_t *stream;
+ uint32_t offset;
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+
+ NTFS_PVT(inode)->mft_no = mft_no;
+ NTFS_PVT(inode)->seq_no = mrec->seq_no;
+
+ NTFS_PVT(inode)->start_cluster = start_blk >> NTFS_SB(fs)->clust_shift;
+ NTFS_PVT(inode)->here = start_blk;
+
+ d_type = get_inode_mode(mrec);
+ if (d_type == DT_DIR) { /* directory stuff */
+ dprintf("Got a directory.\n");
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ /* check if we have a previous allocated state structure */
+ if (readdir_state) {
+ free(readdir_state);
+ readdir_state = NULL;
+ }
+
+ /* allocate our state structure */
+ readdir_state = malloc(sizeof *readdir_state);
+ if (!readdir_state)
+ malloc_error("ntfs_readdir_state structure");
+
+ readdir_state->mft_no = mft_no;
+ /* obviously, the ntfs_readdir() caller will start from INDEX root */
+ readdir_state->in_idx_root = true;
+ } else if (d_type == DT_REG) { /* file stuff */
+ dprintf("Got a file.\n");
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ NTFS_PVT(inode)->non_resident = attr->non_resident;
+ NTFS_PVT(inode)->type = attr->type;
+
+ if (!attr->non_resident) {
+ NTFS_PVT(inode)->data.resident.offset =
+ (uint32_t)((uint8_t *)attr + attr->data.resident.value_offset);
+ inode->size = attr->data.resident.value_len;
+ } else {
+ attr_len = (uint8_t *)attr + attr->len;
+
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ NTFS_PVT(inode)->data.non_resident.rlist = NULL;
+ for (;;) {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("parse_data_run()\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+ if (chunk.flags & MAP_END)
+ break;
+ if (chunk.flags & MAP_ALLOCATED) {
+ /* append new run to the runlist */
+ runlist_append(&NTFS_PVT(inode)->data.non_resident.rlist,
+ (struct runlist_element *)&chunk);
+ /* update for next VCN */
+ chunk.vcn += chunk.len;
+ }
+ }
+
+ if (runlist_is_empty(NTFS_PVT(inode)->data.non_resident.rlist)) {
+ printf("No mapping found\n");
+ goto out;
+ }
+
+ inode->size = attr->data.non_resident.initialized_size;
+ }
+ }
+
+ inode->mode = d_type;
+
+ free(mrec);
+
+ return 0;
+
+out:
+ free(mrec);
+
+ return -1;
+}
+
+static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
+{
+ struct fs_info *fs = dir->fs;
+ struct ntfs_mft_record *mrec, *lmrec;
+ block_t blk;
+ uint64_t blk_offset;
+ struct ntfs_attr_record *attr;
+ struct ntfs_idx_root *ir;
+ struct ntfs_idx_entry *ie;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ uint8_t buf[blk_size];
+ struct ntfs_idx_allocation *iblk;
+ int err;
+ uint8_t *stream;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ int64_t vcn;
+ int64_t lcn;
+ int64_t last_lcn;
+ struct inode *inode;
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ ir = (struct ntfs_idx_root *)((uint8_t *)attr +
+ attr->data.resident.value_offset);
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&ir->index +
+ ir->index.entries_offset);
+ for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)mrec ||
+ (uint8_t *)ie + sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&ir->index + ir->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&ir->index + ir->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key. it can however contain
+ * a pointer to a child node in the B+ tree so we just break out
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+
+ if (ntfs_filename_cmp(dname, ie))
+ goto found;
+ }
+
+ /* check for the presence of a child node */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ printf("No child node, aborting...\n");
+ goto out;
+ }
+
+ /* then descend into child node */
+
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ if (!attr->non_resident) {
+ printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
+ goto out;
+ }
+
+ attr_len = (uint8_t *)attr + attr->len;
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ do {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err)
+ break;
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ continue;
+
+ if (chunk.flags & MAP_ALLOCATED) {
+ dprintf("%d cluster(s) starting at 0x%08llX\n", chunk.len,
+ chunk.lcn);
+
+ vcn = 0;
+ lcn = chunk.lcn;
+ while (vcn < chunk.len) {
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift <<
+ SECTOR_SHIFT(fs) >> BLOCK_SHIFT(fs);
+
+ blk_offset = 0;
+ last_lcn = lcn;
+ lcn += vcn;
+ err = ntfs_read(fs, &buf, blk_size, blk_size, &blk,
+ &blk_offset, NULL, (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto not_found;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
+
+ iblk = (struct ntfs_idx_allocation *)&buf;
+ if (iblk->magic != NTFS_MAGIC_INDX) {
+ printf("Not a valid INDX record.\n");
+ goto not_found;
+ }
+
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
+ iblk->index.entries_offset);
+ for (;; ie = (struct ntfs_idx_entry *)((uint8_t *)ie +
+ ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
+ sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&iblk->index + iblk->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&iblk->index + iblk->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+
+ if (ntfs_filename_cmp(dname, ie))
+ goto found;
+ }
+
+ lcn = last_lcn; /* restore the original LCN */
+ /* go to the next VCN */
+ vcn += (blk_size / (1 << NTFS_SB(fs)->clust_byte_shift));
+ }
+ }
+ } while (!(chunk.flags & MAP_END));
+
+not_found:
+ dprintf("Index not found\n");
+
+out:
+ free(mrec);
+
+ return NULL;
+
+found:
+ dprintf("Index found\n");
+ inode = new_ntfs_inode(fs);
+ err = index_inode_setup(fs, ie->data.dir.indexed_file, inode);
+ if (err) {
+ printf("Error in index_inode_setup()\n");
+ free(inode);
+ goto out;
+ }
+
+ free(mrec);
+
+ return inode;
+
+index_err:
+ printf("Corrupt index. Aborting lookup...\n");
+ goto out;
+}
+
+/* Convert an UTF-16LE LFN to OEM LFN */
+static uint8_t ntfs_cvt_filename(char *filename,
+ const struct ntfs_idx_entry *ie)
+{
+ const uint16_t *entry_fn;
+ uint8_t entry_fn_len;
+ unsigned i;
+
+ entry_fn = ie->key.file_name.file_name;
+ entry_fn_len = ie->key.file_name.file_name_len;
+
+ for (i = 0; i < entry_fn_len; i++)
+ filename[i] = (char)entry_fn[i];
+
+ filename[i] = '\0';
+
+ return entry_fn_len;
+}
+
+static int ntfs_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct fs_info *fs = inode->fs;
+ struct ntfs_sb_info *sbi = NTFS_SB(fs);
+ sector_t pstart = 0;
+ struct runlist *rlist;
+ struct runlist *ret;
+ const uint32_t sec_size = SECTOR_SIZE(fs);
+ const uint32_t sec_shift = SECTOR_SHIFT(fs);
+
+ dprintf("in %s()\n", __func__);
+
+ if (!NTFS_PVT(inode)->non_resident) {
+ pstart = (sbi->mft_blk + NTFS_PVT(inode)->here) << BLOCK_SHIFT(fs) >>
+ sec_shift;
+ inode->next_extent.len = (inode->size + sec_size - 1) >> sec_shift;
+ } else {
+ rlist = NTFS_PVT(inode)->data.non_resident.rlist;
+
+ if (!lstart || lstart >= NTFS_PVT(inode)->here) {
+ if (runlist_is_empty(rlist))
+ goto out; /* nothing to do ;-) */
+
+ ret = runlist_remove(&rlist);
+
+ NTFS_PVT(inode)->here =
+ ((ret->run.len << sbi->clust_byte_shift) >> sec_shift);
+
+ pstart = ret->run.lcn << sbi->clust_shift;
+ inode->next_extent.len =
+ ((ret->run.len << sbi->clust_byte_shift) + sec_size - 1) >>
+ sec_shift;
+
+ NTFS_PVT(inode)->data.non_resident.rlist = rlist;
+
+ free(ret);
+ ret = NULL;
+ }
+ }
+
+ inode->next_extent.pstart = pstart;
+
+ return 0;
+
+out:
+ return -1;
+}
+
+static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
+ bool *have_more)
+{
+ uint8_t non_resident;
+ uint32_t ret;
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ char *p;
+
+ dprintf("in %s()\n", __func__);
+
+ non_resident = NTFS_PVT(inode)->non_resident;
+
+ ret = generic_getfssec(file, buf, sectors, have_more);
+ if (!ret)
+ return ret;
+
+ if (!non_resident) {
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
+ NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ p = (char *)((uint8_t *)attr + attr->data.resident.value_offset);
+
+ /* p now points to the data offset, so let's copy it into buf */
+ memcpy(buf, p, inode->size);
+
+ ret = inode->size;
+
+ free(mrec);
+ }
+
+ return ret;
+
+out:
+ free(mrec);
+
+ return 0;
+}
+
+static inline bool is_filename_printable(const char *s)
+{
+ return s && (*s != '.' && *s != '$');
+}
+
+static int ntfs_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ struct ntfs_mft_record *mrec, *lmrec;
+ block_t blk;
+ uint64_t blk_offset;
+ const uint64_t blk_size = UINT64_C(1) << BLOCK_SHIFT(fs);
+ struct ntfs_attr_record *attr;
+ struct ntfs_idx_root *ir;
+ uint32_t count;
+ int len;
+ struct ntfs_idx_entry *ie = NULL;
+ uint8_t buf[BLOCK_SIZE(fs)];
+ struct ntfs_idx_allocation *iblk;
+ int err;
+ uint8_t *stream;
+ uint8_t *attr_len;
+ struct mapping_chunk chunk;
+ uint32_t offset;
+ int64_t vcn;
+ int64_t lcn;
+ char filename[NTFS_MAX_FILE_NAME_LEN + 1];
+
+ dprintf("in %s()\n", __func__);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ lmrec = mrec;
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ROOT, &mrec, lmrec);
+ if (!attr) {
+ printf("No attribute found.\n");
+ goto out;
+ }
+
+ ir = (struct ntfs_idx_root *)((uint8_t *)attr +
+ attr->data.resident.value_offset);
+
+ if (!file->offset && readdir_state->in_idx_root) {
+ file->offset = (uint32_t)((uint8_t *)&ir->index +
+ ir->index.entries_offset);
+ }
+
+idx_root_next_entry:
+ if (readdir_state->in_idx_root) {
+ ie = (struct ntfs_idx_entry *)(uint8_t *)file->offset;
+ if (ie->flags & INDEX_ENTRY_END) {
+ file->offset = 0;
+ readdir_state->in_idx_root = false;
+ readdir_state->idx_blks_count = 1;
+ readdir_state->entries_count = 0;
+ readdir_state->last_vcn = 0;
+ goto descend_into_child_node;
+ }
+
+ file->offset = (uint32_t)((uint8_t *)ie + ie->len);
+ len = ntfs_cvt_filename(filename, ie);
+ if (!is_filename_printable(filename))
+ goto idx_root_next_entry;
+
+ goto done;
+ }
+
+descend_into_child_node:
+ if (!(ie->flags & INDEX_ENTRY_NODE))
+ goto out;
+
+ attr = ntfs_attr_lookup(fs, NTFS_AT_INDEX_ALLOCATION, &mrec, lmrec);
+ if (!attr)
+ goto out;
+
+ if (!attr->non_resident) {
+ printf("WTF ?! $INDEX_ALLOCATION isn't really resident.\n");
+ goto out;
+ }
+
+ attr_len = (uint8_t *)attr + attr->len;
+
+next_run:
+ stream = mapping_chunk_init(attr, &chunk, &offset);
+ count = readdir_state->idx_blks_count;
+ while (count--) {
+ err = parse_data_run(stream, &offset, attr_len, &chunk);
+ if (err) {
+ printf("Error while parsing data runs.\n");
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED)
+ break;
+ if (chunk.flags & MAP_END)
+ goto out;
+ }
+
+ if (chunk.flags & MAP_UNALLOCATED) {
+ readdir_state->idx_blks_count++;
+ goto next_run;
+ }
+
+next_vcn:
+ vcn = readdir_state->last_vcn;
+ if (vcn >= chunk.len) {
+ readdir_state->last_vcn = 0;
+ readdir_state->idx_blks_count++;
+ goto next_run;
+ }
+
+ lcn = chunk.lcn;
+ blk = (lcn + vcn) << NTFS_SB(fs)->clust_shift << SECTOR_SHIFT(fs) >>
+ BLOCK_SHIFT(fs);
+
+ blk_offset = 0;
+ err = ntfs_read(fs, &buf, blk_size, blk_size, &blk, &blk_offset, NULL,
+ (uint64_t *)&lcn);
+ if (err) {
+ printf("Error while reading from cache.\n");
+ goto not_found;
+ }
+
+ ntfs_fixups_writeback(fs, (struct ntfs_record *)&buf);
+
+ iblk = (struct ntfs_idx_allocation *)&buf;
+ if (iblk->magic != NTFS_MAGIC_INDX) {
+ printf("Not a valid INDX record.\n");
+ goto not_found;
+ }
+
+idx_block_next_entry:
+ ie = (struct ntfs_idx_entry *)((uint8_t *)&iblk->index +
+ iblk->index.entries_offset);
+ count = readdir_state->entries_count;
+ for ( ; count--; ie = (struct ntfs_idx_entry *)((uint8_t *)ie + ie->len)) {
+ /* bounds checks */
+ if ((uint8_t *)ie < (uint8_t *)iblk || (uint8_t *)ie +
+ sizeof(struct ntfs_idx_entry_header) >
+ (uint8_t *)&iblk->index + iblk->index.index_len ||
+ (uint8_t *)ie + ie->len >
+ (uint8_t *)&iblk->index + iblk->index.index_len)
+ goto index_err;
+
+ /* last entry cannot contain a key */
+ if (ie->flags & INDEX_ENTRY_END) {
+ /* go to the next VCN */
+ readdir_state->last_vcn += (blk_size / (1 <<
+ NTFS_SB(fs)->clust_byte_shift));
+ readdir_state->entries_count = 0;
+ goto next_vcn;
+ }
+ }
+
+ readdir_state->entries_count++;
+
+ /* Need to check if this entry has INDEX_ENTRY_END flag set. If
+ * so, then it won't contain a indexed_file file, so continue the
+ * lookup on the next VCN/LCN (if any).
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ goto next_vcn;
+
+ len = ntfs_cvt_filename(filename, ie);
+ if (!is_filename_printable(filename))
+ goto idx_block_next_entry;
+
+ goto done;
+
+out:
+ readdir_state->in_idx_root = true;
+
+ free(mrec);
+
+ return -1;
+
+done:
+ dirent->d_ino = ie->data.dir.indexed_file;
+ dirent->d_off = file->offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name) + len + 1;
+
+ free(mrec);
+
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
+ if (!mrec) {
+ printf("No MFT record found.\n");
+ goto out;
+ }
+
+ dirent->d_type = get_inode_mode(mrec);
+ memcpy(dirent->d_name, filename, len + 1);
+
+ free(mrec);
+
+ return 0;
+
+not_found:
+ printf("Index not found\n");
+ goto out;
+
+index_err:
+ printf("Corrupt index. Aborting lookup...\n");
+ goto out;
+}
+
+static inline struct inode *ntfs_iget(const char *dname, struct inode *parent)
+{
+ return ntfs_index_lookup(dname, parent);
+}
+
+static struct inode *ntfs_iget_root(struct fs_info *fs)
+{
+ uint64_t start_blk;
+ struct ntfs_mft_record *mrec, *lmrec;
+ struct ntfs_attr_record *attr;
+ struct ntfs_vol_info *vol_info;
+ struct inode *inode;
+ int err;
+
+ dprintf("in %s()\n", __func__);
+
+ /* Fetch the $Volume MFT record */
+ start_blk = 0;
+ mrec = NTFS_SB(fs)->mft_record_lookup(fs, FILE_Volume, &start_blk);
+ if (!mrec) {
+ printf("Could not fetch $Volume MFT record!\n");
+ goto err_mrec;
+ }
+
+ lmrec = mrec;
+
+ /* Fetch the volume information attribute */
+ attr = ntfs_attr_lookup(fs, NTFS_AT_VOL_INFO, &mrec, lmrec);
+ if (!attr) {
+ printf("Could not find volume info attribute!\n");
+ goto err_attr;
+ }
+
+ /* Note NTFS version and choose version-dependent functions */
+ vol_info = (void *)((char *)attr + attr->data.resident.value_offset);
+ NTFS_SB(fs)->major_ver = vol_info->major_ver;
+ NTFS_SB(fs)->minor_ver = vol_info->minor_ver;
+ if (vol_info->major_ver == 3 && vol_info->minor_ver == 0)
+ NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_0;
+ else if (vol_info->major_ver == 3 && vol_info->minor_ver == 1 &&
+ mrec->mft_record_no == FILE_Volume)
+ NTFS_SB(fs)->mft_record_lookup = ntfs_mft_record_lookup_3_1;
+
+ /* Free MFT record */
+ free(mrec);
+ mrec = NULL;
+
+ inode = new_ntfs_inode(fs);
+ inode->fs = fs;
+
+ err = index_inode_setup(fs, FILE_root, inode);
+ if (err)
+ goto err_setup;
+
+ NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
+
+ return inode;
+
+err_setup:
+
+ free(inode);
+err_attr:
+
+ free(mrec);
+err_mrec:
+
+ return NULL;
+}
+
+/* Initialize the filesystem metadata and return blk size in bits */
+static int ntfs_fs_init(struct fs_info *fs)
+{
+ int read_count;
+ struct ntfs_bpb ntfs;
+ struct ntfs_sb_info *sbi;
+ struct disk *disk = fs->fs_dev->disk;
+ uint8_t mft_record_shift;
+
+ dprintf("in %s()\n", __func__);
+
+ read_count = disk->rdwr_sectors(disk, &ntfs, 0, 1, 0);
+ if (!read_count)
+ return -1;
+
+ if (!ntfs_check_sb_fields(&ntfs))
+ return -1;
+
+ SECTOR_SHIFT(fs) = disk->sector_shift;
+
+ /* Note: ntfs.clust_per_mft_record can be a negative number.
+ * If negative, it represents a shift count, else it represents
+ * a multiplier for the cluster size.
+ */
+ mft_record_shift = ntfs.clust_per_mft_record < 0 ?
+ -ntfs.clust_per_mft_record :
+ ilog2(ntfs.sec_per_clust) + SECTOR_SHIFT(fs) +
+ ilog2(ntfs.clust_per_mft_record);
+
+ SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs);
+
+ sbi = malloc(sizeof *sbi);
+ if (!sbi)
+ malloc_error("ntfs_sb_info structure");
+
+ fs->fs_info = sbi;
+
+ sbi->clust_shift = ilog2(ntfs.sec_per_clust);
+ sbi->clust_byte_shift = sbi->clust_shift + SECTOR_SHIFT(fs);
+ sbi->clust_mask = ntfs.sec_per_clust - 1;
+ sbi->clust_size = ntfs.sec_per_clust << SECTOR_SHIFT(fs);
+ sbi->mft_record_size = 1 << mft_record_shift;
+ sbi->clust_per_idx_record = ntfs.clust_per_idx_record;
+
+ BLOCK_SHIFT(fs) = ilog2(ntfs.clust_per_idx_record) + sbi->clust_byte_shift;
+ BLOCK_SIZE(fs) = 1 << BLOCK_SHIFT(fs);
+
+ sbi->mft_lcn = ntfs.mft_lclust;
+ sbi->mft_blk = ntfs.mft_lclust << sbi->clust_shift << SECTOR_SHIFT(fs) >>
+ BLOCK_SHIFT(fs);
+ /* 16 MFT entries reserved for metadata files (approximately 16 KiB) */
+ sbi->mft_size = mft_record_shift << sbi->clust_shift << 4;
+
+ sbi->clusters = ntfs.total_sectors << SECTOR_SHIFT(fs) >> sbi->clust_shift;
+ if (sbi->clusters > 0xFFFFFFFFFFF4ULL)
+ sbi->clusters = 0xFFFFFFFFFFF4ULL;
+
+ /*
+ * Assume NTFS version 3.0 to begin with. If we find that the
+ * volume is a different version later on, we will adjust at
+ * that time.
+ */
+ sbi->major_ver = 3;
+ sbi->minor_ver = 0;
+ sbi->mft_record_lookup = ntfs_mft_record_lookup_3_0;
+
+ /* Initialize the cache */
+ cache_init(fs->fs_dev, BLOCK_SHIFT(fs));
+
+ return BLOCK_SHIFT(fs);
+}
+
+const struct fs_ops ntfs_fs_ops = {
+ .fs_name = "ntfs",
+ .fs_flags = FS_USEMEM | FS_THISIND,
+ .fs_init = ntfs_fs_init,
+ .searchdir = NULL,
+ .getfssec = ntfs_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = generic_mangle_name,
+ .load_config = generic_load_config,
+ .readdir = ntfs_readdir,
+ .iget_root = ntfs_iget_root,
+ .iget = ntfs_iget,
+ .next_extent = ntfs_next_extent,
+};
diff --git a/core/fs/ntfs/ntfs.h b/core/fs/ntfs/ntfs.h
new file mode 100644
index 00000000..721a78d7
--- /dev/null
+++ b/core/fs/ntfs/ntfs.h
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2011-2012 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "runlist.h"
+
+#ifndef _NTFS_H_
+#define _NTFS_H_
+
+struct ntfs_bpb {
+ uint8_t jmp_boot[3];
+ char oem_name[8];
+ uint16_t sector_size;
+ uint8_t sec_per_clust;
+ uint16_t res_sectors;
+ uint8_t zero_0[3];
+ uint16_t zero_1;
+ uint8_t media;
+ uint16_t zero_2;
+ uint16_t unused_0;
+ uint16_t unused_1;
+ uint32_t unused_2;
+ uint32_t zero_3;
+ uint32_t unused_3;
+ uint64_t total_sectors;
+ uint64_t mft_lclust;
+ uint64_t mft_mirr_lclust;
+ int8_t clust_per_mft_record;
+ uint8_t unused_4[3];
+ uint8_t clust_per_idx_record;
+ uint8_t unused_5[3];
+ uint64_t vol_serial;
+ uint32_t unused_6;
+
+ uint8_t pad[428]; /* padding to a sector boundary (512 bytes) */
+} __attribute__((__packed__));
+
+/* Function type for an NTFS-version-dependent MFT record lookup */
+struct ntfs_mft_record;
+typedef struct ntfs_mft_record *f_mft_record_lookup(struct fs_info *,
+ uint32_t, block_t *);
+
+struct ntfs_sb_info {
+ block_t mft_blk; /* The first MFT record block */
+ uint64_t mft_lcn; /* LCN of the first MFT record */
+ unsigned mft_size; /* The MFT size in sectors */
+ uint64_t mft_record_size; /* MFT record size in bytes */
+
+ uint8_t clust_per_idx_record; /* Clusters per Index Record */
+
+ unsigned long long clusters; /* Total number of clusters */
+
+ unsigned clust_shift; /* Based on sectors */
+ unsigned clust_byte_shift; /* Based on bytes */
+ unsigned clust_mask;
+ unsigned clust_size;
+
+ uint8_t major_ver; /* Major version from $Volume */
+ uint8_t minor_ver; /* Minor version from $Volume */
+
+ /* NTFS-version-dependent MFT record lookup function to use */
+ f_mft_record_lookup *mft_record_lookup;
+} __attribute__((__packed__));
+
+/* The NTFS in-memory inode structure */
+struct ntfs_inode {
+ int64_t initialized_size;
+ int64_t allocated_size;
+ unsigned long mft_no; /* Number of the mft record / inode */
+ uint16_t seq_no; /* Sequence number of the mft record */
+ uint32_t type; /* Attribute type of this inode */
+ uint8_t non_resident;
+ union { /* Non-resident $DATA attribute */
+ struct { /* Used only if non_resident flags isn't set */
+ uint32_t offset; /* Data offset */
+ } resident;
+ struct { /* Used only if non_resident is set */
+ struct runlist *rlist;
+ } non_resident;
+ } data;
+ uint32_t start_cluster; /* Starting cluster address */
+ sector_t start; /* Starting sector */
+ sector_t offset; /* Current sector offset */
+ sector_t here; /* Sector corresponding to offset */
+};
+
+/* This is structure is used to keep a state for ntfs_readdir() callers.
+ * As NTFS stores directory entries in a complex way, this is structure
+ * ends up saving a state required to find out where we must start from
+ * for the next ntfs_readdir() call.
+ */
+struct ntfs_readdir_state {
+ unsigned long mft_no; /* MFT record number */
+ bool in_idx_root; /* It's true if we're still in the INDEX root */
+ uint32_t idx_blks_count; /* Number of read INDX blocks */
+ uint32_t entries_count; /* Number of read INDEX entries */
+ int64_t last_vcn; /* Last VCN of the INDX block */
+};
+
+enum {
+ MAP_UNSPEC,
+ MAP_START = 1 << 0,
+ MAP_END = 1 << 1,
+ MAP_ALLOCATED = 1 << 2,
+ MAP_UNALLOCATED = 1 << 3,
+ MAP_MASK = 0x0000000F,
+};
+
+struct mapping_chunk {
+ uint64_t vcn;
+ int64_t lcn;
+ uint64_t len;
+ uint32_t flags;
+};
+
+/* System defined attributes (32-bit)
+ * Each attribute type has a corresponding attribute name (in Unicode)
+ */
+enum {
+ NTFS_AT_UNUSED = 0x00,
+ NTFS_AT_STANDARD_INFORMATION = 0x10,
+ NTFS_AT_ATTR_LIST = 0x20,
+ NTFS_AT_FILENAME = 0x30,
+ NTFS_AT_OBJ_ID = 0x40,
+ NTFS_AT_SECURITY_DESCP = 0x50,
+ NTFS_AT_VOL_NAME = 0x60,
+ NTFS_AT_VOL_INFO = 0x70,
+ NTFS_AT_DATA = 0x80,
+ NTFS_AT_INDEX_ROOT = 0x90,
+ NTFS_AT_INDEX_ALLOCATION = 0xA0,
+ NTFS_AT_BITMAP = 0xB0,
+ NTFS_AT_REPARSE_POINT = 0xC0,
+ NTFS_AT_EA_INFO = 0xD0,
+ NTFS_AT_EA = 0xE0,
+ NTFS_AT_PROPERTY_SET = 0xF0,
+ NTFS_AT_LOGGED_UTIL_STREAM = 0x100,
+ NTFS_AT_FIRST_USER_DEFINED_ATTR = 0x1000,
+ NTFS_AT_END = 0xFFFFFFFF,
+};
+
+/* NTFS File Permissions (also called attributes in DOS terminology) */
+enum {
+ NTFS_FILE_ATTR_READONLY = 0x00000001,
+ NTFS_FILE_ATTR_HIDDEN = 0x00000002,
+ NTFS_FILE_ATTR_SYSTEM = 0x00000004,
+ NTFS_FILE_ATTR_DIRECTORY = 0x00000010,
+ NTFS_FILE_ATTR_ARCHIVE = 0x00000020,
+ NTFS_FILE_ATTR_DEVICE = 0x00000040,
+ NTFS_FILE_ATTR_NORMAL = 0x00000080,
+ NTFS_FILE_ATTR_TEMPORARY = 0x00000100,
+ NTFS_FILE_ATTR_SPARSE_FILE = 0x00000200,
+ NTFS_FILE_ATTR_REPARSE_POINT = 0x00000400,
+ NTFS_FILE_ATTR_COMPRESSED = 0x00000800,
+ NTFS_FILE_ATTR_OFFLINE = 0x00001000,
+ NTFS_FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000,
+ NTFS_FILE_ATTR_ENCRYPTED = 0x00004000,
+ NTFS_FILE_ATTR_VALID_FLAGS = 0x00007FB7,
+ NTFS_FILE_ATTR_VALID_SET_FLAGS = 0x000031A7,
+ NTFS_FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT = 0x10000000,
+ NTFS_FILE_ATTR_DUP_VIEW_INDEX_PRESENT = 0x20000000,
+};
+
+/*
+ * Magic identifiers present at the beginning of all ntfs record containing
+ * records (like mft records for example).
+ */
+enum {
+ /* Found in $MFT/$DATA */
+ NTFS_MAGIC_FILE = 0x454C4946, /* MFT entry */
+ NTFS_MAGIC_INDX = 0x58444E49, /* Index buffer */
+ NTFS_MAGIC_HOLE = 0x454C4F48,
+
+ /* Found in $LogFile/$DATA */
+ NTFS_MAGIC_RSTR = 0x52545352,
+ NTFS_MAGIC_RCRD = 0x44524352,
+ /* Found in $LogFile/$DATA (May be found in $MFT/$DATA, also ?) */
+ NTFS_MAGIC_CHKDSK = 0x444B4843,
+ /* Found in all ntfs record containing records. */
+ NTFS_MAGIC_BAAD = 0x44414142,
+ NTFS_MAGIC_EMPTY = 0xFFFFFFFF, /* Record is empty */
+};
+
+struct ntfs_record {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+} __attribute__((__packed__)) NTFS_RECORD;
+
+/* The $MFT metadata file types */
+enum ntfs_system_file {
+ FILE_MFT = 0,
+ FILE_MFTMirr = 1,
+ FILE_LogFile = 2,
+ FILE_Volume = 3,
+ FILE_AttrDef = 4,
+ FILE_root = 5,
+ FILE_Bitmap = 6,
+ FILE_Boot = 7,
+ FILE_BadClus = 8,
+ FILE_Secure = 9,
+ FILE_UpCase = 10,
+ FILE_Extend = 11,
+ FILE_reserved12 = 12,
+ FILE_reserved13 = 13,
+ FILE_reserved14 = 14,
+ FILE_reserved15 = 15,
+ FILE_reserved16 = 16,
+};
+
+enum {
+ MFT_RECORD_IN_USE = 0x0001,
+ MFT_RECORD_IS_DIRECTORY = 0x0002,
+} __attribute__((__packed__));
+
+struct ntfs_mft_record {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+ uint64_t lsn;
+ uint16_t seq_no;
+ uint16_t link_count;
+ uint16_t attrs_offset;
+ uint16_t flags; /* MFT record flags */
+ uint32_t bytes_in_use;
+ uint32_t bytes_allocated;
+ uint64_t base_mft_record;
+ uint16_t next_attr_instance;
+ uint16_t reserved;
+ uint32_t mft_record_no;
+} __attribute__((__packed__)); /* 48 bytes */
+
+/* This is the version without the NTFS 3.1+ specific fields */
+struct ntfs_mft_record_old {
+ uint32_t magic;
+ uint16_t usa_ofs;
+ uint16_t usa_count;
+ uint64_t lsn;
+ uint16_t seq_no;
+ uint16_t link_count;
+ uint16_t attrs_offset;
+ uint16_t flags; /* MFT record flags */
+ uint32_t bytes_in_use;
+ uint32_t bytes_allocated;
+ uint64_t base_mft_record;
+ uint16_t next_attr_instance;
+} __attribute__((__packed__)); /* 42 bytes */
+
+enum {
+ ATTR_DEF_INDEXABLE = 0x02,
+ ATTR_DEF_MULTIPLE = 0x04,
+ ATTR_DEF_NOT_ZERO = 0x08,
+ ATTR_DEF_INDEXED_UNIQUE = 0x10,
+ ATTR_DEF_NAMED_UNIQUE = 0x20,
+ ATTR_DEF_RESIDENT = 0x40,
+ ATTR_DEF_ALWAYS_LOG = 0x80,
+};
+
+struct ntfs_attr_record {
+ uint32_t type; /* Attr. type code */
+ uint32_t len;
+ uint8_t non_resident;
+ uint8_t name_len;
+ uint16_t name_offset;
+ uint16_t flags; /* Attr. flags */
+ uint16_t instance;
+ union {
+ struct { /* Resident attribute */
+ uint32_t value_len;
+ uint16_t value_offset;
+ uint8_t flags; /* Flags of resident attributes */
+ int8_t reserved;
+ } __attribute__((__packed__)) resident;
+ struct { /* Non-resident attributes */
+ uint64_t lowest_vcn;
+ uint64_t highest_vcn;
+ uint16_t mapping_pairs_offset;
+ uint8_t compression_unit;
+ uint8_t reserved[5];
+ int64_t allocated_size;
+ int64_t data_size; /* Byte size of the attribute value.
+ * Note: it can be larger than
+ * allocated_size if attribute value is
+ * compressed or sparse.
+ */
+ int64_t initialized_size;
+ int64_t compressed_size;
+ } __attribute__((__packed__)) non_resident;
+ } __attribute__((__packed__)) data;
+} __attribute__((__packed__));
+
+/* Attribute: Attribute List (0x20)
+ * Note: it can be either resident or non-resident
+ */
+struct ntfs_attr_list_entry {
+ uint32_t type;
+ uint16_t length;
+ uint8_t name_length;
+ uint8_t name_offset;
+ uint64_t lowest_vcn;
+ uint64_t mft_ref;
+ uint16_t instance;
+ uint16_t name[0];
+} __attribute__((__packed__));
+
+#define NTFS_MAX_FILE_NAME_LEN 255
+
+/* Possible namespaces for filenames in ntfs (8-bit) */
+enum {
+ FILE_NAME_POSIX = 0x00,
+ FILE_NAME_WIN32 = 0x01,
+ FILE_NAME_DOS = 0x02,
+ FILE_NAME_WIN32_AND_DOS = 0x03,
+} __attribute__((__packed__));
+
+/* Attribute: Filename (0x30)
+ * Note: always resident
+ */
+struct ntfs_filename_attr {
+ uint64_t parent_directory;
+ int64_t ctime;
+ int64_t atime;
+ int64_t mtime;
+ int64_t rtime;
+ uint64_t allocated_size;
+ uint64_t data_size;
+ uint32_t file_attrs;
+ union {
+ struct {
+ uint16_t packed_ea_size;
+ uint16_t reserved; /* reserved for alignment */
+ } __attribute__((__packed__)) ea;
+ struct {
+ uint32_t reparse_point_tag;
+ } __attribute__((__packed__)) rp;
+ } __attribute__((__packed__)) type;
+ uint8_t file_name_len;
+ uint8_t file_name_type;
+ uint16_t file_name[0]; /* File name in Unicode */
+} __attribute__((__packed__));
+
+/* Attribute: Volume Name (0x60)
+ * Note: always resident
+ * Note: Present only in FILE_volume
+ */
+struct ntfs_vol_name {
+ uint16_t name[0]; /* The name of the volume in Unicode */
+} __attribute__((__packed__));
+
+/* Attribute: Volume Information (0x70)
+ * Note: always resident
+ * Note: present only in FILE_Volume
+ */
+struct ntfs_vol_info {
+ uint64_t reserved;
+ uint8_t major_ver;
+ uint8_t minor_ver;
+ uint16_t flags; /* Volume flags */
+} __attribute__((__packed__));
+
+/* Attribute: Data attribute (0x80)
+ * Note: can be either resident or non-resident
+ */
+struct ntfs_data_attr {
+ uint8_t data[0];
+} __attribute__((__packed__));
+
+/* Index header flags (8-bit) */
+enum {
+ SMALL_INDEX = 0,
+ LARGE_INDEX = 1,
+ LEAF_NODE = 0,
+ INDEX_NODE = 1,
+ NODE_MASK = 1,
+} __attribute__((__packed__));
+
+/* Header for the indexes, describing the INDEX_ENTRY records, which
+ * follow the struct ntfs_idx_header.
+ */
+struct ntfs_idx_header {
+ uint32_t entries_offset;
+ uint32_t index_len;
+ uint32_t allocated_size;
+ uint8_t flags; /* Index header flags */
+ uint8_t reserved[3]; /* Align to 8-byte boundary */
+} __attribute__((__packed__));
+
+/* Attribute: Index Root (0x90)
+ * Note: always resident
+ */
+struct ntfs_idx_root {
+ uint32_t type; /* It is $FILE_NAME for directories, zero for view indexes.
+ * No other values allowed.
+ */
+ uint32_t collation_rule;
+ uint32_t index_block_size;
+ uint8_t clust_per_index_block;
+ uint8_t reserved[3];
+ struct ntfs_idx_header index;
+} __attribute__((__packed__));
+
+/* Attribute: Index allocation (0xA0)
+ * Note: always non-resident, of course! :-)
+ */
+struct ntfs_idx_allocation {
+ uint32_t magic;
+ uint16_t usa_ofs; /* Update Sequence Array offsets */
+ uint16_t usa_count; /* Update Sequence Array number in bytes */
+ int64_t lsn;
+ int64_t index_block_vcn; /* Virtual cluster number of the index block */
+ struct ntfs_idx_header index;
+} __attribute__((__packed__));
+
+enum {
+ INDEX_ENTRY_NODE = 1,
+ INDEX_ENTRY_END = 2,
+ /* force enum bit width to 16-bit */
+ INDEX_ENTRY_SPACE_FILTER = 0xFFFF,
+} __attribute__((__packed__));
+
+struct ntfs_idx_entry_header {
+ union {
+ struct { /* Only valid when INDEX_ENTRY_END is not set */
+ uint64_t indexed_file;
+ } __attribute__((__packed__)) dir;
+ struct { /* Used for views/indexes to find the entry's data */
+ uint16_t data_offset;
+ uint16_t data_len;
+ uint32_t reservedV;
+ } __attribute__((__packed__)) vi;
+ } __attribute__((__packed__)) data;
+ uint16_t len;
+ uint16_t key_len;
+ uint16_t flags; /* Index entry flags */
+ uint16_t reserved; /* Align to 8-byte boundary */
+} __attribute__((__packed__));
+
+struct ntfs_idx_entry {
+ union {
+ struct { /* Only valid when INDEX_ENTRY_END is not set */
+ uint64_t indexed_file;
+ } __attribute__((__packed__)) dir;
+ struct { /* Used for views/indexes to find the entry's data */
+ uint16_t data_offset;
+ uint16_t data_len;
+ uint32_t reservedV;
+ } __attribute__((__packed__)) vi;
+ } __attribute__((__packed__)) data;
+ uint16_t len;
+ uint16_t key_len;
+ uint16_t flags; /* Index entry flags */
+ uint16_t reserved; /* Align to 8-byte boundary */
+ union {
+ struct ntfs_filename_attr file_name;
+ //SII_INDEX_KEY sii;
+ //SDH_INDEX_KEY sdh;
+ //GUID object_id;
+ //REPARSE_INDEX_KEY reparse;
+ //SID sid;
+ uint32_t owner_id;
+ } __attribute__((__packed__)) key;
+} __attribute__((__packed__));
+
+static inline struct ntfs_sb_info *NTFS_SB(struct fs_info *fs)
+{
+ return fs->fs_info;
+}
+
+#define NTFS_PVT(i) ((struct ntfs_inode *)((i)->pvt))
+
+#endif /* _NTFS_H_ */
diff --git a/core/fs/ntfs/runlist.h b/core/fs/ntfs/runlist.h
new file mode 100644
index 00000000..4938696b
--- /dev/null
+++ b/core/fs/ntfs/runlist.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _RUNLIST_H_
+#define _RUNLIST_H_
+
+struct runlist_element {
+ uint64_t vcn;
+ int64_t lcn;
+ uint64_t len;
+};
+
+struct runlist {
+ struct runlist_element run;
+ struct runlist *next;
+};
+
+static struct runlist *tail;
+
+static inline bool runlist_is_empty(struct runlist *rlist)
+{
+ return !rlist;
+}
+
+static inline struct runlist *runlist_alloc(void)
+{
+ struct runlist *rlist;
+
+ rlist = malloc(sizeof *rlist);
+ if (!rlist)
+ malloc_error("runlist structure");
+
+ rlist->next = NULL;
+
+ return rlist;
+}
+
+static inline void runlist_append(struct runlist **rlist,
+ struct runlist_element *elem)
+{
+ struct runlist *n = runlist_alloc();
+
+ n->run = *elem;
+
+ if (runlist_is_empty(*rlist)) {
+ *rlist = n;
+ tail = n;
+ } else {
+ tail->next = n;
+ tail = n;
+ }
+}
+
+static inline struct runlist *runlist_remove(struct runlist **rlist)
+{
+ struct runlist *ret;
+
+ if (runlist_is_empty(*rlist))
+ return NULL;
+
+ ret = *rlist;
+ *rlist = ret->next;
+
+ return ret;
+}
+
+#endif /* _RUNLIST_H_ */
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
index f62f55b2..a2f859d0 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -37,6 +37,8 @@ ROOT_FS_OPS:
dd vfat_fs_ops
extern ext2_fs_ops
dd ext2_fs_ops
+ extern ntfs_fs_ops
+ dd ntfs_fs_ops
extern btrfs_fs_ops
dd btrfs_fs_ops
dd 0