tree 0ff1d145a7771b24643c1b685ecbb3f791cda6fb
parent 43b01fda8b17b2b63e7dcdeed11c2ebba56b1fc9
author Anton Altaparmakov <[EMAIL PROTECTED]> Thu, 10 Mar 2005 11:06:19 +0000
committer Anton Altaparmakov <[EMAIL PROTECTED]> Thu, 05 May 2005 11:26:01 +0100

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 <[EMAIL PROTECTED]>

 fs/ntfs/ChangeLog |    3 +++
 fs/ntfs/aops.c    |   18 ++++++++++++++++++
 fs/ntfs/attrib.c  |   41 +++++++++++++++++++++++++----------------
 3 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -100,6 +100,9 @@ ToDo/Notes:
        - Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
        - Fix sign of various error return values to be negative in
          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.
 
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -355,6 +355,7 @@ static int ntfs_readpage(struct file *fi
        u32 attr_len;
        int err = 0;
 
+retry_readpage:
        BUG_ON(!PageLocked(page));
        /*
         * This can potentially happen because we clear PageUptodate() during
@@ -408,6 +409,14 @@ static int ntfs_readpage(struct file *fi
                err = PTR_ERR(mrec);
                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);
        if (unlikely(!ctx)) {
                err = -ENOMEM;
@@ -1248,6 +1257,7 @@ static int ntfs_writepage(struct page *p
        u32 attr_len;
        int err;
 
+retry_writepage:
        BUG_ON(!PageLocked(page));
        i_size = i_size_read(vi);
        /* Is the page fully outside i_size? (truncate in progress) */
@@ -1338,6 +1348,14 @@ static int ntfs_writepage(struct page *p
                ctx = NULL;
                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);
        if (unlikely(!ctx)) {
                err = -ENOMEM;
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1376,19 +1376,6 @@ int ntfs_attr_make_non_resident(ntfs_ino
        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_ino
                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_ino
                                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_ino
        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);
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to