Re: ramfs problem... (unlink of sparse file in "D" state)

Alexander Viro (viro@math.psu.edu)
Mon, 8 Jan 2001 09:02:46 -0500 (EST)


Alan, consider applying the patch below.
Contents:
* recovery from failing get_block() in __block_write_full_page()
and __block_prepare_write().
* handling of partially mapped pages in generic_file_write().
* use of ->s_maxbytes in default_llseek().
* crapectomy in ext2/file.c
* correct calculation of ->s_maxbytes for ext2.

diff -urN S0-AC4/fs/buffer.c S0-AC4-fixes/fs/buffer.c
--- S0-AC4/fs/buffer.c Mon Jan 8 08:46:17 2001
+++ S0-AC4-fixes/fs/buffer.c Mon Jan 8 08:28:55 2001
@@ -1493,6 +1493,7 @@
int err, i;
unsigned long block;
struct buffer_head *bh, *head;
+ int need_unlock = 1;

if (!PageLocked(page))
BUG();
@@ -1549,7 +1550,28 @@

out:
ClearPageUptodate(page);
- UnlockPage(page);
+ bh = head;
+ need_unlock = 1;
+ /* Recovery: lock and submit the mapped buffers */
+ do {
+ if (buffer_mapped(bh)) {
+ lock_buffer(bh);
+ need_unlock = 0;
+ }
+ bh = bh->b_this_page;
+ } while (bh != head);
+ do {
+ if (buffer_mapped(bh)) {
+ bh->b_end_io = end_buffer_io_async;
+ atomic_inc(&bh->b_count);
+ set_bit(BH_Uptodate, &bh->b_state);
+ set_bit(BH_Dirty, &bh->b_state);
+ submit_bh(WRITE, bh);
+ }
+ bh = bh->b_this_page;
+ }
+ if (need_unlock)
+ UnlockPage(page);
return err;
}

@@ -1620,6 +1642,15 @@
}
return 0;
out:
+ bh = head;
+ do {
+ if (buffer_new(bh) && !buffer_uptodate(bh)) {
+ memset(bh->b_data, 0, bh->b_size);
+ set_bit(BH_Uptodate, &bh->b_state);
+ mark_buffer_dirty(bh);
+ }
+ bh = bh->b_this_page;
+ } while (bh != head);
return err;
}

diff -urN S0-AC4/fs/ext2/file.c S0-AC4-fixes/fs/ext2/file.c
--- S0-AC4/fs/ext2/file.c Mon Jan 8 08:46:18 2001
+++ S0-AC4-fixes/fs/ext2/file.c Mon Jan 8 08:34:04 2001
@@ -22,51 +22,6 @@
#include <linux/ext2_fs.h>
#include <linux/sched.h>

-static loff_t ext2_file_lseek(struct file *, loff_t, int);
-static int ext2_open_file (struct inode *, struct file *);
-
-#define EXT2_MAX_SIZE(bits) \
- (((EXT2_NDIR_BLOCKS + (1LL << (bits - 2)) + \
- (1LL << (bits - 2)) * (1LL << (bits - 2)) + \
- (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * \
- (1LL << bits)) - 1)
-
-static long long ext2_max_sizes[] = {
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13)
-};
-
-/*
- * Make sure the offset never goes beyond the 32-bit mark..
- */
-static loff_t ext2_file_lseek(
- struct file *file,
- loff_t offset,
- int origin)
-{
- struct inode *inode = file->f_dentry->d_inode;
-
- switch (origin) {
- case 2:
- offset += inode->i_size;
- break;
- case 1:
- offset += file->f_pos;
- }
- if (offset<0)
- return -EINVAL;
- if (((unsigned long long) offset >> 32) != 0) {
- if (offset > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(inode->i_sb)])
- return -EINVAL;
- }
- if (offset != file->f_pos) {
- file->f_pos = offset;
- file->f_reada = 0;
- file->f_version = ++event;
- }
- return offset;
-}
-
/*
* Called when an inode is released. Note that this is different
* from ext2_file_open: open gets called at every open, but release
@@ -84,7 +39,6 @@
* the ext2 filesystem.
*/
struct file_operations ext2_file_operations = {
- llseek: ext2_file_lseek,
read: generic_file_read,
write: generic_file_write,
ioctl: ext2_ioctl,
diff -urN S0-AC4/fs/ext2/super.c S0-AC4-fixes/fs/ext2/super.c
--- S0-AC4/fs/ext2/super.c Mon Jan 8 08:46:18 2001
+++ S0-AC4-fixes/fs/ext2/super.c Mon Jan 8 08:35:16 2001
@@ -380,6 +380,20 @@
}

#define log2(n) ffz(~(n))
+
+/*
+ * maximal file size.
+ */
+static loff_t ext2_max_size(int bits)
+{
+ loff_t res = EXT2_NDIR_BLOCKS;
+ res += 1LL << (bits-2);
+ res += 1LL << (2*(bits-2));
+ res += 1LL << (3*(bits-2));
+ if (res > 1LL << 32)
+ res = 1LL << 32;
+ return res << bits;
+}

struct super_block * ext2_read_super (struct super_block * sb, void * data,
int silent)
@@ -476,8 +490,7 @@
le32_to_cpu(EXT2_SB(sb)->s_es->s_log_block_size) + 10;
sb->s_blocksize = 1 << sb->s_blocksize_bits;

- /* We allow 2^32 blocks - not pages */
- sb->s_maxbytes = (1ULL<<(sb->s_blocksize_bits+32))-1;
+ sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);

if (sb->s_blocksize != BLOCK_SIZE &&
(sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
diff -urN S0-AC4/fs/read_write.c S0-AC4-fixes/fs/read_write.c
--- S0-AC4/fs/read_write.c Fri Sep 22 17:21:19 2000
+++ S0-AC4-fixes/fs/read_write.c Mon Jan 8 08:32:27 2001
@@ -36,7 +36,7 @@
offset += file->f_pos;
}
retval = -EINVAL;
- if (offset >= 0) {
+ if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
diff -urN S0-AC4/mm/filemap.c S0-AC4-fixes/mm/filemap.c
--- S0-AC4/mm/filemap.c Mon Jan 8 08:46:34 2001
+++ S0-AC4-fixes/mm/filemap.c Mon Jan 8 08:26:21 2001
@@ -2452,6 +2452,7 @@
unsigned long written;
long status;
int err;
+ unsigned bytes;

cached_page = NULL;

@@ -2537,7 +2538,7 @@
mark_inode_dirty_sync(inode);

while (count) {
- unsigned long bytes, index, offset;
+ unsigned long index, offset;
char *kaddr;
int deactivate = 1;

@@ -2577,7 +2578,7 @@

status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
if (status)
- goto unlock;
+ goto sync_failure;
kaddr = page_address(page);
status = copy_from_user(kaddr+offset, buf, bytes);
flush_dcache_page(page);
@@ -2603,6 +2604,7 @@
if (status < 0)
break;
}
+done:
*ppos = pos;

if (cached_page)
@@ -2627,6 +2629,13 @@
ClearPageUptodate(page);
kunmap(page);
goto unlock;
+sync_failure:
+ UnlockPage(page);
+ deactivate_page(page);
+ page_cache_release(page);
+ if (pos + bytes > inode->i_size)
+ vmtruncate(inode, inode->i_size);
+ goto done;
}

void __init page_cache_init(unsigned long mempages)

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