From patchwork Thu Aug 20 22:48:29 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Morton X-Patchwork-Id: 31780 Return-Path: X-Original-To: patchwork-incoming@bilbo.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ozlabs.org (ozlabs.org [203.10.76.45]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "mx.ozlabs.org", Issuer "CA Cert Signing Authority" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4CB9BB7B9A for ; Fri, 21 Aug 2009 08:49:37 +1000 (EST) Received: by ozlabs.org (Postfix) id 3AB3BDDD0B; Fri, 21 Aug 2009 08:49:37 +1000 (EST) Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by ozlabs.org (Postfix) with ESMTP id 93C79DDD04 for ; Fri, 21 Aug 2009 08:49:36 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755441AbZHTWtc (ORCPT ); Thu, 20 Aug 2009 18:49:32 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1755435AbZHTWtb (ORCPT ); Thu, 20 Aug 2009 18:49:31 -0400 Received: from smtp1.linux-foundation.org ([140.211.169.13]:35126 "EHLO smtp1.linux-foundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932068AbZHTWta (ORCPT ); Thu, 20 Aug 2009 18:49:30 -0400 Received: from imap1.linux-foundation.org (imap1.linux-foundation.org [140.211.169.55]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id n7KMmUiH029973 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 20 Aug 2009 15:48:31 -0700 Received: from localhost.localdomain (localhost [127.0.0.1]) by imap1.linux-foundation.org (8.13.5.20060308/8.13.5/Debian-3ubuntu1.1) with ESMTP id n7KMmT1r029701; Thu, 20 Aug 2009 15:48:30 -0700 Message-Id: <200908202248.n7KMmT1r029701@imap1.linux-foundation.org> Subject: + ext2-convert-to-use-the-new-truncate-convention.patch added to -mm tree To: mm-commits@vger.kernel.org Cc: npiggin@suse.de, chris.mason@oracle.com, hch@lst.de, hirofumi@mail.parknet.co.jp, hugh.dickins@tiscali.co.uk, jack@suse.cz, linux-ext4@vger.kernel.org, miklos@szeredi.hu, sfrench@us.ibm.com, shaggy@linux.vnet.ibm.com, trond.myklebust@fys.uio.no, viro@zeniv.linux.org.uk From: akpm@linux-foundation.org Date: Thu, 20 Aug 2009 15:48:29 -0700 X-Spam-Status: No, hits=-3.012 required=5 tests=AWL,BAYES_00 X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.13 Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org The patch titled ext2: convert to use the new truncate convention. has been added to the -mm tree. Its filename is ext2-convert-to-use-the-new-truncate-convention.patch Before you just go and hit "reply", please: a) Consider who else should be cc'ed b) Prefer to cc a suitable mailing list as well c) Ideally: find the original patch on the mailing list and do a reply-to-all to that, adding suitable additional cc's *** Remember to use Documentation/SubmitChecklist when testing your code *** See http://userweb.kernel.org/~akpm/stuff/added-to-mm.txt to find out what to do about this The current -mm tree may be found at http://userweb.kernel.org/~akpm/mmotm/ ------------------------------------------------------ Subject: ext2: convert to use the new truncate convention. From: Nick Piggin I also have commented a possible bug in existing ext2 code, marked with XXX. Signed-off-by: Nick Piggin Cc: Cc: Christoph Hellwig Cc: Jan Kara Cc: Dave Kleikamp Cc: Chris Mason Cc: OGAWA Hirofumi Cc: Hugh Dickins Cc: Miklos Szeredi Cc: Trond Myklebust Cc: Steven French Cc: Al Viro Signed-off-by: Andrew Morton --- fs/ext2/ext2.h | 1 fs/ext2/file.c | 2 fs/ext2/inode.c | 142 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 116 insertions(+), 29 deletions(-) diff -puN fs/ext2/ext2.h~ext2-convert-to-use-the-new-truncate-convention fs/ext2/ext2.h --- a/fs/ext2/ext2.h~ext2-convert-to-use-the-new-truncate-convention +++ a/fs/ext2/ext2.h @@ -122,7 +122,6 @@ extern int ext2_write_inode (struct inod extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); -extern void ext2_truncate (struct inode *); extern int ext2_setattr (struct dentry *, struct iattr *); extern void ext2_set_inode_flags(struct inode *inode); extern void ext2_get_inode_flags(struct ext2_inode_info *); diff -puN fs/ext2/file.c~ext2-convert-to-use-the-new-truncate-convention fs/ext2/file.c --- a/fs/ext2/file.c~ext2-convert-to-use-the-new-truncate-convention +++ a/fs/ext2/file.c @@ -77,13 +77,13 @@ const struct file_operations ext2_xip_fi #endif const struct inode_operations ext2_file_inode_operations = { - .truncate = ext2_truncate, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr, .listxattr = ext2_listxattr, .removexattr = generic_removexattr, #endif + .new_truncate = 1, .setattr = ext2_setattr, .permission = ext2_permission, .fiemap = ext2_fiemap, diff -puN fs/ext2/inode.c~ext2-convert-to-use-the-new-truncate-convention fs/ext2/inode.c --- a/fs/ext2/inode.c~ext2-convert-to-use-the-new-truncate-convention +++ a/fs/ext2/inode.c @@ -53,6 +53,8 @@ static inline int ext2_inode_is_fast_sym inode->i_blocks - ea_blocks == 0); } +static void ext2_truncate_blocks(struct inode *inode, loff_t offset); + /* * Called at the last iput() if i_nlink is zero. */ @@ -68,7 +70,7 @@ void ext2_delete_inode (struct inode * i inode->i_size = 0; if (inode->i_blocks) - ext2_truncate (inode); + ext2_truncate_blocks(inode, 0); ext2_free_inode (inode); return; @@ -761,8 +763,33 @@ ext2_write_begin(struct file *file, stru loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata); + ret = __ext2_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + if (ret < 0) { + struct inode *inode = mapping->host; + loff_t isize = inode->i_size; + if (pos + len > isize) + ext2_truncate_blocks(inode, isize); + } + return ret; +} + +static int ext2_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int ret; + + ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); + if (ret < len) { + struct inode *inode = mapping->host; + loff_t isize = inode->i_size; + if (pos + len > isize) + ext2_truncate_blocks(inode, isize); + } + return ret; } static int @@ -770,13 +797,22 @@ ext2_nobh_write_begin(struct file *file, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + /* * Dir-in-pagecache still uses ext2_write_begin. Would have to rework * directory handling code to pass around offsets rather than struct * pages in order to make this work easily. */ - return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, ext2_get_block); + if (ret < 0) { + struct inode *inode = mapping->host; + loff_t isize = inode->i_size; + if (pos + len > isize) + ext2_truncate_blocks(inode, isize); + } + return ret; } static int ext2_nobh_writepage(struct page *page, @@ -796,9 +832,15 @@ ext2_direct_IO(int rw, struct kiocb *ioc { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; + ssize_t ret; - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, ext2_get_block, NULL); + if (ret < 0 && (rw & WRITE)) { + loff_t isize = inode->i_size; + ext2_truncate_blocks(inode, isize); + } + return ret; } static int @@ -813,7 +855,7 @@ const struct address_space_operations ex .writepage = ext2_writepage, .sync_page = block_sync_page, .write_begin = ext2_write_begin, - .write_end = generic_write_end, + .write_end = ext2_write_end, .bmap = ext2_bmap, .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, @@ -1022,7 +1064,7 @@ static void ext2_free_branches(struct in ext2_free_data(inode, p, q); } -void ext2_truncate(struct inode *inode) +static void __ext2_truncate_blocks(struct inode *inode, loff_t offset) { __le32 *i_data = EXT2_I(inode)->i_data; struct ext2_inode_info *ei = EXT2_I(inode); @@ -1034,27 +1076,8 @@ void ext2_truncate(struct inode *inode) int n; long iblock; unsigned blocksize; - - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - if (ext2_inode_is_fast_symlink(inode)) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - blocksize = inode->i_sb->s_blocksize; - iblock = (inode->i_size + blocksize-1) - >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); - - if (mapping_is_xip(inode->i_mapping)) - xip_truncate_page(inode->i_mapping, inode->i_size); - else if (test_opt(inode->i_sb, NOBH)) - nobh_truncate_page(inode->i_mapping, - inode->i_size, ext2_get_block); - else - block_truncate_page(inode->i_mapping, - inode->i_size, ext2_get_block); + iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); n = ext2_block_to_path(inode, iblock, offsets, NULL); if (n == 0) @@ -1122,6 +1145,62 @@ do_indirects: ext2_discard_reservation(inode); mutex_unlock(&ei->truncate_mutex); +} + +static void ext2_truncate_blocks(struct inode *inode, loff_t offset) +{ + /* + * XXX: it seems like a bug here that we don't allow + * IS_APPEND inode to have blocks-past-i_size trimmed off. + * review and fix this. + * + * Also would be nice to be able to handle IO errors and such, + * but that's probably too much to ask. + */ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (ext2_inode_is_fast_symlink(inode)) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + __ext2_truncate_blocks(inode, offset); +} + +int ext2_setsize(struct inode *inode, loff_t newsize) +{ + loff_t oldsize; + int error; + + error = inode_newsize_ok(inode, newsize); + if (error) + return error; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + if (ext2_inode_is_fast_symlink(inode)) + return -EINVAL; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; + + if (mapping_is_xip(inode->i_mapping)) + error = xip_truncate_page(inode->i_mapping, newsize); + else if (test_opt(inode->i_sb, NOBH)) + error = nobh_truncate_page(inode->i_mapping, + newsize, ext2_get_block); + else + error = block_truncate_page(inode->i_mapping, + newsize, ext2_get_block); + if (error) + return error; + + oldsize = inode->i_size; + i_size_write(inode, newsize); + truncate_pagecache(inode, oldsize, newsize); + + __ext2_truncate_blocks(inode, newsize); + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; if (inode_needs_sync(inode)) { sync_mapping_buffers(inode->i_mapping); @@ -1129,6 +1208,8 @@ do_indirects: } else { mark_inode_dirty(inode); } + + return 0; } static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, @@ -1461,8 +1542,15 @@ int ext2_setattr(struct dentry *dentry, if (error) return error; } - error = inode_setattr(inode, iattr); + if (iattr->ia_valid & ATTR_SIZE) { + error = ext2_setsize(inode, iattr->ia_size); + if (error) + return error; + } + generic_setattr(inode, iattr); if (!error && (iattr->ia_valid & ATTR_MODE)) error = ext2_acl_chmod(inode); + mark_inode_dirty(inode); + return error; }