aboutsummaryrefslogtreecommitdiff
path: root/fs/ntfs/mft.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ntfs/mft.c')
-rw-r--r--fs/ntfs/mft.c229
1 files changed, 157 insertions, 72 deletions
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
index dfa85ac2f8b..317f7c679fd 100644
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -1,7 +1,7 @@
/**
* mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project.
*
- * Copyright (c) 2001-2004 Anton Altaparmakov
+ * Copyright (c) 2001-2005 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon
*
* This program/include file is free software; you can redistribute it and/or
@@ -45,6 +45,7 @@
*/
static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
{
+ loff_t i_size;
ntfs_volume *vol = ni->vol;
struct inode *mft_vi = vol->mft_ino;
struct page *page;
@@ -60,13 +61,14 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
index = ni->mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
ofs = (ni->mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
+ i_size = i_size_read(mft_vi);
/* The maximum valid index into the page cache for $MFT's data. */
- end_index = mft_vi->i_size >> PAGE_CACHE_SHIFT;
+ end_index = i_size >> PAGE_CACHE_SHIFT;
/* If the wanted index is out of bounds the mft record doesn't exist. */
if (unlikely(index >= end_index)) {
- if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) <
- ofs + vol->mft_record_size) {
+ if (index > end_index || (i_size & ~PAGE_CACHE_MASK) < ofs +
+ vol->mft_record_size) {
page = ERR_PTR(-ENOENT);
ntfs_error(vol->sb, "Attemt to read mft record 0x%lx, "
"which is beyond the end of the mft. "
@@ -285,7 +287,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
}
unmap_mft_record(ni);
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
- "reference! Corrupt file system. "
+ "reference! Corrupt filesystem. "
"Run chkdsk.");
return ERR_PTR(-EIO);
}
@@ -316,7 +318,7 @@ map_err_out:
/* Verify the sequence number if it is present. */
if (seq_no && (le16_to_cpu(m->sequence_number) != seq_no)) {
ntfs_error(base_ni->vol->sb, "Found stale extent mft "
- "reference! Corrupt file system. Run chkdsk.");
+ "reference! Corrupt filesystem. Run chkdsk.");
destroy_ni = TRUE;
m = ERR_PTR(-EIO);
goto unm_err_out;
@@ -531,6 +533,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
LCN lcn;
unsigned int vcn_ofs;
+ bh->b_bdev = vol->sb->s_bdev;
/* Obtain the vcn and offset of the current block. */
vcn = ((VCN)mft_no << vol->mft_record_size_bits) +
(block_start - m_start);
@@ -723,6 +726,7 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync)
LCN lcn;
unsigned int vcn_ofs;
+ bh->b_bdev = vol->sb->s_bdev;
/* Obtain the vcn and offset of the current block. */
vcn = ((VCN)ni->mft_no << vol->mft_record_size_bits) +
(block_start - m_start);
@@ -946,20 +950,23 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no,
na.name_len = 0;
na.type = AT_UNUSED;
/*
- * For inode 0, i.e. $MFT itself, we cannot use ilookup5() from here or
- * we deadlock because the inode is already locked by the kernel
- * (fs/fs-writeback.c::__sync_single_inode()) and ilookup5() waits
- * until the inode is unlocked before returning it and it never gets
- * unlocked because ntfs_should_write_mft_record() never returns. )-:
- * Fortunately, we have inode 0 pinned in icache for the duration of
- * the mount so we can access it directly.
+ * Optimize inode 0, i.e. $MFT itself, since we have it in memory and
+ * we get here for it rather often.
*/
if (!mft_no) {
/* Balance the below iput(). */
vi = igrab(mft_vi);
BUG_ON(vi != mft_vi);
- } else
- vi = ilookup5(sb, mft_no, (test_t)ntfs_test_inode, &na);
+ } else {
+ /*
+ * Have to use ilookup5_nowait() since ilookup5() waits for the
+ * inode lock which causes ntfs to deadlock when a concurrent
+ * inode write via the inode dirty code paths and the page
+ * dirty code path of the inode dirty code path when writing
+ * $MFT occurs.
+ */
+ vi = ilookup5_nowait(sb, mft_no, (test_t)ntfs_test_inode, &na);
+ }
if (vi) {
ntfs_debug("Base inode 0x%lx is in icache.", mft_no);
/* The inode is in icache. */
@@ -1014,7 +1021,13 @@ BOOL ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no,
na.mft_no = MREF_LE(m->base_mft_record);
ntfs_debug("Mft record 0x%lx is an extent record. Looking for base "
"inode 0x%lx in icache.", mft_no, na.mft_no);
- vi = ilookup5(sb, na.mft_no, (test_t)ntfs_test_inode, &na);
+ if (!na.mft_no) {
+ /* Balance the below iput(). */
+ vi = igrab(mft_vi);
+ BUG_ON(vi != mft_vi);
+ } else
+ vi = ilookup5_nowait(sb, na.mft_no, (test_t)ntfs_test_inode,
+ &na);
if (!vi) {
/*
* The base inode is not in icache, write this extent mft
@@ -1121,6 +1134,7 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol,
ntfs_inode *base_ni)
{
s64 pass_end, ll, data_pos, pass_start, ofs, bit;
+ unsigned long flags;
struct address_space *mftbmp_mapping;
u8 *buf, *byte;
struct page *page;
@@ -1134,9 +1148,13 @@ static int ntfs_mft_bitmap_find_and_alloc_free_rec_nolock(ntfs_volume *vol,
* Set the end of the pass making sure we do not overflow the mft
* bitmap.
*/
+ 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;
+ 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;
@@ -1263,6 +1281,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
{
LCN lcn;
s64 ll;
+ unsigned long flags;
struct page *page;
ntfs_inode *mft_ni, *mftbmp_ni;
runlist_element *rl, *rl2 = NULL;
@@ -1284,17 +1303,20 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
/*
* Determine the last lcn of the mft bitmap. The allocated size of the
* mft bitmap cannot be zero so we are ok to do this.
- * ntfs_find_vcn() returns the runlist locked on success.
*/
- rl = ntfs_find_vcn(mftbmp_ni, (mftbmp_ni->allocated_size - 1) >>
- vol->cluster_size_bits, TRUE);
+ down_write(&mftbmp_ni->runlist.lock);
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
+ ll = mftbmp_ni->allocated_size;
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+ rl = ntfs_attr_find_vcn_nolock(mftbmp_ni,
+ (ll - 1) >> vol->cluster_size_bits, TRUE);
if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
+ up_write(&mftbmp_ni->runlist.lock);
ntfs_error(vol->sb, "Failed to determine last allocated "
"cluster of mft bitmap attribute.");
- if (!IS_ERR(rl)) {
- up_write(&mftbmp_ni->runlist.lock);
+ if (!IS_ERR(rl))
ret = -EIO;
- } else
+ else
ret = PTR_ERR(rl);
return ret;
}
@@ -1396,7 +1418,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
BUG_ON(ll < rl2->vcn);
BUG_ON(ll >= rl2->vcn + rl2->length);
/* Get the size for the new mapping pairs array for this extent. */
- mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
if (unlikely(mp_size <= 0)) {
ntfs_error(vol->sb, "Get size for mapping pairs failed for "
"mft bitmap attribute extent.");
@@ -1418,6 +1440,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
// TODO: Deal with this by moving this extent to a new mft
// record or by starting a new extent in a new mft record or by
// moving other attributes out of this mft record.
+ // Note: It will need to be a special mft record and if none of
+ // those are available it gets rather complicated...
ntfs_error(vol->sb, "Not enough space in this mft record to "
"accomodate extended mft bitmap attribute "
"extent. Cannot handle this yet.");
@@ -1428,7 +1452,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
/* Generate the mapping pairs array directly into the attr record. */
ret = ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
- mp_size, rl2, ll, NULL);
+ mp_size, rl2, ll, -1, NULL);
if (unlikely(ret)) {
ntfs_error(vol->sb, "Failed to build mapping pairs array for "
"mft bitmap attribute.");
@@ -1458,9 +1482,11 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
}
a = ctx->attr;
}
+ write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->allocated_size += vol->cluster_size;
a->data.non_resident.allocated_size =
cpu_to_sle64(mftbmp_ni->allocated_size);
+ write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
/* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
@@ -1476,7 +1502,9 @@ restore_undo_alloc:
0, ctx)) {
ntfs_error(vol->sb, "Failed to find last attribute extent of "
"mft bitmap attribute.%s", es);
+ write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->allocated_size += vol->cluster_size;
+ write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni);
up_write(&mftbmp_ni->runlist.lock);
@@ -1512,7 +1540,7 @@ undo_alloc:
a->data.non_resident.mapping_pairs_offset),
old_alen - le16_to_cpu(
a->data.non_resident.mapping_pairs_offset),
- rl2, ll, NULL)) {
+ rl2, ll, -1, NULL)) {
ntfs_error(vol->sb, "Failed to restore mapping pairs "
"array.%s", es);
NVolSetErrors(vol);
@@ -1550,6 +1578,7 @@ undo_alloc:
static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
{
s64 old_data_size, old_initialized_size;
+ unsigned long flags;
struct inode *mftbmp_vi;
ntfs_inode *mft_ni, *mftbmp_ni;
ntfs_attr_search_ctx *ctx;
@@ -1583,7 +1612,8 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
goto put_err_out;
}
a = ctx->attr;
- old_data_size = mftbmp_vi->i_size;
+ write_lock_irqsave(&mftbmp_ni->size_lock, flags);
+ old_data_size = i_size_read(mftbmp_vi);
old_initialized_size = mftbmp_ni->initialized_size;
/*
* We can simply update the initialized_size before filling the space
@@ -1593,11 +1623,12 @@ static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol)
mftbmp_ni->initialized_size += 8;
a->data.non_resident.initialized_size =
cpu_to_sle64(mftbmp_ni->initialized_size);
- if (mftbmp_ni->initialized_size > mftbmp_vi->i_size) {
- mftbmp_vi->i_size = mftbmp_ni->initialized_size;
+ if (mftbmp_ni->initialized_size > old_data_size) {
+ i_size_write(mftbmp_vi, mftbmp_ni->initialized_size);
a->data.non_resident.data_size =
- cpu_to_sle64(mftbmp_vi->i_size);
+ cpu_to_sle64(mftbmp_ni->initialized_size);
}
+ write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
/* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
@@ -1636,22 +1667,28 @@ unm_err_out:
goto err_out;
}
a = ctx->attr;
+ write_lock_irqsave(&mftbmp_ni->size_lock, flags);
mftbmp_ni->initialized_size = old_initialized_size;
a->data.non_resident.initialized_size =
cpu_to_sle64(old_initialized_size);
- if (mftbmp_vi->i_size != old_data_size) {
- mftbmp_vi->i_size = old_data_size;
+ if (i_size_read(mftbmp_vi) != old_data_size) {
+ i_size_write(mftbmp_vi, old_data_size);
a->data.non_resident.data_size = cpu_to_sle64(old_data_size);
}
+ write_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni);
+#ifdef DEBUG
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size,
- (long long)mftbmp_vi->i_size,
+ (long long)i_size_read(mftbmp_vi),
(long long)mftbmp_ni->initialized_size);
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+#endif /* DEBUG */
err_out:
return ret;
}
@@ -1679,7 +1716,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
{
LCN lcn;
VCN old_last_vcn;
- s64 min_nr, nr, ll = 0;
+ s64 min_nr, nr, ll;
+ unsigned long flags;
ntfs_inode *mft_ni;
runlist_element *rl, *rl2;
ntfs_attr_search_ctx *ctx = NULL;
@@ -1695,23 +1733,25 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
* Determine the preferred allocation location, i.e. the last lcn of
* the mft data attribute. The allocated size of the mft data
* attribute cannot be zero so we are ok to do this.
- * ntfs_find_vcn() returns the runlist locked on success.
*/
- rl = ntfs_find_vcn(mft_ni, (mft_ni->allocated_size - 1) >>
- vol->cluster_size_bits, TRUE);
+ down_write(&mft_ni->runlist.lock);
+ read_lock_irqsave(&mft_ni->size_lock, flags);
+ ll = mft_ni->allocated_size;
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
+ rl = ntfs_attr_find_vcn_nolock(mft_ni,
+ (ll - 1) >> vol->cluster_size_bits, TRUE);
if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
+ up_write(&mft_ni->runlist.lock);
ntfs_error(vol->sb, "Failed to determine last allocated "
"cluster of mft data attribute.");
- if (!IS_ERR(rl)) {
- up_write(&mft_ni->runlist.lock);
+ if (!IS_ERR(rl))
ret = -EIO;
- } else
+ else
ret = PTR_ERR(rl);
return ret;
}
lcn = rl->lcn + rl->length;
- ntfs_debug("Last lcn of mft data attribute is 0x%llx.",
- (long long)lcn);
+ ntfs_debug("Last lcn of mft data attribute is 0x%llx.", (long long)lcn);
/* Minimum allocation is one mft record worth of clusters. */
min_nr = vol->mft_record_size >> vol->cluster_size_bits;
if (!min_nr)
@@ -1721,12 +1761,13 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
if (!nr)
nr = min_nr;
/* Ensure we do not go above 2^32-1 mft records. */
- if (unlikely((mft_ni->allocated_size +
- (nr << vol->cluster_size_bits)) >>
+ read_lock_irqsave(&mft_ni->size_lock, flags);
+ ll = mft_ni->allocated_size;
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
+ if (unlikely((ll + (nr << vol->cluster_size_bits)) >>
vol->mft_record_size_bits >= (1ll << 32))) {
nr = min_nr;
- if (unlikely((mft_ni->allocated_size +
- (nr << vol->cluster_size_bits)) >>
+ if (unlikely((ll + (nr << vol->cluster_size_bits)) >>
vol->mft_record_size_bits >= (1ll << 32))) {
ntfs_warning(vol->sb, "Cannot allocate mft record "
"because the maximum number of inodes "
@@ -1772,7 +1813,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
return PTR_ERR(rl);
}
mft_ni->runlist.rl = rl;
- ntfs_debug("Allocated %lli clusters.", nr);
+ ntfs_debug("Allocated %lli clusters.", (long long)nr);
/* Find the last run in the new runlist. */
for (; rl[1].length; rl++)
;
@@ -1808,7 +1849,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
BUG_ON(ll < rl2->vcn);
BUG_ON(ll >= rl2->vcn + rl2->length);
/* Get the size for the new mapping pairs array for this extent. */
- mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
if (unlikely(mp_size <= 0)) {
ntfs_error(vol->sb, "Get size for mapping pairs failed for "
"mft data attribute extent.");
@@ -1832,7 +1873,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
// moving other attributes out of this mft record.
// Note: Use the special reserved mft records and ensure that
// this extent is not required to find the mft record in
- // question.
+ // question. If no free special records left we would need to
+ // move an existing record away, insert ours in its place, and
+ // then place the moved record into the newly allocated space
+ // and we would then need to update all references to this mft
+ // record appropriately. This is rather complicated...
ntfs_error(vol->sb, "Not enough space in this mft record to "
"accomodate extended mft data attribute "
"extent. Cannot handle this yet.");
@@ -1843,7 +1888,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
/* Generate the mapping pairs array directly into the attr record. */
ret = ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
- mp_size, rl2, ll, NULL);
+ mp_size, rl2, ll, -1, NULL);
if (unlikely(ret)) {
ntfs_error(vol->sb, "Failed to build mapping pairs array of "
"mft data attribute.");
@@ -1875,9 +1920,11 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
}
a = ctx->attr;
}
+ write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->allocated_size += nr << vol->cluster_size_bits;
a->data.non_resident.allocated_size =
cpu_to_sle64(mft_ni->allocated_size);
+ write_unlock_irqrestore(&mft_ni->size_lock, flags);
/* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
@@ -1892,7 +1939,9 @@ restore_undo_alloc:
CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx)) {
ntfs_error(vol->sb, "Failed to find last attribute extent of "
"mft data attribute.%s", es);
+ write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->allocated_size += nr << vol->cluster_size_bits;
+ write_unlock_irqrestore(&mft_ni->size_lock, flags);
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni);
up_write(&mft_ni->runlist.lock);
@@ -1921,7 +1970,7 @@ undo_alloc:
a->data.non_resident.mapping_pairs_offset),
old_alen - le16_to_cpu(
a->data.non_resident.mapping_pairs_offset),
- rl2, ll, NULL)) {
+ rl2, ll, -1, NULL)) {
ntfs_error(vol->sb, "Failed to restore mapping pairs "
"array.%s", es);
NVolSetErrors(vol);
@@ -1991,7 +2040,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no,
"reports this as corruption, please email "
"linux-ntfs-dev@lists.sourceforge.net stating "
"that you saw this message and that the "
- "modified file system created was corrupt. "
+ "modified filesystem created was corrupt. "
"Thank you.");
}
/* Set the update sequence number to 1. */
@@ -2036,6 +2085,7 @@ static int ntfs_mft_record_layout(const ntfs_volume *vol, const s64 mft_no,
*/
static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no)
{
+ loff_t i_size;
struct inode *mft_vi = vol->mft_ino;
struct page *page;
MFT_RECORD *m;
@@ -2051,10 +2101,11 @@ static int ntfs_mft_record_format(const ntfs_volume *vol, const s64 mft_no)
index = mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
ofs = (mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
/* The maximum valid index into the page cache for $MFT's data. */
- end_index = mft_vi->i_size >> PAGE_CACHE_SHIFT;
+ i_size = i_size_read(mft_vi);
+ end_index = i_size >> PAGE_CACHE_SHIFT;
if (unlikely(index >= end_index)) {
if (unlikely(index > end_index || ofs + vol->mft_record_size >=
- (mft_vi->i_size & ~PAGE_CACHE_MASK))) {
+ (i_size & ~PAGE_CACHE_MASK))) {
ntfs_error(vol->sb, "Tried to format non-existing mft "
"record 0x%llx.", (long long)mft_no);
return -ENOENT;
@@ -2188,6 +2239,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
ntfs_inode *base_ni, MFT_RECORD **mrec)
{
s64 ll, bit, old_data_initialized, old_data_size;
+ unsigned long flags;
struct inode *vi;
struct page *page;
ntfs_inode *mft_ni, *mftbmp_ni, *ni;
@@ -2237,9 +2289,13 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* the first 24 mft records as they are special and whilst they may not
* be in use, we do not allocate from them.
*/
+ read_lock_irqsave(&mft_ni->size_lock, flags);
ll = mft_ni->initialized_size >> vol->mft_record_size_bits;
- if (mftbmp_ni->initialized_size << 3 > ll &&
- mftbmp_ni->initialized_size > 3) {
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
+ old_data_initialized = mftbmp_ni->initialized_size;
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+ if (old_data_initialized << 3 > ll && old_data_initialized > 3) {
bit = ll;
if (bit < 24)
bit = 24;
@@ -2254,15 +2310,18 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
* mft record that we can allocate.
* Note: The smallest mft record we allocate is mft record 24.
*/
- bit = mftbmp_ni->initialized_size << 3;
+ bit = old_data_initialized << 3;
if (unlikely(bit >= (1ll << 32)))
goto max_err_out;
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
+ old_data_size = mftbmp_ni->allocated_size;
ntfs_debug("Status of mftbmp before extension: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.",
- (long long)mftbmp_ni->allocated_size,
- (long long)vol->mftbmp_ino->i_size,
- (long long)mftbmp_ni->initialized_size);
- if (mftbmp_ni->initialized_size + 8 > mftbmp_ni->allocated_size) {
+ (long long)old_data_size,
+ (long long)i_size_read(vol->mftbmp_ino),
+ (long long)old_data_initialized);
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+ if (old_data_initialized + 8 > old_data_size) {
/* Need to extend bitmap by one more cluster. */
ntfs_debug("mftbmp: initialized_size + 8 > allocated_size.");
err = ntfs_mft_bitmap_extend_allocation_nolock(vol);
@@ -2270,12 +2329,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
up_write(&vol->mftbmp_lock);
goto err_out;
}
+#ifdef DEBUG
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Status of mftbmp after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size,
- (long long)vol->mftbmp_ino->i_size,
+ (long long)i_size_read(vol->mftbmp_ino),
(long long)mftbmp_ni->initialized_size);
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+#endif /* DEBUG */
}
/*
* We now have sufficient allocated space, extend the initialized_size
@@ -2287,12 +2350,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, const int mode,
up_write(&vol->mftbmp_lock);
goto err_out;
}
+#ifdef DEBUG
+ read_lock_irqsave(&mftbmp_ni->size_lock, flags);
ntfs_debug("Status of mftbmp after initialized extention: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(long long)mftbmp_ni->allocated_size,
- (long long)vol->mftbmp_ino->i_size,
+ (long long)i_size_read(vol->mftbmp_ino),
(long long)mftbmp_ni->initialized_size);
+ read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
+#endif /* DEBUG */
ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit);
found_free_rec:
/* @bit is the found free mft record, allocate it in the mft bitmap. */
@@ -2314,7 +2381,10 @@ have_alloc_rec:
* parallel allocation could allocate the same mft record as this one.
*/
ll = (bit + 1) << vol->mft_record_size_bits;
- if (ll <= mft_ni->initialized_size) {
+ read_lock_irqsave(&mft_ni->size_lock, flags);
+ old_data_initialized = mft_ni->initialized_size;
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
+ if (ll <= old_data_initialized) {
ntfs_debug("Allocated mft record already initialized.");
goto mft_rec_already_initialized;
}
@@ -2325,26 +2395,30 @@ have_alloc_rec:
* actually traversed more than once when a freshly formatted volume is
* first written to so it optimizes away nicely in the common case.
*/
+ read_lock_irqsave(&mft_ni->size_lock, flags);
ntfs_debug("Status of mft data before extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(long long)mft_ni->allocated_size,
- (long long)vol->mft_ino->i_size,
+ (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size);
while (ll > mft_ni->allocated_size) {
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
err = ntfs_mft_data_extend_allocation_nolock(vol);
if (unlikely(err)) {
ntfs_error(vol->sb, "Failed to extend mft data "
"allocation.");
goto undo_mftbmp_alloc_nolock;
}
+ read_lock_irqsave(&mft_ni->size_lock, flags);
ntfs_debug("Status of mft data after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.",
(long long)mft_ni->allocated_size,
- (long long)vol->mft_ino->i_size,
+ (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size);
}
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
/*
* Extend mft data initialized size (and data size of course) to reach
* the allocated mft record, formatting the mft records allong the way.
@@ -2352,6 +2426,7 @@ have_alloc_rec:
* needed by ntfs_mft_record_format(). We will update the attribute
* record itself in one fell swoop later on.
*/
+ write_lock_irqsave(&mft_ni->size_lock, flags);
old_data_initialized = mft_ni->initialized_size;
old_data_size = vol->mft_ino->i_size;
while (ll > mft_ni->initialized_size) {
@@ -2360,8 +2435,9 @@ have_alloc_rec:
new_initialized_size = mft_ni->initialized_size +
vol->mft_record_size;
mft_no = mft_ni->initialized_size >> vol->mft_record_size_bits;
- if (new_initialized_size > vol->mft_ino->i_size)
- vol->mft_ino->i_size = new_initialized_size;
+ if (new_initialized_size > i_size_read(vol->mft_ino))
+ i_size_write(vol->mft_ino, new_initialized_size);
+ write_unlock_irqrestore(&mft_ni->size_lock, flags);
ntfs_debug("Initializing mft record 0x%llx.",
(long long)mft_no);
err = ntfs_mft_record_format(vol, mft_no);
@@ -2369,8 +2445,10 @@ have_alloc_rec:
ntfs_error(vol->sb, "Failed to format mft record.");
goto undo_data_init;
}
+ write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->initialized_size = new_initialized_size;
}
+ write_unlock_irqrestore(&mft_ni->size_lock, flags);
record_formatted = TRUE;
/* Update the mft data attribute record to reflect the new sizes. */
m = map_mft_record(mft_ni);
@@ -2396,22 +2474,27 @@ have_alloc_rec:
goto undo_data_init;
}
a = ctx->attr;
+ read_lock_irqsave(&mft_ni->size_lock, flags);
a->data.non_resident.initialized_size =
cpu_to_sle64(mft_ni->initialized_size);
- a->data.non_resident.data_size = cpu_to_sle64(vol->mft_ino->i_size);
+ a->data.non_resident.data_size =
+ cpu_to_sle64(i_size_read(vol->mft_ino));
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
/* Ensure the changes make it to disk. */
flush_dcache_mft_record_page(ctx->ntfs_ino);
mark_mft_record_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
unmap_mft_record(mft_ni);
+ 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.",
(long long)mft_ni->allocated_size,
- (long long)vol->mft_ino->i_size,
+ (long long)i_size_read(vol->mft_ino),
(long long)mft_ni->initialized_size);
- BUG_ON(vol->mft_ino->i_size > mft_ni->allocated_size);
- BUG_ON(mft_ni->initialized_size > vol->mft_ino->i_size);
+ BUG_ON(i_size_read(vol->mft_ino) > mft_ni->allocated_size);
+ BUG_ON(mft_ni->initialized_size > i_size_read(vol->mft_ino));
+ read_unlock_irqrestore(&mft_ni->size_lock, flags);
mft_rec_already_initialized:
/*
* We can finally drop the mft bitmap lock as the mft data attribute
@@ -2652,8 +2735,10 @@ mft_rec_already_initialized:
*mrec = m;
return ni;
undo_data_init:
+ write_lock_irqsave(&mft_ni->size_lock, flags);
mft_ni->initialized_size = old_data_initialized;
- vol->mft_ino->i_size = old_data_size;
+ i_size_write(vol->mft_ino, old_data_size);
+ write_unlock_irqrestore(&mft_ni->size_lock, flags);
goto undo_mftbmp_alloc_nolock;
undo_mftbmp_alloc:
down_write(&vol->mftbmp_lock);