summary refs log tree commit diff
path: root/fs/ntfs/attrib.c
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cantab.net>2005-03-10 11:06:19 +0000
committerAnton Altaparmakov <aia21@cantab.net>2005-05-05 11:26:01 +0100
commit905685f68fc72844b8c2689c39a5c6c35e840152 (patch)
tree0ff1d145a7771b24643c1b685ecbb3f791cda6fb /fs/ntfs/attrib.c
parent43b01fda8b17b2b63e7dcdeed11c2ebba56b1fc9 (diff)
downloadlinux-905685f68fc72844b8c2689c39a5c6c35e840152.tar.gz
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>
Diffstat (limited to 'fs/ntfs/attrib.c')
-rw-r--r--fs/ntfs/attrib.c41
1 files changed, 25 insertions, 16 deletions
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 3b9de4040216..41859343a0c8 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1376,19 +1376,6 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
 	err = ntfs_attr_record_resize(m, a, arec_size);
 	if (unlikely(err))
 		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
 	 * 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),
 				a->name_length * sizeof(ntfschar));
 	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 |
 			ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
 	/* Setup the fields specific to non-resident attributes. */
@@ -1422,6 +1412,25 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
 				err);
 		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. */
 	flush_dcache_mft_record_page(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) {
 		set_page_dirty(page);
 		unlock_page(page);
+		mark_page_accessed(page);
 		page_cache_release(page);
 	}
 	ntfs_debug("Done.");
@@ -1492,11 +1502,10 @@ undo_err_out:
 		memcpy((u8*)a + mp_ofs, kaddr, attr_size);
 		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);
 	ni->allocated_size = arec_size - mp_ofs;
 	write_unlock_irqrestore(&ni->size_lock, flags);
-	NInoClearNonResident(ni);
 	/* Mark the mft record dirty, so it gets written back. */
 	flush_dcache_mft_record_page(ctx->ntfs_ino);
 	mark_mft_record_dirty(ctx->ntfs_ino);