Avoided full runlist updating in more situations

When a hole in a sparse file was filled, the runlist was fully recomputed.
When a sparse file spans over several MFT extents, this patch leads to
only recompute the runlist from the modified extent to the end.
edge.strict_endians
Jean-Pierre André 2014-03-11 10:04:54 +01:00
parent ddd3a8a329
commit d2c7d40a2b
1 changed files with 70 additions and 36 deletions

View File

@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Richard Russon
* Copyright (c) 2002-2008 Szabolcs Szakacsits
* Copyright (c) 2004-2007 Yura Pakhuchiy
* Copyright (c) 2007-2013 Jean-Pierre Andre
* Copyright (c) 2007-2014 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@ -601,22 +601,19 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn)
static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn)
{
LCN lcn;
VCN last_vcn;
VCN highest_vcn;
VCN needed;
VCN existing_vcn;
runlist_element *rl;
ATTR_RECORD *a;
BOOL startseen;
ntfs_attr_search_ctx *ctx;
BOOL done;
BOOL newrunlist;
lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
if (NAttrFullyMapped(na))
return 0;
existing_vcn = (na->rl ? na->rl->vcn : -1);
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
if (!ctx)
return -1;
@ -627,37 +624,56 @@ static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn)
needed = vcn;
highest_vcn = 0;
startseen = FALSE;
done = FALSE;
rl = (runlist_element*)NULL;
do {
newrunlist = FALSE;
/* Find the attribute in the mft record. */
if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
needed, NULL, 0, ctx)) {
a = ctx->attr;
/* Decode and merge the runlist. */
rl = ntfs_mapping_pairs_decompress(na->ni->vol, a,
na->rl);
/* Decode and merge the runlist. */
if (ntfs_rl_vcn_to_lcn(na->rl, needed)
== LCN_RL_NOT_MAPPED) {
rl = ntfs_mapping_pairs_decompress(na->ni->vol,
a, na->rl);
newrunlist = TRUE;
} else
rl = na->rl;
if (rl) {
na->rl = rl;
highest_vcn = le64_to_cpu(a->highest_vcn);
/* corruption detection */
if (((highest_vcn + 1) < last_vcn)
&& ((highest_vcn + 1) <= needed)) {
ntfs_log_error("Corrupt attribute list\n");
rl = (runlist_element*)NULL;
if (highest_vcn < needed) {
/* corruption detection on unchanged runlists */
if (newrunlist
&& ((highest_vcn + 1) < last_vcn)) {
ntfs_log_error("Corrupt attribute list\n");
rl = (runlist_element*)NULL;
errno = EIO;
}
done = TRUE;
}
needed = highest_vcn + 1;
if (!a->lowest_vcn)
startseen = TRUE;
/* reaching a previously allocated part ? */
if ((existing_vcn >= 0)
&& (needed >= existing_vcn)) {
needed = last_vcn;
}
}
} else
rl = (runlist_element*)NULL;
} while (rl && (needed < last_vcn));
} else {
done = TRUE;
}
} while (rl && !done && (needed < last_vcn));
ntfs_attr_put_search_ctx(ctx);
/*
* Make sure we reached the end, unless the last
* runlist was modified earlier (using HOLES_DELAY
* leads to have a visibility over attributes which
* have not yet been fully updated)
*/
if (done && newrunlist && (needed < last_vcn)) {
ntfs_log_error("End of runlist not reached\n");
rl = (runlist_element*)NULL;
errno = EIO;
}
/* mark fully mapped if we did so */
if (rl && startseen)
NAttrSetFullyMapped(na);
@ -685,6 +701,7 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na)
ntfs_volume *vol = na->ni->vol;
ATTR_RECORD *a;
int ret = -1;
int not_mapped;
ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n",
(unsigned long long)na->ni->mft_no, na->type);
@ -704,7 +721,7 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na)
while (1) {
runlist_element *rl;
int not_mapped = 0;
not_mapped = 0;
if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED)
not_mapped = 1;
@ -759,7 +776,13 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na)
ntfs_log_perror("Couldn't find attribute for runlist mapping");
goto err_out;
}
if (highest_vcn && highest_vcn != last_vcn - 1) {
/*
* Cannot check highest_vcn when the last runlist has
* been modified earlier, as runlists and sizes may be
* updated without highest_vcn being in sync, when
* HOLES_DELAY is used
*/
if (not_mapped && highest_vcn && highest_vcn != last_vcn - 1) {
errno = EIO;
ntfs_log_perror("Failed to load full runlist: inode: %llu "
"highest_vcn: 0x%llx last_vcn: 0x%llx",
@ -1245,16 +1268,13 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
/* Map the runlist to be able to update mapping pairs later. */
#if PARTIAL_RUNLIST_UPDATING
if ((!na->rl
|| !NAttrDataAppending(na))) {
|| ((na->data_flags & ATTR_COMPRESSION_MASK)
&& !NAttrDataAppending(na)))) {
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
} else {
/* make sure the previous non-hole is mapped */
rlc = *rl;
rlc--;
if (((*rl)->lcn == LCN_HOLE)
&& cur_vcn
&& (rlc->vcn < 0)) {
/* make sure the run ahead of hole is mapped */
if (((*rl)->lcn == LCN_HOLE) && cur_vcn) {
if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1))
goto err_out;
}
@ -1777,6 +1797,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
BOOL wasnonresident = FALSE;
BOOL compressed;
BOOL updatemap;
BOOL mustupdate = FALSE;
ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count "
"0x%llx.\n", (long long)na->ni->mft_no, na->type,
@ -1931,8 +1952,17 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
* However, for compressed file, we need the full compression
* block, which may be split in several extents.
*/
if (NAttrDataAppending(na)) {
VCN block_begin = pos >> vol->cluster_size_bits;
if (compressed && !NAttrDataAppending(na)) {
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
} else {
VCN block_begin;
if (NAttrDataAppending(na)
|| (pos < na->initialized_size))
block_begin = pos >> vol->cluster_size_bits;
else
block_begin = na->initialized_size >> vol->cluster_size_bits;
if (compressed)
block_begin &= -na->compression_block_clusters;
@ -1942,10 +1972,11 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto err_out;
if ((update_from == -1) || (block_begin < update_from))
update_from = block_begin;
} else
#endif
}
#else
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
#endif
/*
* For a compressed attribute, we must be sure there is an
* available entry, and, when reopening a compressed file,
@ -2124,6 +2155,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl,
&update_from))
goto err_out;
if (!compressed)
mustupdate = TRUE;
}
if (compressed) {
while (rl->length
@ -2224,6 +2257,7 @@ done:
*/
#if PARTIAL_RUNLIST_UPDATING
updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na);
if (mustupdate) updatemap = TRUE;
#else
updatemap = (compressed
? NAttrFullyMapped(na) != 0 : update_from != -1);