diff --git a/ChangeLog b/ChangeLog index 9ea4d171..c356c2d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,7 @@ xx/xx/2004 - 2.0.0-WIP all callers. This allows filling an attribute extent with mapping pairs and then continuing in a different extent once the first extent is full. (Yura) + - Remove vol->nr_mft_records and update all users. (Anton) 04/09/2004 - 1.9.4 - Urgent bug fixes. diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index 8191290c..6fb3be5d 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.h @@ -140,9 +140,6 @@ struct _ntfs_volume { lcn 0 and so on. A set bit means that the cluster and vice versa. */ - s64 nr_mft_records; /* Number of initialized records in the mft, - equals the number of bits in mft_bitmap that - may be set. */ LCN mft_lcn; /* Logical cluster number of the data attribute for FILE_MFT. */ ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */ diff --git a/libntfs/mft.c b/libntfs/mft.c index 14e5cf5d..244e3e82 100644 --- a/libntfs/mft.c +++ b/libntfs/mft.c @@ -72,7 +72,8 @@ int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, } m = MREF(mref); /* Refuse to read non-allocated mft records. */ - if (m + count > vol->nr_mft_records) { + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { errno = ESPIPE; return -1; } @@ -129,7 +130,8 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, } m = MREF(mref); /* Refuse to write non-allocated mft records. */ - if (m + count > vol->nr_mft_records) { + if (m + count > vol->mft_na->initialized_size >> + vol->mft_record_size_bits) { errno = ESPIPE; return -1; } @@ -375,20 +377,88 @@ int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref) * @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 - * at mft record number @start or at the current allocator position if - * @start_mref is -1, on the mounted ntfs volume @vol. + * at mft record number @start or at the current allocator position if @start + * is -1, on the mounted ntfs volume @vol. * * On success return the now opened ntfs inode of the mft record. * * On error return NULL with errno set to the error code. + * + * To find a free mft record, we scan the mft bitmap for a zero bit. To + * optimize this we start scanning at the place specified by @start or if + * @start is -1 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 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 + * which means that when the volume is really out of space we cannot create any + * more files while Windows can still create up to 8 small files. We can start + * doing this at some later time, it does not matter much for now. + * + * When scanning the mft bitmap, we only search up to the last allocated mft + * record. If there are no free records left in the range 24 to number of + * allocated mft records, then we extend the $MFT/$DATA attribute in order to + * create free mft records. We extend the allocated size of $MFT/$DATA by 16 + * records at a time or one cluster, if cluster size is above 16kiB. If there + * is not sufficient space to do this, we try to extend by a single mft record + * or one cluster, if cluster size is above the mft record size, but we only do + * this if there is enough free space, which we know from the values returned + * by the failed cluster allocation function when we tried to do the first + * allocation. + * + * No matter how many mft records we allocate, we initialize only the first + * allocated mft record, incrementing mft data size and initialized size + * accordingly, open an ntfs_inode for it and return it to the caller, unless + * there are less than 24 mft records, in which case we allocate and initialize + * mft records until we reach record 24 which we consider as the first free mft + * record for use by normal files. + * + * If during any stage we overflow the initialized data in the mft bitmap, we + * extend the initialized size (and data size) by 8 bytes, allocating another + * cluster if required. The bitmap data size has to be at least equal to the + * number of mft records in the mft, but it can be bigger, in which case the + * superflous bits are padded with zeroes. + * + * Thus, when we return successfully (return value non-zero), we will have: + * - initialized / extended the mft bitmap if necessary, + * - initialized / extended the mft data if necessary, + * - set the bit corresponding to the mft record being allocated in the + * mft bitmap, + * - open an ntfs_inode for the allocated mft record, and we will + * - return the ntfs_inode. + * + * On error (return value zero), nothing will have changed. If we had changed + * anything before the error occured, we will have reverted back to the + * starting state before returning to the caller. Thus, except for bugs, we + * should always leave the volume in a consistent state when returning from + * this function. + * + * Note, this function cannot make use of most of the normal functions, like + * for example for attribute resizing, etc, because when the run list overflows + * the base mft record and an attribute list is used, it is very important that + * the extension mft records used to store the $DATA attribute of $MFT can be + * reached without having to read the information contained inside them, as + * this would make it impossible to find them in the first place after the + * volume is dismounted. $MFT/$BITMAP probably does not need to follow this + * rule because the bitmap is not essential for finding the mft records, but on + * the other hand, handling the bitmap in this special way would make life + * easier because otherwise there might be circular invocations of functions + * when reading the bitmap but if we are careful, we should be able to avoid + * all problems. */ +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, s64 start) { - if (!vol || !vol->mftbmp_na || start < -1) { + ntfs_debug("Entering (start 0x%llx).", (long long)start); + if (!vol || !vol->mft_na || !vol->mftbmp_na || start < -1) { errno = EINVAL; return NULL; } - errno = ENOTSUP; return NULL; } diff --git a/libntfs/volume.c b/libntfs/volume.c index 75bae327..95a00af3 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -217,9 +217,6 @@ mft_has_no_attr_list: Dperror("Failed to open ntfs attribute"); goto error_exit; } - /* Set the number of initialized mft records. */ - vol->nr_mft_records = vol->mft_na->initialized_size >> - vol->mft_record_size_bits; /* Read all extents from the $DATA attribute in $MFT. */ ntfs_attr_reinit_search_ctx(ctx); last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index bae0835e..2b679e94 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -873,7 +873,8 @@ static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) Printf("Scanning volume ...\n"); - last_mft_rec = volume->nr_mft_records - 1; + last_mft_rec = (volume->mft_na->initialized_size >> + volume->mft_record_size_bits) - 1; progress_init(&progress, inode, last_mft_rec, 100); for (; inode <= last_mft_rec; inode++) { @@ -1331,7 +1332,8 @@ int main(int argc, char **argv) walk_clusters(vol, &backup_clusters); Printf("Num of MFT records = %8lld\n", - (long long)vol->nr_mft_records); + (long long)vol->mft_na->initialized_size >> + vol->mft_record_size_bits); Printf("Num of used MFT records = %8d\n", nr_used_mft_records); Printf("Wiped unused MFT data = %8d\n", wiped_unused_mft_data); diff --git a/ntfsprogs/ntfscluster.c b/ntfsprogs/ntfscluster.c index df5f0e05..194d79d4 100644 --- a/ntfsprogs/ntfscluster.c +++ b/ntfsprogs/ntfscluster.c @@ -311,7 +311,7 @@ static int info (ntfs_volume *vol) d = vol->nr_clusters << cb; e = vol->nr_clusters; f = vol->nr_clusters >> cps; - g = vol->nr_mft_records; + g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; h = inuse; i = h * 100 / g; j = fc; diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 28b689ca..d30a0596 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -339,8 +339,6 @@ static void ntfs_dump_volume(ntfs_volume *vol) (long long)vol->data1_zone_pos); printf("\tCurrent Position in Second Data Zone: %lld\n", (long long)vol->data2_zone_pos); - printf("\tNumber of Initialized Records in MFT: %lld\n", - (long long)vol->nr_mft_records); printf("\tLCN of Data Attribute for FILE_MFT: %lld\n", (long long)vol->mft_lcn); printf("\tFILE_MFTMirr Size: %d\n", vol->mftmirr_size); diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 320aa745..a50f418b 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -953,7 +953,7 @@ static int inode_close(ntfs_inode *ni) */ static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) { - s64 inode = 0; + s64 nr_mft_records, inode = 0; ntfs_inode *ni; struct progress_bar progress; int pb_flags = 0; /* progress bar flags */ @@ -964,9 +964,12 @@ static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) if (fsck->flags & NTFSCK_PROGBAR) pb_flags |= NTFS_PROGBAR; - progress_init(&progress, inode, vol->nr_mft_records - 1, pb_flags); + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; - for (; inode < vol->nr_mft_records; inode++) { + progress_init(&progress, inode, nr_mft_records - 1, pb_flags); + + for (; inode < nr_mft_records; inode++) { progress_update(&progress, inode); if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) { @@ -1033,12 +1036,15 @@ static void resize_constrains_by_attributes(ntfs_resize_t *resize) static void set_resize_constrains(ntfs_resize_t *resize) { - s64 inode; + s64 nr_mft_records, inode; ntfs_inode *ni; printf("Collecting shrinkage constrains ...\n"); - for (inode = 0; inode < resize->vol->nr_mft_records; inode++) { + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + for (inode = 0; inode < nr_mft_records; inode++) { ni = ntfs_inode_open(resize->vol, (MFT_REF)inode); if (ni == NULL) { @@ -1586,6 +1592,7 @@ static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref) static void relocate_inodes(ntfs_resize_t *resize) { + s64 nr_mft_records; MFT_REF mref; printf("Relocating needed data ...\n"); @@ -1597,7 +1604,10 @@ static void relocate_inodes(ntfs_resize_t *resize) if (!resize->mrec) perr_exit("malloc failed"); - for (mref = 1; mref < (MFT_REF)resize->vol->nr_mft_records; mref++) + nr_mft_records = resize->vol->mft_na->initialized_size >> + resize->vol->mft_record_size_bits; + + for (mref = 1; mref < (MFT_REF)nr_mft_records; mref++) relocate_inode(resize, mref); relocate_inode(resize, 0); diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index 7441f741..586207b9 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -1553,6 +1553,7 @@ static int set_date (const char *pathname, time_t date) */ static int scan_disk (ntfs_volume *vol) { + s64 nr_mft_records; const int BUFSIZE = 8192; char *buffer = NULL; int results = 0; @@ -1592,6 +1593,9 @@ static int scan_disk (ntfs_volume *vol) } } + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + Qprintf ("Inode Flags %%age Date Size Filename\n"); Qprintf ("---------------------------------------------------------------\n"); for (i = 0; i < bmpsize; i += BUFSIZE) { @@ -1603,7 +1607,7 @@ static int scan_disk (ntfs_volume *vol) for (j = 0; j < size; j++) { b = buffer[j]; for (k = 0; k < 8; k++, b>>=1) { - if (((i+j)*8+k) >= vol->nr_mft_records) + if (((i+j)*8+k) >= nr_mft_records) goto done; if (b & 1) continue; @@ -1907,6 +1911,7 @@ free: */ static int copy_mft (ntfs_volume *vol, long long mft_begin, long long mft_end) { + s64 nr_mft_records; char pathname[256]; ntfs_attr *mft; char *buffer; @@ -1948,10 +1953,13 @@ static int copy_mft (ntfs_volume *vol, long long mft_begin, long long mft_end) goto attr; } - mft_end = min (mft_end, vol->nr_mft_records - 1); + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + mft_end = min (mft_end, nr_mft_records - 1); Dprintf ("MFT records\n"); - Dprintf (" Total: %8lld\n", vol->nr_mft_records); + Dprintf (" Total: %8lld\n", nr_mft_records); Dprintf (" Begin: %8lld\n", mft_begin); Dprintf (" End: %8lld\n", mft_end); @@ -1988,6 +1996,7 @@ free: */ int main (int argc, char *argv[]) { + s64 nr_mft_records; ntfs_volume *vol; int result = 1; int i; @@ -2028,10 +2037,13 @@ int main (int argc, char *argv[]) } break; case MODE_COPY: + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + result = !copy_mft (vol, opts.mft_begin, opts.mft_end); if (result) Vprintf ("Failed to read MFT blocks %lld-%lld.\n", - opts.mft_begin, min(vol->nr_mft_records, opts.mft_end)); + opts.mft_begin, min(nr_mft_records, opts.mft_end)); break; default: ; /* Cannot happen */ diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c index 08d9f9e5..1c94b093 100644 --- a/ntfsprogs/ntfswipe.c +++ b/ntfsprogs/ntfswipe.c @@ -566,14 +566,17 @@ static s64 wipe_attribute (ntfs_volume *vol, int byte, enum action act, static s64 wipe_tails (ntfs_volume *vol, int byte, enum action act) { s64 total = 0; - s64 inode_num; + s64 nr_mft_records, inode_num; ntfs_inode *ni; ntfs_attr *na; if (!vol || (byte < 0)) return -1; - for (inode_num = 16; inode_num < vol->nr_mft_records; inode_num++) { + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = 16; inode_num < nr_mft_records; inode_num++) { s64 wiped; Vprintf ("Inode %lld - ", inode_num); @@ -646,7 +649,7 @@ static s64 wipe_mft (ntfs_volume *vol, int byte, enum action act) { // by considering the individual attributes we might be able to // wipe a few more bytes at the attr's tail. - s64 i; + s64 nr_mft_records, i; s64 total = 0; s64 result = 0; int size = 0; @@ -661,7 +664,10 @@ static s64 wipe_mft (ntfs_volume *vol, int byte, enum action act) return -1; } - for (i = 0; i < vol->nr_mft_records; i++) { + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (i = 0; i < nr_mft_records; i++) { if (utils_mftrec_in_use (vol, i)) { result = ntfs_attr_mst_pread (vol->mft_na, vol->mft_record_size * i, 1, vol->mft_record_size, buffer); @@ -920,7 +926,7 @@ static u32 get_indx_record_size (ntfs_attr *nar) static s64 wipe_directory (ntfs_volume *vol, int byte, enum action act) { s64 total = 0; - s64 inode_num; + s64 nr_mft_records, inode_num; ntfs_inode *ni; ntfs_attr *naa; ntfs_attr *nab; @@ -929,7 +935,10 @@ static s64 wipe_directory (ntfs_volume *vol, int byte, enum action act) if (!vol || (byte < 0)) return -1; - for (inode_num = 5; inode_num < vol->nr_mft_records; inode_num++) { + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + + for (inode_num = 5; inode_num < nr_mft_records; inode_num++) { u32 indx_record_size; s64 wiped; diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 00830a67..8607c834 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -963,6 +963,7 @@ void mft_put_search_ctx (struct mft_search_ctx *ctx) */ int mft_next_record (struct mft_search_ctx *ctx) { + s64 nr_mft_records; ATTR_RECORD *attr10 = NULL; ATTR_RECORD *attr20 = NULL; ATTR_RECORD *attr80 = NULL; @@ -978,7 +979,10 @@ int mft_next_record (struct mft_search_ctx *ctx) ctx->inode = NULL; } - for (ctx->mft_num++; (s64)ctx->mft_num < ctx->vol->nr_mft_records; ctx->mft_num++) { + nr_mft_records = ctx->vol->mft_na->initialized_size >> + ctx->vol->mft_record_size_bits; + + for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) { int in_use; ctx->flags_match = 0;