X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fext2%2Fsuper.c;h=22993252ed5c62a415dcbb1874014203c7302f45;hb=4e76c8a9fa413ccc09d3f7f664183dcce3555d57;hp=3e54fbeac7223d1747e4853fde9d5107a63bfa60;hpb=a8e794ca871505c8ea96cc102f4ad555c5231d7f;p=linux-2.6.git diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 3e54fbeac..22993252e 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,18 +28,19 @@ #include #include #include +#include +#include #include #include "ext2.h" #include "xattr.h" #include "acl.h" +#include "xip.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); static int ext2_remount (struct super_block * sb, int * flags, char * data); static int ext2_statfs (struct super_block * sb, struct kstatfs * buf); -static char error_buf[1024]; - void ext2_error (struct super_block * sb, const char * function, const char * fmt, ...) { @@ -52,39 +54,19 @@ void ext2_error (struct super_block * sb, const char * function, cpu_to_le16(le16_to_cpu(es->s_state) | EXT2_ERROR_FS); ext2_sync_super(sb, es); } - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - if (test_opt (sb, ERRORS_PANIC)) - panic ("EXT2-fs panic (device %s): %s: %s\n", - sb->s_id, function, error_buf); - printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n", - sb->s_id, function, error_buf); - if (test_opt (sb, ERRORS_RO)) { - printk ("Remounting filesystem read-only\n"); - sb->s_flags |= MS_RDONLY; - } -} -NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, - const char * fmt, ...) -{ - va_list args; - struct ext2_sb_info *sbi = EXT2_SB(sb); + va_start(args, fmt); + printk(KERN_CRIT "EXT2-fs error (device %s): %s: ",sb->s_id, function); + vprintk(fmt, args); + printk("\n"); + va_end(args); - if (!(sb->s_flags & MS_RDONLY)) { - sbi->s_mount_state |= EXT2_ERROR_FS; - sbi->s_es->s_state = - cpu_to_le16(le16_to_cpu(sbi->s_es->s_state) | EXT2_ERROR_FS); - mark_buffer_dirty(sbi->s_sbh); - sb->s_dirt = 1; + if (test_opt(sb, ERRORS_PANIC)) + panic("EXT2-fs panic from previous error\n"); + if (test_opt(sb, ERRORS_RO)) { + printk("Remounting filesystem read-only\n"); + sb->s_flags |= MS_RDONLY; } - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - sb->s_flags |= MS_RDONLY; - panic ("EXT2-fs panic (device %s): %s: %s\n", - sb->s_id, function, error_buf); } void ext2_warning (struct super_block * sb, const char * function, @@ -92,11 +74,12 @@ void ext2_warning (struct super_block * sb, const char * function, { va_list args; - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); - printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n", - sb->s_id, function, error_buf); + va_start(args, fmt); + printk(KERN_WARNING "EXT2-fs warning (device %s): %s: ", + sb->s_id, function); + vprintk(fmt, args); + printk("\n"); + va_end(args); } void ext2_update_dynamic_rev(struct super_block *sb) @@ -134,7 +117,7 @@ static void ext2_put_super (struct super_block * sb) if (!(sb->s_flags & MS_RDONLY)) { struct ext2_super_block *es = sbi->s_es; - es->s_state = le16_to_cpu(sbi->s_mount_state); + es->s_state = cpu_to_le16(sbi->s_mount_state); ext2_sync_super(sb, es); } db_count = sbi->s_gdb_count; @@ -143,6 +126,9 @@ static void ext2_put_super (struct super_block * sb) brelse (sbi->s_group_desc[i]); kfree(sbi->s_group_desc); kfree(sbi->s_debts); + percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeinodes_counter); + percpu_counter_destroy(&sbi->s_dirs_counter); brelse (sbi->s_sbh); sb->s_fs_info = NULL; kfree(sbi); @@ -189,7 +175,8 @@ static int init_inodecache(void) { ext2_inode_cachep = kmem_cache_create("ext2_inode_cache", sizeof(struct ext2_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), init_once, NULL); if (ext2_inode_cachep == NULL) return -ENOMEM; @@ -202,10 +189,9 @@ static void destroy_inodecache(void) printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n"); } -#ifdef CONFIG_EXT2_FS_POSIX_ACL - static void ext2_clear_inode(struct inode *inode) { +#ifdef CONFIG_EXT2_FS_POSIX_ACL struct ext2_inode_info *ei = EXT2_I(inode); if (ei->i_acl && ei->i_acl != EXT2_ACL_NOT_CACHED) { @@ -216,10 +202,35 @@ static void ext2_clear_inode(struct inode *inode) posix_acl_release(ei->i_default_acl); ei->i_default_acl = EXT2_ACL_NOT_CACHED; } +#endif } -#else -# define ext2_clear_inode NULL +static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) +{ + struct ext2_sb_info *sbi = EXT2_SB(vfs->mnt_sb); + + if (sbi->s_mount_opt & EXT2_MOUNT_GRPID) + seq_puts(seq, ",grpid"); + +#if defined(CONFIG_QUOTA) + if (sbi->s_mount_opt & EXT2_MOUNT_USRQUOTA) + seq_puts(seq, ",usrquota"); + + if (sbi->s_mount_opt & EXT2_MOUNT_GRPQUOTA) + seq_puts(seq, ",grpquota"); +#endif + +#if defined(CONFIG_EXT2_FS_XIP) + if (sbi->s_mount_opt & EXT2_MOUNT_XIP) + seq_puts(seq, ",xip"); +#endif + + return 0; +} + +#ifdef CONFIG_QUOTA +static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off); +static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off); #endif static struct super_operations ext2_sops = { @@ -234,16 +245,61 @@ static struct super_operations ext2_sops = { .statfs = ext2_statfs, .remount_fs = ext2_remount, .clear_inode = ext2_clear_inode, + .show_options = ext2_show_options, +#ifdef CONFIG_QUOTA + .quota_read = ext2_quota_read, + .quota_write = ext2_quota_write, +#endif }; +static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp) +{ + __u32 *objp = vobjp; + unsigned long ino = objp[0]; + __u32 generation = objp[1]; + struct inode *inode; + struct dentry *result; + + if (ino != EXT2_ROOT_INO && ino < EXT2_FIRST_INO(sb)) + return ERR_PTR(-ESTALE); + if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count)) + return ERR_PTR(-ESTALE); + + /* iget isn't really right if the inode is currently unallocated!! + * ext2_read_inode currently does appropriate checks, but + * it might be "neater" to call ext2_get_inode first and check + * if the inode is valid..... + */ + inode = iget(sb, ino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + if (is_bad_inode(inode) + || (generation && inode->i_generation != generation) + ) { + /* we didn't find the right inode.. */ + iput(inode); + return ERR_PTR(-ESTALE); + } + /* now to find a dentry. + * If possible, get a well-connected one + */ + result = d_alloc_anon(inode); + if (!result) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + return result; +} + + /* Yes, most of these are left as NULL!! * A NULL value implies the default, which works with ext2-like file * systems, but can be improved upon. * Currently only get_parent is required. */ -struct dentry *ext2_get_parent(struct dentry *child); static struct export_operations ext2_export_ops = { .get_parent = ext2_get_parent, + .get_dentry = ext2_get_dentry, }; static unsigned long get_sb_block(void **data) @@ -268,10 +324,11 @@ static unsigned long get_sb_block(void **data) enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, - Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, - Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, - Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_tagxid, - Opt_ignore, Opt_err, + Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, + Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, + Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, + Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota, + Opt_usrquota, Opt_grpquota, Opt_tagxid }; static match_table_t tokens = { @@ -290,7 +347,6 @@ static match_table_t tokens = { {Opt_nouid32, "nouid32"}, {Opt_nocheck, "check=none"}, {Opt_nocheck, "nocheck"}, - {Opt_check, "check"}, {Opt_debug, "debug"}, {Opt_oldalloc, "oldalloc"}, {Opt_orlov, "orlov"}, @@ -299,11 +355,12 @@ static match_table_t tokens = { {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, + {Opt_xip, "xip"}, {Opt_tagxid, "tagxid"}, - {Opt_ignore, "grpquota"}, + {Opt_grpquota, "grpquota"}, {Opt_ignore, "noquota"}, - {Opt_ignore, "quota"}, - {Opt_ignore, "usrquota"}, + {Opt_quota, "quota"}, + {Opt_usrquota, "usrquota"}, {Opt_err, NULL} }; @@ -365,16 +422,9 @@ static int parse_options (char * options, break; #ifndef CONFIG_INOXID_NONE case Opt_tagxid: - set_opt (sbi->s_mount_opt, TAG_XID); + set_opt (sbi->s_mount_opt, TAGXID); break; #endif - case Opt_check: -#ifdef CONFIG_EXT2_CHECK - set_opt (sbi->s_mount_opt, CHECK); -#else - printk("EXT2 Check option not supported\n"); -#endif - break; case Opt_nocheck: clear_opt (sbi->s_mount_opt, CHECK); break; @@ -416,6 +466,33 @@ static int parse_options (char * options, printk("EXT2 (no)acl options not supported\n"); break; #endif + case Opt_xip: +#ifdef CONFIG_EXT2_FS_XIP + set_opt (sbi->s_mount_opt, XIP); +#else + printk("EXT2 xip option not supported\n"); +#endif + break; + +#if defined(CONFIG_QUOTA) + case Opt_quota: + case Opt_usrquota: + set_opt(sbi->s_mount_opt, USRQUOTA); + break; + + case Opt_grpquota: + set_opt(sbi->s_mount_opt, GRPQUOTA); + break; +#else + case Opt_quota: + case Opt_usrquota: + case Opt_grpquota: + printk(KERN_ERR + "EXT2-fs: quota operations not supported.\n"); + + break; +#endif + case Opt_ignore: break; default: @@ -455,8 +532,8 @@ static int ext2_setup_super (struct super_block * sb, (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= get_seconds())) printk ("EXT2-fs warning: checktime reached, " "running e2fsck is recommended\n"); - if (!(__s16) le16_to_cpu(es->s_max_mnt_count)) - es->s_max_mnt_count = (__s16) cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); + if (!le16_to_cpu(es->s_max_mnt_count)) + es->s_max_mnt_count = cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1); ext2_write_super(sb); if (test_opt (sb, DEBUG)) @@ -468,12 +545,6 @@ static int ext2_setup_super (struct super_block * sb, EXT2_BLOCKS_PER_GROUP(sb), EXT2_INODES_PER_GROUP(sb), sbi->s_mount_opt); -#ifdef CONFIG_EXT2_CHECK - if (test_opt (sb, CHECK)) { - ext2_check_blocks_bitmap (sb); - ext2_check_inodes_bitmap (sb); - } -#endif return res; } @@ -535,12 +606,18 @@ static int ext2_check_descriptors (struct super_block * sb) static loff_t ext2_max_size(int bits) { loff_t res = EXT2_NDIR_BLOCKS; + /* This constant is calculated to be the largest file size for a + * dense, 4k-blocksize file such that the total number of + * sectors in the file, including data and all indirect blocks, + * does not exceed 2^32. */ + const loff_t upper_limit = 0x1ff7fffd000LL; + res += 1LL << (bits-2); res += 1LL << (2*(bits-2)); res += 1LL << (3*(bits-2)); res <<= bits; - if (res > (512LL << 32) - (1 << bits)) - res = (512LL << 32) - (1 << bits); + if (res > upper_limit) + res = upper_limit; return res; } @@ -578,6 +655,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) int blocksize = BLOCK_SIZE; int db_count; int i, j; + __le32 features; sbi = kmalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -620,13 +698,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; sb->s_magic = le16_to_cpu(es->s_magic); - sb->s_flags |= MS_ONE_SECOND; - if (sb->s_magic != EXT2_SUPER_MAGIC) { - if (!silent) - printk ("VFS: Can't find ext2 filesystem on dev %s.\n", - sb->s_id); - goto failed_mount; - } + + if (sb->s_magic != EXT2_SUPER_MAGIC) + goto cantfind_ext2; /* Set defaults before we parse the mount options */ def_mount_opts = le32_to_cpu(es->s_default_mount_opts); @@ -652,12 +726,15 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (!parse_options ((char *) data, sbi)) goto failed_mount; - if (EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_TAG_XID) + if (EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_TAGXID) sb->s_flags |= MS_TAGXID; sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset + EXT2_MOUNT_XIP if not */ + if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) || @@ -669,20 +746,30 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) * previously didn't change the revision level when setting the flags, * so there is a chance incompat flags are set on a rev 0 filesystem. */ - if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) { + features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP); + if (features) { printk("EXT2-fs: %s: couldn't mount because of " "unsupported optional features (%x).\n", - sb->s_id, i); + sb->s_id, le32_to_cpu(features)); goto failed_mount; } if (!(sb->s_flags & MS_RDONLY) && - (i = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){ + (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){ printk("EXT2-fs: %s: couldn't mount RDWR because of " "unsupported optional features (%x).\n", - sb->s_id, i); + sb->s_id, le32_to_cpu(features)); goto failed_mount; } + blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); + + if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) || + (sb->s_blocksize != blocksize))) { + if (!silent) + printk("XIP: Unsupported blocksize\n"); + goto failed_mount; + } + /* If the blocksize doesn't match, re-read the thing.. */ if (sb->s_blocksize != blocksize) { brelse(bh); @@ -702,7 +789,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sbi->s_es = es; - if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) { + if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) { printk ("EXT2-fs: Magic mismatch, very weird !\n"); goto failed_mount; } @@ -724,35 +811,36 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } } + sbi->s_frag_size = EXT2_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); - if (sbi->s_frag_size) - sbi->s_frags_per_block = sb->s_blocksize / - sbi->s_frag_size; - else - sb->s_magic = 0; + if (sbi->s_frag_size == 0) + goto cantfind_ext2; + sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size; + sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group); sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); - sbi->s_inodes_per_block = sb->s_blocksize / - EXT2_INODE_SIZE(sb); + + if (EXT2_INODE_SIZE(sb) == 0) + goto cantfind_ext2; + sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb); + if (sbi->s_inodes_per_block == 0) + goto cantfind_ext2; sbi->s_itb_per_group = sbi->s_inodes_per_group / - sbi->s_inodes_per_block; + sbi->s_inodes_per_block; sbi->s_desc_per_block = sb->s_blocksize / - sizeof (struct ext2_group_desc); + sizeof (struct ext2_group_desc); sbi->s_sbh = bh; sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = log2 (EXT2_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = log2 (EXT2_DESC_PER_BLOCK(sb)); - if (sb->s_magic != EXT2_SUPER_MAGIC) { - if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev " - "%s.\n", - sb->s_id); - goto failed_mount; - } + + if (sb->s_magic != EXT2_SUPER_MAGIC) + goto cantfind_ext2; + if (sb->s_blocksize != bh->b_size) { if (!silent) printk ("VFS: Unsupported blocksize on dev " @@ -782,6 +870,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + if (EXT2_BLOCKS_PER_GROUP(sb) == 0) + goto cantfind_ext2; sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) - le32_to_cpu(es->s_first_data_block) + EXT2_BLOCKS_PER_GROUP(sb) - 1) / @@ -827,6 +917,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) */ sb->s_op = &ext2_sops; sb->s_export_op = &ext2_export_ops; + sb->s_xattr = ext2_xattr_handlers; root = iget(sb, EXT2_ROOT_INO); sb->s_root = d_alloc_root(root); if (!sb->s_root) { @@ -842,7 +933,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_warning(sb, __FUNCTION__, - "mounting ext3 filesystem as ext2\n"); + "mounting ext3 filesystem as ext2"); ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); percpu_counter_mod(&sbi->s_freeblocks_counter, ext2_count_free_blocks(sb)); @@ -851,13 +942,19 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) percpu_counter_mod(&sbi->s_dirs_counter, ext2_count_dirs(sb)); return 0; + +cantfind_ext2: + if (!silent) + printk("VFS: Can't find an ext2 filesystem on dev %s.\n", + sb->s_id); + goto failed_mount; + failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); failed_mount_group_desc: kfree(sbi->s_group_desc); - if (sbi->s_debts) - kfree(sbi->s_debts); + kfree(sbi->s_debts); failed_mount: brelse(bh); failed_sbi: @@ -921,17 +1018,41 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) { struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_super_block * es; + unsigned long old_mount_opt = sbi->s_mount_opt; + struct ext2_mount_options old_opts; + unsigned long old_sb_flags; + int err; + + /* Store the old options */ + old_sb_flags = sb->s_flags; + old_opts.s_mount_opt = sbi->s_mount_opt; + old_opts.s_resuid = sbi->s_resuid; + old_opts.s_resgid = sbi->s_resgid; /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options (data, sbi)) + if (!parse_options (data, sbi)) { + err = -EINVAL; + goto restore_opts; + } + + if ((sbi->s_mount_opt & EXT2_MOUNT_TAGXID) && + !(sb->s_flags & MS_TAGXID)) { + printk("EXT2-fs: %s: tagxid not permitted on remount.\n", + sb->s_id); return -EINVAL; + } sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); es = sbi->s_es; + if (((sbi->s_mount_opt & EXT2_MOUNT_XIP) != + (old_mount_opt & EXT2_MOUNT_XIP)) && + invalidate_inodes(sb)) + ext2_warning(sb, __FUNCTION__, "busy inodes while remounting "\ + "xip remain in cache (no functional problem)"); if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { @@ -945,13 +1066,14 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) es->s_state = cpu_to_le16(sbi->s_mount_state); es->s_mtime = cpu_to_le32(get_seconds()); } else { - int ret; - if ((ret = EXT2_HAS_RO_COMPAT_FEATURE(sb, - ~EXT2_FEATURE_RO_COMPAT_SUPP))) { + __le32 ret = EXT2_HAS_RO_COMPAT_FEATURE(sb, + ~EXT2_FEATURE_RO_COMPAT_SUPP); + if (ret) { printk("EXT2-fs: %s: couldn't remount RDWR because of " "unsupported optional features (%x).\n", - sb->s_id, ret); - return -EROFS; + sb->s_id, le32_to_cpu(ret)); + err = -EROFS; + goto restore_opts; } /* * Mounting a RDONLY partition read-write, so reread and @@ -964,6 +1086,12 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) } ext2_sync_super(sb, es); return 0; +restore_opts: + sbi->s_mount_opt = old_opts.s_mount_opt; + sbi->s_resuid = old_opts.s_resuid; + sbi->s_resgid = old_opts.s_resgid; + sb->s_flags = old_sb_flags; + return err; } static int ext2_statfs (struct super_block * sb, struct kstatfs * buf) @@ -1021,6 +1149,111 @@ static struct super_block *ext2_get_sb(struct file_system_type *fs_type, return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super); } +#ifdef CONFIG_QUOTA + +/* Read data from quotafile - avoid pagecache and such because we cannot afford + * acquiring the locks... As quota files are never truncated and quota code + * itself serializes the operations (and noone else should touch the files) + * we don't have to be afraid of races */ +static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t toread; + struct buffer_head tmp_bh; + struct buffer_head *bh; + loff_t i_size = i_size_read(inode); + + if (off > i_size) + return 0; + if (off+len > i_size) + len = i_size-off; + toread = len; + while (toread > 0) { + tocopy = sb->s_blocksize - offset < toread ? + sb->s_blocksize - offset : toread; + + tmp_bh.b_state = 0; + err = ext2_get_block(inode, blk, &tmp_bh, 0); + if (err) + return err; + if (!buffer_mapped(&tmp_bh)) /* A hole? */ + memset(data, 0, tocopy); + else { + bh = sb_bread(sb, tmp_bh.b_blocknr); + if (!bh) + return -EIO; + memcpy(data, bh->b_data+offset, tocopy); + brelse(bh); + } + offset = 0; + toread -= tocopy; + data += tocopy; + blk++; + } + return len; +} + +/* Write to quotafile */ +static ssize_t ext2_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t towrite = len; + struct buffer_head tmp_bh; + struct buffer_head *bh; + + mutex_lock(&inode->i_mutex); + while (towrite > 0) { + tocopy = sb->s_blocksize - offset < towrite ? + sb->s_blocksize - offset : towrite; + + tmp_bh.b_state = 0; + err = ext2_get_block(inode, blk, &tmp_bh, 1); + if (err) + goto out; + if (offset || tocopy != EXT2_BLOCK_SIZE(sb)) + bh = sb_bread(sb, tmp_bh.b_blocknr); + else + bh = sb_getblk(sb, tmp_bh.b_blocknr); + if (!bh) { + err = -EIO; + goto out; + } + lock_buffer(bh); + memcpy(bh->b_data+offset, data, tocopy); + flush_dcache_page(bh->b_page); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + brelse(bh); + offset = 0; + towrite -= tocopy; + data += tocopy; + blk++; + } +out: + if (len == towrite) + return err; + if (inode->i_size < off+len-towrite) + i_size_write(inode, off+len-towrite); + inode->i_version++; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + mutex_unlock(&inode->i_mutex); + return len - towrite; +} + +#endif + static struct file_system_type ext2_fs_type = { .owner = THIS_MODULE, .name = "ext2",