Messages in this thread Patches in this message |  | | Date | Mon, 8 Jan 2001 03:41:13 -0500 (EST) | From | Alexander Viro <> | Subject | [PATCH(es)] Re: ramfs problem... (unlink of sparse file in "D" state) |
| |
On Mon, 8 Jan 2001, Chris Wedgwood wrote:
> On Mon, Jan 08, 2001 at 02:56:10AM -0500, Alexander Viro wrote: > > Plenty. ext2, for one - e.g. with 4Kb blocks you have limit > at 0x4010040c000 for files and 0x100000000 for directories. With > 1Kb blocks the limit for files is 0x404043000. Notice that the > latter is not a multiple of page size on Alpha. > > There is code to support this in 2.4.0-ac4 -- initially I didn't like > the way Alan had done things (I was think -EFBIG should be used only > for LFS violations) but after some thought has decided that what he > has makes a lot of sense. > > The FS calculates the largest suitable byte-size for a file in put it > in the superblock; when the VFS checks in the generic_file_* paths. > The only time this won't work is if some complex criteria allows some > files to be larger than others, in which case -- we add a callback to > the fs.
It's still not enough. get_block() can fail for a lot of reasons and generic_file_write() should be prepared to deal with that. -EFBIG is nothing special in that respect. We don't need new callbacks, get_block() is quite enough.
IOW, the following is still needed:
diff -urN S0/fs/buffer.c S0-get_block_fail/fs/buffer.c --- S0/fs/buffer.c Wed Jan 3 23:45:26 2001 +++ S0-get_block_fail/fs/buffer.c Mon Jan 8 03:12:07 2001 @@ -1494,6 +1494,7 @@ int err, i; unsigned long block; struct buffer_head *bh, *head; + int need_unlock = 1; if (!PageLocked(page)) BUG(); @@ -1539,18 +1540,39 @@ } while (bh != head); /* Stage 3: submit the IO */ + SetPageUptodate(page); do { submit_bh(WRITE, bh); bh = bh->b_this_page; } while (bh != head); /* Done - end_buffer_io_async will unlock */ - SetPageUptodate(page); return 0; 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; } @@ -1621,6 +1643,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/mm/filemap.c S0-get_block_fail/mm/filemap.c --- S0/mm/filemap.c Tue Jan 2 21:59:45 2001 +++ S0-get_block_fail/mm/filemap.c Mon Jan 8 03:13:37 2001 @@ -2449,6 +2449,7 @@ unsigned long written; long status; int err; + unsigned bytes; cached_page = NULL; @@ -2493,7 +2494,7 @@ } while (count) { - unsigned long bytes, index, offset; + unsigned long index, offset; char *kaddr; int deactivate = 1; @@ -2532,7 +2533,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); @@ -2558,6 +2559,7 @@ if (status < 0) break; } +done: *ppos = pos; if (cached_page) @@ -2578,6 +2580,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)
and with that patch the rest of filesize limits goes into filesystems. For ext2 it would be
diff -urN S0/fs/ext2/inode.c S0-ext2_get_block-EFBIG/fs/ext2/inode.c --- S0/fs/ext2/inode.c Fri Dec 29 17:36:44 2000 +++ S0-ext2_get_block-EFBIG/fs/ext2/inode.c Mon Jan 8 03:22:58 2001 @@ -153,11 +153,13 @@ * This function translates the block number into path in that tree - * return value is the path length and @offsets[n] is the offset of * pointer to (n+1)th node in the nth one. If @block is out of range - * (negative or too large) warning is printed and zero returned. + * (negative or too large) we return zero. Warning is printed if @block + * is negative - that should never happen. Too large value is OK, it + * just means that ext2_get_block() should return -%EFBIG. * * Note: function doesn't find node addresses, so no IO is needed. All * we need to know is the capacity of indirect blocks (taken from the - * inode->i_sb). + * @inode->i_sb). */ /* @@ -196,7 +198,7 @@ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1); offsets[n++] = i_block & (ptrs - 1); } else { - ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big"); + /* Too large, nothing to do here */ } return n; } @@ -505,7 +507,7 @@ static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { - int err = -EIO; + int err = -EFBIG; int offsets[4]; Indirect chain[4]; Indirect *partial; plus
diff -urN S0/fs/ext2/inode.c S0-ext2-LFS/fs/ext2/inode.c --- S0/fs/ext2/inode.c Fri Dec 29 17:36:44 2000 +++ S0-ext2-LFS/fs/ext2/inode.c Fri Jan 5 21:11:31 2001 @@ -1188,7 +1188,7 @@ raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); else { raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); - if (raw_inode->i_size_high) { + if (raw_inode->i_size_high || (inode->i_size & (1<<31))) { struct super_block *sb = inode->i_sb; if (!EXT2_HAS_RO_COMPAT_FEATURE(sb, EXT2_FEATURE_RO_COMPAT_LARGE_FILE) || plus
diff -urN S0/fs/ext2/file.c S0-ext2_max_size/fs/ext2/file.c --- S0/fs/ext2/file.c Wed Sep 27 16:41:33 2000 +++ S0-ext2_max_size/fs/ext2/file.c Mon Jan 8 03:29:41 2001 @@ -25,17 +25,6 @@ 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.. */ @@ -56,7 +45,7 @@ if (offset<0) return -EINVAL; if (((unsigned long long) offset >> 32) != 0) { - if (offset > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(inode->i_sb)]) + if (offset >= inode->i_sb->u.ext2_sb.s_max_size) return -EINVAL; } if (offset != file->f_pos) { diff -urN S0/fs/ext2/super.c S0-ext2_max_size/fs/ext2/super.c --- S0/fs/ext2/super.c Fri Dec 29 17:36:44 2000 +++ S0-ext2_max_size/fs/ext2/super.c Mon Jan 8 03:30:49 2001 @@ -380,6 +380,18 @@ } #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)); + return res << bits; +} struct super_block * ext2_read_super (struct super_block * sb, void * data, int silent) @@ -549,6 +561,7 @@ log2 (EXT2_ADDR_PER_BLOCK(sb)); sb->u.ext2_sb.s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); + sb->u.ext2_sb.s_max_size = ext2_max_size(sb->s_blocksize_bits); if (sb->s_magic != EXT2_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find an ext2 filesystem on dev " diff -urN S0/include/linux/ext2_fs_sb.h S0-ext2_max_size/include/linux/ext2_fs_sb.h --- S0/include/linux/ext2_fs_sb.h Fri Dec 29 17:36:44 2000 +++ S0-ext2_max_size/include/linux/ext2_fs_sb.h Mon Jan 8 03:31:19 2001 @@ -56,6 +56,7 @@ int s_desc_per_block_bits; int s_inode_size; int s_first_ino; + loff_t s_max_size; }; #endif /* _LINUX_EXT2_FS_SB */ plus
diff -urN S0-ext2_max_size/fs/ext2/file.c S0-ext2-LFS-full/fs/ext2/file.c --- S0-ext2_max_size/fs/ext2/file.c Mon Jan 8 03:29:41 2001 +++ S0-ext2-LFS-full/fs/ext2/file.c Mon Jan 8 03:37:41 2001 @@ -99,4 +99,5 @@ struct inode_operations ext2_file_inode_operations = { truncate: ext2_truncate, + setattr: ext2_notify_change, }; diff -urN S0-ext2_max_size/fs/ext2/inode.c S0-ext2-LFS-full/fs/ext2/inode.c --- S0-ext2_max_size/fs/ext2/inode.c Fri Dec 29 17:36:44 2000 +++ S0-ext2-LFS-full/fs/ext2/inode.c Mon Jan 8 03:37:41 2001 @@ -880,8 +880,6 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; ext2_discard_prealloc(inode); @@ -1239,11 +1237,17 @@ return ext2_update_inode (inode, 1); } +static struct {unsigned attr, flag, ext2;} ext2_attr[] = { + {ATTR_FLAG_SYNCRONOUS, S_SYNC, EXT2_SYNC_FL}, + {ATTR_FLAG_NOATIME, S_NOATIME, EXT2_NOATIME_FL}, + {ATTR_FLAG_APPEND, S_APPEND, EXT2_APPEND_FL}, + {ATTR_FLAG_IMMUTABLE, S_IMMUTABLE, EXT2_IMMUTABLE_FL} +}; + int ext2_notify_change(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; int retval; - unsigned int flags; retval = -EPERM; if (iattr->ia_valid & ATTR_ATTR_FLAG && @@ -1260,36 +1264,27 @@ if (retval != 0) goto out; + if (iattr->ia_valid & ATTR_SIZE) { + if (iattr->ia_size > inode->i_sb->u.ext2_sb.s_max_size) { + retval = -EFBIG; + goto out; + } + } + inode_setattr(inode, iattr); - flags = iattr->ia_attr_flags; - if (flags & ATTR_FLAG_SYNCRONOUS) { - inode->i_flags |= S_SYNC; - inode->u.ext2_i.i_flags |= EXT2_SYNC_FL; - } else { - inode->i_flags &= ~S_SYNC; - inode->u.ext2_i.i_flags &= ~EXT2_SYNC_FL; - } - if (flags & ATTR_FLAG_NOATIME) { - inode->i_flags |= S_NOATIME; - inode->u.ext2_i.i_flags |= EXT2_NOATIME_FL; - } else { - inode->i_flags &= ~S_NOATIME; - inode->u.ext2_i.i_flags &= ~EXT2_NOATIME_FL; - } - if (flags & ATTR_FLAG_APPEND) { - inode->i_flags |= S_APPEND; - inode->u.ext2_i.i_flags |= EXT2_APPEND_FL; - } else { - inode->i_flags &= ~S_APPEND; - inode->u.ext2_i.i_flags &= ~EXT2_APPEND_FL; - } - if (flags & ATTR_FLAG_IMMUTABLE) { - inode->i_flags |= S_IMMUTABLE; - inode->u.ext2_i.i_flags |= EXT2_IMMUTABLE_FL; - } else { - inode->i_flags &= ~S_IMMUTABLE; - inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL; + if (iattr->ia_valid & ATTR_ATTR_FLAG) { + unsigned flags = iattr->ia_attr_flags; + int i; + for (i=0; i<sizeof(ext2_attr)/sizeof(ext2_attr[0]); i++) { + if (flags & ext2_attr[i].attr) { + inode->i_flags |= ext2_attr[i].flag; + inode->u.ext2_i.i_flags |= ext2_attr[i].ext2; + } else { + inode->i_flags &= ~ext2_attr[i].flag; + inode->u.ext2_i.i_flags &= ~ext2_attr[i].ext2; + } + } } mark_inode_dirty(inode); out: diff -urN S0-ext2_max_size/include/linux/ext2_fs.h S0-ext2-LFS-full/include/linux/ext2_fs.h --- S0-ext2_max_size/include/linux/ext2_fs.h Thu Jan 4 17:50:47 2001 +++ S0-ext2-LFS-full/include/linux/ext2_fs.h Mon Jan 8 03:37:41 2001 @@ -582,6 +582,8 @@ extern int ext2_sync_inode (struct inode *); extern void ext2_discard_prealloc (struct inode *); +extern int ext2_notify_change (struct dentry *, struct iattr *); + /* ioctl.c */ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, unsigned long); - 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/
|  |