[syslinux:master] NTFS: Fragmented $MFT file was not handled

syslinux-bot for Andy Alex andy at r-tt.com
Thu Apr 17 17:18:05 PDT 2014


Commit-ID:  de403416f573e2e6f8c88a90b882dd5e071fb7f0
Gitweb:     http://www.syslinux.org/commit/de403416f573e2e6f8c88a90b882dd5e071fb7f0
Author:     Andy Alex <andy at r-tt.com>
AuthorDate: Thu, 20 Feb 2014 19:56:47 +0400
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Thu, 17 Apr 2014 17:16:27 -0700

NTFS: Fragmented $MFT file was not handled

NTFS $MFT file may be fragmented by itself (and actually is in most
cases).  However, such a situation was not handled.  This patch adds
support for a fragmented $MFT file.

Signed-off-by: Andy Alex <andy at r-tt.com>
Fixed-by: Ady <ady-sf at hotmail.com>
Signed-off-by: H. Peter Anvin <hpa at zytor.com>

---
 core/fs/ntfs/ntfs.c | 193 +++++++++++++++++++++++++---------------------------
 1 file changed, 93 insertions(+), 100 deletions(-)

diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
index 4e6de65..257c95b 100644
--- a/core/fs/ntfs/ntfs.c
+++ b/core/fs/ntfs/ntfs.c
@@ -40,6 +40,10 @@ 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;
+static inline enum dirent_type get_inode_mode(struct ntfs_mft_record *mrec);
+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);
+static inline uint8_t *mapping_chunk_init(struct ntfs_attr_record *attr,struct mapping_chunk *chunk,uint32_t *offset);
+static int parse_data_run(const void *stream, uint32_t *offset, uint8_t *attr_len, struct mapping_chunk *chunk);
 
 /*** Function definitions */
 
@@ -176,127 +180,116 @@ out:
     return -1;
 }
 
-static struct ntfs_mft_record *ntfs_mft_record_lookup_3_0(struct fs_info *fs,
-                                                uint32_t file, block_t *blk)
+/* AndyAlex: read and validate single MFT record. Keep in mind that MFT itself can be fragmented */
+static struct ntfs_mft_record *ntfs_mft_record_lookup_any(struct fs_info *fs,
+                                                uint32_t file, block_t *out_blk, bool is_v31)
 {
     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;
+    uint8_t *buf = NULL;
     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;
+    uint64_t next_offset = 0;
+    uint64_t lcn = 0;
+    block_t blk = 0;
+    uint64_t offset = 0;
 
-    dprintf("in %s()\n", __func__);
-
-    buf = (uint8_t *)malloc(mft_record_size);
-    if (!buf)
-        malloc_error("uint8_t *");
+    struct ntfs_mft_record *mrec = NULL, *lmrec = NULL;
+    uint64_t start_blk = 0;
+    struct ntfs_attr_record *attr = NULL;
+    uint8_t *stream = NULL;
+    uint32_t attr_offset = 0;
+    uint8_t *attr_len = NULL;
+    struct mapping_chunk chunk;
 
-    /* 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");
+    int err = 0;
+
+    /* determine MFT record's LCN */
+    uint64_t vcn = (file << mft_record_shift >> clust_byte_shift);
+    dprintf("in %s(%s)\n", __func__,(is_v31?"v3.1":"v3.0"));
+    if (0==vcn) {
+      lcn = NTFS_SB(fs)->mft_lcn;
+    } else do {
+      dprintf("%s: looking for VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
+      mrec = NTFS_SB(fs)->mft_record_lookup(fs, 0, &start_blk);
+      if (!mrec) {dprintf("%s: read MFT(0) failed\n", __func__); break;}
+      lmrec = mrec;
+      if (get_inode_mode(mrec) != DT_REG) {dprintf("%s: $MFT is not a file\n", __func__); break;}
+      attr = ntfs_attr_lookup(fs, NTFS_AT_DATA, &mrec, lmrec);
+      if (!attr) {dprintf("%s: $MFT have no data attr\n", __func__); break;}
+      if (!attr->non_resident) {dprintf("%s: $MFT data attr is resident\n", __func__); break;}
+      attr_len = (uint8_t *)attr + attr->len;
+      stream = mapping_chunk_init(attr, &chunk, &attr_offset);
+      while (true) {
+        err = parse_data_run(stream, &attr_offset, attr_len, &chunk);
+        if (err) {dprintf("%s: $MFT data run parse failed with error %d\n", __func__,err); break;}
+        if (chunk.flags & MAP_UNALLOCATED) continue;
+        if (chunk.flags & MAP_END) break;
+        if (chunk.flags & MAP_ALLOCATED) {
+          dprintf("%s: Chunk: VCN=%u, LCN=%u, len=%u\n", __func__,(unsigned)chunk.vcn,(unsigned)chunk.lcn,(unsigned)chunk.len);
+          if ((vcn>=chunk.vcn)&&(vcn<chunk.vcn+chunk.len)) {
+            lcn=vcn-chunk.vcn+chunk.lcn;
+            dprintf("%s: VCN %u for MFT record %u maps to lcn %u\n", __func__,(unsigned)vcn,(unsigned)file,(unsigned)lcn);
             break;
+          }
+          chunk.vcn += chunk.len;
         }
+      }
+    } while(false);
+    if (mrec!=NULL) free(mrec);
+    mrec = NULL;
+    if (0==lcn) {
+      dprintf("%s: unable to map VCN %u for MFT record %u\n", __func__,(unsigned)vcn,(unsigned)file);
+      return NULL;
+    }
 
-        ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+    /* determine MFT record's block number */
+    blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs));
+    offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
 
-        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 */
+    /* Allocate buffer */
+    buf = (uint8_t *)malloc(mft_record_size);
+    if (!buf) {malloc_error("uint8_t *");return 0;}
 
-            return mrec;            /* found MFT record */
-        }
+    /* Read block */
+    err = ntfs_read(fs, buf, mft_record_size, mft_record_size, &blk,
+                    &offset, &next_offset, &lcn);
+    if (err) {
+      dprintf("%s: error read block %u from cache\n", __func__, blk);
+      printf("Error while reading from cache.\n");
+      free(buf);
+      return NULL;
+    }
 
-        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 */
-        }
+    /* Process fixups and make structure pointer */
+    ntfs_fixups_writeback(fs, (struct ntfs_record *)buf);
+    mrec = (struct ntfs_mft_record *)buf;
+
+    /* check if it has a valid magic number and record number */
+    if (mrec->magic != NTFS_MAGIC_FILE) mrec = NULL;
+    if (mrec && is_v31) if (mrec->mft_record_no != file) mrec = NULL;
+    if (mrec!=NULL) {
+      if (out_blk) {
+        *out_blk = (file << mft_record_shift >> BLOCK_SHIFT(fs));   /* update record starting block */
+      }
+      return mrec;          /* found MFT record */
     }
 
+    /* Invalid record */
+    dprintf("%s: MFT record %u is invalid\n", __func__, (unsigned)file);
     free(buf);
-
     return NULL;
 }
 
-static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
+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 *");
-
-    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 ntfs_mft_record_lookup_any(fs,file,blk,false);
+}
 
-    return NULL;
+static struct ntfs_mft_record *ntfs_mft_record_lookup_3_1(struct fs_info *fs,
+                                                uint32_t file, block_t *blk)
+{
+    return ntfs_mft_record_lookup_any(fs,file,blk,true);
 }
 
 static bool ntfs_filename_cmp(const char *dname, struct ntfs_idx_entry *ie)


More information about the Syslinux-commits mailing list