mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
- Make MFT extension work on existing Windows-created volumes by
dynamically reserving MFT tail records, accounting for records added
during allocation, and avoiding false -ENOSPC failures.
- Repack non-resident $MFT/$ATTRIBUTE_LIST when its mapping pairs no longer
fit in the base MFT record, while propagating allocation and writeback
errors.
- Serialize runlist updates with the runlist lock and restore both the
in-memory runlist and on-disk mapping pairs when allocation rollback is
required.
- Propagate folio errors and harden inode failure handling by treating
interrupted reads as transient failures and discarding and unhashing
inodes whose initialization fails.
- Fix the $MFTMirr write offset when mirror records span multiple folios,
preventing mirror records from overwriting the first record with large
MFT record sizes.
-----BEGIN PGP SIGNATURE-----
iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmqtRIEWHGxpbmtpbmpl
b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCBK8EAChFdTxig3sYog3dL27SX+1yNC4
S1QtSQMvPJzcvLG2/mESC57snx1u6AXZ6enaQ/m8zQQvkWHFdwD+odgjOQ3474Yc
MS7xQn5pCsAo3LmSWiDsQfHmvxgDMYlIRU1vmqr7fG2pj+W6BR2LB1PD3RI9exI7
0WWAXBPZH5w5C9GE1Zo7TF9Xwby5Or31RS8+R57PXA/PJ1ivpWnlkpfLi4M/YkZK
GIpunZafSpcKbEsuWcjhdz11bR4G9Qlwuuq0MDguLC/qsqsobHCeSbdx+4IsEAq5
02yBl5hYm2E4u2KBedpe7oRwFvlPN0uakEGYS8SA1ad9XamjGIw6T0tkzkDL48Pq
dhVAeX2oa8O9u+VK+qF/HIUylh/UbmHQJW8iSiZWO8WdULGBG8oCHI1hcSnMguwJ
njyK75UXz4fMsKW6ZpRu0sRGqtKKcbg8IrCvLslPIOS2A9OAwSzytDKI+x1Kbgu0
SVPYjf6XeOz83tvE+2OhfTT1hWkeKezMiUe4E/y9rgEDxv5vE4C5pvpDfRlj3oyn
2JTXXjjUQGSQw/9cKbLsbElDH/FLEojLAsIFgM+2FbcG+x7PUUgSGdC4R4qZ9MUF
cuMzfhi8vKyCYmJc8nNE4J6b6UkBNOFJMYBcArtByFW3LqfIzmqwW81Xx0Hz4cxi
dmOhD6gXXYoi9m3lBQ==
=rT1L
-----END PGP SIGNATURE-----
Merge tag 'ntfs-for-7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs
Pull ntfs fixes from Namjae Jeon:
- Make MFT extension work on existing Windows-created volumes by
dynamically reserving MFT tail records, accounting for records added
during allocation, and avoiding false -ENOSPC failures
- Repack non-resident $MFT/$ATTRIBUTE_LIST when its mapping pairs no
longer fit in the base MFT record, while propagating allocation and
writeback errors
- Serialize runlist updates with the runlist lock and restore both the
in-memory runlist and on-disk mapping pairs when allocation rollback
is required
- Propagate folio errors and harden inode failure handling by treating
interrupted reads as transient failures and discarding and unhashing
inodes whose initialization fails
- Fix the $MFTMirr write offset when mirror records span multiple
folios, preventing mirror records from overwriting the first record
with large MFT record sizes
* tag 'ntfs-for-7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs:
ntfs: fix $MFTMirr write offset when it spans multiple folios
ntfs: unhash failed inode reads
ntfs: discard inodes that fail initialization
ntfs: ignore interrupted inode reads as corruption
ntfs: propagate folio errors
ntfs: protect runlist updates with the runlist lock
ntfs: account for MFT records added during allocation
ntfs: repack $MFT/$ATTRIBUTE LIST
ntfs: use dynamic MFT tail reservation
This commit is contained in:
commit
bfda5a01aa
259
fs/ntfs/attrib.c
259
fs/ntfs/attrib.c
|
|
@ -2917,7 +2917,7 @@ int ntfs_attr_add(struct ntfs_inode *ni, __le32 type,
|
|||
|
||||
attr_ni = NULL;
|
||||
/* Allocate new extent. */
|
||||
err = ntfs_mft_record_alloc(ni->vol, 0, &attr_ni, ni, NULL);
|
||||
err = ntfs_mft_record_alloc(ni->vol, 0, &attr_ni, ni, NULL, -1);
|
||||
if (err) {
|
||||
ntfs_error(sb, "Failed to allocate extent record");
|
||||
goto err_out;
|
||||
|
|
@ -3550,7 +3550,7 @@ int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra)
|
|||
* new extent and move attribute to it.
|
||||
*/
|
||||
ni = NULL;
|
||||
err = ntfs_mft_record_alloc(base_ni->vol, 0, &ni, base_ni, NULL);
|
||||
err = ntfs_mft_record_alloc(base_ni->vol, 0, &ni, base_ni, NULL, -1);
|
||||
if (err) {
|
||||
ntfs_error(sb, "Couldn't allocate MFT record, err : %d", err);
|
||||
return err;
|
||||
|
|
@ -3574,7 +3574,8 @@ int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra)
|
|||
* update allocated and compressed size.
|
||||
*/
|
||||
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;
|
||||
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) &&
|
||||
!(le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use))) {
|
||||
|
||||
if (defer_attrlist)
|
||||
return -ENOSPC;
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
err = ntfs_inode_add_attrlist(base_ni);
|
||||
if (err)
|
||||
|
|
@ -3623,7 +3626,7 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_attrlist_update(base_ni);
|
||||
err = ntfs_attrlist_update_locked(base_ni, locked_ni);
|
||||
if (err)
|
||||
goto out;
|
||||
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
|
||||
* @ni: non-resident ntfs inode for which we need update
|
||||
* @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
|
||||
* 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
|
||||
* 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_inode *base_ni;
|
||||
|
|
@ -3804,7 +3812,8 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
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 == -EAGAIN) {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
|
@ -3844,18 +3853,28 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
*/
|
||||
if (ni->type == AT_ATTRIBUTE_LIST) {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (ntfs_inode_free_space(base_ni, mp_size -
|
||||
cur_max_mp_size)) {
|
||||
ntfs_debug("Attribute list is too big. Defragment the volume\n");
|
||||
return -ENOSPC;
|
||||
ctx = NULL;
|
||||
if (locked_ni == ni || defer_attrlist) {
|
||||
err = -ENOSPC;
|
||||
goto put_err_out;
|
||||
}
|
||||
if (ntfs_attrlist_update(base_ni))
|
||||
return -EIO;
|
||||
err = ntfs_inode_free_space(base_ni, mp_size -
|
||||
cur_max_mp_size);
|
||||
if (err)
|
||||
return err;
|
||||
err = ntfs_attrlist_update_locked(
|
||||
base_ni, locked_ni);
|
||||
if (err)
|
||||
return err;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Add attribute list if it isn't present, and retry. */
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
if (defer_attrlist) {
|
||||
err = -ENOSPC;
|
||||
goto put_err_out;
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (ntfs_inode_add_attrlist(base_ni)) {
|
||||
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. */
|
||||
a->data.non_resident.lowest_vcn = cpu_to_le64(stop_vcn);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
if ((ctx->ntfs_ino->nr_extents == -1 || NInoAttrList(ctx->ntfs_ino)) &&
|
||||
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
||||
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)
|
||||
goto put_err_out;
|
||||
}
|
||||
|
|
@ -3976,7 +4003,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
unsigned int de_cnt = 0;
|
||||
|
||||
/* Allocate new mft record. */
|
||||
err = ntfs_mft_record_alloc(ni->vol, 0, &ext_ni, base_ni, NULL);
|
||||
err = ntfs_mft_record_alloc(ni->vol, 0, &ext_ni, base_ni, NULL,
|
||||
base_ni->mft_no == FILE_MFT &&
|
||||
ni->type == AT_DATA &&
|
||||
ni->name == AT_UNNAMED ? stop_vcn : -1);
|
||||
if (err) {
|
||||
ntfs_error(sb, "Failed to allocate extent record");
|
||||
goto put_err_out;
|
||||
|
|
@ -4061,6 +4091,19 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
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
|
||||
* @ni: open ntfs attribute to make resident
|
||||
|
|
@ -4194,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.
|
||||
*/
|
||||
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_attr_search_ctx *ctx;
|
||||
|
|
@ -4202,6 +4247,7 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
s64 nr_freed_clusters;
|
||||
int err;
|
||||
struct ntfs_inode *base_ni;
|
||||
bool runlist_locked = locked_ni == ni;
|
||||
|
||||
ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n",
|
||||
(unsigned long long)ni->mft_no, ni->type, (long long)newsize);
|
||||
|
|
@ -4247,18 +4293,24 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
* clusters if there is a change.
|
||||
*/
|
||||
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);
|
||||
if (err) {
|
||||
ntfs_debug("Eeek! ntfs_attr_map_whole_runlist failed.\n");
|
||||
return err;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
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. */
|
||||
|
|
@ -4266,7 +4318,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
if (nr_freed_clusters < 0) {
|
||||
ntfs_debug("Eeek! Freeing of clusters failed. Aborting...\n");
|
||||
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);
|
||||
|
||||
|
|
@ -4279,7 +4332,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
kvfree(ni->runlist.rl);
|
||||
ni->runlist.rl = NULL;
|
||||
ntfs_error(vol->sb, "Eeek! Run list truncation failed.\n");
|
||||
return -EIO;
|
||||
err = -EIO;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
|
||||
/* Prepare to mapping pairs update. */
|
||||
|
|
@ -4295,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;
|
||||
|
||||
/* 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) {
|
||||
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. */
|
||||
|
|
@ -4341,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 (!newsize && !NInoEncrypted(ni) && !NInoCompressed(ni)) {
|
||||
if (!runlist_locked)
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_attr_make_resident(ni, ctx);
|
||||
if (!runlist_locked)
|
||||
up_write(&ni->runlist.lock);
|
||||
if (err) {
|
||||
/* If couldn't make resident, just continue. */
|
||||
if (err != -EPERM)
|
||||
|
|
@ -4358,6 +4418,11 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
put_err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return err;
|
||||
|
||||
unlock_runlist:
|
||||
if (!runlist_locked)
|
||||
up_write(&ni->runlist.lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -4366,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
|
||||
* @newsize: new size (in bytes) to which to expand the attribute
|
||||
* @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,
|
||||
* by allocating new clusters.
|
||||
*/
|
||||
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 first_free_vcn;
|
||||
|
|
@ -4519,13 +4585,39 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
ntfs_bytes_to_cluster(vol, ni->allocated_size),
|
||||
first_free_vcn -
|
||||
ntfs_bytes_to_cluster(vol, ni->allocated_size),
|
||||
lcn_seek_from, DATA_ZONE, false, false, false);
|
||||
lcn_seek_from, DATA_ZONE, false,
|
||||
ni->type == AT_ATTRIBUTE_LIST, false);
|
||||
if (IS_ERR(rl)) {
|
||||
ntfs_debug("Cluster allocation failed (%lld)",
|
||||
(long long)first_free_vcn -
|
||||
ntfs_bytes_to_cluster(vol, ni->allocated_size));
|
||||
return PTR_ERR(rl);
|
||||
}
|
||||
/*
|
||||
* A contiguous ATTRIBUTE_LIST allocation keeps its mapping
|
||||
* pairs small enough to fit in the base MFT record. The
|
||||
* allocator can return a short run when contiguity was
|
||||
* requested, so discard it and retry normally if necessary.
|
||||
*/
|
||||
if (ni->type == AT_ATTRIBUTE_LIST &&
|
||||
(rl->vcn != ntfs_bytes_to_cluster(vol,
|
||||
ni->allocated_size) ||
|
||||
rl->length != first_free_vcn -
|
||||
ntfs_bytes_to_cluster(vol, ni->allocated_size) ||
|
||||
rl[1].length)) {
|
||||
ntfs_cluster_free_from_rl(vol, rl);
|
||||
kvfree(rl);
|
||||
rl = ntfs_cluster_alloc(vol,
|
||||
ntfs_bytes_to_cluster(vol,
|
||||
ni->allocated_size),
|
||||
first_free_vcn -
|
||||
ntfs_bytes_to_cluster(vol,
|
||||
ni->allocated_size),
|
||||
lcn_seek_from, DATA_ZONE, false,
|
||||
false, false);
|
||||
if (IS_ERR(rl))
|
||||
return PTR_ERR(rl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!NInoCompressed(ni)) {
|
||||
|
|
@ -4544,7 +4636,8 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
|
||||
/* Prepare to mapping pairs update. */
|
||||
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) {
|
||||
ntfs_debug("Mapping pairs update failed");
|
||||
goto rollback;
|
||||
|
|
@ -4588,11 +4681,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
ntfs_debug("Leaking clusters");
|
||||
|
||||
/* Now, truncate the runlist itself. */
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
down_write(&ni->runlist.lock);
|
||||
err2 = ntfs_rl_truncate_nolock(vol, &ni->runlist,
|
||||
ntfs_bytes_to_cluster(vol, org_alloc_size));
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
up_write(&ni->runlist.lock);
|
||||
if (err2) {
|
||||
/*
|
||||
|
|
@ -4606,11 +4699,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
/* Prepare to mapping pairs update. */
|
||||
ni->allocated_size = org_alloc_size;
|
||||
/* Restore mapping pairs. */
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
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");
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
up_read(&ni->runlist.lock);
|
||||
|
||||
if (NInoSparse(ni) || NInoCompressed(ni)) {
|
||||
|
|
@ -4715,7 +4808,8 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi
|
|||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
/* 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) {
|
||||
ntfs_error(sb, "Failed to make attribute non-resident");
|
||||
goto put_err_out;
|
||||
|
|
@ -4836,7 +4930,7 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi
|
|||
}
|
||||
|
||||
/* Allocate new mft record. */
|
||||
err = ntfs_mft_record_alloc(base_ni->vol, 0, &ext_ni, base_ni, NULL);
|
||||
err = ntfs_mft_record_alloc(base_ni->vol, 0, &ext_ni, base_ni, NULL, -1);
|
||||
if (err) {
|
||||
ntfs_error(sb, "Couldn't allocate MFT record");
|
||||
goto put_err_out;
|
||||
|
|
@ -4890,13 +4984,14 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
|||
if (NInoNonResident(ni)) {
|
||||
if (newsize > i_size) {
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_non_resident_attr_expand(ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK,
|
||||
false);
|
||||
err = ntfs_non_resident_attr_expand(
|
||||
ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
} else
|
||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, NULL);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
|
|
@ -4905,7 +5000,9 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
|||
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;
|
||||
|
||||
|
|
@ -4918,7 +5015,8 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
ntfs_debug("Entering for inode 0x%llx, attr 0x%x, size %lld\n",
|
||||
(unsigned long long)ni->mft_no, ni->type, newsize);
|
||||
|
||||
if (ni->data_size == newsize) {
|
||||
if (ni->data_size == newsize &&
|
||||
(!prealloc_size || prealloc_size <= ni->allocated_size)) {
|
||||
ntfs_debug("Size is already ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4933,10 +5031,11 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
}
|
||||
|
||||
if (NInoNonResident(ni)) {
|
||||
if (newsize > ni->data_size)
|
||||
err = ntfs_non_resident_attr_expand(ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, true);
|
||||
if (newsize > ni->data_size || prealloc_size > ni->allocated_size)
|
||||
err = ntfs_non_resident_attr_expand(
|
||||
ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, locked_ni);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
|
|
@ -4947,6 +5046,12 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
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
|
||||
* @ni: open ntfs inode to resize
|
||||
|
|
@ -4959,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
|
||||
* 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;
|
||||
|
||||
|
|
@ -4993,15 +5100,23 @@ int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int
|
|||
|
||||
if (NInoNonResident(ni)) {
|
||||
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
|
||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, locked_ni);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0, holes);
|
||||
ntfs_debug("Return status %d\n", 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
|
||||
*/
|
||||
|
|
@ -5019,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_attr_search_ctx *ctx;
|
||||
struct runlist_element *rl, *rlc;
|
||||
struct runlist_element *old_rl = NULL;
|
||||
s64 vcn = vcn_start, lcn, clu_count;
|
||||
s64 lcn_seek_from = -1;
|
||||
int err = 0;
|
||||
size_t new_rl_count;
|
||||
size_t new_rl_count, old_rl_count;
|
||||
|
||||
err = ntfs_attr_map_whole_runlist(ni);
|
||||
if (err)
|
||||
|
|
@ -5115,6 +5231,19 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
WARN_ON(rlc->vcn != vcn);
|
||||
lcn = rlc->lcn;
|
||||
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);
|
||||
if (IS_ERR(rl)) {
|
||||
|
|
@ -5138,15 +5267,32 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
|
||||
if (update_mp) {
|
||||
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) {
|
||||
int err2;
|
||||
|
||||
err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx);
|
||||
if (err2 < 0)
|
||||
if (err2 < 0 || err2 != clu_count) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to free cluster allocation. Leaving inconstant metadata.\n");
|
||||
goto out;
|
||||
"Failed to free cluster allocation. Leaving inconsistent metadata.\n");
|
||||
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 {
|
||||
VFS_I(ni)->i_blocks += clu_count << (vol->cluster_size_bits - 9);
|
||||
|
|
@ -5158,6 +5304,7 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
*lcn_count = clu_count;
|
||||
*balloc = true;
|
||||
out:
|
||||
kvfree(old_rl);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -5401,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);
|
||||
if (ntfs_cluster_to_bytes(vol, start_vcn) < ni->initialized_size)
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -5486,7 +5633,7 @@ int ntfs_non_resident_attr_collapse_range(struct ntfs_inode *ni, s64 start_vcn,
|
|||
}
|
||||
|
||||
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) {
|
||||
up_write(&ni->runlist.lock);
|
||||
goto out_rl;
|
||||
|
|
@ -5564,7 +5711,7 @@ int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64
|
|||
ni->runlist.rl = rl;
|
||||
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);
|
||||
if (ret) {
|
||||
kvfree(punch_rl);
|
||||
|
|
@ -5740,7 +5887,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
|||
if (NInoRunlistDirty(ni)) {
|
||||
mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
|
||||
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)
|
||||
ntfs_error(ni->vol->sb, "Updating mapping pairs failed");
|
||||
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,
|
||||
const s64 i_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_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_rm(struct ntfs_inode *ni);
|
||||
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 flags);
|
||||
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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#include "mft.h"
|
||||
#include "attrib.h"
|
||||
#include "attrlist.h"
|
||||
#include "lcnalloc.h"
|
||||
|
||||
#define NTFS_MAX_ATTR_LIST_SIZE (256 * 1024)
|
||||
|
||||
/*
|
||||
* ntfs_attrlist_need - check whether inode need attribute list
|
||||
|
|
@ -51,11 +54,155 @@ int ntfs_attrlist_need(struct ntfs_inode *ni)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||
/*
|
||||
* Repack the $MFT/$ATTRIBUTE_LIST data into one run.
|
||||
*
|
||||
* The mapping pairs for an $ATTRIBUTE_LIST must remain in the base MFT
|
||||
* record. Once that record has no room left, extending a fragmented list
|
||||
* can require one more mapping-pairs byte than the record can hold. There
|
||||
* is no attribute that can legally be moved out in that state: $STANDARD_
|
||||
* INFORMATION, $ATTRIBUTE_LIST, and the first $MFT/$DATA extent all have to
|
||||
* stay in the base record. Move the list data to one contiguous run. The
|
||||
* caller supplies the minimum allocation size so a recovery can use the
|
||||
* smallest useful run while normal updates can still request the maximum
|
||||
* legal list size as a reserve.
|
||||
*/
|
||||
static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||
struct ntfs_inode *attr_ni, s64 min_alloc_size,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
struct ntfs_volume *vol = attr_ni->vol;
|
||||
struct runlist_element *old_rl, *new_rl;
|
||||
u8 *data = NULL;
|
||||
s64 data_size, alloc_size, nr_clusters, written;
|
||||
s64 old_alloc_size;
|
||||
size_t old_rl_count, new_rl_count;
|
||||
unsigned long flags;
|
||||
int err, restore_err;
|
||||
if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
|
||||
min_alloc_size < 0)
|
||||
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);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data_size = attr_ni->data_size;
|
||||
if (data_size < 0)
|
||||
return -EIO;
|
||||
|
||||
if (data_size) {
|
||||
data = kvmalloc(data_size, GFP_NOFS);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
written = ntfs_inode_attr_pread(attr_vi, 0, data_size, data);
|
||||
if (written != data_size) {
|
||||
err = written < 0 ? (int)written : -EIO;
|
||||
goto out_free_data;
|
||||
}
|
||||
}
|
||||
|
||||
old_alloc_size = attr_ni->allocated_size;
|
||||
alloc_size = max_t(s64, old_alloc_size, min_alloc_size);
|
||||
nr_clusters = ntfs_bytes_to_cluster(vol,
|
||||
alloc_size + vol->cluster_size - 1);
|
||||
if (nr_clusters <= 0) {
|
||||
err = -EFBIG;
|
||||
goto out_free_data;
|
||||
}
|
||||
|
||||
/* A single run keeps the mapping pairs at the minimum size. */
|
||||
new_rl = ntfs_cluster_alloc(vol, 0, nr_clusters, -1, DATA_ZONE,
|
||||
true, true, false);
|
||||
if (IS_ERR(new_rl)) {
|
||||
err = PTR_ERR(new_rl);
|
||||
goto out_free_data;
|
||||
}
|
||||
|
||||
new_rl_count = 0;
|
||||
if (new_rl->vcn == 0 && new_rl->length == nr_clusters &&
|
||||
!new_rl[1].length)
|
||||
new_rl_count = 2;
|
||||
|
||||
if (new_rl_count != 2) {
|
||||
ntfs_cluster_free_from_rl(vol, new_rl);
|
||||
kvfree(new_rl);
|
||||
err = -ENOSPC;
|
||||
goto out_free_data;
|
||||
}
|
||||
old_rl = attr_ni->runlist.rl;
|
||||
old_rl_count = attr_ni->runlist.count;
|
||||
down_write(&attr_ni->runlist.lock);
|
||||
attr_ni->runlist.rl = new_rl;
|
||||
attr_ni->runlist.count = new_rl_count;
|
||||
up_write(&attr_ni->runlist.lock);
|
||||
|
||||
write_lock_irqsave(&attr_ni->size_lock, flags);
|
||||
attr_ni->allocated_size = ntfs_cluster_to_bytes(vol, nr_clusters);
|
||||
write_unlock_irqrestore(&attr_ni->size_lock, flags);
|
||||
|
||||
/* Populate the replacement extent before publishing its mapping pairs. */
|
||||
if (data_size) {
|
||||
written = ntfs_inode_attr_pwrite(attr_vi, 0, data_size, data, true);
|
||||
if (written != data_size) {
|
||||
err = written < 0 ? (int)written : -EIO;
|
||||
goto restore_old_runlist;
|
||||
}
|
||||
}
|
||||
|
||||
err = ntfs_attr_update_mapping_pairs_locked(attr_ni, 0, locked_ni);
|
||||
if (err)
|
||||
goto restore_old_runlist;
|
||||
|
||||
/* The new mapping is now authoritative; release the old data runs. */
|
||||
if (ntfs_cluster_free_from_rl(vol, old_rl)) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to free old ATTRIBUTE_LIST extent: inode %#llx",
|
||||
(long long)attr_ni->mft_no);
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
kvfree(old_rl);
|
||||
kvfree(data);
|
||||
return 0;
|
||||
|
||||
restore_old_runlist:
|
||||
down_write(&attr_ni->runlist.lock);
|
||||
attr_ni->runlist.rl = old_rl;
|
||||
attr_ni->runlist.count = old_rl_count;
|
||||
up_write(&attr_ni->runlist.lock);
|
||||
|
||||
write_lock_irqsave(&attr_ni->size_lock, flags);
|
||||
attr_ni->allocated_size = old_alloc_size;
|
||||
write_unlock_irqrestore(&attr_ni->size_lock, flags);
|
||||
|
||||
restore_err = ntfs_attr_update_mapping_pairs_locked(
|
||||
attr_ni, 0, locked_ni);
|
||||
if (restore_err) {
|
||||
ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
|
||||
restore_err);
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
|
||||
ntfs_cluster_free_from_rl(vol, new_rl);
|
||||
kvfree(new_rl);
|
||||
err = err ? err : restore_err;
|
||||
|
||||
out_free_data:
|
||||
kvfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
struct inode *attr_vi;
|
||||
struct ntfs_inode *attr_ni;
|
||||
int err;
|
||||
s64 written;
|
||||
int err, retry_err;
|
||||
|
||||
/*
|
||||
* generic_shutdown_super() clears SB_ACTIVE before evicting cached
|
||||
|
|
@ -72,23 +219,66 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
return err;
|
||||
}
|
||||
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);
|
||||
if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT) {
|
||||
err = ntfs_attr_truncate(attr_ni, 0);
|
||||
if (err || ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO) != 0) {
|
||||
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 &&
|
||||
NInoNonResident(attr_ni)) {
|
||||
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
||||
base_ni->attr_list_size, locked_ni);
|
||||
if (retry_err) {
|
||||
ntfs_error(base_ni->vol->sb, "Failed to repack attribute list");
|
||||
iput(attr_vi);
|
||||
return retry_err;
|
||||
}
|
||||
|
||||
retry_err = ntfs_attr_truncate_i_locked(
|
||||
attr_ni, base_ni->attr_list_size,
|
||||
HOLES_NO, locked_ni);
|
||||
if (retry_err) {
|
||||
ntfs_error(base_ni->vol->sb,
|
||||
"Failed to truncate attribute list of inode %#llx",
|
||||
(long long)base_ni->mft_no);
|
||||
return -EIO;
|
||||
"Failed to resize attribute list after repack");
|
||||
iput(attr_vi);
|
||||
return retry_err;
|
||||
}
|
||||
} else if (err) {
|
||||
iput(attr_vi);
|
||||
ntfs_error(base_ni->vol->sb,
|
||||
"Failed to truncate attribute list of inode %#llx",
|
||||
(long long)base_ni->mft_no);
|
||||
return -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve the maximum legal list size while the MFT metadata area is
|
||||
* still easy to allocate contiguously. This prevents a later list entry
|
||||
* from needing another mapping-pairs byte in the full base MFT record.
|
||||
* Failure to obtain the optional reserve must not reject the current
|
||||
* metadata update; the repack retry above remains available if needed.
|
||||
*/
|
||||
if (base_ni->mft_no == FILE_MFT && NInoNonResident(attr_ni) &&
|
||||
attr_ni->allocated_size < NTFS_MAX_ATTR_LIST_SIZE) {
|
||||
retry_err = ntfs_attr_expand_locked(
|
||||
attr_ni, base_ni->attr_list_size,
|
||||
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||
if (retry_err == -ENOSPC) {
|
||||
retry_err = ntfs_attrlist_repack(
|
||||
attr_vi, attr_ni,
|
||||
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||
if (retry_err == -ENOSPC)
|
||||
retry_err = 0;
|
||||
}
|
||||
if (retry_err) {
|
||||
ntfs_error(base_ni->vol->sb,
|
||||
"Failed to reserve attribute list space");
|
||||
iput(attr_vi);
|
||||
return retry_err;
|
||||
}
|
||||
}
|
||||
|
||||
i_size_write(attr_vi, base_ni->attr_list_size);
|
||||
|
|
@ -96,14 +286,15 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
if (NInoNonResident(attr_ni) && !NInoAttrListNonResident(base_ni))
|
||||
NInoSetAttrListNonResident(base_ni);
|
||||
|
||||
if (ntfs_inode_attr_pwrite(attr_vi, 0, base_ni->attr_list_size,
|
||||
base_ni->attr_list, false) !=
|
||||
base_ni->attr_list_size) {
|
||||
written = ntfs_inode_attr_pwrite(attr_vi, 0, base_ni->attr_list_size,
|
||||
base_ni->attr_list, false);
|
||||
if (written != base_ni->attr_list_size) {
|
||||
err = written < 0 ? (int)written : -EIO;
|
||||
iput(attr_vi);
|
||||
ntfs_error(base_ni->vol->sb,
|
||||
"Failed to write attribute list of inode %#llx",
|
||||
(long long)base_ni->mft_no);
|
||||
return -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
NInoSetAttrListDirty(base_ni);
|
||||
|
|
@ -111,6 +302,11 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
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
|
||||
* @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_rm(struct ntfs_attr_search_ctx *ctx);
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -1450,7 +1450,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
|||
ni->runlist.rl = rl;
|
||||
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);
|
||||
if (err)
|
||||
err = -EIO;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ static int ntfs_trim_prealloc(struct inode *vi)
|
|||
ntfs_error(vol->sb, "Preallocated block rollback failed");
|
||||
} else {
|
||||
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)
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to rollback mapping pairs for prealloc");
|
||||
|
|
|
|||
|
|
@ -170,17 +170,19 @@ struct inode *ntfs_iget(struct super_block *sb, u64 mft_no)
|
|||
/* If this is a freshly allocated inode, need to read it now. */
|
||||
if (inode_state_read_once(vi) & I_NEW) {
|
||||
err = ntfs_read_locked_inode(vi);
|
||||
unlock_new_inode(vi);
|
||||
if (err) {
|
||||
remove_inode_hash(vi);
|
||||
discard_new_inode(vi);
|
||||
} else
|
||||
unlock_new_inode(vi);
|
||||
}
|
||||
/*
|
||||
* There is no point in keeping bad inodes around. This also
|
||||
* simplifies things in that we never need to check for bad inodes
|
||||
* elsewhere.
|
||||
*/
|
||||
if (unlikely(err)) {
|
||||
iput(vi);
|
||||
if (unlikely(err))
|
||||
vi = ERR_PTR(err);
|
||||
}
|
||||
return vi;
|
||||
}
|
||||
|
||||
|
|
@ -231,17 +233,19 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, __le32 type,
|
|||
/* If this is a freshly allocated inode, need to read it now. */
|
||||
if (inode_state_read_once(vi) & I_NEW) {
|
||||
err = ntfs_read_locked_attr_inode(base_vi, vi);
|
||||
unlock_new_inode(vi);
|
||||
if (err) {
|
||||
remove_inode_hash(vi);
|
||||
discard_new_inode(vi);
|
||||
} else
|
||||
unlock_new_inode(vi);
|
||||
}
|
||||
/*
|
||||
* There is no point in keeping bad attribute inodes around. This also
|
||||
* simplifies things in that we never need to check for bad attribute
|
||||
* inodes elsewhere.
|
||||
*/
|
||||
if (unlikely(err)) {
|
||||
iput(vi);
|
||||
if (unlikely(err))
|
||||
vi = ERR_PTR(err);
|
||||
}
|
||||
return vi;
|
||||
}
|
||||
|
||||
|
|
@ -286,17 +290,19 @@ struct inode *ntfs_index_iget(struct inode *base_vi, __le16 *name,
|
|||
/* If this is a freshly allocated inode, need to read it now. */
|
||||
if (inode_state_read_once(vi) & I_NEW) {
|
||||
err = ntfs_read_locked_index_inode(base_vi, vi);
|
||||
unlock_new_inode(vi);
|
||||
if (err) {
|
||||
remove_inode_hash(vi);
|
||||
discard_new_inode(vi);
|
||||
} else
|
||||
unlock_new_inode(vi);
|
||||
}
|
||||
/*
|
||||
* There is no point in keeping bad index inodes around. This also
|
||||
* simplifies things in that we never need to check for bad index
|
||||
* inodes elsewhere.
|
||||
*/
|
||||
if (unlikely(err)) {
|
||||
iput(vi);
|
||||
if (unlikely(err))
|
||||
vi = ERR_PTR(err);
|
||||
}
|
||||
return vi;
|
||||
}
|
||||
|
||||
|
|
@ -1241,7 +1247,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|||
if (m)
|
||||
unmap_mft_record(ni);
|
||||
err_out:
|
||||
if (err != -EOPNOTSUPP && err != -ENOMEM && vol_err == true) {
|
||||
if (err != -EOPNOTSUPP && err != -ENOMEM &&
|
||||
err != -EINTR && err != -ERESTARTSYS && vol_err == true) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed with error code %i. Marking corrupt inode 0x%llx as bad. Run chkdsk.",
|
||||
err, ni->mft_no);
|
||||
|
|
@ -1467,12 +1474,13 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(base_ni);
|
||||
err_out:
|
||||
if (err != -ENOENT)
|
||||
if (err != -ENOENT && err != -EINTR && err != -ERESTARTSYS)
|
||||
ntfs_error(vol->sb,
|
||||
"Failed with error code %i while reading attribute inode (mft_no 0x%llx, type 0x%x, name_len %i). Marking corrupt inode and base inode 0x%llx as bad. Run chkdsk.",
|
||||
err, ni->mft_no, ni->type, ni->name_len,
|
||||
base_ni->mft_no);
|
||||
if (err != -ENOENT && err != -ENOMEM)
|
||||
if (err != -ENOENT && err != -ENOMEM &&
|
||||
err != -EINTR && err != -ERESTARTSYS)
|
||||
NVolSetErrors(vol);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1676,8 +1684,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|||
/* Get the index bitmap attribute inode. */
|
||||
bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len);
|
||||
if (IS_ERR(bvi)) {
|
||||
ntfs_error(vi->i_sb, "Failed to get bitmap attribute.");
|
||||
err = PTR_ERR(bvi);
|
||||
if (err != -EINTR && err != -ERESTARTSYS)
|
||||
ntfs_error(vi->i_sb, "Failed to get bitmap attribute.");
|
||||
goto unm_err_out;
|
||||
}
|
||||
bni = NTFS_I(bvi);
|
||||
|
|
@ -1721,10 +1730,12 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|||
if (m)
|
||||
unmap_mft_record(base_ni);
|
||||
err_out:
|
||||
ntfs_error(vi->i_sb,
|
||||
"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.",
|
||||
err, ni->mft_no, ni->name_len);
|
||||
if (err != -EOPNOTSUPP && err != -ENOMEM)
|
||||
if (err != -EINTR && err != -ERESTARTSYS)
|
||||
ntfs_error(vi->i_sb,
|
||||
"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i.",
|
||||
err, ni->mft_no, ni->name_len);
|
||||
if (err != -EOPNOTSUPP && err != -ENOMEM &&
|
||||
err != -EINTR && err != -ERESTARTSYS)
|
||||
NVolSetErrors(vol);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2772,7 +2783,7 @@ int __ntfs_write_inode(struct inode *vi, int sync)
|
|||
|
||||
if (NInoNonResident(ni) && NInoRunlistDirty(ni)) {
|
||||
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)
|
||||
NInoClearRunlistDirty(ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
|
|
@ -3713,7 +3724,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
|
|||
FGP_CREAT | FGP_LOCK,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio)) {
|
||||
ret = -ENOMEM;
|
||||
ret = PTR_ERR(folio);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -3745,6 +3756,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
|
|||
u64 rl_length = 0;
|
||||
s64 vcn;
|
||||
struct runlist_element *rl;
|
||||
int bio_err;
|
||||
|
||||
lcn_count = max_t(s64, 1, ntfs_bytes_to_cluster(vol, attr_len));
|
||||
vcn = ntfs_pidx_to_cluster(vol, folio->index);
|
||||
|
|
@ -3787,8 +3799,15 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
|
|||
goto err_unlock_folio;
|
||||
}
|
||||
|
||||
submit_bio_wait(bio);
|
||||
bio_err = submit_bio_wait(bio);
|
||||
bio_put(bio);
|
||||
if (bio_err) {
|
||||
ntfs_error(vi->i_sb,
|
||||
"Synchronous attribute write failed (%d)",
|
||||
bio_err);
|
||||
ret = bio_err;
|
||||
goto err_unlock_folio;
|
||||
}
|
||||
vcn += rl_length;
|
||||
offset += length;
|
||||
} while (lcn_count != 0);
|
||||
|
|
|
|||
455
fs/ntfs/mft.c
455
fs/ntfs/mft.c
|
|
@ -213,7 +213,8 @@ struct mft_record *map_mft_record(struct ntfs_inode *ni)
|
|||
return m;
|
||||
|
||||
atomic_dec(&ni->count);
|
||||
ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
|
||||
if (PTR_ERR(m) != -EINTR && PTR_ERR(m) != -ERESTARTSYS)
|
||||
ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
@ -462,7 +463,7 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
|
|||
{
|
||||
u8 *kmirr;
|
||||
struct folio *folio;
|
||||
unsigned int folio_ofs, lcn_folio_off = 0;
|
||||
unsigned int folio_ofs;
|
||||
int err = 0;
|
||||
struct bio *bio;
|
||||
|
||||
|
|
@ -492,15 +493,11 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
|
|||
memcpy(kmirr, m, vol->mft_record_size);
|
||||
kunmap_local(kmirr);
|
||||
|
||||
if (vol->cluster_size_bits > PAGE_SHIFT) {
|
||||
lcn_folio_off = folio->index << PAGE_SHIFT;
|
||||
lcn_folio_off &= vol->cluster_size_mask;
|
||||
}
|
||||
|
||||
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
|
||||
bio->bi_iter.bi_sector =
|
||||
ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
|
||||
lcn_folio_off + folio_ofs);
|
||||
((u64)folio->index << PAGE_SHIFT) +
|
||||
folio_ofs);
|
||||
|
||||
if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs))
|
||||
err = submit_bio_wait(bio);
|
||||
|
|
@ -841,12 +838,126 @@ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no,
|
|||
|
||||
static const char *es = " Leaving inconsistent metadata. Unmount and run chkdsk.";
|
||||
|
||||
#define RESERVED_MFT_RECORDS 64
|
||||
#define FIRST_NORMAL_MFT_RECORD 24
|
||||
#define MFT_RECORD_RESERVE 4
|
||||
|
||||
/*
|
||||
* ntfs_mft_bitmap_find_and_alloc_free_rec_nolock - see name
|
||||
* Records 12-15 are marked in use by Windows but normally have no name
|
||||
* and no links. Keep them as the last bootstrap option when a volume
|
||||
* mounted without an in-memory tail reserve needs its first $MFT metadata
|
||||
* extent.
|
||||
*/
|
||||
static bool mft_reserved_is_free(struct ntfs_volume *vol,
|
||||
struct ntfs_inode *mft_ni, s64 mft_no)
|
||||
{
|
||||
struct attr_record *a;
|
||||
struct mft_record *m;
|
||||
struct folio *folio;
|
||||
void *mapped;
|
||||
pgoff_t index = NTFS_MFT_NR_TO_PIDX(vol, mft_no);
|
||||
unsigned int ofs = NTFS_MFT_NR_TO_POFS(vol, mft_no);
|
||||
u32 attrs_offset, bytes_in_use;
|
||||
bool available = false, have_std = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mft_ni->nr_extents; i++) {
|
||||
if (mft_ni->ext.extent_ntfs_inos[i] &&
|
||||
mft_ni->ext.extent_ntfs_inos[i]->mft_no == mft_no)
|
||||
return false;
|
||||
}
|
||||
m = kmalloc(vol->mft_record_size, GFP_NOFS);
|
||||
if (!m)
|
||||
return false;
|
||||
|
||||
folio = read_mapping_folio(vol->mft_ino->i_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
goto free_m;
|
||||
|
||||
folio_lock(folio);
|
||||
mapped = kmap_local_folio(folio, 0);
|
||||
memcpy(m, (u8 *)mapped + ofs, vol->mft_record_size);
|
||||
kunmap_local(mapped);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
if (post_read_mst_fixup((struct ntfs_record *)m, vol->mft_record_size))
|
||||
goto free_m;
|
||||
|
||||
if (!ntfs_is_mft_record(m->magic) ||
|
||||
!(m->flags & MFT_RECORD_IN_USE) || m->base_mft_record ||
|
||||
m->link_count)
|
||||
goto out;
|
||||
|
||||
attrs_offset = le16_to_cpu(m->attrs_offset);
|
||||
bytes_in_use = le32_to_cpu(m->bytes_in_use);
|
||||
if (attrs_offset > bytes_in_use || bytes_in_use > vol->mft_record_size ||
|
||||
bytes_in_use - attrs_offset < sizeof(a->type))
|
||||
goto out;
|
||||
|
||||
for (a = (struct attr_record *)((u8 *)m + attrs_offset);
|
||||
(u8 *)a + sizeof(a->type) <= (u8 *)m + bytes_in_use;) {
|
||||
u32 len;
|
||||
|
||||
if (a->type == AT_END) {
|
||||
if ((u8 *)a + sizeof(a->type) + sizeof(a->length) >
|
||||
(u8 *)m + bytes_in_use)
|
||||
break;
|
||||
/* Also accept a record emptied by an earlier bootstrap. */
|
||||
available = have_std ||
|
||||
(u8 *)a == (u8 *)m + attrs_offset;
|
||||
break;
|
||||
}
|
||||
if (a->type == AT_FILE_NAME)
|
||||
break;
|
||||
len = le32_to_cpu(a->length);
|
||||
if (len < offsetof(struct attr_record, data) ||
|
||||
(u8 *)a + len > (u8 *)m + bytes_in_use)
|
||||
break;
|
||||
if (a->type == AT_STANDARD_INFORMATION) {
|
||||
u32 value_len, value_ofs;
|
||||
|
||||
if (have_std || a->non_resident ||
|
||||
len < offsetof(struct attr_record,
|
||||
data.resident.reserved) + 1)
|
||||
break;
|
||||
value_len = le32_to_cpu(a->data.resident.value_length);
|
||||
value_ofs = le16_to_cpu(a->data.resident.value_offset);
|
||||
if (value_ofs > len || value_len > len - value_ofs)
|
||||
break;
|
||||
have_std = true;
|
||||
}
|
||||
a = (struct attr_record *)((u8 *)a + len);
|
||||
}
|
||||
out:
|
||||
kfree(m);
|
||||
return available;
|
||||
free_m:
|
||||
kfree(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
static s64 mft_reserve_end(const u8 *buf, s64 buf_start, s64 buf_end,
|
||||
s64 start, s64 pass_end, s64 initialized_mft_records)
|
||||
{
|
||||
s64 end = start + 1;
|
||||
s64 limit = min_t(s64, start + MFT_RECORD_RESERVE, pass_end);
|
||||
|
||||
if (limit > initialized_mft_records)
|
||||
limit = initialized_mft_records;
|
||||
if (limit > buf_end)
|
||||
limit = buf_end;
|
||||
while (end < limit &&
|
||||
!(buf[(end - buf_start) >> 3] &
|
||||
(1 << ((end - buf_start) & 7))))
|
||||
end++;
|
||||
return end;
|
||||
}
|
||||
|
||||
/*
|
||||
* mft_bitmap_alloc_free_rec - find and allocate a free MFT record
|
||||
* @vol: volume on which to search for a free mft record
|
||||
* @base_ni: open base inode if allocating an extent mft record or NULL
|
||||
* @max_mft_no: first record which must not be allocated, or -1
|
||||
* @new_reserve_end: if not NULL, end of a free run starting after the result
|
||||
*
|
||||
* Search for a free mft record in the mft bitmap attribute on the ntfs volume
|
||||
* @vol.
|
||||
|
|
@ -862,10 +973,12 @@ static const char *es = " Leaving inconsistent metadata. Unmount and run chkds
|
|||
*
|
||||
* Locking: Caller must hold vol->mftbmp_lock for writing.
|
||||
*/
|
||||
static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vol,
|
||||
struct ntfs_inode *base_ni)
|
||||
static s64 mft_bitmap_alloc_free_rec(struct ntfs_volume *vol,
|
||||
struct ntfs_inode *base_ni,
|
||||
s64 max_mft_no, s64 *new_reserve_end)
|
||||
{
|
||||
s64 pass_end, ll, data_pos, pass_start, ofs, bit;
|
||||
s64 initialized_mft_records;
|
||||
unsigned long flags;
|
||||
struct address_space *mftbmp_mapping;
|
||||
u8 *buf = NULL, *byte;
|
||||
|
|
@ -882,30 +995,36 @@ static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vo
|
|||
read_lock_irqsave(&NTFS_I(vol->mft_ino)->size_lock, flags);
|
||||
pass_end = NTFS_I(vol->mft_ino)->allocated_size >>
|
||||
vol->mft_record_size_bits;
|
||||
initialized_mft_records = NTFS_I(vol->mft_ino)->initialized_size >>
|
||||
vol->mft_record_size_bits;
|
||||
read_unlock_irqrestore(&NTFS_I(vol->mft_ino)->size_lock, flags);
|
||||
read_lock_irqsave(&NTFS_I(vol->mftbmp_ino)->size_lock, flags);
|
||||
ll = NTFS_I(vol->mftbmp_ino)->initialized_size << 3;
|
||||
read_unlock_irqrestore(&NTFS_I(vol->mftbmp_ino)->size_lock, flags);
|
||||
if (pass_end > ll)
|
||||
pass_end = ll;
|
||||
pass = 1;
|
||||
if (!base_ni)
|
||||
data_pos = vol->mft_data_pos;
|
||||
else
|
||||
data_pos = base_ni->mft_no + 1;
|
||||
if (data_pos < RESERVED_MFT_RECORDS)
|
||||
data_pos = RESERVED_MFT_RECORDS;
|
||||
if (data_pos >= pass_end) {
|
||||
data_pos = RESERVED_MFT_RECORDS;
|
||||
if (max_mft_no >= 0 && pass_end > max_mft_no)
|
||||
pass_end = max_mft_no;
|
||||
if (base_ni && base_ni->mft_no == FILE_MFT) {
|
||||
data_pos = FILE_first_user;
|
||||
pass = 2;
|
||||
/* This happens on a freshly formatted volume. */
|
||||
if (data_pos >= pass_end)
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (base_ni && base_ni->mft_no == FILE_MFT) {
|
||||
data_pos = 0;
|
||||
pass = 2;
|
||||
} else {
|
||||
pass = 1;
|
||||
if (!base_ni)
|
||||
data_pos = vol->mft_data_pos;
|
||||
else
|
||||
data_pos = base_ni->mft_no + 1;
|
||||
if (data_pos < FIRST_NORMAL_MFT_RECORD)
|
||||
data_pos = FIRST_NORMAL_MFT_RECORD;
|
||||
if (data_pos >= pass_end) {
|
||||
data_pos = FIRST_NORMAL_MFT_RECORD;
|
||||
pass = 2;
|
||||
/* This happens on a freshly formatted volume. */
|
||||
if (data_pos >= pass_end)
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
pass_start = data_pos;
|
||||
|
|
@ -940,38 +1059,28 @@ static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vo
|
|||
size, data_pos, bit);
|
||||
for (; bit < size && data_pos + bit < pass_end;
|
||||
bit &= ~7ull, bit += 8) {
|
||||
/*
|
||||
* If we're extending $MFT and running out of the first
|
||||
* mft record (base record) then give up searching since
|
||||
* no guarantee that the found record will be accessible.
|
||||
*/
|
||||
if (base_ni && base_ni->mft_no == FILE_MFT && bit > 400) {
|
||||
folio_unlock(folio);
|
||||
kunmap_local(buf);
|
||||
folio_put(folio);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
byte = buf + (bit >> 3);
|
||||
if (*byte == 0xff)
|
||||
continue;
|
||||
b = ffz((unsigned long)*byte);
|
||||
if (b < 8 && b >= (bit & 7)) {
|
||||
b = bit & 7;
|
||||
for (; b < 8; b++) {
|
||||
if (*byte & (1 << b))
|
||||
continue;
|
||||
ll = data_pos + (bit & ~7ull) + b;
|
||||
if (ll >= pass_end)
|
||||
break;
|
||||
/* Keep the dynamic tail reserve for $MFT metadata. */
|
||||
if ((!base_ni || base_ni->mft_no != FILE_MFT) &&
|
||||
ll >= vol->mft_record_reserve_pos &&
|
||||
ll < vol->mft_record_reserve_end)
|
||||
continue;
|
||||
if (unlikely(ll >= (1ll << 32))) {
|
||||
folio_unlock(folio);
|
||||
kunmap_local(buf);
|
||||
folio_put(folio);
|
||||
return -ENOSPC;
|
||||
}
|
||||
*byte |= 1 << b;
|
||||
folio_mark_dirty(folio);
|
||||
folio_unlock(folio);
|
||||
kunmap_local(buf);
|
||||
folio_put(folio);
|
||||
ntfs_debug("Done. (Found and allocated mft record 0x%llx.)",
|
||||
ll);
|
||||
return ll;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
ntfs_debug("After inner for loop: size 0x%x, data_pos 0x%llx, bit 0x%llx",
|
||||
|
|
@ -994,7 +1103,8 @@ static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vo
|
|||
* part of the zone which we omitted earlier.
|
||||
*/
|
||||
pass_end = pass_start;
|
||||
data_pos = pass_start = RESERVED_MFT_RECORDS;
|
||||
data_pos = FIRST_NORMAL_MFT_RECORD;
|
||||
pass_start = FIRST_NORMAL_MFT_RECORD;
|
||||
ntfs_debug("pass %i, pass_start 0x%llx, pass_end 0x%llx.",
|
||||
pass, pass_start, pass_end);
|
||||
if (data_pos >= pass_end)
|
||||
|
|
@ -1004,9 +1114,22 @@ static s64 ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(struct ntfs_volume *vo
|
|||
/* No free mft records in currently initialized mft bitmap. */
|
||||
ntfs_debug("Done. (No free mft records left in currently initialized mft bitmap.)");
|
||||
return -ENOSPC;
|
||||
found:
|
||||
if (new_reserve_end)
|
||||
*new_reserve_end = mft_reserve_end(buf, data_pos,
|
||||
data_pos + size, ll, pass_end,
|
||||
initialized_mft_records);
|
||||
*byte |= 1 << b;
|
||||
folio_mark_dirty(folio);
|
||||
folio_unlock(folio);
|
||||
kunmap_local(buf);
|
||||
folio_put(folio);
|
||||
ntfs_debug("Done. (Found and allocated mft record 0x%llx.)", 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;
|
||||
struct ntfs_inode *base_ni;
|
||||
|
|
@ -1027,7 +1150,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)
|
||||
pr_err("MP update failed\n");
|
||||
|
||||
|
|
@ -1215,7 +1338,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||
if (unlikely(ret)) {
|
||||
ret = ntfs_mft_attr_extend(mftbmp_ni);
|
||||
ret = ntfs_mft_attr_extend(mftbmp_ni, mftbmp_ni);
|
||||
if (!ret)
|
||||
goto extended_ok;
|
||||
if (ret != -EAGAIN)
|
||||
|
|
@ -1326,7 +1449,9 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
NVolSetErrors(vol);
|
||||
}
|
||||
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);
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
|
|
@ -1414,7 +1539,6 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(struct ntfs_volume *vol)
|
|||
ret = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0);
|
||||
if (likely(!ret)) {
|
||||
ntfs_debug("Done. (Wrote eight initialized bytes to mft bitmap.");
|
||||
ntfs_inc_free_mft_records(vol, 8 * 8);
|
||||
return 0;
|
||||
}
|
||||
ntfs_error(vol->sb, "Failed to write to mft bitmap.");
|
||||
|
|
@ -1471,8 +1595,9 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(struct ntfs_volume *vol)
|
|||
* @vol: volume on which to extend the mft data attribute
|
||||
*
|
||||
* Extend the mft data attribute on the ntfs volume @vol by 16 mft records
|
||||
* worth of clusters or if not enough space for this by one mft record worth
|
||||
* of clusters.
|
||||
* worth of clusters or if not enough space for this by two mft records worth
|
||||
* of clusters. Keeping at least two new records breaks the recursion between
|
||||
* extending $MFT and allocating a record for a new $MFT attribute extent.
|
||||
*
|
||||
* Note: Only changes allocated_size, i.e. does not touch initialized_size or
|
||||
* data_size.
|
||||
|
|
@ -1526,10 +1651,8 @@ static int ntfs_mft_data_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
}
|
||||
lcn = rl->lcn + rl->length;
|
||||
ntfs_debug("Last lcn of mft data attribute is 0x%llx.", lcn);
|
||||
/* Minimum allocation is one mft record worth of clusters. */
|
||||
min_nr = NTFS_B_TO_CLU(vol, vol->mft_record_size);
|
||||
if (!min_nr)
|
||||
min_nr = 1;
|
||||
/* Keep room for the allocating record and at least one MFT reserve. */
|
||||
min_nr = DIV_ROUND_UP_ULL((u64)vol->mft_record_size * 2, vol->cluster_size);
|
||||
/* Want to allocate 16 mft records worth of clusters. */
|
||||
nr = vol->mft_record_size << 4 >> vol->cluster_size_bits;
|
||||
if (!nr)
|
||||
|
|
@ -1653,7 +1776,7 @@ static int ntfs_mft_data_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||
if (unlikely(ret)) {
|
||||
ret = ntfs_mft_attr_extend(mft_ni);
|
||||
ret = ntfs_mft_attr_extend(mft_ni, NULL);
|
||||
if (!ret)
|
||||
goto extended_ok;
|
||||
if (ret != -EAGAIN)
|
||||
|
|
@ -1928,6 +2051,7 @@ static int ntfs_mft_record_format(const struct ntfs_volume *vol, const s64 mft_n
|
|||
* @ni: [OUT] on success, set to the allocated ntfs inode
|
||||
* @base_ni: [IN] open base inode if allocating an extent mft record or NULL
|
||||
* @ni_mrec: [OUT] on successful return this is the mapped mft record
|
||||
* @mft_data_vcn: [IN] lowest VCN of a new $MFT/$DATA extent, or -1
|
||||
*
|
||||
* Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol.
|
||||
*
|
||||
|
|
@ -1955,30 +2079,23 @@ static int ntfs_mft_record_format(const struct ntfs_volume *vol, const s64 mft_n
|
|||
* optimize this we start scanning at the place specified by @base_ni or if
|
||||
* @base_ni is NULL we start where we last stopped and we perform wrap around
|
||||
* when we reach the end. Note, we do not try to allocate mft records below
|
||||
* number 64 because numbers 0 to 15 are the defined system files anyway and 16
|
||||
* to 64 are special in that they are used for storing extension mft records
|
||||
* for the $DATA attribute of $MFT. This is required to avoid the possibility
|
||||
* of creating a runlist with a circular dependency which once written to disk
|
||||
* can never be read in again. Windows will only use records 16 to 24 for
|
||||
* normal files if the volume is completely out of space. We never use them
|
||||
* which means that when the volume is really out of space we cannot create any
|
||||
* more files while Windows can still create up to 8 small files. We can start
|
||||
* doing this at some later time, it does not matter much for now.
|
||||
* number 24 because numbers 0 to 15 are the defined system files and records
|
||||
* 16 to 23 are kept for metadata compatibility. Records reserved dynamically
|
||||
* at the initialized MFT tail are skipped by normal allocation and consumed by
|
||||
* $MFT metadata extent allocation.
|
||||
*
|
||||
* When scanning the mft bitmap, we only search up to the last allocated mft
|
||||
* record. If there are no free records left in the range 64 to number of
|
||||
* record. If there are no free records left in the range 24 to number of
|
||||
* allocated mft records, then we extend the $MFT/$DATA attribute in order to
|
||||
* create free mft records. We extend the allocated size of $MFT/$DATA by 16
|
||||
* records at a time or one cluster, if cluster size is above 16kiB. If there
|
||||
* is not sufficient space to do this, we try to extend by a single mft record
|
||||
* or one cluster, if cluster size is above the mft record size.
|
||||
* is not sufficient space to do this, we try to extend by two mft records or
|
||||
* one cluster, if a cluster already contains at least two mft records.
|
||||
*
|
||||
* No matter how many mft records we allocate, we initialize only the first
|
||||
* allocated mft record, incrementing mft data size and initialized size
|
||||
* accordingly, open an struct ntfs_inode for it and return it to the caller, unless
|
||||
* there are less than 64 mft records, in which case we allocate and initialize
|
||||
* mft records until we reach record 64 which we consider as the first free mft
|
||||
* record for use by normal files.
|
||||
* When extending the initialized MFT tail, we also initialize up to four
|
||||
* additional records and reserve them in memory for future $MFT metadata
|
||||
* extents. If there are less than 24 mft records, records are initialized
|
||||
* until record 24, which is the first record used for normal files.
|
||||
*
|
||||
* If during any stage we overflow the initialized data in the mft bitmap, we
|
||||
* extend the initialized size (and data size) by 8 bytes, allocating another
|
||||
|
|
@ -2014,9 +2131,13 @@ static int ntfs_mft_record_format(const struct ntfs_volume *vol, const s64 mft_n
|
|||
*/
|
||||
int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
||||
struct ntfs_inode **ni, struct ntfs_inode *base_ni,
|
||||
struct mft_record **ni_mrec)
|
||||
struct mft_record **ni_mrec, const s64 mft_data_vcn)
|
||||
{
|
||||
s64 ll, bit, old_data_initialized, old_data_size;
|
||||
s64 nr_new_mft_records = 0;
|
||||
s64 max_mft_no = -1, reserve_start = -1, reserve_end = -1;
|
||||
s64 candidate_reserve_end = -1;
|
||||
s64 *reserve_endp;
|
||||
unsigned long flags;
|
||||
struct folio *folio;
|
||||
struct ntfs_inode *mft_ni, *mftbmp_ni;
|
||||
|
|
@ -2027,7 +2148,9 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
unsigned int ofs;
|
||||
int err;
|
||||
__le16 seq_no, usn;
|
||||
bool record_formatted = false;
|
||||
bool record_formatted = false, from_reserve = false, tail_alloc = false;
|
||||
bool reserve_created = false;
|
||||
bool forced_reserved_record = false;
|
||||
unsigned int memalloc_flags;
|
||||
|
||||
if (base_ni && *ni)
|
||||
|
|
@ -2036,6 +2159,21 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
/* @mode and @base_ni are mutually exclusive. */
|
||||
if (mode && base_ni)
|
||||
return -EINVAL;
|
||||
if (mft_data_vcn >= 0 &&
|
||||
(!base_ni || base_ni->mft_no != FILE_MFT))
|
||||
return -EINVAL;
|
||||
if (mft_data_vcn >= 0) {
|
||||
u64 vbo;
|
||||
|
||||
if ((u64)mft_data_vcn > (U64_MAX >> vol->cluster_size_bits))
|
||||
return -EOVERFLOW;
|
||||
vbo = (u64)mft_data_vcn << vol->cluster_size_bits;
|
||||
/*
|
||||
* The whole extent record must be reachable without this
|
||||
* extent, including when an MFT record spans multiple clusters.
|
||||
*/
|
||||
max_mft_no = vbo >> vol->mft_record_size_bits;
|
||||
}
|
||||
|
||||
if (base_ni)
|
||||
ntfs_debug("Entering (allocating an extent mft record for base mft record 0x%llx).",
|
||||
|
|
@ -2050,10 +2188,39 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
mutex_lock(&mft_ni->mrec_lock);
|
||||
mftbmp_ni = NTFS_I(vol->mftbmp_ino);
|
||||
search_free_rec:
|
||||
from_reserve = false;
|
||||
reserve_created = false;
|
||||
candidate_reserve_end = -1;
|
||||
if (!base_ni || base_ni->mft_no != FILE_MFT)
|
||||
down_write(&vol->mftbmp_lock);
|
||||
bit = ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(vol, base_ni);
|
||||
if (base_ni && base_ni->mft_no == FILE_MFT &&
|
||||
vol->mft_record_reserve_pos < vol->mft_record_reserve_end &&
|
||||
(max_mft_no < 0 || vol->mft_record_reserve_pos < max_mft_no)) {
|
||||
bit = vol->mft_record_reserve_pos;
|
||||
err = ntfs_bitmap_set_bit(vol->mftbmp_ino, bit);
|
||||
if (unlikely(err)) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to allocate reserved MFT record 0x%llx.",
|
||||
bit);
|
||||
goto err_out;
|
||||
}
|
||||
vol->mft_record_reserve_pos++;
|
||||
from_reserve = true;
|
||||
ntfs_debug("Allocated MFT metadata record 0x%llx from tail reserve.",
|
||||
bit);
|
||||
goto have_alloc_rec;
|
||||
}
|
||||
reserve_endp = vol->mft_record_reserve_pos >=
|
||||
vol->mft_record_reserve_end ? &candidate_reserve_end : NULL;
|
||||
bit = mft_bitmap_alloc_free_rec(vol, base_ni, max_mft_no, reserve_endp);
|
||||
if (bit >= 0) {
|
||||
if (candidate_reserve_end > bit + 1) {
|
||||
vol->mft_record_reserve_pos = bit + 1;
|
||||
vol->mft_record_reserve_end = candidate_reserve_end;
|
||||
reserve_created = true;
|
||||
ntfs_debug("Reserved free MFT records [0x%llx, 0x%llx) for metadata.",
|
||||
bit + 1, candidate_reserve_end);
|
||||
}
|
||||
ntfs_debug("Found and allocated free record (#1), bit 0x%llx.",
|
||||
(long long)bit);
|
||||
goto have_alloc_rec;
|
||||
|
|
@ -2068,6 +2235,24 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
}
|
||||
|
||||
if (base_ni && base_ni->mft_no == FILE_MFT) {
|
||||
static const u8 bootstrap_records[] = {
|
||||
FILE_reserved15, FILE_reserved12, FILE_reserved13,
|
||||
FILE_reserved14,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bootstrap_records); i++) {
|
||||
if (max_mft_no >= 0 && bootstrap_records[i] >= max_mft_no)
|
||||
continue;
|
||||
if (!mft_reserved_is_free(vol, mft_ni,
|
||||
bootstrap_records[i]))
|
||||
continue;
|
||||
bit = bootstrap_records[i];
|
||||
forced_reserved_record = true;
|
||||
ntfs_debug("Using reserved MFT record %lld to bootstrap metadata extension.",
|
||||
bit);
|
||||
goto have_alloc_rec;
|
||||
}
|
||||
memalloc_nofs_restore(memalloc_flags);
|
||||
return bit;
|
||||
}
|
||||
|
|
@ -2087,10 +2272,10 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
old_data_initialized = mftbmp_ni->initialized_size;
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
if (old_data_initialized << 3 > ll &&
|
||||
old_data_initialized > RESERVED_MFT_RECORDS / 8) {
|
||||
old_data_initialized << 3 > FIRST_NORMAL_MFT_RECORD) {
|
||||
bit = ll;
|
||||
if (bit < RESERVED_MFT_RECORDS)
|
||||
bit = RESERVED_MFT_RECORDS;
|
||||
if (bit < FIRST_NORMAL_MFT_RECORD)
|
||||
bit = FIRST_NORMAL_MFT_RECORD;
|
||||
if (unlikely(bit >= (1ll << 32)))
|
||||
goto max_err_out;
|
||||
ntfs_debug("Found free record (#2), bit 0x%llx.",
|
||||
|
|
@ -2176,6 +2361,11 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
old_data_initialized = mft_ni->initialized_size;
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
tail_alloc = (!base_ni || base_ni->mft_no != FILE_MFT) &&
|
||||
bit >= (old_data_initialized >> vol->mft_record_size_bits) &&
|
||||
vol->mft_record_reserve_pos >= vol->mft_record_reserve_end;
|
||||
if (tail_alloc)
|
||||
ll = (bit + 2) << vol->mft_record_size_bits;
|
||||
if (ll <= old_data_initialized) {
|
||||
ntfs_debug("Allocated mft record already initialized.");
|
||||
goto mft_rec_already_initialized;
|
||||
|
|
@ -2208,6 +2398,29 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
mft_ni->initialized_size);
|
||||
}
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
if (tail_alloc) {
|
||||
s64 bitmap_records;
|
||||
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
reserve_end = mft_ni->allocated_size >>
|
||||
vol->mft_record_size_bits;
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
read_lock_irqsave(&mftbmp_ni->size_lock, flags);
|
||||
bitmap_records = mftbmp_ni->initialized_size << 3;
|
||||
read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
|
||||
if (reserve_end > bitmap_records)
|
||||
reserve_end = bitmap_records;
|
||||
if (reserve_end > bit + 1 + MFT_RECORD_RESERVE)
|
||||
reserve_end = bit + 1 + MFT_RECORD_RESERVE;
|
||||
reserve_start = bit + 1;
|
||||
if (reserve_end > reserve_start) {
|
||||
ll = reserve_end << vol->mft_record_size_bits;
|
||||
} else {
|
||||
reserve_start = -1;
|
||||
reserve_end = -1;
|
||||
ll = (bit + 1) << vol->mft_record_size_bits;
|
||||
}
|
||||
}
|
||||
} else if (ll > mft_ni->allocated_size) {
|
||||
err = -ENOSPC;
|
||||
goto undo_mftbmp_alloc_nolock;
|
||||
|
|
@ -2276,14 +2489,25 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
unmap_mft_record(mft_ni);
|
||||
if (reserve_start >= 0 && reserve_end > reserve_start) {
|
||||
vol->mft_record_reserve_pos = reserve_start;
|
||||
vol->mft_record_reserve_end = reserve_end;
|
||||
ntfs_debug("Reserved MFT records [0x%llx, 0x%llx) for metadata.",
|
||||
reserve_start, reserve_end);
|
||||
}
|
||||
read_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
ntfs_debug("Status of mft data after mft record initialization: allocated_size 0x%llx, data_size 0x%llx, initialized_size 0x%llx.",
|
||||
mft_ni->allocated_size, i_size_read(vol->mft_ino),
|
||||
mft_ni->initialized_size);
|
||||
WARN_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size);
|
||||
WARN_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino));
|
||||
nr_new_mft_records = (i_size_read(vol->mft_ino) - old_data_size) >>
|
||||
vol->mft_record_size_bits;
|
||||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
mft_rec_already_initialized:
|
||||
/* Account for newly visible MFT records before dropping the lock. */
|
||||
if (nr_new_mft_records > 0)
|
||||
ntfs_inc_free_mft_records(vol, nr_new_mft_records);
|
||||
/*
|
||||
* We can finally drop the mft bitmap lock as the mft data attribute
|
||||
* has been fully updated. The only disparity left is that the
|
||||
|
|
@ -2315,8 +2539,8 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
/* If we just formatted the mft record no need to do it again. */
|
||||
if (!record_formatted) {
|
||||
/* Sanity check that the mft record is really not in use. */
|
||||
if (ntfs_is_file_record(m->magic) &&
|
||||
(m->flags & MFT_RECORD_IN_USE)) {
|
||||
if (!forced_reserved_record && ntfs_is_file_record(m->magic) &&
|
||||
(m->flags & MFT_RECORD_IN_USE)) {
|
||||
ntfs_warning(vol->sb,
|
||||
"Mft record 0x%llx was marked free in mft bitmap but is marked used itself. Unmount and run chkdsk.",
|
||||
bit);
|
||||
|
|
@ -2389,9 +2613,13 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
ntfs_error(vol->sb, "Failed to map allocated extent mft record 0x%llx.",
|
||||
bit);
|
||||
err = PTR_ERR(m_tmp);
|
||||
/* Set the mft record itself not in use. */
|
||||
m->flags &= cpu_to_le16(
|
||||
~le16_to_cpu(MFT_RECORD_IN_USE));
|
||||
if (forced_reserved_record) {
|
||||
m->base_mft_record = 0;
|
||||
m->flags |= MFT_RECORD_IN_USE;
|
||||
} else {
|
||||
/* Set the mft record itself not in use. */
|
||||
m->flags &= cpu_to_le16(~le16_to_cpu(MFT_RECORD_IN_USE));
|
||||
}
|
||||
/* Make sure the mft record is written out to disk. */
|
||||
ntfs_mft_mark_dirty(folio);
|
||||
folio_unlock(folio);
|
||||
|
|
@ -2463,7 +2691,8 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
(*ni)->mft_no = bit;
|
||||
if (ni_mrec)
|
||||
*ni_mrec = (*ni)->mrec;
|
||||
ntfs_dec_free_mft_records(vol, 1);
|
||||
if (!forced_reserved_record)
|
||||
ntfs_dec_free_mft_records(vol, 1);
|
||||
return 0;
|
||||
undo_data_init:
|
||||
write_lock_irqsave(&mft_ni->size_lock, flags);
|
||||
|
|
@ -2475,10 +2704,13 @@ int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
|||
if (!base_ni || base_ni->mft_no != FILE_MFT)
|
||||
down_write(&vol->mftbmp_lock);
|
||||
undo_mftbmp_alloc_nolock:
|
||||
if (ntfs_bitmap_clear_bit(vol->mftbmp_ino, bit)) {
|
||||
if (!forced_reserved_record && ntfs_bitmap_clear_bit(vol->mftbmp_ino, bit)) {
|
||||
ntfs_error(vol->sb, "Failed to clear bit in mft bitmap.%s", es);
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
if ((from_reserve || reserve_created) &&
|
||||
vol->mft_record_reserve_pos == bit + 1)
|
||||
vol->mft_record_reserve_pos = bit;
|
||||
if (!base_ni || base_ni->mft_no != FILE_MFT)
|
||||
up_write(&vol->mftbmp_lock);
|
||||
err_out:
|
||||
|
|
@ -2514,9 +2746,11 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni)
|
|||
int err;
|
||||
u16 seq_no;
|
||||
__le16 old_seq_no;
|
||||
__le64 old_base_mft_record;
|
||||
struct mft_record *ni_mrec;
|
||||
unsigned int memalloc_flags;
|
||||
struct ntfs_inode *base_ni;
|
||||
bool keep_reserved;
|
||||
|
||||
if (!vol || !ni)
|
||||
return -EINVAL;
|
||||
|
|
@ -2529,9 +2763,23 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni)
|
|||
|
||||
/* Cache the mft reference for later. */
|
||||
mft_no = ni->mft_no;
|
||||
if (likely(ni->nr_extents >= 0))
|
||||
base_ni = ni;
|
||||
else
|
||||
base_ni = ni->ext.base_ntfs_ino;
|
||||
keep_reserved = mft_no >= FILE_reserved12 &&
|
||||
mft_no <= FILE_reserved15 &&
|
||||
base_ni->mft_no == FILE_MFT;
|
||||
|
||||
/* Mark the mft record as not in use. */
|
||||
ni_mrec->flags &= ~MFT_RECORD_IN_USE;
|
||||
old_base_mft_record = ni_mrec->base_mft_record;
|
||||
if (keep_reserved) {
|
||||
/* Restore the special, unnamed form used by reserved records. */
|
||||
ni_mrec->base_mft_record = 0;
|
||||
ni_mrec->flags |= MFT_RECORD_IN_USE;
|
||||
} else {
|
||||
/* Mark the mft record as not in use. */
|
||||
ni_mrec->flags &= ~MFT_RECORD_IN_USE;
|
||||
}
|
||||
|
||||
/* Increment the sequence number, skipping zero, if it is not zero. */
|
||||
old_seq_no = ni_mrec->sequence_number;
|
||||
|
|
@ -2560,24 +2808,28 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni)
|
|||
if (err)
|
||||
goto sync_rollback;
|
||||
|
||||
if (likely(ni->nr_extents >= 0))
|
||||
base_ni = ni;
|
||||
else
|
||||
base_ni = ni->ext.base_ntfs_ino;
|
||||
if (keep_reserved) {
|
||||
unmap_mft_record(ni);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear the bit in the $MFT/$BITMAP corresponding to this record. */
|
||||
memalloc_flags = memalloc_nofs_save();
|
||||
if (base_ni->mft_no != FILE_MFT)
|
||||
down_write(&vol->mftbmp_lock);
|
||||
err = ntfs_bitmap_clear_bit(vol->mftbmp_ino, mft_no);
|
||||
if (!err)
|
||||
ntfs_inc_free_mft_records(vol, 1);
|
||||
if (!err && base_ni->mft_no == FILE_MFT &&
|
||||
mft_no + 1 == vol->mft_record_reserve_pos &&
|
||||
mft_no < vol->mft_record_reserve_end)
|
||||
vol->mft_record_reserve_pos = mft_no;
|
||||
if (base_ni->mft_no != FILE_MFT)
|
||||
up_write(&vol->mftbmp_lock);
|
||||
memalloc_nofs_restore(memalloc_flags);
|
||||
if (err)
|
||||
goto bitmap_rollback;
|
||||
|
||||
unmap_mft_record(ni);
|
||||
ntfs_inc_free_mft_records(vol, 1);
|
||||
return 0;
|
||||
|
||||
/* Rollback what we did... */
|
||||
|
|
@ -2595,6 +2847,7 @@ int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni)
|
|||
"Eeek! Rollback failed in %s. Leaving inconsistent metadata!\n", __func__);
|
||||
ni_mrec->flags |= MFT_RECORD_IN_USE;
|
||||
ni_mrec->sequence_number = old_seq_no;
|
||||
ni_mrec->base_mft_record = old_base_mft_record;
|
||||
NInoSetDirty(ni);
|
||||
write_mft_record(ni, ni_mrec, 0);
|
||||
unmap_mft_record(ni);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ static inline int write_mft_record(struct ntfs_inode *ni, struct mft_record *m,
|
|||
|
||||
int ntfs_mft_record_alloc(struct ntfs_volume *vol, const int mode,
|
||||
struct ntfs_inode **ni, struct ntfs_inode *base_ni,
|
||||
struct mft_record **ni_mrec);
|
||||
struct mft_record **ni_mrec, const s64 mft_data_vcn);
|
||||
int ntfs_mft_record_free(struct ntfs_volume *vol, struct ntfs_inode *ni);
|
||||
int ntfs_mft_records_write(const struct ntfs_volume *vol, const u64 mref,
|
||||
const s64 count, struct mft_record *b);
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
|
|||
mark_inode_dirty(dir);
|
||||
|
||||
err = ntfs_mft_record_alloc(dir_ni->vol, mode, &ni, NULL,
|
||||
&ni_mrec);
|
||||
&ni_mrec, -1);
|
||||
if (err) {
|
||||
iput(vi);
|
||||
return ERR_PTR(err);
|
||||
|
|
|
|||
|
|
@ -2064,8 +2064,7 @@ static unsigned long __get_nr_free_mft_records(struct ntfs_volume *vol,
|
|||
/* If errors occurred we may well have gone below zero, fix this. */
|
||||
if (nr_free < 0)
|
||||
nr_free = 0;
|
||||
else
|
||||
atomic64_set(&vol->free_mft_records, nr_free);
|
||||
atomic64_set(&vol->free_mft_records, nr_free);
|
||||
|
||||
ntfs_debug("Exiting.");
|
||||
return nr_free;
|
||||
|
|
@ -2131,7 +2130,14 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs)
|
|||
read_unlock_irqrestore(&mft_ni->size_lock, flags);
|
||||
|
||||
/* Free inodes in fs (based on current total count). */
|
||||
sfs->f_ffree = atomic64_read(&vol->free_mft_records);
|
||||
size = atomic64_read(&vol->free_mft_records);
|
||||
if (unlikely(size < 0 || size > (s64)sfs->f_files))
|
||||
ntfs_warning(vol->sb, "Invalid free MFT record count %lld.", size);
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
else if (size > (s64)sfs->f_files)
|
||||
size = sfs->f_files;
|
||||
sfs->f_ffree = size;
|
||||
|
||||
/*
|
||||
* File system id. This is extremely *nix flavour dependent and even
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@
|
|||
* @attrdef_size: Size of the attribute definition table in bytes.
|
||||
* @attrdef: Table of attribute definitions. Obtained from FILE_AttrDef.
|
||||
* @mft_data_pos: Mft record number at which to allocate the next mft record.
|
||||
* @mft_record_reserve_pos: First record in the in-memory MFT metadata reserve
|
||||
* (protected by mftbmp_lock).
|
||||
* @mft_record_reserve_end: First record beyond the MFT metadata reserve
|
||||
* (protected by mftbmp_lock).
|
||||
* @mft_zone_start: First cluster of the mft zone.
|
||||
* @mft_zone_end: First cluster beyond the mft zone.
|
||||
* @mft_zone_pos: Current position in the mft zone.
|
||||
|
|
@ -119,6 +123,8 @@ struct ntfs_volume {
|
|||
s32 attrdef_size;
|
||||
struct attr_def *attrdef;
|
||||
s64 mft_data_pos;
|
||||
s64 mft_record_reserve_pos;
|
||||
s64 mft_record_reserve_end;
|
||||
s64 mft_zone_start;
|
||||
s64 mft_zone_end;
|
||||
s64 mft_zone_pos;
|
||||
|
|
@ -252,17 +258,11 @@ static inline void ntfs_dec_free_clusters(struct ntfs_volume *vol, s64 nr)
|
|||
|
||||
static inline void ntfs_inc_free_mft_records(struct ntfs_volume *vol, s64 nr)
|
||||
{
|
||||
if (!NVolFreeClusterKnown(vol))
|
||||
return;
|
||||
|
||||
atomic64_add(nr, &vol->free_mft_records);
|
||||
}
|
||||
|
||||
static inline void ntfs_dec_free_mft_records(struct ntfs_volume *vol, s64 nr)
|
||||
{
|
||||
if (!NVolFreeClusterKnown(vol))
|
||||
return;
|
||||
|
||||
atomic64_sub(nr, &vol->free_mft_records);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user