diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index b6bf1875..9b17b26b 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -52,6 +52,9 @@ static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol, return ntfs_mft_records_read(vol, mref, 1, b); } +extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m); + extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr); diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index a2a74f96..03561b98 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -381,10 +381,9 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, ntfs_attr_search_ctx *ctx; ntfs_attr *na; ATTR_RECORD *a; - int err; BOOL cs; - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", + ntfs_log_trace("Entering for inode %lld, attr 0x%x.\n", (unsigned long long)ni->mft_no, type); if (!ni || !ni->vol || !ni->mrec) { errno = EINVAL; @@ -402,16 +401,29 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, } ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - err = errno; + if (!ctx) goto err_out; - } - if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) { - err = errno; + + if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) goto put_err_out; - } a = ctx->attr; + + if (!name) { + if (a->name_length) { + name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( + a->name_offset)), a->name_length); + if (!name) + goto put_err_out; + name_len = a->name_length; + } else { + name = AT_UNNAMED; + name_len = 0; + } + } + + __ntfs_attr_init(na, ni, type, name, name_len); + /* * Wipe the flags in case they are not zero for an attribute list * attribute. Windows does not complain about invalid flags and chkdsk @@ -419,23 +431,28 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, */ if (type == AT_ATTRIBUTE_LIST) a->flags = 0; + cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - if (!name) { - if (a->name_length) { - name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length); - if (!name) { - err = errno; - goto put_err_out; - } - name_len = a->name_length; - } else { - name = AT_UNNAMED; - name_len = 0; - } + + if (na->type == AT_DATA && na->name == AT_UNNAMED && + ((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) || + (!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || + (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { + errno = EIO; + ntfs_log_perror("Inode %lld has corrupt attribute flags " + "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, + a->flags, na->ni->flags); + goto put_err_out; } - __ntfs_attr_init(na, ni, type, name, name_len); + if (a->non_resident) { + if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) { + errno = EIO; + ntfs_log_perror("Compressed inode %lld attr 0x%x has " + "no compression unit", + (unsigned long long)ni->mft_no, type); + goto put_err_out; + } ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, @@ -457,7 +474,6 @@ put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(na); - errno = err; return NULL; } @@ -797,8 +813,8 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) } ntfs_log_trace("Entering for inode %lld attr 0x%x pos %lld count " - "%lld\n", (unsigned long long)na->ni->mft_no, - na->type, (long long)pos, (long long)count); + "%lld flags: 0x%x\n", (unsigned long long)na->ni->mft_no, + na->type, (long long)pos, (long long)count, na->ni->flags); /* * If this is a compressed attribute it needs special treatment, but diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 0e6a7c55..56c583df 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -221,7 +221,7 @@ static u8 *ntfs_ie_get_end(INDEX_HEADER *ih) static int ntfs_ie_end(INDEX_ENTRY *ie) { - return ie->ie_flags & INDEX_ENTRY_END; + return ie->ie_flags & INDEX_ENTRY_END || !ie->length; } /** @@ -302,11 +302,13 @@ static int ntfs_ih_numof_entries(INDEX_HEADER *ih) { int n; INDEX_ENTRY *ie; + u8 *end; ntfs_log_trace("Entering\n"); + end = ntfs_ie_get_end(ih); ie = ntfs_ie_get_first(ih); - for (n = 0; !ntfs_ie_end(ie); n++) + for (n = 0; !ntfs_ie_end(ie) && (u8 *)ie < end; n++) ie = ntfs_ie_get_next(ie); return n; } diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index d4d280f8..7c633a98 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -209,6 +209,40 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, return -1; } +int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + int ret = -1; + + if (!ntfs_is_file_record(m->magic)) { + ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", + (unsigned long long)MREF(mref), *(le32 *)m); + goto err_out; + } + + if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { + ntfs_log_error("Record %llu has corrupt allocation size " + "(%u <> %u)\n", (unsigned long long)MREF(mref), + vol->mft_record_size, + le32_to_cpu(m->bytes_allocated)); + goto err_out; + } + + a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); + if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { + ntfs_log_error("Record %llu is corrupt\n", + (unsigned long long)MREF(mref)); + goto err_out; + } + + ret = 0; +err_out: + if (ret) + errno = EIO; + return ret; +} + /** * ntfs_file_record_read - read a FILE record from the mft from disk * @vol: volume to read from @@ -244,14 +278,13 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr) { MFT_RECORD *m; - ATTR_RECORD *a; - int err; if (!vol || !mrec) { errno = EINVAL; ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } + m = *mrec; if (!m) { m = ntfs_malloc(vol->mft_record_size); @@ -259,36 +292,26 @@ int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, return -1; } if (ntfs_mft_record_read(vol, mref, m)) { - err = errno; ntfs_log_perror("ntfs_mft_record_read failed"); goto err_out; } - err = EIO; - if (!ntfs_is_file_record(m->magic)) { - ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", - (unsigned long long)MREF(mref), *(le32 *)m); + if (ntfs_mft_record_check(vol, mref, m)) goto err_out; - } + if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", (unsigned long long)MREF(mref), MSEQNO(mref), le16_to_cpu(m->sequence_number)); - goto err_out; - } - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size) { - ntfs_log_error("Record %llu is corrupt\n", - (unsigned long long)MREF(mref)); + errno = EIO; goto err_out; } *mrec = m; if (attr) - *attr = a; + *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); return 0; err_out: if (m != *mrec) free(m); - errno = err; return -1; } diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 2cea710e..fa268c94 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -210,24 +210,14 @@ static int ntfs_mft_load(ntfs_volume *vol) ntfs_log_perror("Error reading $MFT"); goto error_exit; } - if (ntfs_is_baad_record(mb->magic)) { - ntfs_log_error("Incomplete multi sector transfer detected in " - "$MFT.\n"); - goto io_error_exit; - } - if (!ntfs_is_mft_record(mb->magic)) { - ntfs_log_error("$MFT has invalid magic.\n"); - goto io_error_exit; - } + + if (ntfs_mft_record_check(vol, 0, mb)) + goto error_exit; + ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); if (!ctx) goto error_exit; - if (p2n(ctx->attr) < p2n(mb) || - (char*)ctx->attr > (char*)mb + vol->mft_record_size) { - ntfs_log_error("$MFT is corrupt.\n"); - goto io_error_exit; - } /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { @@ -855,13 +845,19 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) ntfs_log_perror("Failed to open inode FILE_Bitmap"); goto error_exit; } - /* Get an ntfs attribute for $Bitmap/$DATA. */ + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); if (!vol->lcnbmp_na) { ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } - /* Done with the $Bitmap mft record. */ + + if (vol->lcnbmp_na->data_size > vol->lcnbmp_na->allocated_size) { + ntfs_log_error("Corrupt cluster map size (%lld > %lld)\n", + (long long)vol->lcnbmp_na->data_size, + (long long)vol->lcnbmp_na->allocated_size); + goto io_error_exit; + } /* Now load the upcase table from $UpCase. */ ntfs_log_debug("Loading $UpCase...\n");