[syslinux] [PATCH] NTFS: fragmented $MFT file was not handled
Andy Alex
andy at r-tt.com
Thu Feb 20 07:56:47 PST 2014
NTFS $MFT file may be fragmented by itself (and actually is in most cases).
However, such a situation was not handled.
This patch add support for fragmented $MFT file.
Signed-off-by: Andy Alex <andy at r-tt.com>
---
diff -uprN syslinux-6.02.orig/core/fs/ntfs/ntfs.c
syslinux-6.02/core/fs/ntfs/ntfs.c
--- syslinux-6.02.orig/core/fs/ntfs/ntfs.c 2013-10-13
21:59:03.000000000 +0400
+++ syslinux-6.02/core/fs/ntfs/ntfs.c 2014-02-20 12:21:41.000000000 +0400
@@ -40,6 +40,10 @@ static struct ntfs_readdir_state *readdi
/*** 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__);
+ 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;
- buf = (uint8_t *)malloc(mft_record_size);
- if (!buf)
- malloc_error("uint8_t *");
+ int err = 0;
- /* 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");
+ /* 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);
-
- 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 */
+ /* determine MFT record's block number */
+ blk = (lcn << clust_byte_shift >> BLOCK_SHIFT(fs));
+ offset = (file << mft_record_shift) % BLOCK_SIZE(fs);
- return mrec; /* found MFT record */
- }
+ /* Allocate buffer */
+ buf = (uint8_t *)malloc(mft_record_size);
+ if (!buf) {malloc_error("uint8_t *");return 0;}
- 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 */
- }
+ /* 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;
+ }
+
+ /* 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
mailing list