From e6c46d4fa0725832d187c7147c4f668e2e8c9cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 11 Mar 2014 10:16:26 +0100 Subject: [PATCH] Used MFT record 15 for the first extent to MFT:DATA When the runlist of the data attribute of MFT has to be split across several extents, the location of each extent has to be known from the runlist present in previous extents. So, force the first extent into record 15 to avoid a bad layout. --- include/ntfs-3g/layout.h | 2 +- include/ntfs-3g/mft.h | 2 ++ libntfs-3g/attrib.c | 8 +++++-- libntfs-3g/mft.c | 49 +++++++++++++++++++++++++++++++++++----- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 427f1520..5b5fff6e 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -235,7 +235,7 @@ typedef enum { FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */ FILE_reserved13 = 13, FILE_reserved14 = 14, - FILE_reserved15 = 15, + FILE_mft_data = 15, /* Reserved for first extent of $MFT:$DATA */ FILE_first_user = 16, /* First user file, used as test limit for whether to allow opening a file or not. */ } NTFS_SYSTEM_FILES; diff --git a/include/ntfs-3g/mft.h b/include/ntfs-3g/mft.h index bb15f0f3..b135efce 100644 --- a/include/ntfs-3g/mft.h +++ b/include/ntfs-3g/mft.h @@ -124,6 +124,8 @@ extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref); extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni); +extern ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data); + extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni); extern int ntfs_mft_usn_dec(MFT_RECORD *mrec); diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 6ecf15d2..72e5f81b 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5869,8 +5869,12 @@ retry: ntfs_log_perror("%s: get mp size failed", __FUNCTION__); goto put_err_out; } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); + /* Allocate new mft record, with special case for mft itself */ + if (!na->ni->mft_no) + ni = ntfs_mft_rec_alloc(na->ni->vol, + na->type == AT_DATA); + else + ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); if (!ni) { ntfs_log_perror("Could not allocate new MFT record"); goto put_err_out; diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 0640efe9..ac4c610b 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -5,6 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2014 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1354,7 +1355,7 @@ undo_data_init: goto out; } -static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) +ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data) { s64 ll, bit; ntfs_attr *mft_na, *mftbmp_na; @@ -1363,6 +1364,7 @@ static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) ntfs_inode *base_ni; int err; le16 seq_no, usn; + BOOL forced_mft_data; ntfs_log_enter("Entering\n"); @@ -1371,7 +1373,38 @@ static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol) base_ni = mft_na->ni; - bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); + /* + * The first extent containing $MFT:$AT_DATA is better located + * in record 15 to make sure it can be read at mount time. + * The record 15 is prereserved as a base inode with no + * extents and no name, and it is marked in use. + */ + forced_mft_data = FALSE; + if (mft_data) { + ntfs_inode *ext_ni = ntfs_inode_open(vol, FILE_mft_data); + /* + * If record 15 cannot be opened, it is probably in + * use as an extent. Apply standard procedure for + * further extents. + */ + if (ext_ni) { + /* + * Make sure record 15 is a base extent and has + * no extents. + * Also make sure it has no name : a base inode with + * no extents and no name cannot be in use. + * Otherwise apply standard procedure. + */ + if (!ext_ni->mrec->base_mft_record + && !ext_ni->nr_extents) + forced_mft_data = TRUE; + ntfs_inode_close(ext_ni); + } + } + if (forced_mft_data) + bit = FILE_mft_data; + else + bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni); if (bit >= 0) goto found_free_rec; @@ -1408,7 +1441,9 @@ found_free_rec: goto undo_mftbmp_alloc; } /* Sanity check that the mft record is really not in use. */ - if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) { + if (!forced_mft_data + && (ntfs_is_file_record(m->magic) + && (m->flags & MFT_RECORD_IN_USE))) { ntfs_log_error("Inode %lld is used but it wasn't marked in " "$MFT bitmap. Fixed.\n", (long long)bit); free(m); @@ -1524,8 +1559,9 @@ err_out: * @base_ni is NULL we start where we last stopped and we perform wrap around * when we reach the end. Note, we do not try to allocate mft records below * number 24 because numbers 0 to 15 are the defined system files anyway and 16 - * to 24 are special in that they are used for storing extension mft records - * for the $DATA attribute of $MFT. This is required to avoid the possibility + * to 24 are used for storing extension mft records or used by chkdsk to store + * its log. However the record number 15 is dedicated to the first extent to + * the $DATA attribute of $MFT. This is required to avoid the possibility * of creating a run list with a circular dependence which once written to disk * can never be read in again. Windows will only use records 16 to 24 for * normal files if the volume is completely out of space. We never use them @@ -1584,6 +1620,7 @@ err_out: * when reading the bitmap but if we are careful, we should be able to avoid * all problems. */ +//ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) { s64 ll, bit; @@ -1605,7 +1642,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni) } if (ntfs_is_mft(base_ni)) { - ni = ntfs_mft_rec_alloc(vol); + ni = ntfs_mft_rec_alloc(vol, FALSE); goto out; }