aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Alex <andy@r-tt.com>2014-02-20 19:56:47 +0400
committerH. Peter Anvin <hpa@zytor.com>2014-04-17 17:16:27 -0700
commitde403416f573e2e6f8c88a90b882dd5e071fb7f0 (patch)
treed4e92b67e53d87ba7da7b3b7e8c69224574fec32
parent2bf0922e80b35c79fcc56318e71b1da29f55df0c (diff)
downloadsyslinux-de403416f573e2e6f8c88a90b882dd5e071fb7f0.tar.gz
syslinux-de403416f573e2e6f8c88a90b882dd5e071fb7f0.tar.xz
syslinux-de403416f573e2e6f8c88a90b882dd5e071fb7f0.zip
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@hotmail.com> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--core/fs/ntfs/ntfs.c193
1 files changed, 93 insertions, 100 deletions
diff --git a/core/fs/ntfs/ntfs.c b/core/fs/ntfs/ntfs.c
index 4e6de65b..257c95b3 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)