From 241c7585a6383eb1fcdaebd611a286a9fc906bf3 Mon Sep 17 00:00:00 2001 From: cha0smaster Date: Tue, 21 Feb 2006 04:40:20 +0000 Subject: [PATCH] Fix runlist setting and incorrect file sizes in the index. --- ChangeLog | 5 +- libntfs/attrib.c | 202 +++++++++++++++++++++-------------------------- 2 files changed, 89 insertions(+), 118 deletions(-) diff --git a/ChangeLog b/ChangeLog index 611cfb81..1c2bc75c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -82,10 +82,6 @@ the less encountered flags, and reporting the in_use flag instead of inverting it to unused. (Yuval) - mkntfs: fix segfault when volume had more than 2^31 clusters. (Szaka) - - Add @from_vcn parameter to ntfs_attr_update_mapping_pairs, update all - users to submit it, thus write speed to very fragmented files - dramatically increased, while write speed to low fragmented files - stayed at approximately same level. (Yura) - Automatically update access and change time in ntfs_attr_p{read,write} and ntfs_attr_truncate. (Yura) - Add support of MS_NOATIME flag to ntfs_mount(). (Yura) @@ -118,6 +114,7 @@ - ntfsresize, ntfsclone: always use MS_NOATIME. (Szaka) - Implement simple syslog logging handler (need more work), teach ntfsmount to use it. (Yura) + - Fix a lot of bugs in attribute resizing code. (Yura) 10/10/2005 - 1.12.1 - Minor fix to location of mount.ntfs-fuse and mkfs.ntfs. diff --git a/libntfs/attrib.c b/libntfs/attrib.c index 96423737..44dcf435 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -1337,7 +1337,7 @@ done: ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ if (need_to.update_mapping_pairs) - ntfs_attr_update_mapping_pairs(na, update_from); + ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); /* Finally, return the number of bytes written. */ return total; rl_err_out: @@ -1395,7 +1395,7 @@ err_out: ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ if (need_to.update_mapping_pairs) - ntfs_attr_update_mapping_pairs(na, update_from); + ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); /* Restore original data_size if needed. */ if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) ntfs_log_trace("Failed to restore data_size.\n"); @@ -4092,14 +4092,25 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) * @na: non-resident ntfs open attribute for which we need update * @from_vcn: update runlist starting this VCN * - * Build mapping pairs from na->runlist and write them to the disk. Also, this - * function updates sparse bit and allocates/frees space for compressed_size, - * but doesn't change it, so caller should update it manually. + * Build mapping pairs from @na->rl and write them to the disk. Also, this + * function updates sparse bit, allocated and compressed size (allocates/frees + * space for this field if required). + * + * @na->allocated_size should be set to correct value for the new runlist before + * call to this function. Vice-versa @na->compressed_size will be calculated and + * set to correct value during this function. + * + * FIXME: This function does not update sparse bit and compressed size correctly + * if called with @from_vcn != 0. + * + * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. * * On success return 0 and on error return -1 with errno set to the error code. * The following error codes are defined: + * EINVAL - Invalid arguments passed. * ENOMEM - Not enough memory to complete operation. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. + * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST + * or there is no free MFT records left to allocate. */ int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) { @@ -4112,7 +4123,7 @@ int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) BOOL finished_build; retry: - if (!na || !na->rl) { + if (!na || !na->rl || from_vcn) { ntfs_log_trace("Invalid parameters passed.\n"); errno = EINVAL; return -1; @@ -4203,16 +4214,23 @@ retry: goto put_err_out; } } - /* If we in the first extent, then set/clean sparse bit. */ + /* + * If we in the first extent, then set/clean sparse bit, + * update allocated and compressed size. + */ if (!a->lowest_vcn) { int sparse; + /* Update allocated size. */ + a->allocated_size = cpu_to_sle64(na->allocated_size); + /* Update sparse bit. */ sparse = ntfs_rl_sparse(na->rl); if (sparse == -1) { ntfs_log_trace("Bad runlist.\n"); err = EIO; goto put_err_out; } + /* Attribute become sparse. */ if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { /* @@ -4267,14 +4285,8 @@ retry: a->mapping_pairs_offset = cpu_to_le16(le16_to_cpu( a->mapping_pairs_offset) + 8); - /* - * Set FILE_NAME dirty flag, to update - * sparse bit in the index. - */ - if (na->type == AT_DATA && - na->name == AT_UNNAMED) - NInoFileNameSetDirty(na->ni); } + /* Attribute no longer sparse. */ if (!sparse && (a->flags & ATTR_IS_SPARSE) && !(a->flags & ATTR_IS_COMPRESSED)) { NAttrClearSparse(na); @@ -4288,13 +4300,35 @@ retry: a->mapping_pairs_offset = cpu_to_le16(le16_to_cpu( a->mapping_pairs_offset) - 8); - /* - * Set FILE_NAME dirty flag, to update - * sparse bit in the index. - */ - if (na->type == AT_DATA && - na->name == AT_UNNAMED) - NInoFileNameSetDirty(na->ni); + } + /* Update compressed size if required. */ + if (sparse) { + s64 new_compr_size; + + new_compr_size = ntfs_rl_get_compressed_size( + na->ni->vol, na->rl); + if (new_compr_size == -1) { + err = errno; + ntfs_log_trace("BUG! Leaving inconstant" + " metadata.\n"); + goto put_err_out; + } + na->compressed_size = new_compr_size; + a->compressed_size = cpu_to_sle64( + new_compr_size); + } + /* + * Set FILE_NAME dirty flag, to update sparse bit in + * the index. Update allocated size in the index. + */ + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (sparse) + na->ni->allocated_size = + na->compressed_size; + else + na->ni->allocated_size = + na->allocated_size; + NInoFileNameSetDirty(na->ni); } } /* Get the size for the rest of mapping pairs array. */ @@ -4533,7 +4567,6 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) { ntfs_volume *vol; ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; VCN first_free_vcn; s64 nr_freed_clusters; int err; @@ -4598,11 +4631,14 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) return -1; } + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, first_free_vcn)) { + if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { err = errno; - ntfs_log_trace("Eeek! Mapping pairs update failed. Leaving " - "inconstant metadata. Run chkdsk.\n"); + ntfs_log_trace("Eeek! Mapping pairs update failed. " + "Leaving inconstant metadata. " + "Run chkdsk.\n"); errno = err; return -1; } @@ -4612,12 +4648,7 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) { err = errno; - if ((na->allocated_size >> vol->cluster_size_bits) - != first_free_vcn) - ntfs_log_trace("Couldn't get attribute search context. " - "Leaving inconstant metadata.\n"); - else - ntfs_log_trace("Couldn't get attribute search context.\n"); + ntfs_log_trace("Couldn't get attribute search context.\n"); errno = err; return -1; } @@ -4630,36 +4661,13 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) "Leaving inconstant metadata.\n"); goto put_err_out; } - a = ctx->attr; - - /* Update allocated size only if it is changed. */ - if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - a->allocated_size = cpu_to_sle64(na->allocated_size); - - /* Update compressed_size if present. */ - if (NAttrSparse(na) || NAttrCompressed(na)) { - s64 new_compr_size; - - new_compr_size = ntfs_rl_get_compressed_size(vol, - na->rl); - if (new_compr_size == -1) { - err = errno; - ntfs_log_trace("BUG! Leaving inconstant " - "metadata.\n"); - goto put_err_out; - } - na->compressed_size = new_compr_size; - a->compressed_size = cpu_to_sle64(new_compr_size); - } - } /* Update data and initialized size. */ na->data_size = newsize; - a->data_size = cpu_to_sle64(newsize); + ctx->attr->data_size = cpu_to_sle64(newsize); if (newsize < na->initialized_size) { na->initialized_size = newsize; - a->initialized_size = cpu_to_sle64(newsize); + ctx->attr->initialized_size = cpu_to_sle64(newsize); } /* If the attribute now has zero size, make it resident. */ @@ -4667,8 +4675,8 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) if (ntfs_attr_make_resident(na, ctx)) { /* If couldn't make resident, just continue. */ if (errno != EPERM) - ntfs_log_trace("Failed to make attribute resident. " - "Leaving as is...\n"); + ntfs_log_error("Failed to make attribute " + "resident. Leaving as is...\n"); } } @@ -4701,10 +4709,10 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) { LCN lcn_seek_from; VCN first_free_vcn; - ATTR_RECORD *a; ntfs_volume *vol; ntfs_attr_search_ctx *ctx; runlist *rl, *rln; + s64 org_alloc_size; int err; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld, " @@ -4729,6 +4737,8 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) return -1; } + /* Save for future use. */ + org_alloc_size = na->allocated_size; /* The first cluster outside the new allocation. */ first_free_vcn = (newsize + vol->cluster_size - 1) >> vol->cluster_size_bits; @@ -4816,9 +4826,11 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) } na->rl = rln; + /* Prepare to mapping pairs update. */ + na->allocated_size = first_free_vcn << vol->cluster_size_bits; /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, na->allocated_size >> - vol->cluster_size_bits)) { + if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> + vol->cluster_size_bits*/)) { err = errno; ntfs_log_trace("Eeek! Mapping pairs update failed.\n"); goto rollback; @@ -4829,8 +4841,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) if (!ctx) { err = errno; ntfs_log_trace("Failed to get search context.\n"); - if ((na->allocated_size >> vol->cluster_size_bits) == - first_free_vcn) { + if (na->allocated_size == org_alloc_size) { errno = err; return -1; } else @@ -4839,44 +4850,20 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - ntfs_log_trace("Eeek! Lookup of first attribute extent failed.\n"); err = errno; + ntfs_log_trace("Lookup of first attribute extent failed.\n"); if (err == ENOENT) err = EIO; - if ((na->allocated_size >> vol->cluster_size_bits) != - first_free_vcn) { + if (na->allocated_size != org_alloc_size) { ntfs_attr_put_search_ctx(ctx); goto rollback; } else goto put_err_out; } - a = ctx->attr; - - /* Update allocated and compressed size only if we changed runlist. */ - if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - a->allocated_size = cpu_to_sle64(na->allocated_size); - - /* Update compressed_size if present. */ - if (NAttrSparse(na) || NAttrCompressed(na)) { - s64 new_compr_size; - - new_compr_size = ntfs_rl_get_compressed_size(vol, - na->rl); - if (new_compr_size == -1) { - err = errno; - ntfs_log_trace("BUG! Leaving inconstant " - "metadata.\n"); - goto put_err_out; - } - na->compressed_size = new_compr_size; - a->compressed_size = cpu_to_sle64(new_compr_size); - } - } /* Update data size. */ na->data_size = newsize; - a->data_size = cpu_to_sle64(newsize); + ctx->attr->data_size = cpu_to_sle64(newsize); /* Set the inode dirty so it is written out later. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); /* Done! */ @@ -4884,27 +4871,28 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) return 0; rollback: /* Free allocated clusters. */ - if (ntfs_cluster_free(vol, na, na->allocated_size >> - vol->cluster_size_bits, -1) < 0) { + if (ntfs_cluster_free(vol, na, org_alloc_size >> + vol->cluster_size_bits, -1) < 0) { ntfs_log_trace("Eeek! Leaking clusters. Run chkdsk!\n"); err = EIO; } /* Now, truncate the runlist itself. */ - if (ntfs_rl_truncate(&na->rl, na->allocated_size >> - vol->cluster_size_bits)) { + if (ntfs_rl_truncate(&na->rl, org_alloc_size >> + vol->cluster_size_bits)) { /* * Failed to truncate the runlist, so just throw it away, it * will be mapped afresh on next use. */ free(na->rl); na->rl = NULL; - ntfs_log_trace("Eeek! Couldn't truncate runlist. Rollback " - "failed.\n"); + ntfs_log_trace("Couldn't truncate runlist. Rollback failed.\n"); } else { + /* Prepare to mapping pairs update. */ + na->allocated_size = org_alloc_size << vol->cluster_size_bits; /* Restore mapping pairs. */ - if (ntfs_attr_update_mapping_pairs(na, na->allocated_size >> - vol->cluster_size_bits)) { - ntfs_log_trace("Eeek! Couldn't restore old mapping pairs. " + if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> + vol->cluster_size_bits*/)) { + ntfs_log_trace("Failed to restore old mapping pairs. " "Rollback failed.\n"); } } @@ -4970,20 +4958,6 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) ret = ntfs_non_resident_attr_shrink(na, newsize); } else ret = ntfs_resident_attr_resize(na, newsize); - /* Set FILE_NAME dirty flag, to update file length in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - /* - * If attribute sparse or compressed then allocated size in - * index should be equal to compressed size, not to allocated - * size. - */ - if (NAttrCompressed(na) || NAttrSparse(na)) - na->ni->allocated_size = na->compressed_size; - else - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty(na->ni); - } /* Update access and change times if needed. */ if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || na->type == AT_INDEX_ALLOCATION)