From b78d8955750e054aef58a2713800bcaba240d5d5 Mon Sep 17 00:00:00 2001 From: jpandre Date: Tue, 3 Nov 2009 14:35:53 +0000 Subject: [PATCH] Added indexing of reparse data into $Extend/$Reparse --- include/ntfs-3g/index.h | 1 + include/ntfs-3g/reparse.h | 2 + libntfs-3g/collate.c | 50 +++++- libntfs-3g/dir.c | 9 + libntfs-3g/index.c | 3 +- libntfs-3g/reparse.c | 336 ++++++++++++++++++++++++++++++++------ 6 files changed, 346 insertions(+), 55 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index 4c8ff51a..75e7b106 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -158,6 +158,7 @@ extern void ntfs_ih_filename_dump(INDEX_HEADER *ih); /* the following was added by JPA for use in security.c */ extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie); +extern int ntfs_index_rm(ntfs_index_context *icx); #endif /* _NTFS_INDEX_H */ diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 6aa45c26..91ede03e 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -34,4 +34,6 @@ int ntfs_set_ntfs_reparse_data(const char *path, const char *value, size_t size, int flags, ntfs_inode *ni); int ntfs_remove_ntfs_reparse_data(const char *path, ntfs_inode *ni); +int ntfs_delete_reparse_index(ntfs_inode *ni); + #endif /* REPARSE_H */ diff --git a/libntfs-3g/collate.c b/libntfs-3g/collate.c index 7fb85779..20e5db1e 100644 --- a/libntfs-3g/collate.c +++ b/libntfs-3g/collate.c @@ -43,7 +43,8 @@ BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) */ if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH) + && cr != COLLATION_NTOFS_SECURITY_HASH + && cr != COLLATION_NTOFS_ULONGS) return FALSE; return TRUE; } @@ -116,6 +117,47 @@ static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)), return rc; } +/** + * ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first + * + * Returns: -1, 0 or 1 depending of how the arrays compare + */ + +static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)), + const void *data1, const int data1_len, + const void *data2, const int data2_len) +{ + int rc; + int len; + const le32 *p1, *p2; + u32 d1, d2; + + ntfs_log_trace("Entering.\n"); + if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) { + ntfs_log_error("data1_len or data2_len not valid\n"); + return NTFS_COLLATION_ERROR; + } + p1 = (const le32*)data1; + p2 = (const le32*)data2; + len = data1_len; + do { + d1 = le32_to_cpup(p1); + p1++; + d2 = le32_to_cpup(p2); + p2++; + } while ((d1 == d2) && ((len -= 4) > 0)); + if (d1 < d2) + rc = -1; + else { + if (d1 == d2) + rc = 0; + else + rc = 1; + } + ntfs_log_trace("Done, returning %i.\n", rc); + return rc; +} + /** * ntfs_collate_ntofs_security_hash - Which of two security descriptors * should be listed first @@ -212,7 +254,7 @@ static ntfs_collate_func_t ntfs_do_collate0x1[4] = { ntfs_collate_ntofs_ulong, NULL/*ntfs_collate_ntofs_sid*/, ntfs_collate_ntofs_security_hash, - NULL/*ntfs_collate_ntofs_ulongs*/, + ntfs_collate_ntofs_ulongs }; /** @@ -249,10 +291,12 @@ int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error * for everything else. * JPA added COLLATION_NTOFS_SECURITY_HASH + * JPA added COLLATION_NTOFS_ULONGS */ if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH) + && cr != COLLATION_NTOFS_SECURITY_HASH + && cr != COLLATION_NTOFS_ULONGS) goto err; i = le32_to_cpu(cr); if (i < 0) diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index de07f8ef..297f10f1 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1647,6 +1647,15 @@ search: ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); goto ok; } + if (ntfs_delete_reparse_index(ni)) { + /* + * Failed to remove the reparse index : proceed anyway + * This is not a critical error, the entry is useless + * because of sequence_number, and stopping file deletion + * would be much worse as the file is not referenced now. + */ + err = errno; + } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index cfcbe6c3..768f0593 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1771,7 +1771,8 @@ out: * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_index_rm(ntfs_index_context *icx) +/*static JPA*/ +int ntfs_index_rm(ntfs_index_context *icx) { INDEX_HEADER *ih; int err, ret = STATUS_OK; diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index cfaffcfe..115429ed 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -95,6 +95,12 @@ struct INODE_STACK { ntfs_inode *ni; } ; +struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ + INDEX_ENTRY_HEADER header; + REPARSE_INDEX_KEY key; + le32 filling; +} ; + static const ntfschar dir_junction_head[] = { const_cpu_to_le16('\\'), const_cpu_to_le16('?'), @@ -116,6 +122,9 @@ static const ntfschar vol_junction_head[] = { const_cpu_to_le16('{'), } ; +static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('R') }; + static const char mappingdir[] = ".NTFS-3G/"; /* @@ -896,6 +905,208 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni) return (possible); } +/* + * Set the index for new reparse data + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr, + le32 reparse_tag) +{ + struct REPARSE_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(REPARSE_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16(0); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct REPARSE_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(REPARSE_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + indx.key.reparse_tag = reparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&indx.key.file_id, &file_id, 8); + indx.filling = const_cpu_to_le32(0); + ntfs_index_ctx_reinit(xr); + return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx)); +} + +/* + * Remove a reparse data index entry if attribute present + * + * Returns the size of existing reparse data + * (the existing reparse tag is returned) + * -1 if failure, explained by errno + */ + +static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, + le32 *preparse_tag) +{ + REPARSE_INDEX_KEY key; + u64 file_id_cpu; + le64 file_id; + s64 size; + le16 seqn; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing reparse_tag */ + size = ntfs_attr_pread(na, 0, 4, preparse_tag); + if (size == 4) { + seqn = na->ni->mrec->sequence_number; + file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + key.reparse_tag = *preparse_tag; + /* danger on processors which require proper alignment ! */ + memcpy(&key.file_id, &file_id, 8); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr)) + ret = ntfs_index_rm(xr); + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +/* + * Open the $Extend/$Reparse file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_reparse_index(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_index_context *xr; + + ni = ntfs_pathname_to_inode(vol, NULL, "$Extend/$Reparse"); + if (ni) { + xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); + if (!xr) { + ntfs_inode_close(ni); + } + } else + xr = (ntfs_index_context*)NULL; + return (xr); +} + +/* + * Update the reparse data and index + * + * The reparse data attribute should have been created, and + * an existing index is expected if there is an existing value. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, + const char *value, size_t size) +{ + int res; + int written; + int oldsize; + ntfs_attr *na; + le32 reparse_tag; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* remove the existing reparse data */ + oldsize = remove_reparse_index(na,xr,&reparse_tag); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)size); + /* overwrite value if any */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)size, value); + if (written != (s64)size) { + ntfs_log_error("Failed to update " + "reparse data\n"); + errno = EIO; + res = -1; + } + } + if (!res + && set_reparse_index(ni,xr, + ((const REPARSE_POINT*)value)->reparse_tag) + && (oldsize > 0)) { + /* + * If cannot index, try to remove the reparse + * data and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index reparse data." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Delete a reparse index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_reparse_index(ntfs_inode *ni) +{ + ntfs_index_context *xr; + ntfs_inode *xrni; + ntfs_attr *na; + le32 reparse_tag; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); + if (na) { + /* + * read the existing reparse data (the tag is enough) + * and un-index it + */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr,&reparse_tag) < 0) + res = -1; + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } + ntfs_attr_close(na); + } + return (res); +} + /* * Get the ntfs reparse data into an extended attribute * @@ -943,64 +1154,56 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), ntfs_inode *ni) { int res; - int written; - ntfs_attr *na; + u8 dummy; + ntfs_inode *xrni; + ntfs_index_context *xr; res = 0; - if (ni && (value || !size)) { - if (!ntfs_attr_exist(ni,AT_REPARSE_POINT,AT_UNNAMED,0)) { - if (!(flags & XATTR_REPLACE)) { + if (ni && value && (size >= 4)) { + xr = open_reparse_index(ni->vol); + if (xr) { + if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, + AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { /* * no reparse data attribute : add one, * apparently, this does not feed the new value in * Note : NTFS version must be >= 3 */ - if (ni->vol->major_ver >= 3) { - res = ntfs_attr_add(ni,AT_REPARSE_POINT, - AT_UNNAMED,0,(u8*)NULL, - (s64)size); - if (!res) - ni->flags - |= FILE_ATTR_REPARSE_POINT; - NInoSetDirty(ni); + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, + AT_REPARSE_POINT, + AT_UNNAMED,0,&dummy, + (s64)0); + if (!res) + ni->flags |= + FILE_ATTR_REPARSE_POINT; + NInoSetDirty(ni); + } else { + errno = EOPNOTSUPP; + res = -1; + } } else { - errno = EOPNOTSUPP; + errno = ENODATA; res = -1; } } else { - errno = ENODATA; - res = -1; - } - } else { - if (flags & XATTR_CREATE) { - errno = EEXIST; - res = -1; - } - } - if (!res) { - /* - * open and update the existing reparse data - */ - na = ntfs_attr_open(ni, AT_REPARSE_POINT, - AT_UNNAMED,0); - if (na) { - /* resize attribute */ - res = ntfs_attr_truncate(na, (s64)size); - /* overwrite value if any */ - if (!res && value) { - written = (int)ntfs_attr_pwrite(na, - (s64)0, (s64)size, value); - if (written != (s64)size) { - ntfs_log_error("Failed to update " - "reparse data\n"); - errno = EIO; - res = -1; - } + if (flags & XATTR_CREATE) { + errno = EEXIST; + res = -1; } - ntfs_attr_close(na); - NInoSetDirty(ni); - } else - res = -1; + } + if (!res) { + /* update value and index */ + res = update_reparse_data(ni,xr,value,size); + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } else { + res = -1; } } else { errno = EINVAL; @@ -1021,6 +1224,9 @@ int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), int res; int olderrno; ntfs_attr *na; + ntfs_inode *xrni; + ntfs_index_context *xr; + le32 reparse_tag; res = 0; if (ni) { @@ -1030,10 +1236,39 @@ int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED,0); if (na) { - /* remove attribute */ - res = ntfs_attr_rm(na); - if (!res) - ni->flags &= ~FILE_ATTR_REPARSE_POINT; + /* first remove index (reparse data needed) */ + xr = open_reparse_index(ni->vol); + if (xr) { + if (remove_reparse_index(na,xr, + &reparse_tag) < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (!res) { + ni->flags &= + ~FILE_ATTR_REPARSE_POINT; + } else { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_reparse_index(ni, xr, + reparse_tag); + ntfs_log_error( + "Failed to remove reparse data." + " Possible corruption.\n"); + } + } + xrni = xr->ni; + ntfs_index_entry_mark_dirty(xr); + NInoSetDirty(xrni); + ntfs_index_ctx_put(xr); + ntfs_inode_close(xrni); + } olderrno = errno; ntfs_attr_close(na); /* avoid errno pollution */ @@ -1050,4 +1285,3 @@ int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), } return (res ? -1 : 0); } -