From 779ee27626e1d39ec02b91eb2e8fbcfd48d3bf00 Mon Sep 17 00:00:00 2001 From: Shao Miller Date: Thu, 1 Sep 2011 04:44:18 +0000 Subject: [PATCH] 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 Signed-off-by: Paulo Alcantara --- 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; -- 2.7.4