diff --git a/include/ntfs/attrib.h b/include/ntfs/attrib.h index 0597f493..12048a93 100644 --- a/include/ntfs/attrib.h +++ b/include/ntfs/attrib.h @@ -295,6 +295,10 @@ extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, ATTR_FLAGS flags); extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx); +extern ntfs_attr *ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, s64 size); +extern int ntfs_attr_rm(ntfs_attr *na); + extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, @@ -307,11 +311,6 @@ extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na); extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); -extern ntfs_attr *ntfs_inode_add_attr(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, s64 size); - -extern int ntfs_inode_rm_attr(ntfs_attr *na); - // FIXME / TODO: Above here the file is cleaned up. (AIA) /** * get_attribute_value_length - return the length of the value of an attribute diff --git a/libntfs/attrib.c b/libntfs/attrib.c index 61be9484..daa43067 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -2854,6 +2854,257 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { return 0; } +/** + * ntfs_attr_add - add attribute to inode + * @ni: opened ntfs inode to which add attribute + * @type: type of the new attribute + * @name: name in unicode of the new attribute + * @name_len: name length in unicode charcters of the new attribute + * @size: size of the new attribute + * + * If inode haven't got enogh space to add attribute, add attribute to one of it + * extents, if no extents present or no one of them have enough space, than + * allocate new extent and add attribute to it. + * + * If on one of this steps attribute list is needed but not present, than it is + * added transparently to caller. So, this function should not be called with + * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call + * ntfs_inode_add_attrlist instead. + * + * On success return opened new ntfs attribute. On error return NULL with errno + * set to the error code. + */ +ntfs_attr *ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, + ntfschar *name, u8 name_len, s64 size) +{ + u32 attr_rec_size; + int err, i, offset; + ntfs_inode *attr_ni; + ntfs_attr *na; + + if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { + Dprintf("%s(): Invalid arguments passed.\n", __FUNCTION__); + errno = EINVAL; + return NULL; + } + + Dprintf("%s(): Entering for inode 0x%llx, attr %x, size %lld.\n", + __FUNCTION__, (long long) ni->mft_no, type, size); + + if (ni->nr_extents == -1) + ni = ni->base_ni; + + /* Validate attribute type. */ + if (!ntfs_attr_find_in_attrdef(ni->vol, type)) { + if (errno == ENOENT) { + Dprintf("%s(): Invalid attribute type.\n", + __FUNCTION__); + errno = EINVAL; + return NULL; + } else { + err = errno; + Dprintf("%s(): ntfs_attr_find_in_attrdef failed.\n", + __FUNCTION__); + errno = err; + return NULL; + } + } + + /* + * Determine resident or not will be new attribute. We add 8 to size in + * non resident case for mapping pairs. + */ + if (ntfs_attr_can_be_resident(ni->vol, type)) { + if (errno != EPERM) { + err = errno; + Dprintf("%s(): ntfs_attr_can_be resident failed.\n", + __FUNCTION__); + goto err_out; + } + /* Attribute can't be resident. */ + attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; + } else { + /* Attribute can be resident. */ + attr_rec_size = offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7); + /* Check whether attribute will fit into the MFT record. */ + if (size + attr_rec_size >= ni->vol->mft_record_size) + /* Will not fit, make it non resident. */ + attr_rec_size = offsetof(ATTR_RECORD, + non_resident_end) + ((name_len * + sizeof(ntfschar) + 7) & ~7) + 8; + } + + if (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { + attr_ni = ni; + goto add_attr_record; + } + + /* Try to add to extent inodes. */ + if (ntfs_inode_attach_all_extents(ni)) { + err = errno; + Dprintf("%s(): Failed to attach all extents to inode.\n", + __FUNCTION__); + goto err_out; + } + for (i = 0; i < ni->nr_extents; i++) { + attr_ni = ni->extent_nis[i]; + if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - + le32_to_cpu(attr_ni->mrec->bytes_in_use) >= + attr_rec_size) + goto add_attr_record; + } + + /* There is no extent that contain enough space for new attribute. */ + if (!NInoAttrList(ni)) { + /* Add attribute list not present, add it and retry. */ + if (ntfs_inode_add_attrlist(ni)) { + err = errno; + Dprintf("%s(): Failed to add attribute list.\n", + __FUNCTION__); + goto err_out; + } + return ntfs_attr_add(ni, type, name, name_len, size); + } + /* Allocate new extent. */ + attr_ni = ntfs_mft_record_alloc(ni->vol, ni); + if (!attr_ni) { + err = errno; + Dprintf("%s(): Failed to allocate extent record.\n", + __FUNCTION__); + goto err_out; + } + +add_attr_record: + if (attr_rec_size == offsetof(ATTR_RECORD, resident_end) + + ((name_len * sizeof(ntfschar) + 7) & ~7)) { + /* Add resident attribute. */ + offset = ntfs_resident_attr_record_add(attr_ni, type, name, + name_len, 0); + if (offset < 0) { + err = errno; + Dprintf("%s(): Failed to add resident attribute.\n", + __FUNCTION__); + goto free_err_out; + } + } else { + /* Add non resident attribute. */ + offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, + name_len, 0, 8, 0); + if (offset < 0) { + err = errno; + Dprintf("%s(): Failed to add non resident attribute.\n", + __FUNCTION__); + goto free_err_out; + } + } + + /* Open new attribute and resize it. */ + na = ntfs_attr_open(ni, type, name, name_len); + if (!na) { + err = errno; + Dprintf("%s(): Failed to open just added attribute.\n", + __FUNCTION__); + goto rm_attr_err_out; + } + if (!size) + return na; + if (ntfs_attr_truncate(na, size)) { + err = errno; + Dprintf("%s(): Failed to resize just added attribute.\n", + __FUNCTION__); + if (ntfs_attr_rm(na)) { + Dprintf("%s(): Failed to remove just added attribute. " + "Probably leaving inconsist metadata.\n", + __FUNCTION__); + } + goto err_out; + } + /* Done !*/ + return na; + +rm_attr_err_out: + /* Remove just added attribute. */ + if (ntfs_attr_record_resize(attr_ni->mrec, + (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) { + Dprintf("%s(): Failed to remove just added attribute.\n", + __FUNCTION__); + } +free_err_out: + /* Free MFT record, if it isn't contain attributes. */ + if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - + le32_to_cpu(attr_ni->mrec->attrs_offset) == 8) { + if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) { + Dprintf("%s(): Failed to free MFT record. Leaving " + "inconsist metadata.\n", __FUNCTION__); + } + } +err_out: + errno = err; + return NULL; +} + +/** + * ntfs_attr_rm - remove attribute from ntfs inode + * @na: opened ntfs attribute to delete + * + * Remove attribute and all it's extents from ntfs inode. If attribute was non + * resident also free all clusters allocated by attribute. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_attr_rm(ntfs_attr *na) +{ + ntfs_attr_search_ctx *ctx; + int ret = 0; + + if (!na) { + Dprintf("%s(): Invalid arguments passed.\n", __FUNCTION__); + errno = EINVAL; + return -1; + } + + Dprintf("%s(): Entering for inode 0x%llx, attr 0x%x.\n", + __FUNCTION__, (long long) na->ni->mft_no, na->type); + + /* Free cluster allocation. */ + if (NAttrNonResident(na)) { + if (ntfs_attr_map_whole_runlist(na)) + return -1; + if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { + Dprintf("%s(): Failed to free cluster allocation. " + "Leaving inconsist metadata.\n", __FUNCTION__); + ret = -1; + } + } + + /* Search for attribute extents and remove them all. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + while (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + if (ntfs_attr_record_rm(ctx)) { + Dprintf("%s(): Failed to remove attribute extent. " + "Leaving inconsist metadata.\n", __FUNCTION__); + ret = -1; + } + ntfs_attr_reinit_search_ctx(ctx); + } + if (errno != ENOENT) { + Dprintf("%s(): Attribute lookup failed. Probably leaving " + "inconsist metadata.\n", __FUNCTION__); + ret = -1; + } + + /* Throw away now non-exist attribute. */ + ntfs_attr_close(na); + /* Done. */ + return ret; +} + /** * ntfs_attr_record_resize - resize an attribute record * @m: mft record containing attribute record @@ -4588,254 +4839,3 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) } return ntfs_resident_attr_resize(na, newsize); } - -/** - * ntfs_inode_add_attr - add attribute to inode - * @ni: opened ntfs inode to which add attribute - * @type: type of the new attribute - * @name: name in unicode of the new attribute - * @name_len: name length in unicode charcters of the new attribute - * @size: size of the new attribute - * - * If inode haven't got enogh space to add attribute, add attribute to one of it - * extents, if no extents present or no one of them have enough space, than - * allocate new extent and add attribute to it. - * - * If on one of this steps attribute list is needed but not present, than it is - * added transparently to caller. So, this function should not be called with - * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call - * ntfs_inode_add_attrlist instead. - * - * On success return opened new ntfs attribute. On error return NULL with errno - * set to the error code. - */ -ntfs_attr *ntfs_inode_add_attr(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, s64 size) -{ - u32 attr_rec_size; - int err, i, offset; - ntfs_inode *attr_ni; - ntfs_attr *na; - - if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { - Dprintf("%s(): Invalid arguments passed.\n", __FUNCTION__); - errno = EINVAL; - return NULL; - } - - Dprintf("%s(): Entering for inode 0x%llx, attr %x, size %lld.\n", - __FUNCTION__, (long long) ni->mft_no, type, size); - - if (ni->nr_extents == -1) - ni = ni->base_ni; - - /* Validate attribute type. */ - if (!ntfs_attr_find_in_attrdef(ni->vol, type)) { - if (errno == ENOENT) { - Dprintf("%s(): Invalid attribute type.\n", - __FUNCTION__); - errno = EINVAL; - return NULL; - } else { - err = errno; - Dprintf("%s(): ntfs_attr_find_in_attrdef failed.\n", - __FUNCTION__); - errno = err; - return NULL; - } - } - - /* - * Determine resident or not will be new attribute. We add 8 to size in - * non resident case for mapping pairs. - */ - if (ntfs_attr_can_be_resident(ni->vol, type)) { - if (errno != EPERM) { - err = errno; - Dprintf("%s(): ntfs_attr_can_be resident failed.\n", - __FUNCTION__); - goto err_out; - } - /* Attribute can't be resident. */ - attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; - } else { - /* Attribute can be resident. */ - attr_rec_size = offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7); - /* Check whether attribute will fit into the MFT record. */ - if (size + attr_rec_size >= ni->vol->mft_record_size) - /* Will not fit, make it non resident. */ - attr_rec_size = offsetof(ATTR_RECORD, - non_resident_end) + ((name_len * - sizeof(ntfschar) + 7) & ~7) + 8; - } - - if (le32_to_cpu(ni->mrec->bytes_allocated) - - le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { - attr_ni = ni; - goto add_attr_record; - } - - /* Try to add to extent inodes. */ - if (ntfs_inode_attach_all_extents(ni)) { - err = errno; - Dprintf("%s(): Failed to attach all extents to inode.\n", - __FUNCTION__); - goto err_out; - } - for (i = 0; i < ni->nr_extents; i++) { - attr_ni = ni->extent_nis[i]; - if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - - le32_to_cpu(attr_ni->mrec->bytes_in_use) >= - attr_rec_size) - goto add_attr_record; - } - - /* There is no extent that contain enough space for new attribute. */ - if (!NInoAttrList(ni)) { - /* Add attribute list not present, add it and retry. */ - if (ntfs_inode_add_attrlist(ni)) { - err = errno; - Dprintf("%s(): Failed to add attribute list.\n", - __FUNCTION__); - goto err_out; - } - return ntfs_inode_add_attr(ni, type, name, name_len, size); - } - /* Allocate new extent. */ - attr_ni = ntfs_mft_record_alloc(ni->vol, ni); - if (!attr_ni) { - err = errno; - Dprintf("%s(): Failed to allocate extent record.\n", - __FUNCTION__); - goto err_out; - } - -add_attr_record: - if (attr_rec_size == offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7)) { - /* Add resident attribute. */ - offset = ntfs_resident_attr_record_add(attr_ni, type, name, - name_len, 0); - if (offset < 0) { - err = errno; - Dprintf("%s(): Failed to add resident attribute.\n", - __FUNCTION__); - goto free_err_out; - } - } else { - /* Add non resident attribute. */ - offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, - name_len, 0, 8, 0); - if (offset < 0) { - err = errno; - Dprintf("%s(): Failed to add non resident attribute.\n", - __FUNCTION__); - goto free_err_out; - } - } - - /* Open new attribute and resize it. */ - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - err = errno; - Dprintf("%s(): Failed to open just added attribute.\n", - __FUNCTION__); - goto rm_attr_err_out; - } - if (!size) - return na; - if (ntfs_attr_truncate(na, size)) { - err = errno; - Dprintf("%s(): Failed to resize just added attribute.\n", - __FUNCTION__); - if (ntfs_inode_rm_attr(na)) { - Dprintf("%s(): Failed to remove just added attribute. " - "Probably leaving inconsist metadata.\n", - __FUNCTION__); - } - goto err_out; - } - /* Done !*/ - return na; - -rm_attr_err_out: - /* Remove just added attribute. */ - if (ntfs_attr_record_resize(attr_ni->mrec, - (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) { - Dprintf("%s(): Failed to remove just added attribute.\n", - __FUNCTION__); - } -free_err_out: - /* Free MFT record, if it isn't contain attributes. */ - if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - - le32_to_cpu(attr_ni->mrec->attrs_offset) == 8) { - if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) { - Dprintf("%s(): Failed to free MFT record. Leaving " - "inconsist metadata.\n", __FUNCTION__); - } - } -err_out: - errno = err; - return NULL; -} - -/** - * ntfs_inode_rm_attr - remove attribute from ntfs inode - * @na: opened ntfs attribute to delete - * - * Remove attribute and all it's extents from ntfs inode. If attribute was non - * resident also free all clusters allocated by attribute. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_inode_rm_attr(ntfs_attr *na) -{ - ntfs_attr_search_ctx *ctx; - int ret = 0; - - if (!na) { - Dprintf("%s(): Invalid arguments passed.\n", __FUNCTION__); - errno = EINVAL; - return -1; - } - - Dprintf("%s(): Entering for inode 0x%llx, attr 0x%x.\n", - __FUNCTION__, (long long) na->ni->mft_no, na->type); - - /* Free cluster allocation. */ - if (NAttrNonResident(na)) { - if (ntfs_attr_map_whole_runlist(na)) - return -1; - if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { - Dprintf("%s(): Failed to free cluster allocation. " - "Leaving inconsist metadata.\n", __FUNCTION__); - ret = -1; - } - } - - /* Search for attribute extents and remove them all. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_attr_record_rm(ctx)) { - Dprintf("%s(): Failed to remove attribute extent. " - "Leaving inconsist metadata.\n", __FUNCTION__); - ret = -1; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - Dprintf("%s(): Attribute lookup failed. Probably leaving " - "inconsist metadata.\n", __FUNCTION__); - ret = -1; - } - - /* Throw away now non-exist attribute. */ - ntfs_attr_close(na); - /* Done. */ - return ret; -} diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index fa0b557b..f3fdb8b0 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -317,8 +317,8 @@ int main (int argc, char *argv[]) goto close_dst; } /* Requested attribute isn't present, add it. */ - na = ntfs_inode_add_attr(out, opts.attribute, attr_name, - attr_name_len, 0); + na = ntfs_attr_add(out, opts.attribute, attr_name, + attr_name_len, 0); if (!na) { perror("ERROR: Couldn't add attribute"); goto close_dst;