From 07a4e2da7dd3c9345f84b2552872f9d38c257451 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Wed, 12 Jan 2005 13:08:26 +0000 Subject: NTFS: Use i_size_{read,write}() in fs/ntfs/{aops.c,mft.c} and protect access to the i_size and other size fields using the size_lock. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 56 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 45d56e41ed98..a53212793809 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -66,19 +66,22 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ni = NTFS_I(page->mapping->host); if (likely(uptodate)) { - s64 file_ofs; + s64 file_ofs, initialized_size; set_buffer_uptodate(bh); file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) + bh_offset(bh); + read_lock_irqsave(&ni->size_lock, flags); + initialized_size = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); /* Check for the current buffer head overflowing. */ - if (file_ofs + bh->b_size > ni->initialized_size) { + if (file_ofs + bh->b_size > initialized_size) { char *addr; int ofs = 0; - if (file_ofs < ni->initialized_size) - ofs = ni->initialized_size - file_ofs; + if (file_ofs < initialized_size) + ofs = initialized_size - file_ofs; addr = kmap_atomic(page, KM_BIO_SRC_IRQ); memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs); flush_dcache_page(page); @@ -168,6 +171,7 @@ static int ntfs_read_block(struct page *page) runlist_element *rl; struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; sector_t iblock, lblock, zblock; + unsigned long flags; unsigned int blocksize, vcn_ofs; int i, nr; unsigned char blocksize_bits; @@ -190,8 +194,10 @@ static int ntfs_read_block(struct page *page) } iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + read_lock_irqsave(&ni->size_lock, flags); lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits; zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits; + read_unlock_irqrestore(&ni->size_lock, flags); /* Loop through all the buffers in the page. */ rl = NULL; @@ -463,12 +469,15 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) { VCN vcn; LCN lcn; + s64 initialized_size; + loff_t i_size; sector_t block, dblock, iblock; struct inode *vi; ntfs_inode *ni; ntfs_volume *vol; runlist_element *rl; struct buffer_head *bh, *head; + unsigned long flags; unsigned int blocksize, vcn_ofs; int err; BOOL need_end_writeback; @@ -510,11 +519,16 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) /* The first block in the page. */ block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + read_lock_irqsave(&ni->size_lock, flags); + i_size = i_size_read(vi); + initialized_size = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); + /* The first out of bounds block for the data size. */ - dblock = (vi->i_size + blocksize - 1) >> blocksize_bits; + dblock = (i_size + blocksize - 1) >> blocksize_bits; /* The last (fully or partially) initialized block. */ - iblock = ni->initialized_size >> blocksize_bits; + iblock = initialized_size >> blocksize_bits; /* * Be very careful. We have no exclusion from __set_page_dirty_buffers @@ -559,7 +573,7 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) /* Make sure we have enough initialized size. */ if (unlikely((block >= iblock) && - (ni->initialized_size < vi->i_size))) { + (initialized_size < i_size))) { /* * If this page is fully outside initialized size, zero * out all pages between the current initialized size @@ -846,7 +860,7 @@ static int ntfs_write_mst_block(struct page *page, (PAGE_CACHE_SHIFT - bh_size_bits); /* The first out of bounds block for the data size. */ - dblock = (vi->i_size + bh_size - 1) >> bh_size_bits; + dblock = (i_size_read(vi) + bh_size - 1) >> bh_size_bits; rl = NULL; err = err2 = nr_bhs = nr_recs = nr_locked_nis = 0; @@ -1367,7 +1381,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) */ attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); - i_size = i_size_read(VFS_I(ni)); + i_size = i_size_read(vi); kaddr = kmap_atomic(page, KM_USER0); if (unlikely(attr_len > i_size)) { /* Zero out of bounds area in the mft record. */ @@ -1425,12 +1439,15 @@ static int ntfs_prepare_nonresident_write(struct page *page, { VCN vcn; LCN lcn; + s64 initialized_size; + loff_t i_size; sector_t block, ablock, iblock; struct inode *vi; ntfs_inode *ni; ntfs_volume *vol; runlist_element *rl; struct buffer_head *bh, *head, *wait[2], **wait_bh = wait; + unsigned long flags; unsigned int vcn_ofs, block_start, block_end, blocksize; int err; BOOL is_retry; @@ -1462,6 +1479,7 @@ static int ntfs_prepare_nonresident_write(struct page *page, /* The first block in the page. */ block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); + read_lock_irqsave(&ni->size_lock, flags); /* * The first out of bounds block for the allocated size. No need to * round up as allocated_size is in multiples of cluster size and the @@ -1470,8 +1488,12 @@ static int ntfs_prepare_nonresident_write(struct page *page, */ ablock = ni->allocated_size >> blocksize_bits; + i_size = i_size_read(vi); + initialized_size = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); + /* The last (fully or partially) initialized block. */ - iblock = ni->initialized_size >> blocksize_bits; + iblock = initialized_size >> blocksize_bits; /* Loop through all the buffers in the page. */ block_start = 0; @@ -1518,7 +1540,7 @@ static int ntfs_prepare_nonresident_write(struct page *page, * request, i.e. block < ablock is true. */ if (unlikely((block >= iblock) && - (ni->initialized_size < vi->i_size))) { + (initialized_size < i_size))) { /* * If this page is fully outside initialized size, zero * out all pages between the current initialized size @@ -1868,7 +1890,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, BUG_ON(page_has_buffers(page)); new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to; /* If we do not need to resize the attribute allocation we are done. */ - if (new_size <= vi->i_size) + if (new_size <= i_size_read(vi)) goto done; // FIXME: We abort for now as this code is not safe. @@ -1904,7 +1926,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, a = ctx->attr; /* The total length of the attribute value. */ attr_len = le32_to_cpu(a->data.resident.value_length); - BUG_ON(vi->i_size != attr_len); + BUG_ON(i_size_read(vi) != attr_len); /* Check if new size is allowed in $AttrDef. */ err = ntfs_attr_size_bounds_check(vol, ni->type, new_size); if (unlikely(err)) { @@ -2047,7 +2069,7 @@ static int ntfs_commit_nonresident_write(struct page *page, * now we know ntfs_prepare_write() would have failed in the write * exceeds i_size case, so this will never trigger which is fine. */ - if (pos > vi->i_size) { + if (pos > i_size_read(vi)) { ntfs_error(vi->i_sb, "Writing beyond the existing file size is " "not supported yet. Sorry."); return -EOPNOTSUPP; @@ -2183,9 +2205,13 @@ static int ntfs_commit_write(struct file *file, struct page *page, } kunmap_atomic(kaddr, KM_USER0); /* Update i_size if necessary. */ - if (vi->i_size < attr_len) { + if (i_size_read(vi) < attr_len) { + unsigned long flags; + + write_lock_irqsave(&ni->size_lock, flags); ni->allocated_size = ni->initialized_size = attr_len; i_size_write(vi, attr_len); + write_unlock_irqrestore(&ni->size_lock, flags); } /* Mark the mft record dirty, so it gets written back. */ flush_dcache_mft_record_page(ctx->ntfs_ino); -- cgit v1.2.3 From 149f0c5200188a43f1fc11ca2fb14d8183013d10 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Wed, 12 Jan 2005 13:52:30 +0000 Subject: NTFS: Repeat a failed ntfs_truncate() in fs/ntfs/aops.c::ntfs_writepage() and abort if it fails again. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index a53212793809..ac65806ee515 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1237,19 +1237,30 @@ done: static int ntfs_writepage(struct page *page, struct writeback_control *wbc) { loff_t i_size; - struct inode *vi; - ntfs_inode *ni, *base_ni; + struct inode *vi = page->mapping->host; + ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); char *kaddr; - ntfs_attr_search_ctx *ctx; - MFT_RECORD *m; + ntfs_attr_search_ctx *ctx = NULL; + MFT_RECORD *m = NULL; u32 attr_len; int err; BUG_ON(!PageLocked(page)); - - vi = page->mapping->host; + /* + * If a previous ntfs_truncate() failed, repeat it and abort if it + * fails again. + */ + if (unlikely(NInoTruncateFailed(ni))) { + down_write(&vi->i_alloc_sem); + err = ntfs_truncate(vi); + up_write(&vi->i_alloc_sem); + if (err || NInoTruncateFailed(ni)) { + if (!err) + err = -EIO; + goto err_out; + } + } i_size = i_size_read(vi); - /* Is the page fully outside i_size? (truncate in progress) */ if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)) { @@ -1262,8 +1273,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ntfs_debug("Write outside i_size - truncated?"); return 0; } - ni = NTFS_I(vi); - /* NInoNonResident() == NInoIndexAllocPresent() */ if (NInoNonResident(ni)) { /* @@ -1419,8 +1428,10 @@ err_out: err = 0; } else { ntfs_error(vi->i_sb, "Resident attribute write failed with " - "error %i. Setting page error flag.", err); + "error %i.", err); SetPageError(page); + NVolSetErrors(ni->vol); + make_bad_inode(vi); } unlock_page(page); if (ctx) -- cgit v1.2.3 From 946929d813a3bde095678526dd037ab9ac6efe35 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Thu, 13 Jan 2005 15:26:29 +0000 Subject: NTFS: Fixup the resident attribute resizing code in fs/ntfs/aops.c::ntfs_{prepare,commit}_write()() and re-enable it. It should be safe now. (Famous last words...) Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index ac65806ee515..92215228eeab 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -872,6 +872,7 @@ static int ntfs_write_mst_block(struct page *page, if (likely(block < rec_block)) { if (unlikely(block >= dblock)) { clear_buffer_dirty(bh); + set_buffer_uptodate(bh); continue; } /* @@ -1830,6 +1831,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { s64 new_size; + unsigned long flags; struct inode *vi = page->mapping->host; ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); ntfs_volume *vol = ni->vol; @@ -1903,12 +1905,6 @@ static int ntfs_prepare_write(struct file *file, struct page *page, /* If we do not need to resize the attribute allocation we are done. */ if (new_size <= i_size_read(vi)) goto done; - - // FIXME: We abort for now as this code is not safe. - ntfs_error(vi->i_sb, "Changing the file size is not supported yet. " - "Sorry."); - return -EOPNOTSUPP; - /* Map, pin, and lock the (base) mft record. */ if (!NInoAttr(ni)) base_ni = ni; @@ -1937,7 +1933,17 @@ static int ntfs_prepare_write(struct file *file, struct page *page, a = ctx->attr; /* The total length of the attribute value. */ attr_len = le32_to_cpu(a->data.resident.value_length); - BUG_ON(i_size_read(vi) != attr_len); + /* Fix an eventual previous failure of ntfs_commit_write(). */ + read_lock_irqsave(&ni->size_lock, flags); + if (unlikely(ni->initialized_size < attr_len)) { + attr_len = ni->initialized_size; + a->data.resident.value_length = cpu_to_le32(attr_len); + BUG_ON(attr_len < i_size_read(vi)); + } + read_unlock_irqrestore(&ni->size_lock, flags); + /* If we do not need to resize the attribute allocation we are done. */ + if (new_size <= attr_len) + goto done_unm; /* Check if new size is allowed in $AttrDef. */ err = ntfs_attr_size_bounds_check(vol, ni->type, new_size); if (unlikely(err)) { @@ -1995,6 +2001,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, } flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); +done_unm: ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); /* -- cgit v1.2.3 From f40661be038ce6ed9ef6a8b80307a9153bd95769 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Thu, 13 Jan 2005 16:03:38 +0000 Subject: NTFS: Optimise/reorganise some error handling code in fs/ntfs/aops.c. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 92215228eeab..812d53e93354 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1392,14 +1392,11 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); i_size = i_size_read(vi); - kaddr = kmap_atomic(page, KM_USER0); if (unlikely(attr_len > i_size)) { - /* Zero out of bounds area in the mft record. */ - memset((u8*)ctx->attr + le16_to_cpu( - ctx->attr->data.resident.value_offset) + - i_size, 0, attr_len - i_size); attr_len = i_size; + ctx->attr->data.resident.value_length = cpu_to_le32(attr_len); } + kaddr = kmap_atomic(page, KM_USER0); /* Copy the data from the page to the mft record. */ memcpy((u8*)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset), @@ -1831,7 +1828,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { s64 new_size; - unsigned long flags; + loff_t i_size; struct inode *vi = page->mapping->host; ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi); ntfs_volume *vol = ni->vol; @@ -1934,13 +1931,11 @@ static int ntfs_prepare_write(struct file *file, struct page *page, /* The total length of the attribute value. */ attr_len = le32_to_cpu(a->data.resident.value_length); /* Fix an eventual previous failure of ntfs_commit_write(). */ - read_lock_irqsave(&ni->size_lock, flags); - if (unlikely(ni->initialized_size < attr_len)) { - attr_len = ni->initialized_size; + i_size = i_size_read(vi); + if (unlikely(attr_len > i_size)) { + attr_len = i_size; a->data.resident.value_length = cpu_to_le32(attr_len); - BUG_ON(attr_len < i_size_read(vi)); } - read_unlock_irqrestore(&ni->size_lock, flags); /* If we do not need to resize the attribute allocation we are done. */ if (new_size <= attr_len) goto done_unm; -- cgit v1.2.3 From b6ad6c52fe36ab35d0fe28c064f59de2ba670c2a Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Tue, 15 Feb 2005 10:08:43 +0000 Subject: NTFS: - Split ntfs_map_runlist() into ntfs_map_runlist() and a non-locking helper ntfs_map_runlist_nolock() which is used by ntfs_map_runlist(). This allows us to map runlist fragments with the runlist lock already held without having to drop and reacquire it around the call. Adapt all callers. - Change ntfs_find_vcn() to ntfs_find_vcn_nolock() which takes a locked runlist. This allows us to find runlist elements with the runlist lock already held without having to drop and reacquire it around the call. Adapt all callers. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 812d53e93354..2b4b8b9e8796 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -2,7 +2,7 @@ * aops.c - NTFS kernel address space operations and page cache handling. * 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 @@ -135,7 +135,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) i * rec_size), rec_size); flush_dcache_page(page); kunmap_atomic(addr, KM_BIO_SRC_IRQ); - if (likely(!PageError(page) && page_uptodate)) + if (likely(page_uptodate && !PageError(page))) SetPageUptodate(page); } unlock_page(page); @@ -347,11 +347,11 @@ handle_zblock: */ static int ntfs_readpage(struct file *file, struct page *page) { - loff_t i_size; ntfs_inode *ni, *base_ni; u8 *kaddr; ntfs_attr_search_ctx *ctx; MFT_RECORD *mrec; + unsigned long flags; u32 attr_len; int err = 0; @@ -389,9 +389,9 @@ static int ntfs_readpage(struct file *file, struct page *page) * Attribute is resident, implying it is not compressed or encrypted. * This also means the attribute is smaller than an mft record and * hence smaller than a page, so can simply zero out any pages with - * index above 0. We can also do this if the file size is 0. + * index above 0. */ - if (unlikely(page->index > 0 || !i_size_read(VFS_I(ni)))) { + if (unlikely(page->index > 0)) { kaddr = kmap_atomic(page, KM_USER0); memset(kaddr, 0, PAGE_CACHE_SIZE); flush_dcache_page(page); @@ -418,9 +418,10 @@ static int ntfs_readpage(struct file *file, struct page *page) if (unlikely(err)) goto put_unm_err_out; attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); - i_size = i_size_read(VFS_I(ni)); - if (unlikely(attr_len > i_size)) - attr_len = i_size; + read_lock_irqsave(&ni->size_lock, flags); + if (unlikely(attr_len > ni->initialized_size)) + attr_len = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); kaddr = kmap_atomic(page, KM_USER0); /* Copy the data to the page. */ memcpy(kaddr, (u8*)ctx->attr + @@ -1247,20 +1248,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) int err; BUG_ON(!PageLocked(page)); - /* - * If a previous ntfs_truncate() failed, repeat it and abort if it - * fails again. - */ - if (unlikely(NInoTruncateFailed(ni))) { - down_write(&vi->i_alloc_sem); - err = ntfs_truncate(vi); - up_write(&vi->i_alloc_sem); - if (err || NInoTruncateFailed(ni)) { - if (!err) - err = -EIO; - goto err_out; - } - } i_size = i_size_read(vi); /* Is the page fully outside i_size? (truncate in progress) */ if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >> @@ -1490,13 +1477,12 @@ static int ntfs_prepare_nonresident_write(struct page *page, read_lock_irqsave(&ni->size_lock, flags); /* - * The first out of bounds block for the allocated size. No need to + * The first out of bounds block for the allocated size. No need to * round up as allocated_size is in multiples of cluster size and the * minimum cluster size is 512 bytes, which is equal to the smallest * blocksize. */ ablock = ni->allocated_size >> blocksize_bits; - i_size = i_size_read(vi); initialized_size = ni->initialized_size; read_unlock_irqrestore(&ni->size_lock, flags); -- cgit v1.2.3 From 8907547d4b099e67762ea4891c127ea1f6dd1cb7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 3 Mar 2005 11:19:53 +0000 Subject: NTFS: Fix printk format warnings on ia64. (Randy Dunlap) Signed-off-by: Randy Dunlap Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 2b4b8b9e8796..2a7cba258cca 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -965,7 +965,8 @@ lock_retry_remap: "attribute type 0x%x) because " "its location on disk could " "not be determined (error " - "code %lli).", (s64)block << + "code %lli).", + (long long)block << bh_size_bits >> vol->mft_record_size_bits, ni->mft_no, ni->type, -- cgit v1.2.3 From 905685f68fc72844b8c2689c39a5c6c35e840152 Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Thu, 10 Mar 2005 11:06:19 +0000 Subject: 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 --- fs/ntfs/aops.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 2a7cba258cca..6241c4cfbe28 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -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; -- cgit v1.2.3 From d53ee3222459f347cb18985a845864bc81a44eaf Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Wed, 6 Apr 2005 16:11:20 +0100 Subject: NTFS: Use MAX_BUF_PER_PAGE instead of variable sized array allocation for better code generation and one less sparse warning in fs/ntfs/aops.c. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 6241c4cfbe28..24c46c200337 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -825,17 +825,15 @@ static int ntfs_write_mst_block(struct page *page, ntfs_inode *ni = NTFS_I(vi); ntfs_volume *vol = ni->vol; u8 *kaddr; - unsigned char bh_size_bits = vi->i_blkbits; - unsigned int bh_size = 1 << bh_size_bits; unsigned int rec_size = ni->itype.index.block_size; ntfs_inode *locked_nis[PAGE_CACHE_SIZE / rec_size]; struct buffer_head *bh, *head, *tbh, *rec_start_bh; - int max_bhs = PAGE_CACHE_SIZE / bh_size; - struct buffer_head *bhs[max_bhs]; + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; runlist_element *rl; - int i, nr_locked_nis, nr_recs, nr_bhs, bhs_per_rec, err, err2; - unsigned rec_size_bits; + int i, nr_locked_nis, nr_recs, nr_bhs, max_bhs, bhs_per_rec, err, err2; + unsigned bh_size, rec_size_bits; BOOL sync, is_mft, page_is_dirty, rec_is_dirty; + unsigned char bh_size_bits; ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index " "0x%lx.", vi->i_ino, ni->type, page->index); @@ -850,7 +848,11 @@ static int ntfs_write_mst_block(struct page *page, */ BUG_ON(!(is_mft || S_ISDIR(vi->i_mode) || (NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION))); + bh_size_bits = vi->i_blkbits; + bh_size = 1 << bh_size_bits; + max_bhs = PAGE_CACHE_SIZE / bh_size; BUG_ON(!max_bhs); + BUG_ON(max_bhs > MAX_BUF_PER_PAGE); /* Were we called for sync purposes? */ sync = (wbc->sync_mode == WB_SYNC_ALL); -- cgit v1.2.3 From 9f993fe4634b39ca4404ba278053b03f360ec08a Mon Sep 17 00:00:00 2001 From: Anton Altaparmakov Date: Sat, 25 Jun 2005 16:15:36 +0100 Subject: NTFS: Fix a bug in address space operations error recovery code paths where if the runlist was not mapped at all and a mapping error occured we would leave the runlist locked on exit to the function so that the next access to the same file would try to take the lock and deadlock. Signed-off-by: Anton Altaparmakov --- fs/ntfs/aops.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'fs/ntfs/aops.c') diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 24c46c200337..3f43bfe6184e 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -264,7 +264,8 @@ lock_retry_remap: goto lock_retry_remap; rl = NULL; lcn = err; - } + } else if (!rl) + up_read(&ni->runlist.lock); /* Hard error, zero out region. */ bh->b_blocknr = -1; SetPageError(page); @@ -690,7 +691,8 @@ lock_retry_remap: goto lock_retry_remap; rl = NULL; lcn = err; - } + } else if (!rl) + up_read(&ni->runlist.lock); /* Failed to map the buffer, even after retrying. */ bh->b_blocknr = -1; ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " @@ -965,8 +967,11 @@ lock_retry_remap: if (err2 == -ENOMEM) page_is_dirty = TRUE; lcn = err2; - } else + } else { err2 = -EIO; + if (!rl) + up_read(&ni->runlist.lock); + } /* Hard error. Abort writing this record. */ if (!err || err == -ENOMEM) err = err2; @@ -1660,6 +1665,8 @@ lock_retry_remap: "not supported yet. " "Sorry."); err = -EOPNOTSUPP; + if (!rl) + up_read(&ni->runlist.lock); goto err_out; } else if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { @@ -1674,7 +1681,8 @@ lock_retry_remap: goto lock_retry_remap; rl = NULL; lcn = err; - } + } else if (!rl) + up_read(&ni->runlist.lock); /* * Failed to map the buffer, even after * retrying. -- cgit v1.2.3