Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 905685f6 authored by Anton Altaparmakov's avatar Anton Altaparmakov
Browse files

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: default avatarAnton Altaparmakov <aia21@cantab.net>
parent 43b01fda
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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.

+18 −0
Original line number Diff line number Diff line
@@ -355,6 +355,7 @@ static int ntfs_readpage(struct file *file, struct page *page)
	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 *file, struct page *page)
		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 *page, struct writeback_control *wbc)
	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 *page, struct writeback_control *wbc)
		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;
+25 −16
Original line number Diff line number Diff line
@@ -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 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
		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);