mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ntfs: protect runlist updates with the runlist lock
ntfs_non_resident_attr_shrink() calls runlist helpers that require the
runlist write lock, but did not hold it while freeing clusters and
truncating the runlist. Serialize those operations and the resident
conversion with the runlist lock.
ntfs_attr_map_cluster() can merge a newly allocated run before updating
mapping pairs. If the update fails, free the clusters and restore both
the in-memory runlist and on-disk mapping pairs from a saved runlist.
Mark the volume in error if either rollback step fails.
Fixes: 495e90fa33 ("ntfs: update attrib operations")
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
631946431d
commit
91709ba5d6
205
fs/ntfs/attrib.c
205
fs/ntfs/attrib.c
|
|
@ -3574,7 +3574,8 @@ int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra)
|
||||||
* update allocated and compressed size.
|
* update allocated and compressed size.
|
||||||
*/
|
*/
|
||||||
static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||||
struct mft_record *m, struct ntfs_attr_search_ctx *ctx)
|
struct mft_record *m, struct ntfs_attr_search_ctx *ctx,
|
||||||
|
struct ntfs_inode *locked_ni, bool defer_attrlist)
|
||||||
{
|
{
|
||||||
int sparse, err = 0;
|
int sparse, err = 0;
|
||||||
struct ntfs_inode *base_ni;
|
struct ntfs_inode *base_ni;
|
||||||
|
|
@ -3610,6 +3611,8 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset) == 8) &&
|
le16_to_cpu(a->data.non_resident.mapping_pairs_offset) == 8) &&
|
||||||
!(le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use))) {
|
!(le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use))) {
|
||||||
|
|
||||||
|
if (defer_attrlist)
|
||||||
|
return -ENOSPC;
|
||||||
if (!NInoAttrList(base_ni)) {
|
if (!NInoAttrList(base_ni)) {
|
||||||
err = ntfs_inode_add_attrlist(base_ni);
|
err = ntfs_inode_add_attrlist(base_ni);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -3623,7 +3626,7 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ntfs_attrlist_update(base_ni);
|
err = ntfs_attrlist_update_locked(base_ni, locked_ni);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
err = -EAGAIN;
|
err = -EAGAIN;
|
||||||
|
|
@ -3703,6 +3706,8 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||||
* ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
|
* ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
|
||||||
* @ni: non-resident ntfs inode for which we need update
|
* @ni: non-resident ntfs inode for which we need update
|
||||||
* @from_vcn: update runlist starting this VCN
|
* @from_vcn: update runlist starting this VCN
|
||||||
|
* @locked_ni: inode whose runlist write lock is already held
|
||||||
|
* @defer_attrlist: return -ENOSPC instead of updating an attribute list
|
||||||
*
|
*
|
||||||
* Build mapping pairs from @na->rl and write them to the disk. Also, this
|
* Build mapping pairs from @na->rl and write them to the disk. Also, this
|
||||||
* function updates sparse bit, allocated and compressed size (allocates/frees
|
* function updates sparse bit, allocated and compressed size (allocates/frees
|
||||||
|
|
@ -3712,7 +3717,10 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||||
* call to this function. Vice-versa @na->compressed_size will be calculated and
|
* call to this function. Vice-versa @na->compressed_size will be calculated and
|
||||||
* set to correct value during this function.
|
* set to correct value during this function.
|
||||||
*/
|
*/
|
||||||
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
static int __ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni,
|
||||||
|
s64 from_vcn,
|
||||||
|
struct ntfs_inode *locked_ni,
|
||||||
|
bool defer_attrlist)
|
||||||
{
|
{
|
||||||
struct ntfs_attr_search_ctx *ctx;
|
struct ntfs_attr_search_ctx *ctx;
|
||||||
struct ntfs_inode *base_ni;
|
struct ntfs_inode *base_ni;
|
||||||
|
|
@ -3804,7 +3812,8 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ntfs_attr_update_meta(a, ni, m, ctx);
|
err = ntfs_attr_update_meta(a, ni, m, ctx, locked_ni,
|
||||||
|
defer_attrlist);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (err == -EAGAIN) {
|
if (err == -EAGAIN) {
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
|
@ -3844,11 +3853,17 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
*/
|
*/
|
||||||
if (ni->type == AT_ATTRIBUTE_LIST) {
|
if (ni->type == AT_ATTRIBUTE_LIST) {
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
if (locked_ni == ni || defer_attrlist) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto put_err_out;
|
||||||
|
}
|
||||||
err = ntfs_inode_free_space(base_ni, mp_size -
|
err = ntfs_inode_free_space(base_ni, mp_size -
|
||||||
cur_max_mp_size);
|
cur_max_mp_size);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
err = ntfs_attrlist_update(base_ni);
|
err = ntfs_attrlist_update_locked(
|
||||||
|
base_ni, locked_ni);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
goto retry;
|
goto retry;
|
||||||
|
|
@ -3856,6 +3871,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
|
|
||||||
/* Add attribute list if it isn't present, and retry. */
|
/* Add attribute list if it isn't present, and retry. */
|
||||||
if (!NInoAttrList(base_ni)) {
|
if (!NInoAttrList(base_ni)) {
|
||||||
|
if (defer_attrlist) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto put_err_out;
|
||||||
|
}
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
if (ntfs_inode_add_attrlist(base_ni)) {
|
if (ntfs_inode_add_attrlist(base_ni)) {
|
||||||
ntfs_error(sb, "Can not add attrlist");
|
ntfs_error(sb, "Can not add attrlist");
|
||||||
|
|
@ -3883,13 +3902,21 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defer_attrlist &&
|
||||||
|
(ctx->ntfs_ino->nr_extents == -1 ||
|
||||||
|
NInoAttrList(ctx->ntfs_ino)) &&
|
||||||
|
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto put_err_out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update lowest vcn. */
|
/* Update lowest vcn. */
|
||||||
a->data.non_resident.lowest_vcn = cpu_to_le64(stop_vcn);
|
a->data.non_resident.lowest_vcn = cpu_to_le64(stop_vcn);
|
||||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||||
if ((ctx->ntfs_ino->nr_extents == -1 || NInoAttrList(ctx->ntfs_ino)) &&
|
if ((ctx->ntfs_ino->nr_extents == -1 || NInoAttrList(ctx->ntfs_ino)) &&
|
||||||
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
||||||
ctx->al_entry->lowest_vcn = cpu_to_le64(stop_vcn);
|
ctx->al_entry->lowest_vcn = cpu_to_le64(stop_vcn);
|
||||||
err = ntfs_attrlist_update(base_ni);
|
err = ntfs_attrlist_update_locked(base_ni, locked_ni);
|
||||||
if (err)
|
if (err)
|
||||||
goto put_err_out;
|
goto put_err_out;
|
||||||
}
|
}
|
||||||
|
|
@ -4064,6 +4091,19 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
|
||||||
|
s64 from_vcn,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
|
{
|
||||||
|
return __ntfs_attr_update_mapping_pairs(ni, from_vcn, locked_ni,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||||
|
{
|
||||||
|
return ntfs_attr_update_mapping_pairs_locked(ni, from_vcn, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ntfs_attr_make_resident - convert a non-resident to a resident attribute
|
* ntfs_attr_make_resident - convert a non-resident to a resident attribute
|
||||||
* @ni: open ntfs attribute to make resident
|
* @ni: open ntfs attribute to make resident
|
||||||
|
|
@ -4197,7 +4237,9 @@ static int ntfs_attr_make_resident(struct ntfs_inode *ni, struct ntfs_attr_searc
|
||||||
*
|
*
|
||||||
* Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
|
* Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
|
||||||
*/
|
*/
|
||||||
static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsize)
|
static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni,
|
||||||
|
const s64 newsize,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
struct ntfs_volume *vol;
|
struct ntfs_volume *vol;
|
||||||
struct ntfs_attr_search_ctx *ctx;
|
struct ntfs_attr_search_ctx *ctx;
|
||||||
|
|
@ -4205,6 +4247,7 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
s64 nr_freed_clusters;
|
s64 nr_freed_clusters;
|
||||||
int err;
|
int err;
|
||||||
struct ntfs_inode *base_ni;
|
struct ntfs_inode *base_ni;
|
||||||
|
bool runlist_locked = locked_ni == ni;
|
||||||
|
|
||||||
ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n",
|
ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n",
|
||||||
(unsigned long long)ni->mft_no, ni->type, (long long)newsize);
|
(unsigned long long)ni->mft_no, ni->type, (long long)newsize);
|
||||||
|
|
@ -4250,18 +4293,24 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
* clusters if there is a change.
|
* clusters if there is a change.
|
||||||
*/
|
*/
|
||||||
if (ntfs_bytes_to_cluster(vol, ni->allocated_size) != first_free_vcn) {
|
if (ntfs_bytes_to_cluster(vol, ni->allocated_size) != first_free_vcn) {
|
||||||
struct ntfs_attr_search_ctx *ctx;
|
/*
|
||||||
|
* ntfs_cluster_free() and ntfs_rl_truncate_nolock()
|
||||||
|
* both require this lock.
|
||||||
|
*/
|
||||||
|
if (!runlist_locked)
|
||||||
|
down_write(&ni->runlist.lock);
|
||||||
|
|
||||||
err = ntfs_attr_map_whole_runlist(ni);
|
err = ntfs_attr_map_whole_runlist(ni);
|
||||||
if (err) {
|
if (err) {
|
||||||
ntfs_debug("Eeek! ntfs_attr_map_whole_runlist failed.\n");
|
ntfs_debug("Eeek! ntfs_attr_map_whole_runlist failed.\n");
|
||||||
return err;
|
goto unlock_runlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
ntfs_error(vol->sb, "%s: Failed to get search context", __func__);
|
ntfs_error(vol->sb, "%s: Failed to get search context", __func__);
|
||||||
return -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
goto unlock_runlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deallocate all clusters starting with the first free one. */
|
/* Deallocate all clusters starting with the first free one. */
|
||||||
|
|
@ -4269,7 +4318,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
if (nr_freed_clusters < 0) {
|
if (nr_freed_clusters < 0) {
|
||||||
ntfs_debug("Eeek! Freeing of clusters failed. Aborting...\n");
|
ntfs_debug("Eeek! Freeing of clusters failed. Aborting...\n");
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
return (int)nr_freed_clusters;
|
err = (int)nr_freed_clusters;
|
||||||
|
goto unlock_runlist;
|
||||||
}
|
}
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
|
||||||
|
|
@ -4282,7 +4332,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
kvfree(ni->runlist.rl);
|
kvfree(ni->runlist.rl);
|
||||||
ni->runlist.rl = NULL;
|
ni->runlist.rl = NULL;
|
||||||
ntfs_error(vol->sb, "Eeek! Run list truncation failed.\n");
|
ntfs_error(vol->sb, "Eeek! Run list truncation failed.\n");
|
||||||
return -EIO;
|
err = -EIO;
|
||||||
|
goto unlock_runlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare to mapping pairs update. */
|
/* Prepare to mapping pairs update. */
|
||||||
|
|
@ -4298,11 +4349,13 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9;
|
VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9;
|
||||||
|
|
||||||
/* Write mapping pairs for new runlist. */
|
/* Write mapping pairs for new runlist. */
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0 /*first_free_vcn*/);
|
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
if (err) {
|
if (err) {
|
||||||
ntfs_debug("Eeek! Mapping pairs update failed. Leaving inconstant metadata. Run chkdsk.\n");
|
ntfs_debug("Eeek! Mapping pairs update failed. Leaving inconstant metadata. Run chkdsk.\n");
|
||||||
return err;
|
goto unlock_runlist;
|
||||||
}
|
}
|
||||||
|
if (!runlist_locked)
|
||||||
|
up_write(&ni->runlist.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the first attribute record. */
|
/* Get the first attribute record. */
|
||||||
|
|
@ -4344,7 +4397,11 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
|
|
||||||
/* If the attribute now has zero size, make it resident. */
|
/* If the attribute now has zero size, make it resident. */
|
||||||
if (!newsize && !NInoEncrypted(ni) && !NInoCompressed(ni)) {
|
if (!newsize && !NInoEncrypted(ni) && !NInoCompressed(ni)) {
|
||||||
|
if (!runlist_locked)
|
||||||
|
down_write(&ni->runlist.lock);
|
||||||
err = ntfs_attr_make_resident(ni, ctx);
|
err = ntfs_attr_make_resident(ni, ctx);
|
||||||
|
if (!runlist_locked)
|
||||||
|
up_write(&ni->runlist.lock);
|
||||||
if (err) {
|
if (err) {
|
||||||
/* If couldn't make resident, just continue. */
|
/* If couldn't make resident, just continue. */
|
||||||
if (err != -EPERM)
|
if (err != -EPERM)
|
||||||
|
|
@ -4361,6 +4418,11 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
put_err_out:
|
put_err_out:
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
unlock_runlist:
|
||||||
|
if (!runlist_locked)
|
||||||
|
up_write(&ni->runlist.lock);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -4369,13 +4431,14 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
||||||
* @prealloc_size: preallocation size (in bytes) to which to expand the attribute
|
* @prealloc_size: preallocation size (in bytes) to which to expand the attribute
|
||||||
* @newsize: new size (in bytes) to which to expand the attribute
|
* @newsize: new size (in bytes) to which to expand the attribute
|
||||||
* @holes: how to create a hole if expanding
|
* @holes: how to create a hole if expanding
|
||||||
* @need_lock: whether mrec lock is needed or not
|
* @locked_ni: inode whose runlist lock is already held
|
||||||
*
|
*
|
||||||
* Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
|
* Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
|
||||||
* by allocating new clusters.
|
* by allocating new clusters.
|
||||||
*/
|
*/
|
||||||
static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsize,
|
static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsize,
|
||||||
const s64 prealloc_size, unsigned int holes, bool need_lock)
|
const s64 prealloc_size, unsigned int holes,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
s64 lcn_seek_from;
|
s64 lcn_seek_from;
|
||||||
s64 first_free_vcn;
|
s64 first_free_vcn;
|
||||||
|
|
@ -4573,7 +4636,8 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
||||||
|
|
||||||
/* Prepare to mapping pairs update. */
|
/* Prepare to mapping pairs update. */
|
||||||
ni->allocated_size = ntfs_cluster_to_bytes(vol, first_free_vcn);
|
ni->allocated_size = ntfs_cluster_to_bytes(vol, first_free_vcn);
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(
|
||||||
|
ni, 0, locked_ni);
|
||||||
if (err) {
|
if (err) {
|
||||||
ntfs_debug("Mapping pairs update failed");
|
ntfs_debug("Mapping pairs update failed");
|
||||||
goto rollback;
|
goto rollback;
|
||||||
|
|
@ -4617,11 +4681,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
||||||
ntfs_debug("Leaking clusters");
|
ntfs_debug("Leaking clusters");
|
||||||
|
|
||||||
/* Now, truncate the runlist itself. */
|
/* Now, truncate the runlist itself. */
|
||||||
if (need_lock)
|
if (ni != locked_ni)
|
||||||
down_write(&ni->runlist.lock);
|
down_write(&ni->runlist.lock);
|
||||||
err2 = ntfs_rl_truncate_nolock(vol, &ni->runlist,
|
err2 = ntfs_rl_truncate_nolock(vol, &ni->runlist,
|
||||||
ntfs_bytes_to_cluster(vol, org_alloc_size));
|
ntfs_bytes_to_cluster(vol, org_alloc_size));
|
||||||
if (need_lock)
|
if (ni != locked_ni)
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
if (err2) {
|
if (err2) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -4635,11 +4699,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
||||||
/* Prepare to mapping pairs update. */
|
/* Prepare to mapping pairs update. */
|
||||||
ni->allocated_size = org_alloc_size;
|
ni->allocated_size = org_alloc_size;
|
||||||
/* Restore mapping pairs. */
|
/* Restore mapping pairs. */
|
||||||
if (need_lock)
|
if (ni != locked_ni)
|
||||||
down_read(&ni->runlist.lock);
|
down_read(&ni->runlist.lock);
|
||||||
if (ntfs_attr_update_mapping_pairs(ni, 0))
|
if (__ntfs_attr_update_mapping_pairs(ni, 0, locked_ni, true))
|
||||||
ntfs_error(sb, "Failed to restore old mapping pairs");
|
ntfs_error(sb, "Failed to restore old mapping pairs");
|
||||||
if (need_lock)
|
if (ni != locked_ni)
|
||||||
up_read(&ni->runlist.lock);
|
up_read(&ni->runlist.lock);
|
||||||
|
|
||||||
if (NInoSparse(ni) || NInoCompressed(ni)) {
|
if (NInoSparse(ni) || NInoCompressed(ni)) {
|
||||||
|
|
@ -4744,7 +4808,8 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi
|
||||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
/* Resize non-resident attribute */
|
/* Resize non-resident attribute */
|
||||||
return ntfs_non_resident_attr_expand(attr_ni, newsize, prealloc_size, holes, true);
|
return ntfs_non_resident_attr_expand(
|
||||||
|
attr_ni, newsize, prealloc_size, holes, NULL);
|
||||||
} else if (err != -ENOSPC && err != -EPERM) {
|
} else if (err != -ENOSPC && err != -EPERM) {
|
||||||
ntfs_error(sb, "Failed to make attribute non-resident");
|
ntfs_error(sb, "Failed to make attribute non-resident");
|
||||||
goto put_err_out;
|
goto put_err_out;
|
||||||
|
|
@ -4919,13 +4984,14 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
||||||
if (NInoNonResident(ni)) {
|
if (NInoNonResident(ni)) {
|
||||||
if (newsize > i_size) {
|
if (newsize > i_size) {
|
||||||
down_write(&ni->runlist.lock);
|
down_write(&ni->runlist.lock);
|
||||||
err = ntfs_non_resident_attr_expand(ni, newsize, 0,
|
err = ntfs_non_resident_attr_expand(
|
||||||
NVolDisableSparse(ni->vol) ?
|
ni, newsize, 0,
|
||||||
HOLES_NO : HOLES_OK,
|
NVolDisableSparse(ni->vol) ?
|
||||||
false);
|
HOLES_NO : HOLES_OK, ni);
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
} else
|
} else
|
||||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
err = ntfs_non_resident_attr_shrink(
|
||||||
|
ni, newsize, NULL);
|
||||||
} else
|
} else
|
||||||
err = ntfs_resident_attr_resize(ni, newsize, 0,
|
err = ntfs_resident_attr_resize(ni, newsize, 0,
|
||||||
NVolDisableSparse(ni->vol) ?
|
NVolDisableSparse(ni->vol) ?
|
||||||
|
|
@ -4934,7 +5000,9 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size)
|
int ntfs_attr_expand_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
const s64 prealloc_size,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
|
@ -4964,9 +5032,10 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
||||||
|
|
||||||
if (NInoNonResident(ni)) {
|
if (NInoNonResident(ni)) {
|
||||||
if (newsize > ni->data_size || prealloc_size > ni->allocated_size)
|
if (newsize > ni->data_size || prealloc_size > ni->allocated_size)
|
||||||
err = ntfs_non_resident_attr_expand(ni, newsize, prealloc_size,
|
err = ntfs_non_resident_attr_expand(
|
||||||
NVolDisableSparse(ni->vol) ?
|
ni, newsize, prealloc_size,
|
||||||
HOLES_NO : HOLES_OK, true);
|
NVolDisableSparse(ni->vol) ?
|
||||||
|
HOLES_NO : HOLES_OK, locked_ni);
|
||||||
} else
|
} else
|
||||||
err = ntfs_resident_attr_resize(ni, newsize, prealloc_size,
|
err = ntfs_resident_attr_resize(ni, newsize, prealloc_size,
|
||||||
NVolDisableSparse(ni->vol) ?
|
NVolDisableSparse(ni->vol) ?
|
||||||
|
|
@ -4977,6 +5046,12 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
const s64 prealloc_size)
|
||||||
|
{
|
||||||
|
return ntfs_attr_expand_locked(ni, newsize, prealloc_size, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ntfs_attr_truncate_i - resize an ntfs attribute
|
* ntfs_attr_truncate_i - resize an ntfs attribute
|
||||||
* @ni: open ntfs inode to resize
|
* @ni: open ntfs inode to resize
|
||||||
|
|
@ -4989,7 +5064,9 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
||||||
* newly allocated space is marked as not initialised and no real allocation
|
* newly allocated space is marked as not initialised and no real allocation
|
||||||
* on disk is performed.
|
* on disk is performed.
|
||||||
*/
|
*/
|
||||||
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes)
|
int ntfs_attr_truncate_i_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
unsigned int holes,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
@ -5023,15 +5100,23 @@ int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int
|
||||||
|
|
||||||
if (NInoNonResident(ni)) {
|
if (NInoNonResident(ni)) {
|
||||||
if (newsize > ni->data_size)
|
if (newsize > ni->data_size)
|
||||||
err = ntfs_non_resident_attr_expand(ni, newsize, 0, holes, true);
|
err = ntfs_non_resident_attr_expand(
|
||||||
|
ni, newsize, 0, holes, locked_ni);
|
||||||
else
|
else
|
||||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
err = ntfs_non_resident_attr_shrink(
|
||||||
|
ni, newsize, locked_ni);
|
||||||
} else
|
} else
|
||||||
err = ntfs_resident_attr_resize(ni, newsize, 0, holes);
|
err = ntfs_resident_attr_resize(ni, newsize, 0, holes);
|
||||||
ntfs_debug("Return status %d\n", err);
|
ntfs_debug("Return status %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
unsigned int holes)
|
||||||
|
{
|
||||||
|
return ntfs_attr_truncate_i_locked(ni, newsize, holes, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resize an attribute, creating a hole if relevant
|
* Resize an attribute, creating a hole if relevant
|
||||||
*/
|
*/
|
||||||
|
|
@ -5049,10 +5134,11 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
||||||
struct ntfs_volume *vol = ni->vol;
|
struct ntfs_volume *vol = ni->vol;
|
||||||
struct ntfs_attr_search_ctx *ctx;
|
struct ntfs_attr_search_ctx *ctx;
|
||||||
struct runlist_element *rl, *rlc;
|
struct runlist_element *rl, *rlc;
|
||||||
|
struct runlist_element *old_rl = NULL;
|
||||||
s64 vcn = vcn_start, lcn, clu_count;
|
s64 vcn = vcn_start, lcn, clu_count;
|
||||||
s64 lcn_seek_from = -1;
|
s64 lcn_seek_from = -1;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
size_t new_rl_count;
|
size_t new_rl_count, old_rl_count;
|
||||||
|
|
||||||
err = ntfs_attr_map_whole_runlist(ni);
|
err = ntfs_attr_map_whole_runlist(ni);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -5145,6 +5231,19 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
||||||
WARN_ON(rlc->vcn != vcn);
|
WARN_ON(rlc->vcn != vcn);
|
||||||
lcn = rlc->lcn;
|
lcn = rlc->lcn;
|
||||||
clu_count = rlc->length;
|
clu_count = rlc->length;
|
||||||
|
old_rl_count = ni->runlist.count;
|
||||||
|
old_rl = kmemdup(ni->runlist.rl,
|
||||||
|
old_rl_count * sizeof(*old_rl), GFP_NOFS);
|
||||||
|
if (!old_rl) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
if (ntfs_cluster_free_from_rl(vol, rlc)) {
|
||||||
|
ntfs_error(vol->sb,
|
||||||
|
"Failed to free cluster allocation after runlist backup failure.");
|
||||||
|
NVolSetErrors(vol);
|
||||||
|
}
|
||||||
|
kvfree(rlc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count);
|
rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count);
|
||||||
if (IS_ERR(rl)) {
|
if (IS_ERR(rl)) {
|
||||||
|
|
@ -5168,15 +5267,32 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
||||||
|
|
||||||
if (update_mp) {
|
if (update_mp) {
|
||||||
ntfs_attr_reinit_search_ctx(ctx);
|
ntfs_attr_reinit_search_ctx(ctx);
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
if (err) {
|
if (err) {
|
||||||
int err2;
|
int err2;
|
||||||
|
|
||||||
err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx);
|
err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx);
|
||||||
if (err2 < 0)
|
if (err2 < 0 || err2 != clu_count) {
|
||||||
ntfs_error(vol->sb,
|
ntfs_error(vol->sb,
|
||||||
"Failed to free cluster allocation. Leaving inconstant metadata.\n");
|
"Failed to free cluster allocation. Leaving inconsistent metadata.\n");
|
||||||
goto out;
|
NVolSetErrors(vol);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore the runlist before repairing the on-disk
|
||||||
|
* mapping pairs.
|
||||||
|
*/
|
||||||
|
kvfree(ni->runlist.rl);
|
||||||
|
ni->runlist.rl = old_rl;
|
||||||
|
ni->runlist.count = old_rl_count;
|
||||||
|
old_rl = NULL;
|
||||||
|
if (ntfs_attr_update_mapping_pairs_locked(
|
||||||
|
ni, 0, ni)) {
|
||||||
|
ntfs_error(vol->sb,
|
||||||
|
"Failed to restore mapping pairs after allocation rollback.\n");
|
||||||
|
NVolSetErrors(vol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
VFS_I(ni)->i_blocks += clu_count << (vol->cluster_size_bits - 9);
|
VFS_I(ni)->i_blocks += clu_count << (vol->cluster_size_bits - 9);
|
||||||
|
|
@ -5188,6 +5304,7 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
||||||
*lcn_count = clu_count;
|
*lcn_count = clu_count;
|
||||||
*balloc = true;
|
*balloc = true;
|
||||||
out:
|
out:
|
||||||
|
kvfree(old_rl);
|
||||||
ntfs_attr_put_search_ctx(ctx);
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -5431,7 +5548,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6
|
||||||
ni->data_size += ntfs_cluster_to_bytes(vol, len);
|
ni->data_size += ntfs_cluster_to_bytes(vol, len);
|
||||||
if (ntfs_cluster_to_bytes(vol, start_vcn) < ni->initialized_size)
|
if (ntfs_cluster_to_bytes(vol, start_vcn) < ni->initialized_size)
|
||||||
ni->initialized_size += ntfs_cluster_to_bytes(vol, len);
|
ni->initialized_size += ntfs_cluster_to_bytes(vol, len);
|
||||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -5516,7 +5633,7 @@ int ntfs_non_resident_attr_collapse_range(struct ntfs_inode *ni, s64 start_vcn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ni->allocated_size > 0) {
|
if (ni->allocated_size > 0) {
|
||||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
goto out_rl;
|
goto out_rl;
|
||||||
|
|
@ -5594,7 +5711,7 @@ int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64
|
||||||
ni->runlist.rl = rl;
|
ni->runlist.rl = rl;
|
||||||
ni->runlist.count = new_rl_count;
|
ni->runlist.count = new_rl_count;
|
||||||
|
|
||||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvfree(punch_rl);
|
kvfree(punch_rl);
|
||||||
|
|
@ -5770,7 +5887,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
||||||
if (NInoRunlistDirty(ni)) {
|
if (NInoRunlistDirty(ni)) {
|
||||||
mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
|
mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
|
||||||
down_write(&ni->runlist.lock);
|
down_write(&ni->runlist.lock);
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
if (err)
|
if (err)
|
||||||
ntfs_error(ni->vol->sb, "Updating mapping pairs failed");
|
ntfs_error(ni->vol->sb, "Updating mapping pairs failed");
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,13 @@ int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64
|
||||||
int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
||||||
const s64 i_size);
|
const s64 i_size);
|
||||||
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size);
|
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size);
|
||||||
|
int ntfs_attr_expand_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
const s64 prealloc_size,
|
||||||
|
struct ntfs_inode *locked_ni);
|
||||||
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes);
|
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes);
|
||||||
|
int ntfs_attr_truncate_i_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||||
|
unsigned int holes,
|
||||||
|
struct ntfs_inode *locked_ni);
|
||||||
int ntfs_attr_truncate(struct ntfs_inode *ni, const s64 newsize);
|
int ntfs_attr_truncate(struct ntfs_inode *ni, const s64 newsize);
|
||||||
int ntfs_attr_rm(struct ntfs_inode *ni);
|
int ntfs_attr_rm(struct ntfs_inode *ni);
|
||||||
int ntfs_attr_exist(struct ntfs_inode *ni, const __le32 type, __le16 *name,
|
int ntfs_attr_exist(struct ntfs_inode *ni, const __le32 type, __le16 *name,
|
||||||
|
|
@ -133,6 +139,9 @@ int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
|
||||||
__le16 *name, u8 name_len, u8 *val, u32 size,
|
__le16 *name, u8 name_len, u8 *val, u32 size,
|
||||||
__le16 flags);
|
__le16 flags);
|
||||||
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn);
|
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn);
|
||||||
|
int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
|
||||||
|
s64 from_vcn,
|
||||||
|
struct ntfs_inode *locked_ni);
|
||||||
struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn);
|
struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ int ntfs_attrlist_need(struct ntfs_inode *ni)
|
||||||
* legal list size as a reserve.
|
* legal list size as a reserve.
|
||||||
*/
|
*/
|
||||||
static int ntfs_attrlist_repack(struct inode *attr_vi,
|
static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
struct ntfs_inode *attr_ni, s64 min_alloc_size)
|
struct ntfs_inode *attr_ni, s64 min_alloc_size,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
struct ntfs_volume *vol = attr_ni->vol;
|
struct ntfs_volume *vol = attr_ni->vol;
|
||||||
struct runlist_element *old_rl, *new_rl;
|
struct runlist_element *old_rl, *new_rl;
|
||||||
|
|
@ -78,10 +79,12 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
size_t old_rl_count, new_rl_count;
|
size_t old_rl_count, new_rl_count;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int err, restore_err;
|
int err, restore_err;
|
||||||
|
|
||||||
if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
|
if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
|
||||||
min_alloc_size < 0)
|
min_alloc_size < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
/* The buffered I/O below can reacquire the attribute runlist lock. */
|
||||||
|
if (attr_ni == locked_ni)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
err = ntfs_attr_map_whole_runlist(attr_ni);
|
err = ntfs_attr_map_whole_runlist(attr_ni);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -131,7 +134,6 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
err = -ENOSPC;
|
err = -ENOSPC;
|
||||||
goto out_free_data;
|
goto out_free_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_rl = attr_ni->runlist.rl;
|
old_rl = attr_ni->runlist.rl;
|
||||||
old_rl_count = attr_ni->runlist.count;
|
old_rl_count = attr_ni->runlist.count;
|
||||||
down_write(&attr_ni->runlist.lock);
|
down_write(&attr_ni->runlist.lock);
|
||||||
|
|
@ -152,7 +154,7 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ntfs_attr_update_mapping_pairs(attr_ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(attr_ni, 0, locked_ni);
|
||||||
if (err)
|
if (err)
|
||||||
goto restore_old_runlist;
|
goto restore_old_runlist;
|
||||||
|
|
||||||
|
|
@ -177,7 +179,8 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
attr_ni->allocated_size = old_alloc_size;
|
attr_ni->allocated_size = old_alloc_size;
|
||||||
write_unlock_irqrestore(&attr_ni->size_lock, flags);
|
write_unlock_irqrestore(&attr_ni->size_lock, flags);
|
||||||
|
|
||||||
restore_err = ntfs_attr_update_mapping_pairs(attr_ni, 0);
|
restore_err = ntfs_attr_update_mapping_pairs_locked(
|
||||||
|
attr_ni, 0, locked_ni);
|
||||||
if (restore_err) {
|
if (restore_err) {
|
||||||
ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
|
ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
|
||||||
restore_err);
|
restore_err);
|
||||||
|
|
@ -193,7 +196,8 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
struct inode *attr_vi;
|
struct inode *attr_vi;
|
||||||
struct ntfs_inode *attr_ni;
|
struct ntfs_inode *attr_ni;
|
||||||
|
|
@ -215,20 +219,27 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
attr_ni = NTFS_I(attr_vi);
|
attr_ni = NTFS_I(attr_vi);
|
||||||
|
/* Truncation and page-cache writes can reacquire this runlist lock. */
|
||||||
|
if (attr_ni == locked_ni) {
|
||||||
|
iput(attr_vi);
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO);
|
err = ntfs_attr_truncate_i_locked(
|
||||||
|
attr_ni, base_ni->attr_list_size, HOLES_NO, locked_ni);
|
||||||
if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT &&
|
if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT &&
|
||||||
NInoNonResident(attr_ni)) {
|
NInoNonResident(attr_ni)) {
|
||||||
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
||||||
base_ni->attr_list_size);
|
base_ni->attr_list_size, locked_ni);
|
||||||
if (retry_err) {
|
if (retry_err) {
|
||||||
ntfs_error(base_ni->vol->sb, "Failed to repack attribute list");
|
ntfs_error(base_ni->vol->sb, "Failed to repack attribute list");
|
||||||
iput(attr_vi);
|
iput(attr_vi);
|
||||||
return retry_err;
|
return retry_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size,
|
retry_err = ntfs_attr_truncate_i_locked(
|
||||||
HOLES_NO);
|
attr_ni, base_ni->attr_list_size,
|
||||||
|
HOLES_NO, locked_ni);
|
||||||
if (retry_err) {
|
if (retry_err) {
|
||||||
ntfs_error(base_ni->vol->sb,
|
ntfs_error(base_ni->vol->sb,
|
||||||
"Failed to resize attribute list after repack");
|
"Failed to resize attribute list after repack");
|
||||||
|
|
@ -252,11 +263,13 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||||
*/
|
*/
|
||||||
if (base_ni->mft_no == FILE_MFT && NInoNonResident(attr_ni) &&
|
if (base_ni->mft_no == FILE_MFT && NInoNonResident(attr_ni) &&
|
||||||
attr_ni->allocated_size < NTFS_MAX_ATTR_LIST_SIZE) {
|
attr_ni->allocated_size < NTFS_MAX_ATTR_LIST_SIZE) {
|
||||||
retry_err = ntfs_attr_expand(attr_ni, base_ni->attr_list_size,
|
retry_err = ntfs_attr_expand_locked(
|
||||||
NTFS_MAX_ATTR_LIST_SIZE);
|
attr_ni, base_ni->attr_list_size,
|
||||||
|
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||||
if (retry_err == -ENOSPC) {
|
if (retry_err == -ENOSPC) {
|
||||||
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
retry_err = ntfs_attrlist_repack(
|
||||||
NTFS_MAX_ATTR_LIST_SIZE);
|
attr_vi, attr_ni,
|
||||||
|
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||||
if (retry_err == -ENOSPC)
|
if (retry_err == -ENOSPC)
|
||||||
retry_err = 0;
|
retry_err = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +302,11 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||||
|
{
|
||||||
|
return ntfs_attrlist_update_locked(base_ni, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||||
* @ni: opened ntfs inode, which contains that attribute
|
* @ni: opened ntfs inode, which contains that attribute
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,7 @@ int ntfs_attrlist_need(struct ntfs_inode *ni);
|
||||||
int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr);
|
int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr);
|
||||||
int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx);
|
int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx);
|
||||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni);
|
int ntfs_attrlist_update(struct ntfs_inode *base_ni);
|
||||||
|
int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
|
||||||
|
struct ntfs_inode *locked_ni);
|
||||||
|
|
||||||
#endif /* defined _NTFS_ATTRLIST_H */
|
#endif /* defined _NTFS_ATTRLIST_H */
|
||||||
|
|
|
||||||
|
|
@ -1450,7 +1450,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
||||||
ni->runlist.rl = rl;
|
ni->runlist.rl = rl;
|
||||||
rlc = NULL;
|
rlc = NULL;
|
||||||
|
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
if (err)
|
if (err)
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,8 @@ static int ntfs_trim_prealloc(struct inode *vi)
|
||||||
ntfs_error(vol->sb, "Preallocated block rollback failed");
|
ntfs_error(vol->sb, "Preallocated block rollback failed");
|
||||||
} else {
|
} else {
|
||||||
ni->allocated_size = ntfs_cluster_to_bytes(vol, vcn_tr);
|
ni->allocated_size = ntfs_cluster_to_bytes(vol, vcn_tr);
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(
|
||||||
|
ni, 0, ni);
|
||||||
if (err)
|
if (err)
|
||||||
ntfs_error(vol->sb,
|
ntfs_error(vol->sb,
|
||||||
"Failed to rollback mapping pairs for prealloc");
|
"Failed to rollback mapping pairs for prealloc");
|
||||||
|
|
|
||||||
|
|
@ -2772,7 +2772,7 @@ int __ntfs_write_inode(struct inode *vi, int sync)
|
||||||
|
|
||||||
if (NInoNonResident(ni) && NInoRunlistDirty(ni)) {
|
if (NInoNonResident(ni) && NInoRunlistDirty(ni)) {
|
||||||
down_write(&ni->runlist.lock);
|
down_write(&ni->runlist.lock);
|
||||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||||
if (!err)
|
if (!err)
|
||||||
NInoClearRunlistDirty(ni);
|
NInoClearRunlistDirty(ni);
|
||||||
up_write(&ni->runlist.lock);
|
up_write(&ni->runlist.lock);
|
||||||
|
|
|
||||||
|
|
@ -1131,7 +1131,8 @@ static s64 mft_bitmap_alloc_free_rec(struct ntfs_volume *vol,
|
||||||
return ll;
|
return ll;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ntfs_mft_attr_extend(struct ntfs_inode *ni)
|
static int ntfs_mft_attr_extend(struct ntfs_inode *ni,
|
||||||
|
struct ntfs_inode *locked_ni)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct ntfs_inode *base_ni;
|
struct ntfs_inode *base_ni;
|
||||||
|
|
@ -1152,7 +1153,7 @@ static int ntfs_mft_attr_extend(struct ntfs_inode *ni)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, locked_ni);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("MP update failed\n");
|
pr_err("MP update failed\n");
|
||||||
|
|
||||||
|
|
@ -1340,7 +1341,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
||||||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||||
if (unlikely(ret)) {
|
if (unlikely(ret)) {
|
||||||
ret = ntfs_mft_attr_extend(mftbmp_ni);
|
ret = ntfs_mft_attr_extend(mftbmp_ni, mftbmp_ni);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
goto extended_ok;
|
goto extended_ok;
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
|
|
@ -1451,7 +1452,9 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
||||||
NVolSetErrors(vol);
|
NVolSetErrors(vol);
|
||||||
}
|
}
|
||||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||||
} else if (status.mp_extended && ntfs_attr_update_mapping_pairs(mftbmp_ni, 0)) {
|
} else if (status.mp_extended &&
|
||||||
|
ntfs_attr_update_mapping_pairs_locked(mftbmp_ni, 0,
|
||||||
|
mftbmp_ni)) {
|
||||||
ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es);
|
ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es);
|
||||||
NVolSetErrors(vol);
|
NVolSetErrors(vol);
|
||||||
}
|
}
|
||||||
|
|
@ -1776,7 +1779,7 @@ static int ntfs_mft_data_extend_allocation_nolock(struct ntfs_volume *vol)
|
||||||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||||
if (unlikely(ret)) {
|
if (unlikely(ret)) {
|
||||||
ret = ntfs_mft_attr_extend(mft_ni);
|
ret = ntfs_mft_attr_extend(mft_ni, NULL);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
goto extended_ok;
|
goto extended_ok;
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user