diff options
Diffstat (limited to 'fs/ext2/super.c')
| -rw-r--r-- | fs/ext2/super.c | 163 | 
1 files changed, 114 insertions, 49 deletions
diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d89e0b6a2d7..3750031cfa2 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -42,10 +42,13 @@ static void ext2_sync_super(struct super_block *sb,  static int ext2_remount (struct super_block * sb, int * flags, char * data);  static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf);  static int ext2_sync_fs(struct super_block *sb, int wait); +static int ext2_freeze(struct super_block *sb); +static int ext2_unfreeze(struct super_block *sb); -void ext2_error (struct super_block * sb, const char * function, -		 const char * fmt, ...) +void ext2_error(struct super_block *sb, const char *function, +		const char *fmt, ...)  { +	struct va_format vaf;  	va_list args;  	struct ext2_sb_info *sbi = EXT2_SB(sb);  	struct ext2_super_block *es = sbi->s_es; @@ -59,9 +62,13 @@ void ext2_error (struct super_block * sb, const char * function,  	}  	va_start(args, fmt); -	printk(KERN_CRIT "EXT2-fs (%s): error: %s: ", sb->s_id, function); -	vprintk(fmt, args); -	printk("\n"); + +	vaf.fmt = fmt; +	vaf.va = &args; + +	printk(KERN_CRIT "EXT2-fs (%s): error: %s: %pV\n", +	       sb->s_id, function, &vaf); +  	va_end(args);  	if (test_opt(sb, ERRORS_PANIC)) @@ -76,12 +83,16 @@ void ext2_error (struct super_block * sb, const char * function,  void ext2_msg(struct super_block *sb, const char *prefix,  		const char *fmt, ...)  { +	struct va_format vaf;  	va_list args;  	va_start(args, fmt); -	printk("%sEXT2-fs (%s): ", prefix, sb->s_id); -	vprintk(fmt, args); -	printk("\n"); + +	vaf.fmt = fmt; +	vaf.va = &args; + +	printk("%sEXT2-fs (%s): %pV\n", prefix, sb->s_id, &vaf); +  	va_end(args);  } @@ -121,9 +132,6 @@ static void ext2_put_super (struct super_block * sb)  	dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED); -	if (sb->s_dirt) -		ext2_write_super(sb); -  	ext2_xattr_put_super(sb);  	if (!(sb->s_flags & MS_RDONLY)) {  		struct ext2_super_block *es = sbi->s_es; @@ -161,11 +169,17 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)  	return &ei->vfs_inode;  } -static void ext2_destroy_inode(struct inode *inode) +static void ext2_i_callback(struct rcu_head *head)  { +	struct inode *inode = container_of(head, struct inode, i_rcu);  	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));  } +static void ext2_destroy_inode(struct inode *inode) +{ +	call_rcu(&inode->i_rcu, ext2_i_callback); +} +  static void init_once(void *foo)  {  	struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; @@ -178,7 +192,7 @@ static void init_once(void *foo)  	inode_init_once(&ei->vfs_inode);  } -static int init_inodecache(void) +static int __init init_inodecache(void)  {  	ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",  					     sizeof(struct ext2_inode_info), @@ -192,12 +206,17 @@ static int init_inodecache(void)  static void destroy_inodecache(void)  { +	/* +	 * Make sure all delayed rcu free inodes are flushed before we +	 * destroy cache. +	 */ +	rcu_barrier();  	kmem_cache_destroy(ext2_inode_cachep);  } -static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) +static int ext2_show_options(struct seq_file *seq, struct dentry *root)  { -	struct super_block *sb = vfs->mnt_sb; +	struct super_block *sb = root->d_sb;  	struct ext2_sb_info *sbi = EXT2_SB(sb);  	struct ext2_super_block *es = sbi->s_es;  	unsigned long def_mount_opts; @@ -213,13 +232,15 @@ static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)  		seq_puts(seq, ",grpid");  	if (!test_opt(sb, GRPID) && (def_mount_opts & EXT2_DEFM_BSDGROUPS))  		seq_puts(seq, ",nogrpid"); -	if (sbi->s_resuid != EXT2_DEF_RESUID || +	if (!uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT2_DEF_RESUID)) ||  	    le16_to_cpu(es->s_def_resuid) != EXT2_DEF_RESUID) { -		seq_printf(seq, ",resuid=%u", sbi->s_resuid); +		seq_printf(seq, ",resuid=%u", +				from_kuid_munged(&init_user_ns, sbi->s_resuid));  	} -	if (sbi->s_resgid != EXT2_DEF_RESGID || +	if (!gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT2_DEF_RESGID)) ||  	    le16_to_cpu(es->s_def_resgid) != EXT2_DEF_RESGID) { -		seq_printf(seq, ",resgid=%u", sbi->s_resgid); +		seq_printf(seq, ",resgid=%u", +				from_kgid_munged(&init_user_ns, sbi->s_resgid));  	}  	if (test_opt(sb, ERRORS_RO)) {  		int def_errors = le16_to_cpu(es->s_errors); @@ -290,8 +311,9 @@ static const struct super_operations ext2_sops = {  	.write_inode	= ext2_write_inode,  	.evict_inode	= ext2_evict_inode,  	.put_super	= ext2_put_super, -	.write_super	= ext2_write_super,  	.sync_fs	= ext2_sync_fs, +	.freeze_fs	= ext2_freeze, +	.unfreeze_fs	= ext2_unfreeze,  	.statfs		= ext2_statfs,  	.remount_fs	= ext2_remount,  	.show_options	= ext2_show_options, @@ -311,10 +333,10 @@ static struct inode *ext2_nfs_get_inode(struct super_block *sb,  	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..... +	/* +	 * ext2_iget isn't quite right if the inode is currently unallocated! +	 * However ext2_iget currently does appropriate checks to handle stale +	 * inodes so everything is OK.  	 */  	inode = ext2_iget(sb, ino);  	if (IS_ERR(inode)) @@ -341,11 +363,6 @@ static struct dentry *ext2_fh_to_parent(struct super_block *sb, struct fid *fid,  				    ext2_nfs_get_inode);  } -/* 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. - */  static const struct export_operations ext2_export_ops = {  	.fh_to_dentry = ext2_fh_to_dentry,  	.fh_to_parent = ext2_fh_to_parent, @@ -421,6 +438,8 @@ static int parse_options(char *options, struct super_block *sb)  	struct ext2_sb_info *sbi = EXT2_SB(sb);  	substring_t args[MAX_OPT_ARGS];  	int option; +	kuid_t uid; +	kgid_t gid;  	if (!options)  		return 1; @@ -447,12 +466,23 @@ static int parse_options(char *options, struct super_block *sb)  		case Opt_resuid:  			if (match_int(&args[0], &option))  				return 0; -			sbi->s_resuid = option; +			uid = make_kuid(current_user_ns(), option); +			if (!uid_valid(uid)) { +				ext2_msg(sb, KERN_ERR, "Invalid uid value %d", option); +				return 0; + +			} +			sbi->s_resuid = uid;  			break;  		case Opt_resgid:  			if (match_int(&args[0], &option))  				return 0; -			sbi->s_resgid = option; +			gid = make_kgid(current_user_ns(), option); +			if (!gid_valid(gid)) { +				ext2_msg(sb, KERN_ERR, "Invalid gid value %d", option); +				return 0; +			} +			sbi->s_resgid = gid;  			break;  		case Opt_sb:  			/* handled by get_sb_block() instead of here */ @@ -750,13 +780,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)  	err = -ENOMEM;  	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);  	if (!sbi) -		goto failed_unlock; +		goto failed;  	sbi->s_blockgroup_lock =  		kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);  	if (!sbi->s_blockgroup_lock) {  		kfree(sbi); -		goto failed_unlock; +		goto failed;  	}  	sb->s_fs_info = sbi;  	sbi->s_sb_block = sb_block; @@ -826,8 +856,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)  	else  		set_opt(sbi->s_mount_opt, ERRORS_RO); -	sbi->s_resuid = le16_to_cpu(es->s_def_resuid); -	sbi->s_resgid = le16_to_cpu(es->s_def_resgid); +	sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid)); +	sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));  	set_opt(sbi->s_mount_opt, RESERVATION); @@ -882,7 +912,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)  		brelse(bh);  		if (!sb_set_blocksize(sb, blocksize)) { -			ext2_msg(sb, KERN_ERR, "error: blocksize is too small"); +			ext2_msg(sb, KERN_ERR, +				"error: bad blocksize %d", blocksize);  			goto failed_sbi;  		} @@ -903,6 +934,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)  	}  	sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits); +	sb->s_max_links = EXT2_LINK_MAX;  	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {  		sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; @@ -1071,9 +1103,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)  		goto failed_mount3;  	} -	sb->s_root = d_alloc_root(root); +	sb->s_root = d_make_root(root);  	if (!sb->s_root) { -		iput(root);  		ext2_msg(sb, KERN_ERR, "error: get root inode failed");  		ret = -ENOMEM;  		goto failed_mount3; @@ -1108,7 +1139,7 @@ failed_sbi:  	sb->s_fs_info = NULL;  	kfree(sbi->s_blockgroup_lock);  	kfree(sbi); -failed_unlock: +failed:  	return ret;  } @@ -1145,7 +1176,6 @@ static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,  	mark_buffer_dirty(EXT2_SB(sb)->s_sbh);  	if (wait)  		sync_dirty_buffer(EXT2_SB(sb)->s_sbh); -	sb->s_dirt = 0;  }  /* @@ -1163,6 +1193,12 @@ static int ext2_sync_fs(struct super_block *sb, int wait)  	struct ext2_sb_info *sbi = EXT2_SB(sb);  	struct ext2_super_block *es = EXT2_SB(sb)->s_es; +	/* +	 * Write quota structures to quota file, sync_blockdev() will write +	 * them to disk later +	 */ +	dquot_writeback_dquots(sb, -1); +  	spin_lock(&sbi->s_lock);  	if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) {  		ext2_debug("setting valid to 0\n"); @@ -1173,13 +1209,40 @@ static int ext2_sync_fs(struct super_block *sb, int wait)  	return 0;  } +static int ext2_freeze(struct super_block *sb) +{ +	struct ext2_sb_info *sbi = EXT2_SB(sb); + +	/* +	 * Open but unlinked files present? Keep EXT2_VALID_FS flag cleared +	 * because we have unattached inodes and thus filesystem is not fully +	 * consistent. +	 */ +	if (atomic_long_read(&sb->s_remove_count)) { +		ext2_sync_fs(sb, 1); +		return 0; +	} +	/* Set EXT2_FS_VALID flag */ +	spin_lock(&sbi->s_lock); +	sbi->s_es->s_state = cpu_to_le16(sbi->s_mount_state); +	spin_unlock(&sbi->s_lock); +	ext2_sync_super(sb, sbi->s_es, 1); + +	return 0; +} + +static int ext2_unfreeze(struct super_block *sb) +{ +	/* Just write sb to clear EXT2_VALID_FS flag */ +	ext2_write_super(sb); + +	return 0; +}  void ext2_write_super(struct super_block *sb)  {  	if (!(sb->s_flags & MS_RDONLY))  		ext2_sync_fs(sb, 1); -	else -		sb->s_dirt = 0;  }  static int ext2_remount (struct super_block * sb, int * flags, char * data) @@ -1191,6 +1254,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)  	unsigned long old_sb_flags;  	int err; +	sync_filesystem(sb);  	spin_lock(&sbi->s_lock);  	/* Store the old options */ @@ -1366,7 +1430,7 @@ static struct dentry *ext2_mount(struct file_system_type *fs_type,  /* 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) + * itself serializes the operations (and no one 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) @@ -1425,12 +1489,12 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,  	struct buffer_head tmp_bh;  	struct buffer_head *bh; -	mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);  	while (towrite > 0) {  		tocopy = sb->s_blocksize - offset < towrite ?  				sb->s_blocksize - offset : towrite;  		tmp_bh.b_state = 0; +		tmp_bh.b_size = sb->s_blocksize;  		err = ext2_get_block(inode, blk, &tmp_bh, 1);  		if (err < 0)  			goto out; @@ -1438,7 +1502,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,  			bh = sb_bread(sb, tmp_bh.b_blocknr);  		else  			bh = sb_getblk(sb, tmp_bh.b_blocknr); -		if (!bh) { +		if (unlikely(!bh)) {  			err = -EIO;  			goto out;  		} @@ -1455,16 +1519,13 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,  		blk++;  	}  out: -	if (len == towrite) { -		mutex_unlock(&inode->i_mutex); +	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;  } @@ -1477,6 +1538,7 @@ static struct file_system_type ext2_fs_type = {  	.kill_sb	= kill_block_super,  	.fs_flags	= FS_REQUIRES_DEV,  }; +MODULE_ALIAS_FS("ext2");  static int __init init_ext2_fs(void)  { @@ -1504,5 +1566,8 @@ static void __exit exit_ext2_fs(void)  	exit_ext2_xattr();  } +MODULE_AUTHOR("Remy Card and others"); +MODULE_DESCRIPTION("Second Extended Filesystem"); +MODULE_LICENSE("GPL");  module_init(init_ext2_fs)  module_exit(exit_ext2_fs)  | 
