diff --git a/include/ntfs/mft.h b/include/ntfs/mft.h index e3f18e7a..0ece8c95 100644 --- a/include/ntfs/mft.h +++ b/include/ntfs/mft.h @@ -101,6 +101,11 @@ static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m) return le32_to_cpu(m->bytes_in_use); } +extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *mrec); + +extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); + extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, s64 start); extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); diff --git a/libntfs/mft.c b/libntfs/mft.c index 1edac6bd..f42719b5 100644 --- a/libntfs/mft.c +++ b/libntfs/mft.c @@ -123,7 +123,7 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, int cnt = 0, res = 0; Dprintf("%s(): Entering for inode 0x%llx.\n", __FUNCTION__, MREF(mref)); - if (!vol || !vol->mft_na || !b || count < 0) { + if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { errno = EINVAL; return -1; } @@ -134,6 +134,10 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, return -1; } if (m < vol->mftmirr_size) { + if (!vol->mftmirr_na) { + errno = EINVAL; + return -1; + } cnt = vol->mftmirr_size - m; if (cnt > count) cnt = count; @@ -248,9 +252,126 @@ read_failed: return -1; } +/** + * ntfs_mft_record_layout - layout an mft record into a memory buffer + * @vol: volume to which the mft record will belong + * @mref: mft reference specifying the mft record number + * @m: destination buffer of size >= @vol->mft_record_size bytes + * + * Layout an empty, unused mft record with the mft reference @mref into the + * buffer @m. The volume @vol is needed because the mft record structure was + * modified in NTFS 3.1 so we need to know which volume version this mft record + * will be used on. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, + MFT_RECORD *m) +{ + ATTR_RECORD *a; + + if (!vol || !m) { + errno = EINVAL; + return -1; + } + /* Aligned to 2-byte boundary. */ + if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) + m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); + else { + /* Abort if mref is > 32 bits. */ + if (MREF(mref) & 0x0000ffff00000000ull) { + fprintf(stderr, "Mft reference exceeds 32 bits!"); + errno = ERANGE; + return -1; + } + m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); + /* + * Set the NTFS 3.1+ specific fields while we know that the + * volume version is 3.1+. + */ + m->reserved = cpu_to_le16(0); + m->mft_record_number = cpu_to_le32(MREF(mref)); + } + m->magic = magic_FILE; + if (vol->mft_record_size >= NTFS_SECTOR_SIZE) + m->usa_count = cpu_to_le16(vol->mft_record_size / + NTFS_SECTOR_SIZE + 1); + else { + m->usa_count = cpu_to_le16(1); + fprintf(stderr, "Sector size is bigger than MFT record size. " + "Setting usa_count to 1. If Windows\nchkdsk " + "reports this as corruption, please email " + "linux-ntfs-dev@lists.sf.net\nstating that " + "you saw this message and that the file " + "system created was corrupt.\nThank you."); + } + /* Set the update sequence number to 1. */ + *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = cpu_to_le16(1); + m->lsn = cpu_to_le64(0ull); + m->sequence_number = cpu_to_le16(1); + m->link_count = cpu_to_le16(0); + /* Aligned to 8-byte boundary. */ + m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) + + (le16_to_cpu(m->usa_count) << 1) + 7) & ~7); + m->flags = cpu_to_le16(0); + /* + * Using attrs_offset plus eight bytes (for the termination attribute), + * aligned to 8-byte boundary. + */ + m->bytes_in_use = cpu_to_le32((le16_to_cpu(m->attrs_offset) + 8 + 7) & + ~7); + m->bytes_allocated = cpu_to_le32(vol->mft_record_size); + m->base_mft_record = cpu_to_le64((MFT_REF)0); + m->next_attr_instance = cpu_to_le16(0); + a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset)); + a->type = AT_END; + a->length = cpu_to_le32(0); + /* Finally, clear the unused part of the mft record. */ + memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)m)); + return 0; +} + +/** + * ntfs_mft_record_format - format an mft record on an ntfs volume + * @vol: volume on which to format the mft record + * @mref: mft reference specifying mft record to format + * + * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay + * out an empty, unused mft record in memory and write it to the volume @vol. + * + * On success return 0 and on error return -1 with errno set to the error code. + */ +int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) +{ + MFT_RECORD *m; + int err; + + if (!vol || !vol->mft_na) { + errno = EINVAL; + return -1; + } + m = malloc(vol->mft_record_size); + if (!m) + return -1; + if (ntfs_mft_record_layout(vol, mref, m)) { + err = errno; + free(m); + errno = err; + return -1; + } + if (ntfs_mft_record_write(vol, mref, m)) { + err = errno; + free(m); + errno = err; + return -1; + } + free(m); + return 0; +} + /** * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume - * @vol: mounted ntfs volume on which to allocate the mft record + * @vol: volume on which to allocate the mft record * @start: starting mft record at which to allocate (or -1 if none) * * Allocate an mft record in $MFT/$DATA starting to search for a free record @@ -274,7 +395,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, s64 start) /** * ntfs_mft_record_free - free an mft record on an ntfs volume - * @vol: mounted ntfs volume on which to free the mft record + * @vol: volume on which to free the mft record * @ni: open ntfs inode of the mft record to free * * Free the mft record of the open inode @ni on the mounted ntfs volume @vol. diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c index d4847061..fe4fc148 100644 --- a/ntfsprogs/mkntfs.c +++ b/ntfsprogs/mkntfs.c @@ -123,6 +123,7 @@ #include "mst.h" #include "dir.h" #include "runlist.h" +#include "mft.h" #include "utils.h" #ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS @@ -954,59 +955,6 @@ static void dump_mft_record(MFT_RECORD *m) printf("-- End of attributes. --\n"); } -/** - * format_mft_record - */ -static void format_mft_record(MFT_RECORD *m) -{ - ATTR_RECORD *a; - - memset(m, 0, vol->mft_record_size); - m->magic = magic_FILE; - /* - * Aligned to 2-byte boundary. Note, we use the MFT_RECORD_OLD here - * explicitly as the MFT_RECORD structure has extra fields at the end - * which are only present in NTFS 3.1+. - */ - m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); - if (vol->mft_record_size >= NTFS_SECTOR_SIZE) - m->usa_count = cpu_to_le16(vol->mft_record_size / - NTFS_SECTOR_SIZE + 1); - else { - m->usa_count = cpu_to_le16(1); - Qprintf("Sector size is bigger than MFT record size. Setting " - "usa_count to 1. If Windows\nchkdsk reports this as " - "corruption, please email linux-ntfs-dev@lists.sf.net\n" - "stating that you saw this message and that the file " - "system created was corrupt.\nThank you."); - } - /* Set the update sequence number to 1. */ - *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = cpu_to_le16(1); - m->lsn = cpu_to_le64(0LL); - m->sequence_number = cpu_to_le16(1); - m->link_count = cpu_to_le16(0); - /* Aligned to 8-byte boundary. */ - m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) + - (le16_to_cpu(m->usa_count) << 1) + 7) & ~7); - m->flags = cpu_to_le16(0); - /* - * Using attrs_offset plus eight bytes (for the termination attribute), - * aligned to 8-byte boundary. - */ - m->bytes_in_use = cpu_to_le32((le16_to_cpu(m->attrs_offset) + 8 + 7) & - ~7); - m->bytes_allocated = cpu_to_le32(vol->mft_record_size); - m->base_mft_record = cpu_to_le64((MFT_REF)0); - m->next_attr_instance = cpu_to_le16(0); - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - a->type = AT_END; - a->length = cpu_to_le32(0); -#if 0 - if (!opts.quiet && opts.verbose > 1) - dump_mft_record(m); -#endif -} - /** * make_room_for_attribute - make room for an attribute inside an mft record * @m: mft record @@ -3092,8 +3040,17 @@ int main(int argc, char **argv) Qprintf(" - Done.\n"); } Qprintf("Creating NTFS volume structures.\n"); - /* Setup an empty mft record. */ - format_mft_record((MFT_RECORD*)buf); + /* + * Setup an empty mft record. Note, we can just give 0 as the mft + * reference as we are creating an NTFS 1.2 volume for which the mft + * reference is ignored by ntfs_mft_record_layout(). + */ + if (ntfs_mft_record_layout(vol, 0, (MFT_RECORD *)buf)) + err_exit("Error: Failed to layout mft record.\n"); +#if 0 + if (!opts.quiet && opts.verbose > 1) + dump_mft_record((MFT_RECORD*)buf); +#endif /* * Copy the mft record onto all 16 records in the buffer and setup the * sequence numbers of each system file to equal the mft record number