On Sun, 3 Sep 2000, [iso-8859-1] Henrik Størner wrote:

> On Sat, Sep 02, 2000 at 01:58:58PM -0700, Linus Torvalds wrote:
> 
> [ext2 truncate bug which caused the innd file corruption may
>  also affect other filesystems]
> 
> > Anyway, the way to test if you have the bug is this simple program from
> > Al Viro who noticed the bug and has the fix - notice that you need to
> > have some file that is clean, non-zero and NOT in memory (ie an old
> > forgotten /tmp entry that can be happily trashed by the test-program):
> 
> OK, I checked this with 2.4.0-test5 on reiserfs, and it does show the
> bug in the manner you described. So I guess the reiserfs guys need to
> come up with a similar fix as the one that is being done for ext2.

That should be it for ext2. Reiserfs may be trickier - with their stuff
for file tails... hell knows. One thing to watch for: don't create
buffer-cache aliases for pagecache data.

diff -urN rc8-2/fs/buffer.c linux-truncate/fs/buffer.c
--- rc8-2/fs/buffer.c   Sat Sep  2 21:10:37 2000
+++ linux-truncate/fs/buffer.c  Sun Sep  3 02:34:56 2000
@@ -1721,6 +1721,54 @@
        return 0;
 }
 
+/*
+ * If it would be '74 that would go into libc...
+ */
+int mem_is_zero(char *p, unsigned len)
+{
+       while (len--)
+               if (*p++)
+                       return 0;
+       return 1;
+}
+
+int block_zero_page(struct address_space *mapping, loff_t from, unsigned length)
+{
+       unsigned long index = from >> PAGE_CACHE_SHIFT;
+       unsigned offset = from & (PAGE_CACHE_SIZE-1);
+       struct inode *inode = (struct inode *)mapping->host;
+       struct page *page;
+       char *kaddr;
+       int err;
+
+       if (!length)
+               return 0;
+
+       page = read_cache_page(mapping, index,
+                               (filler_t *)mapping->a_ops->readpage, NULL);
+       err = PTR_ERR(page);
+       if (ERR_PTR(page))
+               goto out;
+       lock_page(page);
+       err = -EIO;
+       if (!Page_Uptodate(page))
+               goto unlock;
+       kaddr = (char*)kmap(page);
+       err = 0;
+       if (!mem_is_zero(kaddr+offset, length))
+               goto unmap;
+       memset(kaddr+offset, 0, length);
+       flush_dcache_page(page);
+       __block_commit_write(inode, page, offset, offset+length);
+unmap:
+       kunmap(page);
+unlock:
+       UnlockPage(page);
+       page_cache_release(page);
+out:
+       return err;
+}
+
 int block_write_full_page(struct page *page, get_block_t *get_block)
 {
        struct inode *inode = (struct inode*)page->mapping->host;
diff -urN rc8-2/fs/ext2/inode.c linux-truncate/fs/ext2/inode.c
--- rc8-2/fs/ext2/inode.c       Sun Sep  3 02:26:35 2000
+++ linux-truncate/fs/ext2/inode.c      Sun Sep  3 02:25:32 2000
@@ -904,6 +904,7 @@
        int nr = 0;
        int n;
        long iblock;
+       unsigned blocksize, tail;
 
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
            S_ISLNK(inode->i_mode)))
@@ -913,8 +914,13 @@
 
        ext2_discard_prealloc(inode);
 
-       iblock = (inode->i_size + inode->i_sb->s_blocksize-1)
+       blocksize = inode->i_sb->s_blocksize;
+       iblock = (inode->i_size + blocksize-1)
                                        >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
+       tail = (iblock << EXT2_BLOCK_SIZE_BITS(inode->i_sb)) - inode->i_size;
+
+       if (block_zero_page(inode, inode->i_size, tail) != 0)
+               return;
 
        n = ext2_block_to_path(inode, iblock, offsets);
        if (n == 0)
diff -urN rc8-2/include/linux/fs.h linux-truncate/include/linux/fs.h
--- rc8-2/include/linux/fs.h    Sat Sep  2 21:10:38 2000
+++ linux-truncate/include/linux/fs.h   Sun Sep  3 02:20:04 2000
@@ -1162,6 +1162,7 @@
 
 int generic_block_bmap(struct address_space *, long, get_block_t *);
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
+int block_zero_page(struct address_space *mapping, loff_t, unsigned);
 
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
diff -urN rc8-2/kernel/ksyms.c linux-truncate/kernel/ksyms.c
--- rc8-2/kernel/ksyms.c        Sat Sep  2 21:10:39 2000
+++ linux-truncate/kernel/ksyms.c       Sun Sep  3 02:20:33 2000
@@ -203,6 +203,7 @@
 EXPORT_SYMBOL(block_sync_page);
 EXPORT_SYMBOL(cont_prepare_write);
 EXPORT_SYMBOL(generic_commit_write);
+EXPORT_SYMBOL(block_zero_page);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_file_read);
 EXPORT_SYMBOL(do_generic_file_read);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
Please read the FAQ at http://www.tux.org/lkml/

Reply via email to