Added indexing of reparse data into $Extend/$Reparse

This commit is contained in:
jpandre 2009-11-03 14:35:53 +00:00
parent ad69474a5a
commit b78d895575
6 changed files with 346 additions and 55 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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)

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}