diff --git a/ChangeLog b/ChangeLog index 5a480dea..c2caa5d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -153,6 +153,11 @@ xx/xx/2005 - 2.0.0-WIP ntfs_is_collation_rule_supported. (Yura) - Port index.[ch] from kernel to library. New API's: ntfs_index_lookup, ntfs_index_ctx_{get,put}, ntfs_index_entry_mark_dirty. (Yura) + - Implement FILE_NAME attributes update in index during inode sync and + enable code that set/clean sparse bit. Also add new inode state bit + FileNameDirty to indicate that FILE_NAME attributes need update. + At least after attribute resize we leave absolutely consist + volume. (Yura) 04/09/2004 - 1.9.4 - Urgent bug fixes. diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h index 4b9c2c48..ddcff691 100644 --- a/include/ntfs/inode.h +++ b/include/ntfs/inode.h @@ -45,6 +45,8 @@ typedef enum { NI_Compressed, /* 1: Inode is compressed. */ NI_Encrypted, /* 1: Inode is encrypted. */ NI_Sparse, /* 1: Inode is sparse. */ + NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated + in the index. */ } ntfs_inode_state_bits; #define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) @@ -94,6 +96,17 @@ typedef enum { #define NInoSetSparse(ni) set_nino_flag(ni, Sparse) #define NInoClearSparse(ni) clear_nino_flag(ni, Sparse) +#define NInoFileNameDirty(ni) \ + test_nino_flag(ni, FileNameDirty) +#define NInoFileNameSetDirty(ni) \ + set_nino_flag(ni, FileNameDirty) +#define NInoFileNameClearDirty(ni) \ + clear_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndSetDirty(ni) \ + test_and_set_nino_flag(ni, FileNameDirty) +#define NInoFileNameTestAndClearDirty(ni) \ + test_and_clear_nino_flag(ni, FileNameDirty) + /* * The NTFS in-memory inode structure. It is just used as an extension to the * fields already provided in the VFS inode. @@ -126,6 +139,9 @@ struct _ntfs_inode { void *private_data; /* Temp: for directory handling */ int ref_count; + /* Belows fields needed to update indexes. They valid if != -1. */ + s64 data_size; + s64 allocated_size; }; extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); diff --git a/libntfs/attrib.c b/libntfs/attrib.c index a7414dac..cf963d6e 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -3976,12 +3976,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) * function updates sparse bit and allocates/frees space for compressed_size, * but doesn't change it, so caller should update it manually. * - * NOTE: Code that set/clean sparse bit currently disabled, because chkdsk - * complain if we don't update index entry's for inode to which we set sparse - * bit. But it doesn't if attribute's runlist has lcn holes and sparse bit - * isn't set. So until we have no API for updating index entries it's the best - * choice. - * * On success return 0 and on error return -1 with errno set to the error code. * The following error codes are defined: * ENOMEM - Not enough memory to complete operation. @@ -4064,7 +4058,6 @@ retry: goto put_err_out; } } -#if 0 /* If we in the first extent, then set/clean sparse bit. */ if (!a->lowest_vcn) { int sparse; @@ -4130,6 +4123,13 @@ 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); } if (!sparse && (a->flags & ATTR_IS_SPARSE) && !(a->flags & ATTR_IS_COMPRESSED)) { @@ -4144,9 +4144,15 @@ 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); } } -#endif /* Get the size for the rest of mapping pairs array. */ mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl, stop_vcn); @@ -4811,6 +4817,8 @@ put_err_out: */ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { + int ret; + if (!na || newsize < 0 || (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { Dprintf("%s(): Invalid aruments passed.\n", __FUNCTION__); @@ -4837,9 +4845,24 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) } if (NAttrNonResident(na)) { if (newsize > na->data_size) - return ntfs_non_resident_attr_expand(na, newsize); + ret = ntfs_non_resident_attr_expand(na, newsize); else - return ntfs_non_resident_attr_shrink(na, 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) { + NInoFileNameSetDirty(na->ni); + na->ni->data_size = na->data_size; + /* + * If attribute sparse or commpreseed then allocated size in + * index should be euqal 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; } - return ntfs_resident_attr_resize(na, newsize); + return ret; } diff --git a/libntfs/collate.c b/libntfs/collate.c index 8a778ae7..576930a8 100644 --- a/libntfs/collate.c +++ b/libntfs/collate.c @@ -79,7 +79,7 @@ static int ntfs_collate_file_name(ntfs_volume *vol, fn2 = (const FILE_NAME_ATTR *)data2; rc = ntfs_names_collate(fn1->file_name, fn1->file_name_length, fn2->file_name, fn2->file_name_length, - NTFS_COLLATION_ERROR, CASE_SENSITIVE, vol->upcase, + NTFS_COLLATION_ERROR, IGNORE_CASE, vol->upcase, vol->upcase_len); ntfs_debug("Done, returning %i.", rc); return rc; diff --git a/libntfs/index.c b/libntfs/index.c index 9c4919cb..14f8a5d0 100644 --- a/libntfs/index.c +++ b/libntfs/index.c @@ -26,6 +26,7 @@ #include "collate.h" #include "debug.h" #include "index.h" +#include "mst.h" /** * ntfs_index_ctx_get - allocate and initialize a new index context @@ -71,14 +72,15 @@ void ntfs_index_ctx_put(ntfs_index_context *ictx) ntfs_attr_put_search_ctx(ictx->actx); } else { /* Write out index block it it's dirty. */ - if (ictx->ia_dirty) - if (ntfs_attr_pwrite(ictx->ia_na, + if (ictx->ia_dirty) { + if (ntfs_attr_mst_pwrite(ictx->ia_na, ictx->ia_vcn << - ictx->ni->vol->cluster_size, + ictx->ni->vol->cluster_size, 1, ictx->block_size, ictx->ia) != - ictx->block_size) + 1) ntfs_error(, "Failed to write out " "index block."); + } /* Free resources. */ free(ictx->ia); ntfs_attr_close(ictx->ia_na); @@ -264,12 +266,13 @@ done: goto err_out; } descend_into_child_node: + ntfs_debug("Descend into node with VCN %lld.", vcn); /* Read index allocation block. */ - if (ntfs_attr_pread(na, vcn << vol->cluster_size_bits, ictx->block_size, - ia) != ictx->block_size) { + if (ntfs_attr_mst_pread(na, vcn << vol->cluster_size_bits, 1, + ictx->block_size, ia) != 1) { ntfs_error(, "Failed to read index allocation."); goto err_out; - } + } /* Catch multi sector transfer fixup errors. */ if (!ntfs_is_indx_record(ia->magic)) { ntfs_error(sb, "Index record with vcn 0x%llx is corrupt. " @@ -392,3 +395,4 @@ idx_err_out: ntfs_error(sb, "Corrupt index. Aborting lookup."); goto err_out; } + diff --git a/libntfs/inode.c b/libntfs/inode.c index 487eccf7..49b64320 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -36,6 +36,8 @@ #include "attrlist.h" #include "runlist.h" #include "lcnalloc.h" +#include "index.h" +#include "dir.h" /** * Internal: @@ -123,6 +125,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) goto err_out; ni->mft_no = MREF(mref); + ni->data_size = -1; + ni->allocated_size = -1; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) goto err_out; @@ -453,6 +457,113 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) { return 0; } +/** + * ntfs_inode_sync_standard_information - update FILE_NAME attribute's + * @ni: ntfs inode to update FILE_NAME attribute's + * + * Update all FILE_NAME attribute's for inode @ni in the index. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +static int ntfs_inode_sync_file_name(ntfs_inode *ni) { + ntfs_attr_search_ctx *ctx = NULL; + ntfs_index_context *ictx; + ntfs_inode *index_ni; + FILE_NAME_ATTR *fn; + int err = 0; + + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + err = errno; + Dprintf("%s(): Failed to get attribute search context.\n", + __FUNCTION__); + goto err_out; + } + /* Walk through all FILE_NAME attributes and update them. */ + while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (MREF_LE(fn->parent_directory) == ni->mft_no) { + /* + * WARNING: We cheater here and obtain 2 attribute + * search contextes for one inode (first we obtained + * above, second will be obtained inside + * ntfs_index_lookup), it's acceptable for library, + * but will lock kernel. + */ + index_ni = ni; + } else + index_ni = ntfs_inode_open(ni->vol, + le64_to_cpu(fn->parent_directory)); + if (!index_ni) { + if (!err) + err = errno; + Dprintf("%s(): Failed to open inode with index.\n", + __FUNCTION__); + continue; + } + ictx = ntfs_index_ctx_get(index_ni, I30, 4); + if (!ictx) { + if (!err) + err = errno; + Dprintf("%s(): Failed to get index context.\n", + __FUNCTION__); + ntfs_inode_close(index_ni); + continue; + } + if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { + if (!err) { + if (errno == ENOENT) + err = EIO; + else + err = errno; + } + Dprintf("%s(): Index lookup failed.\n", __FUNCTION__); + ntfs_index_ctx_put(ictx); + ntfs_inode_close(index_ni); + continue; + } + /* Update flags and file size. */ + fn = (FILE_NAME_ATTR *)ictx->data; + if (NInoCompressed(ni)) + fn->file_attributes |= FILE_ATTR_COMPRESSED; + else + fn->file_attributes &= ~FILE_ATTR_COMPRESSED; + if (NInoEncrypted(ni)) + fn->file_attributes |= FILE_ATTR_ENCRYPTED; + else + fn->file_attributes &= ~FILE_ATTR_ENCRYPTED; + if (NInoSparse(ni)) + fn->file_attributes |= FILE_ATTR_SPARSE_FILE; + else + fn->file_attributes &= ~FILE_ATTR_SPARSE_FILE; + if (ni->allocated_size != -1) + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + if (ni->data_size != -1) + fn->data_size = cpu_to_sle64(ni->data_size); + ntfs_index_entry_mark_dirty(ictx); + ntfs_index_ctx_put(ictx); + ntfs_inode_close(index_ni); + } + /* Check for real error occured. */ + if (errno != ENOENT) { + err = errno; + Dprintf("%s(): Attribute lookup failed.\n", __FUNCTION__); + goto err_out; + } + ntfs_attr_put_search_ctx(ctx); + if (err) { + errno = err; + return -1; + } + return 0; +err_out: + if (ctx) + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + /** * ntfs_inode_sync - write the inode (and its dirty extents) to disk * @ni: ntfs inode to write @@ -480,7 +591,7 @@ int ntfs_inode_sync(ntfs_inode *ni) errno = EINVAL; return -1; } - + Dprintf("%s(): Entering for inode 0x%llx.\n", __FUNCTION__, (long long) ni->mft_no); @@ -495,6 +606,19 @@ int ntfs_inode_sync(ntfs_inode *ni) __FUNCTION__); } + /* Update FILE_NAME's in the index. */ + if (ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && + ntfs_inode_sync_file_name(ni)) { + if (!err || errno == EIO) { + err = errno; + if (err != EIO) + err = EBUSY; + } + Dprintf("%s(): Failed to sync FILE_NAME attributes.\n", + __FUNCTION__); + NInoFileNameSetDirty(ni); + } + /* Write out attribute list from cache to disk. */ if (ni->nr_extents != -1 && NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { @@ -522,7 +646,6 @@ int ntfs_inode_sync(ntfs_inode *ni) Dprintf("%s(): Attribute list " "sync failed (write failed).\n", __FUNCTION__); - } NInoAttrListSetDirty(ni); } @@ -574,8 +697,6 @@ int ntfs_inode_sync(ntfs_inode *ni) } } - // TODO: Update FILE_NAME attribute in the index. - if (!err) return 0; errno = err; diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index f459c237..32afa639 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -273,7 +273,7 @@ int main (int argc, char *argv[]) return 1; utils_set_locale(); - + /* Set SIGINT handler. */ if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("Failed to set SIGINT handler"); @@ -288,7 +288,7 @@ int main (int argc, char *argv[]) perror("ERROR: couldn't mount volume"); return 1; } - + if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) goto umount; @@ -382,7 +382,7 @@ int main (int argc, char *argv[]) goto close_dst; } } - + Vprintf("Old file size: %lld\n", na->data_size); if (na->data_size != new_size) { if (ntfs_attr_truncate(na, new_size)) { diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 7f0cd8ef..4804d66e 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -1390,9 +1390,12 @@ static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) printf("\tIndex name:\t\t '%s'\n",index_name); free(index_name); } else { - /* an error occured, errno holds the reason - notify the user */ - fprintf(stderr, "ntfsinfo error: could not parse index name: %s\n", - strerror(errno)); + /* + * An error occured, errno holds the reason - + * notify the user + */ + fprintf(stderr, "ntfsinfo error: could not parse " + "index name: %s\n", strerror(errno)); } } else { printf("\tIndex name:\t\t unnamed\n");