All of lore.kernel.org
 help / color / mirror / Atom feed
From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
To: Andrew Morton <akpm@osdl.org>
Cc: linux-kernel@vger.kernel.org
Subject: Re: [PATCH 7/6] fat: Support a truncate() for expanding size
Date: Wed, 09 Nov 2005 05:40:32 +0900	[thread overview]
Message-ID: <87oe4una9r.fsf@devron.myhome.or.jp> (raw)
In-Reply-To: <874q6nww63.fsf@devron.myhome.or.jp> (OGAWA Hirofumi's message of "Tue, 08 Nov 2005 14:22:44 +0900")

OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> writes:

>> -int generic_cont_expand(struct inode *inode, loff_t size)
>> +static int __generic_cont_expand(struct inode *inode, loff_t size, int dont_do_that)
>>  {
>>  	struct address_space *mapping = inode->i_mapping;
>>  	struct page *page;
>> @@ -2182,9 +2182,8 @@ int generic_cont_expand(struct inode *in
>>  	** skip the prepare.  make sure we never send an offset for the start
>>  	** of a block
>>  	*/
>> -	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
>> +	if (!dont_do_that &&  (offset & (inode->i_sb->s_blocksize - 1)) == 0)
>>  		offset++;
>
> Yes. But, if size is the page boundary, index is different.
>
> Probably something like the below. And I'd like to do vmtruncate()
> if ->prepare_write() returns a error. The sync_page_range_nolock()
> can do by caller, so not necessary.
>
> Hmm, I'll rethink this at tonight (10 hours later), the result may be
> same after all though.

How about this patch? If ok, please replace

fat-support-a-truncate-for-expanding-size.patch

with this.

Thanks.
-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>



[PATCH] fat: Support a truncate() for expanding size (generic_cont_expand)

This patch changes generic_cont_expand(), in order to share the code
with fatfs.

  - Use vmtruncate() if ->prepare_write() returns a error.

Even if ->prepare_write() returns an error, it may already have added some
blocks. So, this truncates blocks outside of ->i_size by vmtruncate().

  - Add generic_cont_expand_simple().

The generic_cont_expand_simple() assumes that ->prepare_write() can handle
the block boundary. With this, we don't need to care the extra byte.


And for expanding a file size by truncate(), fatfs uses the
added generic_cont_expand_simple().

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
---

 fs/buffer.c                 |   60 +++++++++++++++++++++++++++++++++-----------
 fs/fat/file.c               |   31 ++++++++++++++++++++--
 include/linux/buffer_head.h |    3 +-
 3 files changed, 76 insertions(+), 18 deletions(-)

diff -puN fs/fat/file.c~fat_support-expand-truncate fs/fat/file.c
--- linux-2.6.14/fs/fat/file.c~fat_support-expand-truncate	2005-11-07 23:03:02.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c	2005-11-09 04:00:53.000000000 +0900
@@ -11,6 +11,7 @@
 #include <linux/msdos_fs.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
 		      unsigned int cmd, unsigned long arg)
@@ -124,6 +125,24 @@ struct file_operations fat_file_operatio
 	.sendfile	= generic_file_sendfile,
 };
 
+static int fat_cont_expand(struct inode *inode, loff_t size)
+{
+	struct address_space *mapping = inode->i_mapping;
+	loff_t start = inode->i_size, count = size - inode->i_size;
+	int err;
+
+	err = generic_cont_expand_simple(inode, size);
+	if (err)
+		goto out;
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	if (IS_SYNC(inode))
+		err = sync_page_range_nolock(inode, mapping, start, count);
+out:
+	return err;
+}
+
 int fat_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
@@ -132,11 +151,17 @@ int fat_notify_change(struct dentry *den
 
 	lock_kernel();
 
-	/* FAT cannot truncate to a longer file */
+	/*
+	 * Expand the file. Since inode_setattr() updates ->i_size
+	 * before calling the ->truncate(), but FAT needs to fill the
+	 * hole before it.
+	 */
 	if (attr->ia_valid & ATTR_SIZE) {
 		if (attr->ia_size > inode->i_size) {
-			error = -EPERM;
-			goto out;
+			error = fat_cont_expand(inode, attr->ia_size);
+			if (error || attr->ia_valid == ATTR_SIZE)
+				goto out;
+			attr->ia_valid &= ~ATTR_SIZE;
 		}
 	}
 
diff -puN fs/buffer.c~fat_support-expand-truncate fs/buffer.c
--- linux-2.6.14/fs/buffer.c~fat_support-expand-truncate	2005-11-09 03:35:10.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/buffer.c	2005-11-09 04:33:29.000000000 +0900
@@ -2160,11 +2160,12 @@ int block_read_full_page(struct page *pa
  * truncates.  Uses prepare/commit_write to allow the filesystem to
  * deal with the hole.  
  */
-int generic_cont_expand(struct inode *inode, loff_t size)
+static int __generic_cont_expand(struct inode *inode, loff_t size,
+				 pgoff_t index, unsigned int offset)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
-	unsigned long index, offset, limit;
+	unsigned long limit;
 	int err;
 
 	err = -EFBIG;
@@ -2176,24 +2177,24 @@ int generic_cont_expand(struct inode *in
 	if (size > inode->i_sb->s_maxbytes)
 		goto out;
 
-	offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
-
-	/* ugh.  in prepare/commit_write, if from==to==start of block, we 
-	** skip the prepare.  make sure we never send an offset for the start
-	** of a block
-	*/
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
-		offset++;
-	}
-	index = size >> PAGE_CACHE_SHIFT;
 	err = -ENOMEM;
 	page = grab_cache_page(mapping, index);
 	if (!page)
 		goto out;
 	err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
-	if (!err) {
-		err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+	if (err) {
+		/*
+		 * ->prepare_write() may have instantiated a few blocks
+		 * outside i_size.  Trim these off again.
+		 */
+		unlock_page(page);
+		page_cache_release(page);
+		vmtruncate(inode, inode->i_size);
+		goto out;
 	}
+
+	err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+
 	unlock_page(page);
 	page_cache_release(page);
 	if (err > 0)
@@ -2202,6 +2203,36 @@ out:
 	return err;
 }
 
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+
+	offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
+
+	/* ugh.  in prepare/commit_write, if from==to==start of block, we
+	** skip the prepare.  make sure we never send an offset for the start
+	** of a block
+	*/
+	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+		/* caller must handle this extra byte. */
+		offset++;
+	}
+	index = size >> PAGE_CACHE_SHIFT;
+
+	return __generic_cont_expand(inode, size, index, offset);
+}
+
+int generic_cont_expand_simple(struct inode *inode, loff_t size)
+{
+	loff_t pos = size - 1;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
+
+	/* prepare/commit_write can handle even if from==to==start of block. */
+	return __generic_cont_expand(inode, size, index, offset);
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
@@ -3145,6 +3176,7 @@ EXPORT_SYMBOL(fsync_bdev);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_commit_write);
 EXPORT_SYMBOL(generic_cont_expand);
+EXPORT_SYMBOL(generic_cont_expand_simple);
 EXPORT_SYMBOL(init_buffer);
 EXPORT_SYMBOL(invalidate_bdev);
 EXPORT_SYMBOL(ll_rw_block);
diff -puN include/linux/buffer_head.h~fat_support-expand-truncate include/linux/buffer_head.h
--- linux-2.6.14/include/linux/buffer_head.h~fat_support-expand-truncate	2005-11-09 03:37:26.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/buffer_head.h	2005-11-09 03:37:36.000000000 +0900
@@ -197,7 +197,8 @@ int block_read_full_page(struct page*, g
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
 int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
 				loff_t *);
-int generic_cont_expand(struct inode *inode, loff_t size) ;
+int generic_cont_expand(struct inode *inode, loff_t size);
+int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
 int block_sync_page(struct page *);
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
_

      reply	other threads:[~2005-11-08 20:40 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-11-07 17:32 [PATCH 1/6] fat: move fat_clusters_flush() to write_super() OGAWA Hirofumi
2005-11-07 17:36 ` [PATCH] fat: use sb_find_get_block() instead of sb_getblk() OGAWA Hirofumi
2005-11-07 17:37   ` [PATCH 3/6] fat: add the read/writepages() OGAWA Hirofumi
2005-11-07 17:39     ` [PATCH 4/6] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/ OGAWA Hirofumi
2005-11-07 17:41       ` [PATCH 5/6] fat: support ->direct_IO() OGAWA Hirofumi
2005-11-07 17:42         ` [PATCH 6/6] export/change sync_page_range/_nolock() OGAWA Hirofumi
2005-11-07 17:46           ` [PATCH 7/6] fat: Support a truncate() for expanding size OGAWA Hirofumi
2005-11-07 17:51             ` OGAWA Hirofumi
2005-11-08  1:06             ` Andrew Morton
2005-11-08  3:19               ` OGAWA Hirofumi
2005-11-08  4:19                 ` Andrew Morton
2005-11-08  5:22                   ` OGAWA Hirofumi
2005-11-08 20:40                     ` OGAWA Hirofumi [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87oe4una9r.fsf@devron.myhome.or.jp \
    --to=hirofumi@mail.parknet.co.jp \
    --cc=akpm@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.