[syslinux:master] ntfs: Deal with NTFS versions for MFT record lookups

syslinux-bot for Shao Miller shao.miller at yrdsb.edu.on.ca
Sat Dec 17 21:19:31 PST 2011


Commit-ID:  779ee27626e1d39ec02b91eb2e8fbcfd48d3bf00
Gitweb:     http://www.syslinux.org/commit/779ee27626e1d39ec02b91eb2e8fbcfd48d3bf00
Author:     Shao Miller <shao.miller at yrdsb.edu.on.ca>
AuthorDate: Thu, 1 Sep 2011 04:44:18 +0000
Committer:  Paulo Alcantara <pcacjr at gmail.com>
CommitDate: Sun, 11 Sep 2011 04:10:00 +0000

ntfs: Deal with NTFS versions for MFT record lookups

The MFT record lookup strategies are different for NTFS
versions 3.0 and 3.1, so we use a function pointer for
the appropriate function.

We start off using 3.0 by default, then use that to read
the $Volume meta-file and find out the actual version.
Then we adjust the function pointer, as needed.

Signed-off-by: Shao Miller <shao.miller at yrdsb.edu.on.ca>
Signed-off-by: Paulo Alcantara <pcacjr at gmail.com>

---
 core/fs/ntfs/ntfs.c |  140 ++++++++++++++++++++++++++++++++++++++++++++++----
 core/fs/ntfs/ntfs.h |   12 ++++-
 2 files changed, 139 insertions(+), 13 deletions(-)

diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
index 95af54a..86d2e40 100644
--- a/core/fs/ntfs/ntfs.c
+++ b/core/fs/ntfs/ntfs.c
@@ -35,6 +35,12 @@
 #include "ntfs.h"
 #include "runlist.h"
 
+/*** 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)
 {
@@ -164,14 +170,14 @@ out:
     return -1;
 }
 
-static MFT_RECORD *ntfs_mft_record_lookup(struct fs_info *fs, uint32_t file, block_t *blk)
+static 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 = 0;
+    uint64_t offset;
     uint64_t next_offset;
     const unsigned mft_record_shift = ilog2(mft_record_size);
     const unsigned clust_byte_shift = NTFS_SB(fs)->clust_byte_shift;
@@ -185,7 +191,8 @@ static MFT_RECORD *ntfs_mft_record_lookup(struct fs_info *fs, uint32_t file, blo
 
     /* 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 - 1;
+    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,
@@ -198,7 +205,8 @@ static MFT_RECORD *ntfs_mft_record_lookup(struct fs_info *fs, uint32_t file, blo
         ntfs_fixups_writeback(fs, (NTFS_RECORD *)buf);
 
         mrec = (MFT_RECORD *)buf;
-        if (mrec->mft_record_no == file) {
+        /* check if it has a valid magic number */
+        if (mrec->magic == NTFS_MAGIC_FILE) {
             if (blk)
                 *blk = cur_blk;     /* update record starting block */
 
@@ -221,6 +229,59 @@ static MFT_RECORD *ntfs_mft_record_lookup(struct fs_info *fs, uint32_t file, blo
     return NULL;
 }
 
+static 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;
+    block_t cur_blk;
+    block_t right_blk;
+    uint64_t offset = 0;
+    uint64_t next_offset;
+    uint64_t lcn = NTFS_SB(fs)->mft_lcn;
+    int err;
+    MFT_RECORD *mrec;
+
+    buf = (uint8_t *)malloc(mft_record_size);
+    if (!buf)
+        malloc_error("uint8_t *");
+
+    cur_blk = blk ? *blk : 0;
+    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 on reading from cache.\n");
+            break;
+        }
+
+        ntfs_fixups_writeback(fs, (NTFS_RECORD *)buf);
+
+        mrec = (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 ATTR_RECORD *ntfs_attr_lookup(uint32_t type, const MFT_RECORD *mrec)
 {
     ATTR_RECORD *attr;
@@ -375,7 +436,7 @@ static int index_inode_setup(struct fs_info *fs, unsigned long mft_no,
     uint8_t *stream;
     uint32_t offset;
 
-    mrec = ntfs_mft_record_lookup(fs, mft_no, &start_blk);
+    mrec = NTFS_SB(fs)->mft_record_lookup(fs, mft_no, &start_blk);
     if (!mrec) {
         printf("No MFT record found.\n");
         goto out;
@@ -509,7 +570,7 @@ static struct inode *ntfs_index_lookup(const char *dname, struct inode *dir)
 
     dprintf("ntfs_index_lookup() - mft record number: %d\n",
             NTFS_PVT(dir)->mft_no);
-    mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
+    mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(dir)->mft_no, NULL);
     if (!mrec) {
         printf("No MFT record found.\n");
         goto out;
@@ -756,7 +817,8 @@ static uint32_t ntfs_getfssec(struct file *file, char *buf, int sectors,
         return ret;
 
     if (!non_resident) {
-        mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
+        mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no,
+					      NULL);
         if (!mrec) {
             printf("No MFT record found.\n");
             goto out;
@@ -815,7 +877,7 @@ static int ntfs_readdir(struct file *file, struct dirent *dirent)
     int64_t lcn;
     char filename[NTFS_MAX_FILE_NAME_LEN + 1];
 
-    mrec = ntfs_mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
+    mrec = NTFS_SB(fs)->mft_record_lookup(fs, NTFS_PVT(inode)->mft_no, NULL);
     if (!mrec) {
         printf("No MFT record found.\n");
         goto out;
@@ -967,7 +1029,7 @@ done:
 
     free(mrec);
 
-    mrec = ntfs_mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
+    mrec = NTFS_SB(fs)->mft_record_lookup(fs, ie->data.dir.indexed_file, NULL);
     if (!mrec) {
         printf("No MFT record found.\n");
         goto out;
@@ -996,21 +1058,66 @@ static struct inode *ntfs_iget(const char *dname, struct inode *parent)
 
 static struct inode *ntfs_iget_root(struct fs_info *fs)
 {
-    struct inode *inode = new_ntfs_inode(fs);
+    uint64_t start_blk;
+    MFT_RECORD * mrec;
+    ATTR_RECORD * attr;
+    VOLUME_INFORMATION * vol_info;
+    struct inode * inode;
     int err;
 
+    /* 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;
+    }
+
+    /* Fetch the volume information attribute */
+    attr = ntfs_attr_lookup(NTFS_AT_VOL_INFO, mrec);
+    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);
+    if (!inode) {
+        printf("Could not allocate root inode!\n");
+        goto err_alloc_inode;
+    }
     inode->fs = fs;
 
     err = index_inode_setup(fs, FILE_root, inode);
     if (err)
-        goto free_out;
+        goto err_setup;
 
     NTFS_PVT(inode)->start = NTFS_PVT(inode)->here;
 
     return inode;
 
-free_out:
+err_setup:
+
     free(inode);
+err_alloc_inode:
+
+err_attr:
+
+    free(mrec);
+err_mrec:
 
     return NULL;
 }
@@ -1068,6 +1175,15 @@ static int ntfs_fs_init(struct fs_info *fs)
     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));
 
diff --git a/core/fs/ntfs/ntfs.h b/core/fs/ntfs/ntfs.h
index 6bd78ab..50194db 100644
--- a/core/fs/ntfs/ntfs.h
+++ b/core/fs/ntfs/ntfs.h
@@ -48,6 +48,11 @@ struct ntfs_bpb {
     uint8_t pad[428];       /* padding to a sector boundary (512 bytes) */
 } __attribute__((__packed__));
 
+/* Function type for an NTFS-version-dependent MFT record lookup */
+struct s_mft_record_;
+typedef struct s_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 */
@@ -63,6 +68,11 @@ struct ntfs_sb_info {
     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 */
@@ -226,7 +236,7 @@ enum {
     MFT_RECORD_IS_DIRECTORY = 0x0002,
 } __attribute__((__packed__));
 
-typedef struct {
+typedef struct s_mft_record_ {
     uint32_t magic;
     uint16_t usa_ofs;
     uint16_t usa_count;


More information about the Syslinux-commits mailing list