diff --git a/ChangeLog b/ChangeLog index 18afbdae..d1de3448 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,7 +13,7 @@ - Rewrite disk_io.[ch] and mft.[ch] defining new access API: disk_io.[ch] provide: ntfs_p{read,write}(), ntfs_mst_p{read,write}(), and - ntfs_clusters_{read,write}(). + ntfs_cluster_{read,write}(). mft.[ch] provide: ntfs_mft_record_{read,write}(), ntfs_mft_records_{read,write}(), and ntfs_mft_record_get_data_size(). @@ -100,7 +100,7 @@ and writes and deal with end of file properly. Affected functions: ntfs_p{read,write}(), ntfs_mst_p{read,write}(), and - ntfs_clusters_{read,write}(). + ntfs_cluster_{read,write}(). - Change ntfsfix to take into account the automatic mft mirror updates. - Add new API provided by new files dir.[ch]: ntfs_inode_lookup_by_name(), and @@ -167,6 +167,15 @@ Thanks to Szakacsits Szabolcs for pointing this problem out. - New API function provided by unistr.[hc] and use it in mkntfs: ntfs_ucsnlen(). + - New API function provided by attrib.[hc] and use it in mkntfs: + ntfs_resident_attr_value_resize(), + ntfs_attr_truncate(). -- WIP + - New API functions provided by inode.[hc]: + ntfs_inode_mark_dirty(), + ntfs_inode_sync(). -- WIP + - Change ntfs_inode_close() to write out dirty inodes and inode extents. + - New API functions provided by lcnalloc.[hc]: + ntfs_cluster_{alloc,free}(). 12/03/2002 - 1.6.0 - More mkntfs options and cleanups. Fix typo in usage information of mkntfs. Thanks to Richard Russon for diff --git a/include/attrib.h b/include/attrib.h index f62dc52f..e97f1c07 100644 --- a/include/attrib.h +++ b/include/attrib.h @@ -261,6 +261,10 @@ extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, extern int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max, const s64 n); +extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 newsize); + +extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize); // FIXME / TODO: Above here the file is cleaned up. (AIA) /** diff --git a/include/disk_io.h b/include/disk_io.h index deb34272..266b9eb1 100644 --- a/include/disk_io.h +++ b/include/disk_io.h @@ -32,9 +32,9 @@ extern s64 ntfs_mst_pread(const int fd, const s64 pos, s64 count, extern s64 ntfs_mst_pwrite(const int fd, const s64 pos, s64 count, const u32 bksize, const void *b); -extern s64 ntfs_clusters_read(const ntfs_volume *vol, const s64 lcn, +extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, const void *b); -extern s64 ntfs_clusters_write(const ntfs_volume *vol, const s64 lcn, +extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, const s64 count, const void *b); #endif /* defined _NTFS_DISK_IO_H */ diff --git a/include/inode.h b/include/inode.h index f3a1e9f0..7f93730f 100644 --- a/include/inode.h +++ b/include/inode.h @@ -85,6 +85,7 @@ struct _ntfs_inode { u32 attr_list_size; /* Length of attribute list value in bytes. */ u8 *attr_list; /* Attribute list value itself. */ runlist *attr_list_rl; /* Run list for the attribute list value. */ + /* Below fields are always valid. */ s32 nr_extents; /* For a base mft record, the number of attached extent inodes (0 if none), for extent records this is -1. */ @@ -105,5 +106,23 @@ extern int ntfs_inode_close(ntfs_inode *ni); extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref); +/** + * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty + * @ni: ntfs inode to set dirty + * + * Set the inode @ni dirty so it is written out later (at the latest at + * ntfs_inode_close() time). If @ni is an extent inode, set the base inode + * dirty, too. + * + * This function cannot fail. + */ +static __inline__ void ntfs_inode_mark_dirty(ntfs_inode *ni) { + NInoSetDirty(ni); + if (ni->nr_extents == -1) + NInoSetDirty(ni->base_ni); +} + +extern int ntfs_inode_sync(ntfs_inode *ni); + #endif /* defined _NTFS_INODE_H */ diff --git a/libntfs/attrib.c b/libntfs/attrib.c index 44805948..c06cd269 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -2037,3 +2037,156 @@ err_out: return -1; } +/** + * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute + * @na: non-resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOTSUP - The desired resize is not implemented yet. + */ +static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) { + errno = ENOTSUP; + return -1; +} + +/** + * ntfs_resident_attr_value_resize - resize the value of a resident attribute + * @m: mft record containing attribute record + * @a: attribute record whose value to resize + * @newsize: new size in bytes to which to resize the attribute value of @a + * + * Resize the value of the attribute @a in the mft record @m to @newsize bytes. + * + * Return 0 on success and -1 on error with errno set to the error code. + * The following error codes are defined: + * ENOSPC - Not enough space in mft record to perform the resize. + * Note that on error no modifications have been performed whatsoever. + */ +int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, + const u32 newsize) +{ + u32 new_alen, new_muse; + + /* Calculate the new attribute length and mft record bytes used. */ + new_alen = (le32_to_cpu(a->length) - le32_to_cpu(a->value_length) + + newsize + 7) & ~7; + new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) + + new_alen; + /* Not enough space in this mft record. */ + if (new_muse > le32_to_cpu(m->bytes_allocated)) { + errno = ENOSPC; + return -1; + } + /* Move attributes following @a to their new location. */ + memmove((u8*)a + new_alen, (u8*)a + le32_to_cpu(a->length), + le32_to_cpu(m->bytes_in_use) - ((u8*)a - (u8*)m) - + le32_to_cpu(a->length)); + /* Adjust @a to reflect the new value size. */ + a->length = cpu_to_le32(new_alen); + a->value_length = cpu_to_le32(newsize); + /* Adjust @m to reflect the change in used space. */ + m->bytes_in_use = cpu_to_le32(new_muse); + return 0; +} + +/** + * ntfs_resident_attr_shrink - shrink a resident, open ntfs attribute + * @na: resident ntfs attribute to shrink + * @newsize: new size (in bytes) to which to shrink the attribute + * + * Reduce the size of a resident, open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * ENOTSUP - The desired resize is not implemented yet. + */ +static int ntfs_resident_attr_shrink(ntfs_attr *na, const u32 newsize) { + ntfs_attr_search_ctx *ctx; + int err; + + Dprintf("%s(): Entering for inode 0x%Lx, attr 0x%x.\n", __FUNCTION__, + (unsigned long long)na->ni->mft_no, na->type); + /* Get the attribute record that needs modification. */ + ctx = ntfs_attr_get_search_ctx(na->ni, NULL); + if (!ctx) + return -1; + if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, + ctx)) { + err = errno; + goto put_err_out; + } + + // TODO: Check the attribute type and the corresponding minimum size + // against @newsize and fail if @newsize is too small! (AIA) + + /* Perform the resize of the attribute record. */ + if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, newsize)) { + err = errno; + goto put_err_out; + } + /* Update the ntfs attribute structure, too. */ + na->allocated_size = na->data_size = na->initialized_size = newsize; + if (NAttrCompressed(na) || NAttrSparse(na)) + na->compressed_size = newsize; + + /* + * Set the inode (and its base inode if it exists) dirty so it is + * written out later. + */ + ntfs_inode_mark_dirty(ctx->ntfs_ino); + + ntfs_attr_put_search_ctx(ctx); + return 0; +put_err_out: + ntfs_attr_put_search_ctx(ctx); + errno = err; + return -1; +} + +/** + * ntfs_attr_truncate - resize an ntfs attribute + * @na: open ntfs attribute to resize + * @newsize: new size (in bytes) to which to resize the attribute + * + * Change the size of an open ntfs attribute @na to @newsize bytes. + * + * On success return 0 and on error return -1 with errno set to the error code. + * The following error codes are defined: + * EINVAL - Invalid arguments were passed to the function. + * ENOTSUP - The desired resize is not implemented yet. + * + * NOTE: At present attributes can only be made smaller using this function, + * never bigger. + */ +int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) +{ + if (!na || newsize < 0) { + errno = EINVAL; + return -1; + } + /* + * Encrypted attributes are not supported. We return access denied, + * which is what Windows NT4 does, too. + */ + if (NAttrEncrypted(na)) { + errno = EACCES; + return -1; + } + /* + * TODO: Implement making attributes bigger/filling in of uninitialized + * holes as well as handling of compressed attributes. (AIA) + */ + if (newsize > na->initialized_size || NAttrCompressed(na)) { + errno = ENOTSUP; + return -1; + } + + if (NAttrNonResident(na)) + return ntfs_non_resident_attr_shrink(na, newsize); + return ntfs_resident_attr_shrink(na, newsize); +} + diff --git a/libntfs/disk_io.c b/libntfs/disk_io.c index 28ba6453..eb38b1d0 100644 --- a/libntfs/disk_io.c +++ b/libntfs/disk_io.c @@ -264,7 +264,7 @@ s64 ntfs_mst_pwrite(const int fd, const s64 pos, s64 count, } /** - * ntfs_clusters_read - read ntfs clusters + * ntfs_cluster_read - read ntfs clusters * @vol: volume to read from * @lcn: starting logical cluster number * @count: number of clusters to read @@ -274,7 +274,7 @@ s64 ntfs_mst_pwrite(const int fd, const s64 pos, s64 count, * volume @vol into buffer @b. Return number of clusters read or -1 on error, * with errno set to the error code. */ -s64 ntfs_clusters_read(const ntfs_volume *vol, const s64 lcn, +s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count, const void *b) { s64 br; @@ -297,7 +297,7 @@ s64 ntfs_clusters_read(const ntfs_volume *vol, const s64 lcn, } /** - * ntfs_clusters_write - write ntfs clusters + * ntfs_cluster_write - write ntfs clusters * @vol: volume to write to * @lcn: starting logical cluster number * @count: number of clusters to write @@ -307,7 +307,7 @@ s64 ntfs_clusters_read(const ntfs_volume *vol, const s64 lcn, * buffer @b to volume @vol. Return the number of clusters written or -1 on * error, with errno set to the error code. */ -s64 ntfs_clusters_write(const ntfs_volume *vol, const s64 lcn, +s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn, const s64 count, const void *b) { s64 bw; diff --git a/libntfs/inode.c b/libntfs/inode.c index f40ae1ea..62be3ea9 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -193,22 +193,25 @@ err_out: * error, @ni has not been freed. The user should attempt to handle the error * and call ntfs_inode_close() again. The following error codes are defined: * - * EBUSY @ni is dirty and/or the attribute list runlist is dirty. + * EBUSY @ni and/or its attribute list runlist is/are dirty and the + * attempt to write it/them to disk failed. * EINVAL @ni is invalid (probably it is an extent inode!) */ int ntfs_inode_close(ntfs_inode *ni) { - /* If the inode is an extent inode, comply rudely! */ + /* If the inode is an extent inode, complain rudely! */ if (ni->nr_extents == -1) { Dprintf("%s(): BUG: Tried to close extent inode!\n", __FUNCTION__); errno = EINVAL; return -1; } - // TODO: This needs to be replaced with a flush to disk attempt. (AIA) + /* If we have dirty metadata, write it out. */ if (NInoDirty(ni) || NInoAttrListDirty(ni)) { - errno = EBUSY; - return -1; + if (ntfs_inode_sync(ni)) { + errno = EBUSY; + return -1; + } } /* Is this a base inode with mapped extent inodes? */ if (ni->nr_extents > 0) { @@ -318,3 +321,23 @@ err_out: return NULL; } +/** + * ntfs_inode_sync - write the inode (and its dirty extents) to disk + * @ni: ntfs inode to write + * + * Write the inode @ni to disk as well as its dirty extent inodes if such + * exist. + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_inode_sync(ntfs_inode *ni) +{ + if (!ni) { + errno = EINVAL; + return -1; + } + + errno = ENOTSUP; + return -1; +} + diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c index 3ff2ca8f..a016bde9 100644 --- a/ntfsprogs/mkntfs.c +++ b/ntfsprogs/mkntfs.c @@ -860,35 +860,6 @@ int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) return 0; } -/* Return 0 on success and -errno on error. */ -int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_vsize) -{ - int new_alen, new_muse; - - /* New attribute length and mft record bytes used. */ - new_alen = (le32_to_cpu(a->length) - le32_to_cpu(a->value_length) + - new_vsize + 7) & ~7; - new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) + - new_alen; - /* Check for sufficient space. */ - if (new_muse > le32_to_cpu(m->bytes_allocated) ) { - // Aarrgghh! Need to make space. Probably want generic function - // for this as we need to call it from other places, too. - return -ENOTSUP; - } - /* Move attributes behind @a to their new location. */ - memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length), - le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) - - le32_to_cpu(a->length)); - /* Adjust @m to reflect change in used space. */ - m->bytes_in_use = cpu_to_le32(new_muse); - /* Adjust @a to reflect new value size. */ - a->length = cpu_to_le32(new_alen); - a->value_length = cpu_to_le32(new_vsize); - return 0; -} - void deallocate_scattered_clusters(const runlist *rl) { LCN j; @@ -1976,12 +1947,12 @@ int upgrade_to_large_index(MFT_RECORD *m, const char *name, + le16_to_cpu(re->length)); r->index.allocated_size = r->index.index_length; /* Resize index root attribute. */ - err = resize_resident_attribute_value(m, a, sizeof(INDEX_ROOT) - + if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + - le32_to_cpu(r->index.allocated_size)); - if (err) { + le32_to_cpu(r->index.allocated_size))) { // TODO: Remove the added bitmap! // Revert index root from index allocation. + err = -errno; goto err_out; } /* Set VCN pointer to 0LL. */ diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index 37d2e4ec..3042db90 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -1823,7 +1823,7 @@ int undelete_file (ntfs_volume *vol, long long inode) goto free; } } else { - if (ntfs_clusters_read(vol, j, 1, buffer) < 1) { + if (ntfs_cluster_read(vol, j, 1, buffer) < 1) { Eprintf ("Read failed: %s\n", strerror (errno)); close (fd); goto free;