Fixed creating empty encrypted extended attributes

PERMISSION_HANDLING_BRANCH
Jean-Pierre André 2010-07-22 14:46:01 +02:00
parent 5533eb565f
commit 09df7eade5
5 changed files with 263 additions and 111 deletions

View File

@ -304,6 +304,7 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
const ATTR_TYPES type);
int ntfs_attr_make_non_resident(ntfs_attr *na,
ntfs_attr_search_ctx *ctx);
int ntfs_attr_force_non_resident(ntfs_attr *na);
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,

View File

@ -4681,6 +4681,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize);
* @newsize: new size (in bytes) to which to resize the attribute
*
* Change the size of a resident, open ntfs attribute @na to @newsize bytes.
* Can also be used to force an attribute non-resident. In this case, the
* size cannot be changed.
*
* On success return 0
* On error return values are:
@ -4691,7 +4693,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize);
* ERANGE - @newsize is not valid for the attribute type of @na.
* ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
*/
static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize,
BOOL force_non_resident)
{
ntfs_attr_search_ctx *ctx;
ntfs_volume *vol;
@ -4729,7 +4732,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
* attribute non-resident if the attribute type supports it. If it is
* smaller we can go ahead and attempt the resize.
*/
if (newsize < vol->mft_record_size) {
if ((newsize < vol->mft_record_size) && !force_non_resident) {
/* Perform the resize of the attribute record. */
if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
newsize))) {
@ -4769,6 +4772,21 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
if (!ntfs_attr_make_non_resident(na, ctx)) {
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
/*
* do not truncate when forcing non-resident, this
* could cause the attribute to be made resident again,
* so size changes are not allowed.
*/
if (force_non_resident) {
ret = 0;
if (newsize != na->data_size) {
ntfs_log_error("Cannot change size when"
" forcing non-resident\n");
errno = EIO;
ret = STATUS_ERROR;
}
return (ret);
}
/* Resize non-resident attribute */
return ntfs_attr_truncate(na, newsize);
} else if (errno != ENOSPC && errno != EPERM) {
@ -4817,7 +4835,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_inode_mark_dirty(tna->ni);
ntfs_attr_close(tna);
ntfs_attr_put_search_ctx(ctx);
return ntfs_resident_attr_resize(na, newsize);
return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
}
/* Check whether error occurred. */
if (errno != ENOENT) {
@ -4837,7 +4855,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_log_perror("Could not free space in MFT record");
return -1;
}
return ntfs_resident_attr_resize(na, newsize);
return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
}
/*
@ -4876,7 +4894,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_attr_put_search_ctx(ctx);
if (ntfs_inode_add_attrlist(ni))
return -1;
return ntfs_resident_attr_resize(na, newsize);
return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
}
/* Allocate new mft record. */
ni = ntfs_mft_record_alloc(vol, ni);
@ -4897,7 +4915,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_attr_put_search_ctx(ctx);
/* Try to perform resize once again. */
return ntfs_resident_attr_resize(na, newsize);
return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
resize_done:
/*
@ -4918,11 +4936,39 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize)
int ret;
ntfs_log_enter("Entering\n");
ret = ntfs_resident_attr_resize_i(na, newsize);
ret = ntfs_resident_attr_resize_i(na, newsize, FALSE);
ntfs_log_leave("\n");
return ret;
}
/*
* Force an attribute to be made non-resident without
* changing its size.
*
* This is particularly needed when the attribute has no data,
* as the non-resident variant requires more space in the MFT
* record, and may imply expelling some other attribute.
*
* As a consequence the existing ntfs_attr_search_ctx's have to
* be closed or reinitialized.
*
* returns 0 if successful,
* < 0 if failed, with errno telling why
*/
int ntfs_attr_force_non_resident(ntfs_attr *na)
{
int res;
res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE);
if (!res && !NAttrNonResident(na)) {
res = -1;
errno = EIO;
ntfs_log_error("Failed to force non-resident\n");
}
return (res);
}
/**
* ntfs_attr_make_resident - convert a non-resident to a resident attribute
* @na: open ntfs attribute to make resident

View File

@ -4,7 +4,7 @@
* This module is part of ntfs-3g library
*
* Copyright (c) 2009 Martin Bene
* Copyright (c) 2009 Jean-Pierre Andre
* Copyright (c) 2009-2010 Jean-Pierre Andre
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@ -120,6 +120,93 @@ int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size)
return (attr_size ? (int)attr_size : -errno);
}
/*
* Fix all encrypted AT_DATA attributes of an inode
*
* The fix may require making an attribute non resident, which
* requires more space in the MFT record, and may cause some
* attribute to be expelled and the full record to be reorganized.
* When this happens, the search for data attributes has to be
* reinitialized.
*
* Returns zero if successful.
* -1 if there is a problem.
*/
static int fixup_loop(ntfs_inode *ni)
{
ntfs_attr_search_ctx *ctx;
ntfs_attr *na;
ATTR_RECORD *a;
BOOL restart;
BOOL first;
int cnt;
int maxcnt;
int res = 0;
maxcnt = 0;
do {
restart = FALSE;
ctx = ntfs_attr_get_search_ctx(ni, NULL);
if (!ctx) {
ntfs_log_error("Failed to get ctx for efs\n");
res = -1;
}
cnt = 0;
while (!restart && !res
&& !ntfs_attr_lookup(AT_DATA, NULL, 0,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
cnt++;
a = ctx->attr;
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
a->name_length);
if (!na) {
ntfs_log_error("can't open DATA Attribute\n");
res = -1;
}
if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) {
if (!NAttrNonResident(na)
&& ntfs_attr_make_non_resident(na, ctx)) {
/*
* ntfs_attr_make_non_resident fails if there
* is not enough space in the MFT record.
* When this happens, force making non-resident
* so that some other attribute is expelled.
*/
if (ntfs_attr_force_non_resident(na)) {
res = -1;
} else {
/* make sure there is some progress */
if (cnt <= maxcnt) {
errno = EIO;
ntfs_log_error("Multiple failure"
" making non resident\n");
res = -1;
} else {
ntfs_attr_put_search_ctx(ctx);
ctx = (ntfs_attr_search_ctx*)NULL;
restart = TRUE;
maxcnt = cnt;
}
}
}
if (!restart && !res
&& ntfs_efs_fixup_attribute(ctx, na)) {
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
res = -1;
}
}
if (na)
ntfs_attr_close(na);
}
first = FALSE;
} while (restart && !res);
if (ctx)
ntfs_attr_put_search_ctx(ctx);
return (res);
}
/*
* Set the efs data from an extended attribute
* Warning : the new data is not checked
@ -134,7 +221,6 @@ int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
int written;
ntfs_attr *na;
const EFS_ATTR_HEADER *info_header;
ntfs_attr_search_ctx *ctx;
res = 0;
if (ni && value && size) {
@ -210,20 +296,8 @@ int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
/* iterate over AT_DATA attributes */
/* set encrypted flag, truncate attribute to match padding bytes */
ctx = ntfs_attr_get_search_ctx(ni, NULL);
if (!ctx) {
ntfs_log_error("Failed to get ctx for efs\n");
return (-1);
}
while (!ntfs_attr_lookup(AT_DATA, NULL, 0,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
if (ntfs_efs_fixup_attribute(ctx, NULL)) {
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
ntfs_attr_put_search_ctx(ctx);
return(-1);
}
}
ntfs_attr_put_search_ctx(ctx);
if (fixup_loop(ni))
return -1;
}
ni->flags |= FILE_ATTR_ENCRYPTED;
NInoSetDirty(ni);
@ -250,15 +324,14 @@ int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
{
u64 newsize;
u64 oldsize;
le16 appended_bytes;
u16 padding_length;
ATTR_RECORD *a;
ntfs_inode *ni;
BOOL close_na = FALSE;
BOOL close_ctx = FALSE;
if (!ctx && !na) {
ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n");
if (!na) {
ntfs_log_error("no na specified for efs_fixup_attribute\n");
goto err_out;
}
if (!ctx) {
@ -267,55 +340,79 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
ntfs_log_error("Failed to get ctx for efs\n");
goto err_out;
}
close_ctx=TRUE;
close_ctx = TRUE;
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
goto err_out;
}
}
a = ctx->attr;
if (!na) {
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
a->name_length);
if (!na) {
ntfs_log_error("can't open DATA Attribute\n");
return (-1);
} else {
if (!NAttrNonResident(na)) {
ntfs_log_error("Cannot make non resident"
" when a context has been allocated\n");
goto err_out;
}
close_na = TRUE;
}
/* make sure size is valid for a raw encrypted stream */
if ((na->data_size & 511) != 2) {
ntfs_log_error("Bad raw encrypted stream\n");
goto err_out;
}
/* read padding length from last two bytes of attribute */
if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) {
ntfs_log_error("Error reading padding length\n");
goto err_out;
}
padding_length = le16_to_cpu(appended_bytes);
if (padding_length > 511 || padding_length > na->data_size-2) {
errno = EINVAL;
ntfs_log_error("invalid padding length %d for data_size %lld\n",
padding_length, (long long)na->data_size);
goto err_out;
}
newsize = na->data_size - padding_length - 2;
/* truncate attribute to possibly free clusters allocated
for the last two bytes */
if (ntfs_attr_truncate(na, na->data_size-2)) {
ntfs_log_error("Error truncating attribute\n");
goto err_out;
}
/* Encrypted AT_DATA Attributes MUST be non-resident */
/* no extra bytes are added to void attributes */
oldsize = na->data_size;
if (oldsize) {
/* make sure size is valid for a raw encrypted stream */
if ((oldsize & 511) != 2) {
ntfs_log_error("Bad raw encrypted stream\n");
goto err_out;
}
/* read padding length from last two bytes of attribute */
if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) {
ntfs_log_error("Error reading padding length\n");
goto err_out;
}
padding_length = le16_to_cpu(appended_bytes);
if (padding_length > 511 || padding_length > na->data_size-2) {
errno = EINVAL;
ntfs_log_error("invalid padding length %d for data_size %lld\n",
padding_length, (long long)oldsize);
goto err_out;
}
newsize = oldsize - padding_length - 2;
/*
* truncate attribute to possibly free clusters allocated
* for the last two bytes, but do not truncate to new size
* to avoid losing useful data
*/
if (ntfs_attr_truncate(na, oldsize - 2)) {
ntfs_log_error("Error truncating attribute\n");
goto err_out;
}
} else
newsize = 0;
/*
* Encrypted AT_DATA Attributes MUST be non-resident
* This has to be done after the attribute is resized, as
* resizing down to zero may cause the attribute to be made
* resident.
*/
if (!NAttrNonResident(na)
&& ntfs_attr_make_non_resident(na, ctx)) {
ntfs_log_error("Error making DATA attribute non-resident\n");
goto err_out;
&& ntfs_attr_make_non_resident(na, ctx)) {
if (!close_ctx
|| ntfs_attr_force_non_resident(na)) {
ntfs_log_error("Error making DATA attribute non-resident\n");
goto err_out;
} else {
/*
* must reinitialize context after forcing
* non-resident. We need a context for updating
* the state, and at this point, we are sure
* the context is not used elsewhere.
*/
ntfs_attr_reinit_search_ctx(ctx);
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
goto err_out;
}
}
}
ni = na->ni;
if (!na->name_len) {
@ -324,8 +421,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
}
NInoSetDirty(ni);
NInoFileNameSetDirty(ni);
if (close_na)
ntfs_attr_close(na);
ctx->attr->data_size = cpu_to_le64(newsize);
if (le64_to_cpu(ctx->attr->initialized_size) > newsize)
@ -336,8 +431,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
return (0);
err_out:
if (close_na && na)
ntfs_attr_close(na);
if (close_ctx && ctx)
ntfs_attr_put_search_ctx(ctx);
return (-1);

View File

@ -695,7 +695,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx,
* encrypted files to include padding required for decryption
* also include 2 bytes for padding info
*/
if (ctx->efs_raw && ni->flags & FILE_ATTR_ENCRYPTED)
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED)
&& ni->data_size)
stbuf->st_size = ((ni->data_size + 511) & ~511) + 2;
#endif /* HAVE_SETXATTR */
/*
@ -1325,8 +1327,10 @@ static void ntfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size,
max_read = na->data_size;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
/* limit reads at next 512 byte boundary for encrypted attributes */
if (ctx->efs_raw && (na->data_flags & ATTR_IS_ENCRYPTED) &&
NAttrNonResident(na)) {
if (ctx->efs_raw
&& max_read
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na)) {
max_read = ((na->data_size+511) & ~511) + 2;
}
#endif /* HAVE_SETXATTR */
@ -3069,10 +3073,11 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
goto exit;
}
rsize = na->data_size;
if (ctx->efs_raw &&
(na->data_flags & ATTR_IS_ENCRYPTED) &&
NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511)+2;
if (ctx->efs_raw
&& rsize
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511) + 2;
if (size) {
if (size >= (size_t)rsize) {
value = (char*)ntfs_malloc(rsize);
@ -3311,6 +3316,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
}
}
total = 0;
res = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
@ -3318,20 +3324,20 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
if (total != size)
res = -errno;
else
if (!(res = ntfs_attr_pclose(na)))
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED))
res = ntfs_efs_fixup_attribute(NULL,
na);
if (total && !(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
if ((total != size) || ntfs_attr_pclose(na))
res = -errno;
else {
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED)) {
if (ntfs_efs_fixup_attribute(NULL,na))
res = -errno;
}
} else
res = 0;
}
if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
exit:
if (na)
ntfs_attr_close(na);

View File

@ -768,7 +768,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
* encrypted files to include padding required for decryption
* also include 2 bytes for padding info
*/
if (ctx->efs_raw && ni->flags & FILE_ATTR_ENCRYPTED)
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED)
&& ni->data_size)
stbuf->st_size = ((ni->data_size + 511) & ~511) + 2;
#endif /* HAVE_SETXATTR */
/*
@ -1230,8 +1232,10 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size,
max_read = na->data_size;
#ifdef HAVE_SETXATTR /* extended attributes interface required */
/* limit reads at next 512 byte boundary for encrypted attributes */
if (ctx->efs_raw && (na->data_flags & ATTR_IS_ENCRYPTED) &&
NAttrNonResident(na)) {
if (ctx->efs_raw
&& max_read
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na)) {
max_read = ((na->data_size+511) & ~511) + 2;
}
#endif /* HAVE_SETXATTR */
@ -2984,10 +2988,11 @@ static int ntfs_fuse_getxattr(const char *path, const char *name,
goto exit;
}
rsize = na->data_size;
if (ctx->efs_raw &&
(na->data_flags & ATTR_IS_ENCRYPTED) &&
NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511)+2;
if (ctx->efs_raw
&& rsize
&& (na->data_flags & ATTR_IS_ENCRYPTED)
&& NAttrNonResident(na))
rsize = ((na->data_size + 511) & ~511) + 2;
if (size) {
if (size >= (size_t)rsize) {
res = ntfs_attr_pread(na, 0, rsize, value);
@ -3235,6 +3240,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
}
}
total = 0;
res = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
@ -3242,20 +3248,20 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
if (total != size)
res = -errno;
else
if (!(res = ntfs_attr_pclose(na)))
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED))
res = ntfs_efs_fixup_attribute(NULL,
na);
if (total && !(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
if ((total != size) || ntfs_attr_pclose(na))
res = -errno;
else {
if (ctx->efs_raw
&& (ni->flags & FILE_ATTR_ENCRYPTED)) {
if (ntfs_efs_fixup_attribute(NULL,na))
res = -errno;
}
} else
res = 0;
}
if (!res && !(ni->flags & FILE_ATTR_ARCHIVE)) {
set_archive(ni);
NInoFileNameSetDirty(ni);
}
exit:
if (na)
ntfs_attr_close(na);