diff options
author | Andy Alex <andy@r-tt.com> | 2014-02-20 19:56:47 +0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2014-04-17 17:16:27 -0700 |
commit | de403416f573e2e6f8c88a90b882dd5e071fb7f0 (patch) | |
tree | d4e92b67e53d87ba7da7b3b7e8c69224574fec32 | |
parent | 2bf0922e80b35c79fcc56318e71b1da29f55df0c (diff) | |
download | syslinux-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.c | 193 |
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) |