public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/6] fat: move fat_clusters_flush() to write_super()
@ 2005-11-07 17:32 OGAWA Hirofumi
  2005-11-07 17:36 ` [PATCH] fat: use sb_find_get_block() instead of sb_getblk() OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:32 UTC (permalink / raw)
  To: Andrew Morton, linux-kernel

It is overkill to update the FS_INFO whenever modifying
prev_free/free_clusters, because those are just a hint.

So, this patch uses ->write_super() for updating FS_INFO instead.

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

 fs/fat/fatent.c |    8 ++++++--
 fs/fat/inode.c  |   10 ++++++++--
 fs/fat/misc.c   |    2 --
 3 files changed, 14 insertions(+), 6 deletions(-)

diff -puN fs/fat/fatent.c~fat_write_super fs/fat/fatent.c
--- linux-2.6.14/fs/fat/fatent.c~fat_write_super	2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/fatent.c	2005-11-07 03:46:35.000000000 +0900
@@ -476,6 +476,7 @@ int fat_alloc_clusters(struct inode *ino
 				sbi->prev_free = entry;
 				if (sbi->free_clusters != -1)
 					sbi->free_clusters--;
+				sb->s_dirt = 1;
 
 				cluster[idx_clus] = entry;
 				idx_clus++;
@@ -496,6 +497,7 @@ int fat_alloc_clusters(struct inode *ino
 
 	/* Couldn't allocate the free entries */
 	sbi->free_clusters = 0;
+	sb->s_dirt = 1;
 	err = -ENOSPC;
 
 out:
@@ -509,7 +511,6 @@ out:
 	}
 	for (i = 0; i < nr_bhs; i++)
 		brelse(bhs[i]);
-	fat_clusters_flush(sb);
 
 	if (err && idx_clus)
 		fat_free_clusters(inode, cluster[0]);
@@ -542,8 +543,10 @@ int fat_free_clusters(struct inode *inod
 		}
 
 		ops->ent_put(&fatent, FAT_ENT_FREE);
-		if (sbi->free_clusters != -1)
+		if (sbi->free_clusters != -1) {
 			sbi->free_clusters++;
+			sb->s_dirt = 1;
+		}
 
 		if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
 			if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -605,6 +608,7 @@ int fat_count_free_clusters(struct super
 		} while (fat_ent_next(sbi, &fatent));
 	}
 	sbi->free_clusters = free;
+	sb->s_dirt = 1;
 	fatent_brelse(&fatent);
 out:
 	unlock_fat(sbi);
diff -puN fs/fat/inode.c~fat_write_super fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_write_super	2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c	2005-11-07 03:55:16.000000000 +0900
@@ -374,12 +374,17 @@ static void fat_clear_inode(struct inode
 	unlock_kernel();
 }
 
-static void fat_put_super(struct super_block *sb)
+static void fat_write_super(struct super_block *sb)
 {
-	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	sb->s_dirt = 0;
 
 	if (!(sb->s_flags & MS_RDONLY))
 		fat_clusters_flush(sb);
+}
+
+static void fat_put_super(struct super_block *sb)
+{
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
 	if (sbi->nls_disk) {
 		unload_nls(sbi->nls_disk);
@@ -546,6 +551,7 @@ static struct super_operations fat_sops 
 	.write_inode	= fat_write_inode,
 	.delete_inode	= fat_delete_inode,
 	.put_super	= fat_put_super,
+	.write_super	= fat_write_super,
 	.statfs		= fat_statfs,
 	.clear_inode	= fat_clear_inode,
 	.remount_fs	= fat_remount,
diff -puN fs/fat/misc.c~fat_write_super fs/fat/misc.c
--- linux-2.6.14/fs/fat/misc.c~fat_write_super	2005-11-07 02:14:05.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/misc.c	2005-11-07 03:46:35.000000000 +0900
@@ -67,8 +67,6 @@ void fat_clusters_flush(struct super_blo
 		if (sbi->prev_free != -1)
 			fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
 		mark_buffer_dirty(bh);
-		if (sb->s_flags & MS_SYNCHRONOUS)
-			sync_dirty_buffer(bh);
 	}
 	brelse(bh);
 }
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH] fat: use sb_find_get_block() instead of sb_getblk()
  2005-11-07 17:32 [PATCH 1/6] fat: move fat_clusters_flush() to write_super() OGAWA Hirofumi
@ 2005-11-07 17:36 ` OGAWA Hirofumi
  2005-11-07 17:37   ` [PATCH 3/6] fat: add the read/writepages() OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:36 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

We don't need to allocate buffer for checking the buffer is uptodate.
This use sb_find_get_block() instead, and if it returns NULL it's not
uptodate.

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

 fs/fat/dir.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff -puN fs/fat/dir.c~fat_dir_readahead-optimize fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_dir_readahead-optimize	2005-11-07 03:58:44.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c	2005-11-07 03:58:44.000000000 +0900
@@ -45,8 +45,8 @@ static inline void fat_dir_readahead(str
 	if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO))
 		return;
 
-	bh = sb_getblk(sb, phys);
-	if (bh && !buffer_uptodate(bh)) {
+	bh = sb_find_get_block(sb, phys);
+	if (bh == NULL || !buffer_uptodate(bh)) {
 		for (sec = 0; sec < sbi->sec_per_clus; sec++)
 			sb_breadahead(sb, phys + sec);
 	}
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 3/6] fat: add the read/writepages()
  2005-11-07 17:36 ` [PATCH] fat: use sb_find_get_block() instead of sb_getblk() OGAWA Hirofumi
@ 2005-11-07 17:37   ` OGAWA Hirofumi
  2005-11-07 17:39     ` [PATCH 4/6] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/ OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:37 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel


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

 fs/fat/inode.c |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff -puN fs/fat/inode.c~fat_read-writepages fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_read-writepages	2005-11-07 03:58:49.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c	2005-11-07 03:58:49.000000000 +0900
@@ -18,6 +18,7 @@
 #include <linux/seq_file.h>
 #include <linux/msdos_fs.h>
 #include <linux/pagemap.h>
+#include <linux/mpage.h>
 #include <linux/buffer_head.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
@@ -90,9 +91,21 @@ static int fat_writepage(struct page *pa
 	return block_write_full_page(page, fat_get_block, wbc);
 }
 
+static int fat_writepages(struct address_space *mapping,
+			  struct writeback_control *wbc)
+{
+	return mpage_writepages(mapping, wbc, fat_get_block);
+}
+
 static int fat_readpage(struct file *file, struct page *page)
 {
-	return block_read_full_page(page, fat_get_block);
+	return mpage_readpage(page, fat_get_block);
+}
+
+static int fat_readpages(struct file *file, struct address_space *mapping,
+			 struct list_head *pages, unsigned nr_pages)
+{
+	return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
 }
 
 static int fat_prepare_write(struct file *file, struct page *page,
@@ -122,7 +135,9 @@ static sector_t _fat_bmap(struct address
 
 static struct address_space_operations fat_aops = {
 	.readpage	= fat_readpage,
+	.readpages	= fat_readpages,
 	.writepage	= fat_writepage,
+	.writepages	= fat_writepages,
 	.sync_page	= block_sync_page,
 	.prepare_write	= fat_prepare_write,
 	.commit_write	= fat_commit_write,
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 4/6] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/
  2005-11-07 17:37   ` [PATCH 3/6] fat: add the read/writepages() OGAWA Hirofumi
@ 2005-11-07 17:39     ` OGAWA Hirofumi
  2005-11-07 17:41       ` [PATCH 5/6] fat: support ->direct_IO() OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:39 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

All EXPORT_SYMBOL of fatfs is only for vfat/msdos. _GPL would be proper.

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

 fs/fat/dir.c    |   14 +++++++-------
 fs/fat/fatent.c |    2 +-
 fs/fat/file.c   |    2 +-
 fs/fat/inode.c  |   10 +++++-----
 fs/fat/misc.c   |    6 +++---
 5 files changed, 17 insertions(+), 17 deletions(-)

diff -puN fs/fat/dir.c~fat_export_symbol_gpl fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_export_symbol_gpl	2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c	2005-11-07 03:58:53.000000000 +0900
@@ -418,7 +418,7 @@ EODir:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_search_long);
+EXPORT_SYMBOL_GPL(fat_search_long);
 
 struct fat_ioctl_filldir_callback {
 	struct dirent __user *dirent;
@@ -780,7 +780,7 @@ int fat_get_dotdot_entry(struct inode *d
 	return -ENOENT;
 }
 
-EXPORT_SYMBOL(fat_get_dotdot_entry);
+EXPORT_SYMBOL_GPL(fat_get_dotdot_entry);
 
 /* See if directory is empty */
 int fat_dir_empty(struct inode *dir)
@@ -803,7 +803,7 @@ int fat_dir_empty(struct inode *dir)
 	return result;
 }
 
-EXPORT_SYMBOL(fat_dir_empty);
+EXPORT_SYMBOL_GPL(fat_dir_empty);
 
 /*
  * fat_subdirs counts the number of sub-directories of dir. It can be run
@@ -849,7 +849,7 @@ int fat_scan(struct inode *dir, const un
 	return -ENOENT;
 }
 
-EXPORT_SYMBOL(fat_scan);
+EXPORT_SYMBOL_GPL(fat_scan);
 
 static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
 {
@@ -936,7 +936,7 @@ int fat_remove_entries(struct inode *dir
 	return 0;
 }
 
-EXPORT_SYMBOL(fat_remove_entries);
+EXPORT_SYMBOL_GPL(fat_remove_entries);
 
 static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
 			      struct buffer_head **bhs, int nr_bhs)
@@ -1048,7 +1048,7 @@ error:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_alloc_new_dir);
+EXPORT_SYMBOL_GPL(fat_alloc_new_dir);
 
 static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
 			       int *nr_cluster, struct msdos_dir_entry **de,
@@ -1264,4 +1264,4 @@ error_remove:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_add_entries);
+EXPORT_SYMBOL_GPL(fat_add_entries);
diff -puN fs/fat/fatent.c~fat_export_symbol_gpl fs/fat/fatent.c
--- linux-2.6.14/fs/fat/fatent.c~fat_export_symbol_gpl	2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/fatent.c	2005-11-07 03:58:53.000000000 +0900
@@ -581,7 +581,7 @@ error:
 	return err;
 }
 
-EXPORT_SYMBOL(fat_free_clusters);
+EXPORT_SYMBOL_GPL(fat_free_clusters);
 
 int fat_count_free_clusters(struct super_block *sb)
 {
diff -puN fs/fat/file.c~fat_export_symbol_gpl fs/fat/file.c
--- linux-2.6.14/fs/fat/file.c~fat_export_symbol_gpl	2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c	2005-11-07 03:58:53.000000000 +0900
@@ -173,7 +173,7 @@ out:
 	return error;
 }
 
-EXPORT_SYMBOL(fat_notify_change);
+EXPORT_SYMBOL_GPL(fat_notify_change);
 
 /* Free all clusters after the skip'th cluster. */
 static int fat_free(struct inode *inode, int skip)
diff -puN fs/fat/inode.c~fat_export_symbol_gpl fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_export_symbol_gpl	2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c	2005-11-07 03:58:53.000000000 +0900
@@ -197,7 +197,7 @@ void fat_attach(struct inode *inode, lof
 	spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL_GPL(fat_attach);
 
 void fat_detach(struct inode *inode)
 {
@@ -208,7 +208,7 @@ void fat_detach(struct inode *inode)
 	spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL_GPL(fat_detach);
 
 struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
 {
@@ -362,7 +362,7 @@ out:
 	return inode;
 }
 
-EXPORT_SYMBOL(fat_build_inode);
+EXPORT_SYMBOL_GPL(fat_build_inode);
 
 static void fat_delete_inode(struct inode *inode)
 {
@@ -557,7 +557,7 @@ int fat_sync_inode(struct inode *inode)
 	return fat_write_inode(inode, 1);
 }
 
-EXPORT_SYMBOL(fat_sync_inode);
+EXPORT_SYMBOL_GPL(fat_sync_inode);
 
 static int fat_show_options(struct seq_file *m, struct vfsmount *mnt);
 static struct super_operations fat_sops = {
@@ -1365,7 +1365,7 @@ out_fail:
 	return error;
 }
 
-EXPORT_SYMBOL(fat_fill_super);
+EXPORT_SYMBOL_GPL(fat_fill_super);
 
 int __init fat_cache_init(void);
 void fat_cache_destroy(void);
diff -puN fs/fat/misc.c~fat_export_symbol_gpl fs/fat/misc.c
--- linux-2.6.14/fs/fat/misc.c~fat_export_symbol_gpl	2005-11-07 03:58:53.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/misc.c	2005-11-07 03:58:53.000000000 +0900
@@ -33,7 +33,7 @@ void fat_fs_panic(struct super_block *s,
 	}
 }
 
-EXPORT_SYMBOL(fat_fs_panic);
+EXPORT_SYMBOL_GPL(fat_fs_panic);
 
 /* Flushes the number of free clusters on FAT32 */
 /* XXX: Need to write one per FSINFO block.  Currently only writes 1 */
@@ -192,7 +192,7 @@ void fat_date_unix2dos(int unix_date, __
 	*date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
 }
 
-EXPORT_SYMBOL(fat_date_unix2dos);
+EXPORT_SYMBOL_GPL(fat_date_unix2dos);
 
 int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
 {
@@ -220,4 +220,4 @@ int fat_sync_bhs(struct buffer_head **bh
 	return err;
 }
 
-EXPORT_SYMBOL(fat_sync_bhs);
+EXPORT_SYMBOL_GPL(fat_sync_bhs);
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 5/6] fat: support ->direct_IO()
  2005-11-07 17:39     ` [PATCH 4/6] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/ OGAWA Hirofumi
@ 2005-11-07 17:41       ` OGAWA Hirofumi
  2005-11-07 17:42         ` [PATCH 6/6] export/change sync_page_range/_nolock() OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:41 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

This patch add to support of ->direct_IO() for mostly read.

The user of this seems to want to use for streaming read.  So, current
direct I/O has limitation, it can only overwrite.
(For write operation, mainly we need to handle the hole etc..)

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

 fs/fat/cache.c           |   14 ++++++--
 fs/fat/dir.c             |    6 +--
 fs/fat/inode.c           |   82 +++++++++++++++++++++++++++++++++++++++++------
 include/linux/msdos_fs.h |    3 +
 4 files changed, 89 insertions(+), 16 deletions(-)

diff -puN fs/fat/cache.c~fat_direct-io fs/fat/cache.c
--- linux-2.6.14/fs/fat/cache.c~fat_direct-io	2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/cache.c	2005-11-07 03:58:58.000000000 +0900
@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode
 	return dclus;
 }
 
-int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
+int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
+	     unsigned long *mapped_blocks)
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector
 	int cluster, offset;
 
 	*phys = 0;
+	*mapped_blocks = 0;
 	if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) {
-		if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits))
+		if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) {
 			*phys = sector + sbi->dir_start;
+			*mapped_blocks = 1;
+		}
 		return 0;
 	}
 	last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
@@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector
 	cluster = fat_bmap_cluster(inode, cluster);
 	if (cluster < 0)
 		return cluster;
-	else if (cluster)
+	else if (cluster) {
 		*phys = fat_clus_to_blknr(sbi, cluster) + offset;
+		*mapped_blocks = sbi->sec_per_clus - offset;
+		if (*mapped_blocks > last_block - sector)
+			*mapped_blocks = last_block - sector;
+	}
 	return 0;
 }
diff -puN fs/fat/dir.c~fat_direct-io fs/fat/dir.c
--- linux-2.6.14/fs/fat/dir.c~fat_direct-io	2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/dir.c	2005-11-07 03:58:58.000000000 +0900
@@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *
 {
 	struct super_block *sb = dir->i_sb;
 	sector_t phys, iblock;
-	int offset;
-	int err;
+	unsigned long mapped_blocks;
+	int err, offset;
 
 next:
 	if (*bh)
@@ -77,7 +77,7 @@ next:
 
 	*bh = NULL;
 	iblock = *pos >> sb->s_blocksize_bits;
-	err = fat_bmap(dir, iblock, &phys);
+	err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
 	if (err || !phys)
 		return -1;	/* beyond EOF or error */
 
diff -puN fs/fat/inode.c~fat_direct-io fs/fat/inode.c
--- linux-2.6.14/fs/fat/inode.c~fat_direct-io	2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/inode.c	2005-11-07 03:58:58.000000000 +0900
@@ -23,6 +23,7 @@
 #include <linux/mount.h>
 #include <linux/vfs.h>
 #include <linux/parser.h>
+#include <linux/uio.h>
 #include <asm/unaligned.h>
 
 #ifndef CONFIG_FAT_DEFAULT_IOCHARSET
@@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode 
 	return err;
 }
 
-static int fat_get_block(struct inode *inode, sector_t iblock,
-			 struct buffer_head *bh_result, int create)
+static int __fat_get_blocks(struct inode *inode, sector_t iblock,
+			    unsigned long *max_blocks,
+			    struct buffer_head *bh_result, int create)
 {
 	struct super_block *sb = inode->i_sb;
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	sector_t phys;
-	int err;
+	unsigned long mapped_blocks;
+	int err, offset;
 
-	err = fat_bmap(inode, iblock, &phys);
+	err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
 	if (err)
 		return err;
 	if (phys) {
 		map_bh(bh_result, sb, phys);
+		*max_blocks = min(mapped_blocks, *max_blocks);
 		return 0;
 	}
 	if (!create)
 		return 0;
+
 	if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
 		fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
 			     MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
 		return -EIO;
 	}
-	if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) {
+
+	offset = (unsigned long)iblock & (sbi->sec_per_clus - 1);
+	if (!offset) {
+		/* TODO: multiple cluster allocation would be desirable. */
 		err = fat_add_cluster(inode);
 		if (err)
 			return err;
 	}
-	MSDOS_I(inode)->mmu_private += sb->s_blocksize;
-	err = fat_bmap(inode, iblock, &phys);
+	/* available blocks on this cluster */
+	mapped_blocks = sbi->sec_per_clus - offset;
+
+	*max_blocks = min(mapped_blocks, *max_blocks);
+	MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
+
+	err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
 	if (err)
 		return err;
-	if (!phys)
-		BUG();
+	BUG_ON(!phys);
+	BUG_ON(*max_blocks != mapped_blocks);
 	set_buffer_new(bh_result);
 	map_bh(bh_result, sb, phys);
 	return 0;
 }
 
+static int fat_get_blocks(struct inode *inode, sector_t iblock,
+			  unsigned long max_blocks,
+			  struct buffer_head *bh_result, int create)
+{
+	struct super_block *sb = inode->i_sb;
+	int err;
+
+	err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+	if (err)
+		return err;
+	bh_result->b_size = max_blocks << sb->s_blocksize_bits;
+	return 0;
+}
+
+static int fat_get_block(struct inode *inode, sector_t iblock,
+			 struct buffer_head *bh_result, int create)
+{
+	unsigned long max_blocks = 1;
+	return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+}
+
 static int fat_writepage(struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, fat_get_block, wbc);
@@ -128,6 +163,34 @@ static int fat_commit_write(struct file 
 	return err;
 }
 
+static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
+			     const struct iovec *iov,
+			     loff_t offset, unsigned long nr_segs)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_mapping->host;
+
+	if (rw == WRITE) {
+		/*
+		 * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(),
+		 * so we need to update the ->mmu_private to block boundary.
+		 *
+		 * But we must fill the remaining area or hole by nul for
+		 * updating ->mmu_private.
+		 */
+		loff_t size = offset + iov_length(iov, nr_segs);
+		if (MSDOS_I(inode)->mmu_private < size)
+			return -EINVAL;
+	}
+
+	/*
+	 * FAT need to use the DIO_LOCKING for avoiding the race
+	 * condition of fat_get_block() and ->truncate().
+	 */
+	return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+				  offset, nr_segs, fat_get_blocks, NULL);
+}
+
 static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
 {
 	return generic_block_bmap(mapping, block, fat_get_block);
@@ -141,6 +204,7 @@ static struct address_space_operations f
 	.sync_page	= block_sync_page,
 	.prepare_write	= fat_prepare_write,
 	.commit_write	= fat_commit_write,
+	.direct_IO	= fat_direct_IO,
 	.bmap		= _fat_bmap
 };
 
diff -puN include/linux/msdos_fs.h~fat_direct-io include/linux/msdos_fs.h
--- linux-2.6.14/include/linux/msdos_fs.h~fat_direct-io	2005-11-07 03:58:58.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/msdos_fs.h	2005-11-07 03:58:58.000000000 +0900
@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *d
 extern void fat_cache_inval_inode(struct inode *inode);
 extern int fat_get_cluster(struct inode *inode, int cluster,
 			   int *fclus, int *dclus);
-extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys);
+extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
+		    unsigned long *mapped_blocks);
 
 /* fat/dir.c */
 extern struct file_operations fat_dir_operations;
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 6/6] export/change sync_page_range/_nolock()
  2005-11-07 17:41       ` [PATCH 5/6] fat: support ->direct_IO() OGAWA Hirofumi
@ 2005-11-07 17:42         ` OGAWA Hirofumi
  2005-11-07 17:46           ` [PATCH 7/6] fat: Support a truncate() for expanding size OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:42 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

This exports/changes the sync_page_range/_nolock(). The fatfs needs
sync_page_range/_nolock() for expanding truncate, and changes "size_t
count" to "loff_t count".

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

 include/linux/writeback.h |    4 +++-
 mm/filemap.c              |    8 ++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff -puN include/linux/writeback.h~export-sync_page_range_nolock include/linux/writeback.h
--- linux-2.6.14/include/linux/writeback.h~export-sync_page_range_nolock	2005-11-07 03:59:03.000000000 +0900
+++ linux-2.6.14-hirofumi/include/linux/writeback.h	2005-11-07 03:59:03.000000000 +0900
@@ -108,7 +108,9 @@ void balance_dirty_pages_ratelimited(str
 int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0);
 int do_writepages(struct address_space *mapping, struct writeback_control *wbc);
 int sync_page_range(struct inode *inode, struct address_space *mapping,
-			loff_t pos, size_t count);
+			loff_t pos, loff_t count);
+int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
+			   loff_t pos, loff_t count);
 
 /* pdflush.c */
 extern int nr_pdflush_threads;	/* Global so it can be exported to sysctl
diff -puN mm/filemap.c~export-sync_page_range_nolock mm/filemap.c
--- linux-2.6.14/mm/filemap.c~export-sync_page_range_nolock	2005-11-07 03:59:03.000000000 +0900
+++ linux-2.6.14-hirofumi/mm/filemap.c	2005-11-07 03:59:03.000000000 +0900
@@ -280,7 +280,7 @@ static int wait_on_page_writeback_range(
  * it is otherwise livelockable.
  */
 int sync_page_range(struct inode *inode, struct address_space *mapping,
-			loff_t pos, size_t count)
+			loff_t pos, loff_t count)
 {
 	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
 	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
@@ -305,9 +305,8 @@ EXPORT_SYMBOL(sync_page_range);
  * as it forces O_SYNC writers to different parts of the same file
  * to be serialised right until io completion.
  */
-static int sync_page_range_nolock(struct inode *inode,
-				  struct address_space *mapping,
-				  loff_t pos, size_t count)
+int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
+			   loff_t pos, loff_t count)
 {
 	pgoff_t start = pos >> PAGE_CACHE_SHIFT;
 	pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
@@ -322,6 +321,7 @@ static int sync_page_range_nolock(struct
 		ret = wait_on_page_writeback_range(mapping, start, end);
 	return ret;
 }
+EXPORT_SYMBOL(sync_page_range_nolock);
 
 /**
  * filemap_fdatawait - walk the list of under-writeback pages of the given
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 7/6] fat: Support a truncate() for expanding size
  2005-11-07 17:42         ` [PATCH 6/6] export/change sync_page_range/_nolock() OGAWA Hirofumi
@ 2005-11-07 17:46           ` OGAWA Hirofumi
  2005-11-07 17:51             ` OGAWA Hirofumi
  2005-11-08  1:06             ` Andrew Morton
  0 siblings, 2 replies; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:46 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel


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

 fs/fat/file.c |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 3 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 03:59:07.000000000 +0900
+++ linux-2.6.14-hirofumi/fs/fat/file.c	2005-11-07 03:59:07.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,60 @@ 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;
+	struct page *page;
+	unsigned long index, limit;
+	loff_t count, pos, start;
+	unsigned int from, to;
+	int err;
+
+	err = -EFBIG;
+	limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+	if (limit != RLIM_INFINITY && size > (loff_t)limit) {
+		send_sig(SIGXFSZ, current, 0);
+		goto out;
+	}
+	if (size > inode->i_sb->s_maxbytes)
+		goto out;
+
+	start = inode->i_size;
+	count = size - inode->i_size;
+	/* calculate the stuff of last page */
+	pos = size - 1;
+	index = pos >> PAGE_CACHE_SHIFT;
+	from = to = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
+
+	err = -ENOMEM;
+	page = grab_cache_page(mapping, index);
+	if (!page)
+		goto out;
+	err = mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (unlikely(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, from, to);
+
+	unlock_page(page);
+	page_cache_release(page);
+
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	if (!err && 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 +187,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;
 		}
 	}
 
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  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
  1 sibling, 0 replies; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-07 17:51 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

Sorry for broken index.

[PATCH 1/7] fat: move fat_clusters_flush() to write_super()
[PATCH 2/7] fat: use sb_find_get_block() instead of sb_getblk()
[PATCH 3/7] fat: add the read/writepages()
[PATCH 4/7] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/
[PATCH 5/7] fat: support ->direct_IO()
[PATCH 6/7] export/change sync_page_range/_nolock()
[PATCH 7/7] fat: Support a truncate() for expanding size

The above is right index. If I need to resend, please tell me.

Note for reviewers, [6/7] patch is not a part of fatfs.

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  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
  1 sibling, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2005-11-08  1:06 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: linux-kernel

OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> wrote:
>
> +static int fat_cont_expand(struct inode *inode, loff_t size)

Is it not possible to extend generic_cont_expand() so that fatfs can use it?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  2005-11-08  1:06             ` Andrew Morton
@ 2005-11-08  3:19               ` OGAWA Hirofumi
  2005-11-08  4:19                 ` Andrew Morton
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-08  3:19 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

Andrew Morton <akpm@osdl.org> writes:

> OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> wrote:
>>
>> +static int fat_cont_expand(struct inode *inode, loff_t size)
>
> Is it not possible to extend generic_cont_expand() so that fatfs can use it?

The generic_cont_expand() is too generic.

If "size" is block boundary, generic_cont_expand() expands the
->i_size to "size + 1", after it, the caller of it will truncate to
"size" by vmtruncate().

This sequence is not need if ->prepare_write() is cont_prepare_write().
The cont_prepare_write() will just fill the blocks with zero until
"size" if blocks is not allocated yet.

FAT is using cont_parepare_write(), so for avoiding the above extra
work, is using own version.

Probably, this version is generic only for cont_parepare_write().

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

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  2005-11-08  3:19               ` OGAWA Hirofumi
@ 2005-11-08  4:19                 ` Andrew Morton
  2005-11-08  5:22                   ` OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2005-11-08  4:19 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: linux-kernel

OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> wrote:
>
> Andrew Morton <akpm@osdl.org> writes:
> 
> > OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> wrote:
> >>
> >> +static int fat_cont_expand(struct inode *inode, loff_t size)
> >
> > Is it not possible to extend generic_cont_expand() so that fatfs can use it?
> 
> The generic_cont_expand() is too generic.

But can it be fixed??

> If "size" is block boundary, generic_cont_expand() expands the
> ->i_size to "size + 1", after it, the caller of it will truncate to
> "size" by vmtruncate().

Something like this?

--- devel/fs/buffer.c~a	2005-11-07 20:17:49.000000000 -0800
+++ devel-akpm/fs/buffer.c	2005-11-07 20:18:59.000000000 -0800
@@ -2160,7 +2160,7 @@ 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, 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++;
-	}
 	index = size >> PAGE_CACHE_SHIFT;
 	err = -ENOMEM;
 	page = grab_cache_page(mapping, index);
@@ -2202,6 +2201,16 @@ out:
 	return err;
 }
 
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+	return __generic_cont_expand(inode, size, 0);
+}
+
+int generic_cont_expand_dont_do_that(struct inode *inode, loff_t size)
+{
+	return __generic_cont_expand(inode, size, 1);
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
_


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  2005-11-08  4:19                 ` Andrew Morton
@ 2005-11-08  5:22                   ` OGAWA Hirofumi
  2005-11-08 20:40                     ` OGAWA Hirofumi
  0 siblings, 1 reply; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-08  5:22 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

Andrew Morton <akpm@osdl.org> writes:

>> The generic_cont_expand() is too generic.
>
> But can it be fixed??

Oh, probably we can...

>> If "size" is block boundary, generic_cont_expand() expands the
>> ->i_size to "size + 1", after it, the caller of it will truncate to
>> "size" by vmtruncate().
>
> Something like this?
>

[...]

> -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.

Thanks.


if (!dont_do_that) {
	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;
} else {
	/* calculate the stuff of last page */
	loff_t pos = size - 1;
	index = pos >> PAGE_CACHE_SHIFT;
	offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
}
-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 7/6] fat: Support a truncate() for expanding size
  2005-11-08  5:22                   ` OGAWA Hirofumi
@ 2005-11-08 20:40                     ` OGAWA Hirofumi
  0 siblings, 0 replies; 13+ messages in thread
From: OGAWA Hirofumi @ 2005-11-08 20:40 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel

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 *);
_

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2005-11-08 20:40 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox