NTFS: - Modify ->readpage and ->writepage (fs/ntfs/aops.c) so they detect
and handle the case where an attribute is converted from resident to non-resident by a concurrent file write. - Reorder some operations when converting an attribute from resident to non-resident (fs/ntfs/attrib.c) so it is safe wrt concurrent ->readpage and ->writepage. Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
This commit is contained in:
@@ -100,6 +100,9 @@ ToDo/Notes:
|
|||||||
- Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
|
- Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
|
||||||
- Fix sign of various error return values to be negative in
|
- Fix sign of various error return values to be negative in
|
||||||
fs/ntfs/lcnalloc.c.
|
fs/ntfs/lcnalloc.c.
|
||||||
|
- Modify ->readpage and ->writepage (fs/ntfs/aops.c) so they detect and
|
||||||
|
handle the case where an attribute is converted from resident to
|
||||||
|
non-resident by a concurrent file write.
|
||||||
|
|
||||||
2.1.22 - Many bug and race fixes and error handling improvements.
|
2.1.22 - Many bug and race fixes and error handling improvements.
|
||||||
|
|
||||||
|
@@ -355,6 +355,7 @@ static int ntfs_readpage(struct file *file, struct page *page)
|
|||||||
u32 attr_len;
|
u32 attr_len;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
retry_readpage:
|
||||||
BUG_ON(!PageLocked(page));
|
BUG_ON(!PageLocked(page));
|
||||||
/*
|
/*
|
||||||
* This can potentially happen because we clear PageUptodate() during
|
* This can potentially happen because we clear PageUptodate() during
|
||||||
@@ -408,6 +409,14 @@ static int ntfs_readpage(struct file *file, struct page *page)
|
|||||||
err = PTR_ERR(mrec);
|
err = PTR_ERR(mrec);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* If a parallel write made the attribute non-resident, drop the mft
|
||||||
|
* record and retry the readpage.
|
||||||
|
*/
|
||||||
|
if (unlikely(NInoNonResident(ni))) {
|
||||||
|
unmap_mft_record(base_ni);
|
||||||
|
goto retry_readpage;
|
||||||
|
}
|
||||||
ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
|
ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
|
||||||
if (unlikely(!ctx)) {
|
if (unlikely(!ctx)) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
@@ -1248,6 +1257,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
|
|||||||
u32 attr_len;
|
u32 attr_len;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
retry_writepage:
|
||||||
BUG_ON(!PageLocked(page));
|
BUG_ON(!PageLocked(page));
|
||||||
i_size = i_size_read(vi);
|
i_size = i_size_read(vi);
|
||||||
/* Is the page fully outside i_size? (truncate in progress) */
|
/* Is the page fully outside i_size? (truncate in progress) */
|
||||||
@@ -1338,6 +1348,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
|
|||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* If a parallel write made the attribute non-resident, drop the mft
|
||||||
|
* record and retry the writepage.
|
||||||
|
*/
|
||||||
|
if (unlikely(NInoNonResident(ni))) {
|
||||||
|
unmap_mft_record(base_ni);
|
||||||
|
goto retry_writepage;
|
||||||
|
}
|
||||||
ctx = ntfs_attr_get_search_ctx(base_ni, m);
|
ctx = ntfs_attr_get_search_ctx(base_ni, m);
|
||||||
if (unlikely(!ctx)) {
|
if (unlikely(!ctx)) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@@ -1376,19 +1376,6 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
|
|||||||
err = ntfs_attr_record_resize(m, a, arec_size);
|
err = ntfs_attr_record_resize(m, a, arec_size);
|
||||||
if (unlikely(err))
|
if (unlikely(err))
|
||||||
goto err_out;
|
goto err_out;
|
||||||
/* Setup the in-memory attribute structure to be non-resident. */
|
|
||||||
NInoSetNonResident(ni);
|
|
||||||
ni->runlist.rl = rl;
|
|
||||||
write_lock_irqsave(&ni->size_lock, flags);
|
|
||||||
ni->allocated_size = new_size;
|
|
||||||
write_unlock_irqrestore(&ni->size_lock, flags);
|
|
||||||
/*
|
|
||||||
* FIXME: For now just clear all of these as we do not support them
|
|
||||||
* when writing.
|
|
||||||
*/
|
|
||||||
NInoClearCompressed(ni);
|
|
||||||
NInoClearSparse(ni);
|
|
||||||
NInoClearEncrypted(ni);
|
|
||||||
/*
|
/*
|
||||||
* Convert the resident part of the attribute record to describe a
|
* Convert the resident part of the attribute record to describe a
|
||||||
* non-resident attribute.
|
* non-resident attribute.
|
||||||
@@ -1399,7 +1386,10 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
|
|||||||
memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
|
memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
|
||||||
a->name_length * sizeof(ntfschar));
|
a->name_length * sizeof(ntfschar));
|
||||||
a->name_offset = cpu_to_le16(name_ofs);
|
a->name_offset = cpu_to_le16(name_ofs);
|
||||||
/* Update the flags to match the in-memory ones. */
|
/*
|
||||||
|
* FIXME: For now just clear all of these as we do not support them
|
||||||
|
* when writing.
|
||||||
|
*/
|
||||||
a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
|
a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
|
||||||
ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
|
ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
|
||||||
/* Setup the fields specific to non-resident attributes. */
|
/* Setup the fields specific to non-resident attributes. */
|
||||||
@@ -1422,6 +1412,25 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
|
|||||||
err);
|
err);
|
||||||
goto undo_err_out;
|
goto undo_err_out;
|
||||||
}
|
}
|
||||||
|
/* Setup the in-memory attribute structure to be non-resident. */
|
||||||
|
/*
|
||||||
|
* FIXME: For now just clear all of these as we do not support them
|
||||||
|
* when writing.
|
||||||
|
*/
|
||||||
|
NInoClearSparse(ni);
|
||||||
|
NInoClearEncrypted(ni);
|
||||||
|
NInoClearCompressed(ni);
|
||||||
|
ni->runlist.rl = rl;
|
||||||
|
write_lock_irqsave(&ni->size_lock, flags);
|
||||||
|
ni->allocated_size = new_size;
|
||||||
|
write_unlock_irqrestore(&ni->size_lock, flags);
|
||||||
|
/*
|
||||||
|
* This needs to be last since the address space operations ->readpage
|
||||||
|
* and ->writepage can run concurrently with us as they are not
|
||||||
|
* serialized on i_sem. Note, we are not allowed to fail once we flip
|
||||||
|
* this switch, which is another reason to do this last.
|
||||||
|
*/
|
||||||
|
NInoSetNonResident(ni);
|
||||||
/* Mark the mft record dirty, so it gets written back. */
|
/* Mark the mft record dirty, so it gets written back. */
|
||||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||||
@@ -1431,6 +1440,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
|
|||||||
if (page) {
|
if (page) {
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
|
mark_page_accessed(page);
|
||||||
page_cache_release(page);
|
page_cache_release(page);
|
||||||
}
|
}
|
||||||
ntfs_debug("Done.");
|
ntfs_debug("Done.");
|
||||||
@@ -1492,11 +1502,10 @@ undo_err_out:
|
|||||||
memcpy((u8*)a + mp_ofs, kaddr, attr_size);
|
memcpy((u8*)a + mp_ofs, kaddr, attr_size);
|
||||||
kunmap_atomic(kaddr, KM_USER0);
|
kunmap_atomic(kaddr, KM_USER0);
|
||||||
}
|
}
|
||||||
/* Finally setup the ntfs inode appropriately. */
|
/* Setup the allocated size in the ntfs inode in case it changed. */
|
||||||
write_lock_irqsave(&ni->size_lock, flags);
|
write_lock_irqsave(&ni->size_lock, flags);
|
||||||
ni->allocated_size = arec_size - mp_ofs;
|
ni->allocated_size = arec_size - mp_ofs;
|
||||||
write_unlock_irqrestore(&ni->size_lock, flags);
|
write_unlock_irqrestore(&ni->size_lock, flags);
|
||||||
NInoClearNonResident(ni);
|
|
||||||
/* Mark the mft record dirty, so it gets written back. */
|
/* Mark the mft record dirty, so it gets written back. */
|
||||||
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
flush_dcache_mft_record_page(ctx->ntfs_ino);
|
||||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||||
|
Reference in New Issue
Block a user