diff --git a/ChangeLog b/ChangeLog index 7c687fd5..18e1e8af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -48,8 +48,6 @@ xx/xx/2004 - 2.0.0-WIP - Add new API unistr.[hc]::ntfs_ucsndup(). (Anton) - Make libntfs/attrib.c::ntfs_attr_open() make a copy of the attribute name unless it is one of the internal names. (Anton) - - Move out common part of ntfs_attrlist_entry_{add,rm} to new API: - attrlist.[ch]::ntfs_attrlist_set. (Yura) - New API: attrib.[ch]::ntfs_resident_attr_record_add. (Yura) - New API: inode.[ch]::ntfs_inode_add_attrlist. (Yura) - New API: attrlist.[ch]::ntfs_attrlist_need. (Yura) diff --git a/include/ntfs/attrlist.h b/include/ntfs/attrlist.h index 3c7259f9..9d34546e 100644 --- a/include/ntfs/attrlist.h +++ b/include/ntfs/attrlist.h @@ -28,7 +28,6 @@ extern int ntfs_attrlist_need(ntfs_inode *ni); -extern int ntfs_attrlist_set(ntfs_inode *ni, u8 *new_al, int new_al_len); extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx); diff --git a/libntfs/attrib.c b/libntfs/attrib.c index f89fdb0c..5f1fd789 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -2396,7 +2396,7 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, base_ni = ni->base_ni; else base_ni = ni; - if (NInoAttrList(base_ni)) { + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { if (ntfs_attrlist_entry_add(ni, a)) { err = errno; ntfs_attr_record_resize(m, a, 0); @@ -2526,7 +2526,7 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, base_ni = ni->base_ni; else base_ni = ni; - if (NInoAttrList(base_ni)) { + if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { if (ntfs_attrlist_entry_add(ni, a)) { err = errno; ntfs_attr_record_resize(m, a, 0); @@ -2583,29 +2583,15 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { return -1; } - Dprintf("%s(): Entering for inode 0x%llx, attr 0x%x, lowest_vcn " - "%lld.\n", __FUNCTION__, (long long) ctx->ntfs_ino->mft_no, - (unsigned) le32_to_cpu(ctx->attr->type), - (long long) sle64_to_cpu(ctx->attr->lowest_vcn)); + Dprintf("%s(): Entering for inode 0x%llx, attr 0x%x.\n", + __FUNCTION__, (long long) ctx->ntfs_ino->mft_no, + (unsigned) le32_to_cpu(ctx->attr->type)); type = ctx->attr->type; ni = ctx->ntfs_ino; if (ctx->base_ntfs_ino) base_ni = ctx->base_ntfs_ino; else base_ni = ctx->ntfs_ino; - /* - * Remove record from $ATTRIBUTE_LIST if present and we don't want - * delete $ATTRIBUTE_LIST itself. - */ - if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { - if (ntfs_attrlist_entry_rm(ctx)) { - err = errno; - Dprintf("%s(): Coudn't delete record from " - "$ATTRIBUTE_LIST.\n", __FUNCTION__); - errno = err; - return -1; - } - } /* Remove attribute itself. */ if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { @@ -2620,10 +2606,25 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { } ntfs_inode_mark_dirty(ni); + /* + * Remove record from $ATTRIBUTE_LIST if present and we don't want + * delete $ATTRIBUTE_LIST itself. + */ + if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { + if (ntfs_attrlist_entry_rm(ctx)) { + err = errno; + Dprintf("%s(): Coudn't delete record from " + "$ATTRIBUTE_LIST.\n", __FUNCTION__); + errno = err; + return -1; + } + } + /* Post $ATTRIBUTE_LIST delete setup. */ if (type == AT_ATTRIBUTE_LIST) { if (NInoAttrList(base_ni) && base_ni->attr_list) free(base_ni->attr_list); + base_ni->attr_list = NULL; NInoClearAttrList(base_ni); NInoAttrListClearDirty(base_ni); } @@ -3321,10 +3322,19 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) goto put_err_out; } - /* We can't move out attribute list. */ + /* We can't move out attribute list, thus move out others. */ if (na->type == AT_ATTRIBUTE_LIST) { - err = ENOSPC; - goto put_err_out; + ntfs_attr_put_search_ctx(ctx); + if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, + non_resident_attr_end) + 8)) { + err = errno; + Dprintf("%s(): Couldn't free space in the MFT record " + "to make attribute list non resident.\n", + __FUNCTION__); + errno = err; + return -1; + } + return ntfs_resident_attr_resize(na, newsize); } /* @@ -3712,7 +3722,7 @@ int ntfs_attr_update_mapping_pairs(ntfs_attr *na) if (na->type == AT_ATTRIBUTE_LIST) { ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_free_space(na->ni, mp_size - - exp_max_mp_size)) { + cur_max_mp_size)) { if (errno != ENOSPC) return -1; Dprintf("%s(): Attribute list mapping " @@ -4287,6 +4297,7 @@ put_err_out: int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) { if (!na || newsize < 0 || (na->ni == FILE_MFT && na->type == AT_DATA)) { + Dprintf("%s(): Invalid aruments passed.\n", __FUNCTION__); errno = EINVAL; return -1; } diff --git a/libntfs/attrlist.c b/libntfs/attrlist.c index ac72fd05..cbef17f1 100644 --- a/libntfs/attrlist.c +++ b/libntfs/attrlist.c @@ -82,77 +82,6 @@ int ntfs_attrlist_need(ntfs_inode *ni) return 0; } -/** - * ntfs_attrlist_set - set new attribute list for ntfs inode - * @ni: opened ntfs inode attribute list set for - * @new_al: new attribute list - * @new_al_len: length of new attribute list - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EINVAL - Invalid argumets passed to function. - * ENOMEM - Not enough memory to allocate necessary buffers. - * ENOTSUP - Code that required for set is not implemented yet. - * EIO - I/O error occured or damaged filesystem. - */ -int ntfs_attrlist_set(ntfs_inode *ni, u8 *new_al, int new_al_len) -{ - ntfs_attr *na = NULL; - int err; - - if (!ni || !new_al || new_al_len < 1) { - Dprintf("%s(): Invalid argumets.\n", __FUNCTION__); - errno = EINVAL; - return -1; - } - - Dprintf("%s(): Entering for inode 0x%llx, new_al_len %d.\n", - __FUNCTION__, (long long) ni->mft_no, new_al_len); - - /* Make attribute list length 8 byte aligment. */ - new_al_len = (new_al_len + 7) & ~7; - - na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, 0, 0); - if (!na) { - err = errno; - Dprintf("%s(): Coudn't open $ATTRIBUTE_LIST.\n", __FUNCTION__); - goto err_out; - } - /* - * Setup im-memory attribute list. We need this to perform attribute - * truncate (we need update attribute list in case other attributes - * will be moved away from their current MFT record). - */ - if (NInoAttrList(ni) && ni->attr_list) - free(ni->attr_list); - ni->attr_list = new_al; - ni->attr_list_size = new_al_len; - NInoSetAttrList(ni); - NInoAttrListSetDirty(ni); - /* Resize $ATTRIBUTE_LIST attribute. */ - if (ntfs_attr_truncate(na, new_al_len)) { - /* - * FIXME: We leave new attribute list. But need to restore old - * and update in it records for moved attributes. Difficult to - * do if we haven't attribute list before truncate and records - * were moved. - */ - err = errno; - Dprintf("%s(): Eeek! $ATTRIBUTE_LIST resize failed. Probably " - "leaving inconsist metadata.\n", __FUNCTION__); - goto err_out; - } - - /* Done! */ - ntfs_attr_close(na); - return 0; -err_out: - if (na) - ntfs_attr_close(na); - errno = err; - return -1; -} - /** * ntfs_attrlist_entry_add - add an attribute list attribute entry * @ni: opened ntfs inode, which contains that attribute @@ -169,6 +98,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { ATTR_LIST_ENTRY *ale; MFT_REF mref; + ntfs_attr *na = NULL; u8 *new_al; int new_al_len; int err; @@ -193,6 +123,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) return -1; } + /* Determine size and allocate memory for new attribute list. */ new_al_len = (ni->attr_list_size + sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * attr->name_length + 7) & ~7; new_al = malloc(new_al_len); @@ -202,6 +133,20 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) return -1; } + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, NULL, 0); + if (!na) { + err = errno; + Dprintf("%s(): Failed to open $ATTRIBUTE_LIST attribute.\n", + __FUNCTION__); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + Dprintf("%s(): $ATTRIBUTE_LIST resize failed.\n", __FUNCTION__); + goto err_out; + } + /* Find offset at which insert new entry. */ ale = (ATTR_LIST_ENTRY *) ni->attr_list; for(; (u8 *)ale < ni->attr_list + ni->attr_list_size; @@ -218,7 +163,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) err = EIO; Dprintf("%s(): Corrupt attribute name. Run chkdsk.\n", __FUNCTION__); - goto err_out; + goto rollback; } if (err < 0) continue; @@ -233,7 +178,7 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) Dprintf("%s(): Attribute with same type, name and " "lowest vcn already present in attribute " "list.\n", __FUNCTION__); - goto err_out; + goto rollback; } break; } @@ -262,13 +207,22 @@ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) attr->name_length * sizeof(ntfschar)); /* Set new runlist. */ - if (ntfs_attrlist_set(ni, new_al, new_al_len)) { - err = errno; - goto err_out; - } - + if (ni->attr_list) + free(ni->attr_list); + ni->attr_list = new_al; + ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(ni); + /* Done! */ + ntfs_attr_close(na); return 0; +rollback: + if (ntfs_attr_truncate(na, ni->attr_list_size)) { + Dprintf("%s(): $ATTRIBUTE_LIST resize failed. Rollback failed. " + "Leaving inconsist metadata.\n", __FUNCTION__); + } err_out: + if (na) + ntfs_attr_close(na); free(new_al); errno = err; return -1; @@ -278,8 +232,7 @@ err_out: * ntfs_attrlist_entry_rm - remove an attribute list attribute entry * @ctx: attribute search context describing the attrubute list entry * - * Remove the attribute list entry @ctx->al_entry from the attribute list - * attribute of the base mft record to which the attribute @ctx->attr belongs. + * Remove the attribute list entry @ctx->al_entry from the attribute list. * * Return 0 on success and -1 on error with errno set to the error code. */ @@ -288,10 +241,11 @@ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) u8 *new_al; int new_al_len; ntfs_inode *base_ni; + ntfs_attr *na; ATTR_LIST_ENTRY *ale; int err; - if (!ctx || !ctx->ntfs_ino || !ctx->attr || !ctx->al_entry) { + if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { Dprintf("%s(): Invalid argumets.\n", __FUNCTION__); errno = EINVAL; return -1; @@ -305,8 +259,8 @@ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) Dprintf("%s(): Entering for inode 0x%llx, attr 0x%x, lowest_vcn " "%lld.\n", __FUNCTION__, (long long) ctx->ntfs_ino->mft_no, - (unsigned) le32_to_cpu(ctx->attr->type), - (long long) le64_to_cpu(ctx->attr->lowest_vcn)); + (unsigned) le32_to_cpu(ctx->al_entry->type), + (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); if (!NInoAttrList(base_ni)) { Dprintf("%s(): Attribute list isn't present.\n", __FUNCTION__); @@ -322,6 +276,20 @@ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) errno = ENOMEM; return -1; } + + /* Reisze $ATTRIBUTE_LIST to new length. */ + na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, NULL, 0); + if (!na) { + err = errno; + Dprintf("%s(): Failed to open $ATTRIBUTE_LIST attribute.\n", + __FUNCTION__); + goto err_out; + } + if (ntfs_attr_truncate(na, new_al_len)) { + err = errno; + Dprintf("%s(): $ATTRIBUTE_LIST resize failed.\n", __FUNCTION__); + goto err_out; + } /* Copy entries from old attribute list to new. */ memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); @@ -329,12 +297,17 @@ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); /* Set new runlist. */ - if (ntfs_attrlist_set(base_ni, new_al, new_al_len)) { - err = errno; - goto err_out; - } + if (base_ni->attr_list) + free(base_ni->attr_list); + base_ni->attr_list = new_al; + base_ni->attr_list_size = new_al_len; + NInoAttrListSetDirty(base_ni); + /* Done! */ + ntfs_attr_close(na); return 0; err_out: + if (na) + ntfs_attr_close(na); free(new_al); errno = err; return -1; diff --git a/libntfs/inode.c b/libntfs/inode.c index 171d16c3..4124cb35 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -525,6 +525,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) u8 *al, *aln; int al_len, al_allocated; ATTR_LIST_ENTRY *ale; + ntfs_attr *na; if (!ni) { Dprintf("%s(): Invalid argumets.\n", __FUNCTION__); @@ -617,52 +618,47 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) al = aln; ntfs_attr_put_search_ctx(ctx); + /* Set in-memory attribute list. */ + ni->attr_list = al; + ni->attr_list_size = al_len; + NInoSetAttrList(ni); + /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ if (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use) < offsetof(ATTR_RECORD, resident_attr_end)) { - /* - * Set temporary in-memory attribute list. We need this to be - * able perform attribute lookups and move out attributes. - */ - ni->attr_list = al; - ni->attr_list_size = al_len; - NInoSetAttrList(ni); - /* Free space. */ if (ntfs_inode_free_space(ni, offsetof(ATTR_RECORD, resident_attr_end))) { - /* - * Couldn't free space, unset temporary in-memory - * attribute list and fail. - */ + /* Failed to free space. */ err = errno; Dprintf("%s(): Failed to free space for " "$ATTRIBUTE_LIST.\n", __FUNCTION__); - ni->attr_list = NULL; - NInoClearAttrList(ni); - goto err_out; + free(al); + goto rollback; } - /* Unset temporary in-memory attribute list. */ - ni->attr_list = NULL; - NInoClearAttrList(ni); } /* Add $ATTRIBUTE_LIST to mft record. */ - if (ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, 0, 0, 0) < 0) { + na = ntfs_inode_add_attr(ni, AT_ATTRIBUTE_LIST, NULL, 0, al_len); + if (!na) { err = errno; - Dprintf("%s(): Couldn't add $ATTRIBUTE_LIST to MFT record.\n", - __FUNCTION__); - goto err_out; - } - - /* Set new attribute list. */ - if (ntfs_attrlist_set(ni, al, al_len)) { - err = errno; - Dprintf("%s(): Coudn't set attribute list.\n", __FUNCTION__); - goto err_out; + Dprintf("%s(): Failed to add $ATTRIBUTE_LIST.\n", __FUNCTION__); + goto rollback; } + /* Done! */ + ntfs_attr_close(na); return 0; +rollback: + /* + * FIXME: We should here scan attribute list for attributes that placed + * not in the base MFT record and move them to it. + */ + /* Unset in-memory attribute list. */ + ni->attr_list = NULL; + NInoClearAttrList(ni); + errno = err; + return -1; put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: @@ -681,7 +677,7 @@ err_out: int ntfs_inode_free_space(ntfs_inode *ni, int size) { ntfs_attr_search_ctx *ctx; - int freed = 0, err; + int freed, err; if (!ni || size < 0) { Dprintf("%s(): Invalid argumets.\n", __FUNCTION__); @@ -691,8 +687,11 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) Dprintf("%s(): Entering for inode 0x%llx, size %d.\n", __FUNCTION__, (long long) ni->mft_no, size); + + freed = (le32_to_cpu(ni->mrec->bytes_allocated) - + le32_to_cpu(ni->mrec->bytes_in_use)); - if (!size) + if (size <= freed) return 0; ctx = ntfs_attr_get_search_ctx(ni, 0); @@ -820,11 +819,12 @@ put_err_out: ntfs_attr *ntfs_inode_add_attr(ntfs_inode *ni, ATTR_TYPES type, ntfschar *name, u8 name_len, s64 size) { - int attr_rec_size, err, i, offset; + u32 attr_rec_size; + int err, i, offset; ntfs_inode *attr_ni; ntfs_attr *na; - if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { + if (!ni || size < 0) { Dprintf("%s(): Invalid arguments passed.\n", __FUNCTION__); errno = EINVAL; return NULL; @@ -883,6 +883,12 @@ ntfs_attr *ntfs_inode_add_attr(ntfs_inode *ni, ATTR_TYPES type, goto add_attr_record; } + /* Attribute list can be placed only in the base MFT record. */ + if (type == AT_ATTRIBUTE_LIST) { + err = ENOSPC; + goto err_out; + } + /* Try to add to extent inodes. */ if (ntfs_inode_attach_all_extents(ni)) { err = errno;