diff options
Diffstat (limited to 'fs/ocfs2/ioctl.c')
| -rw-r--r-- | fs/ocfs2/ioctl.c | 620 | 
1 files changed, 547 insertions, 73 deletions
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 7a486819615..6f66b3751ac 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -7,9 +7,9 @@  #include <linux/fs.h>  #include <linux/mount.h> +#include <linux/blkdev.h>  #include <linux/compat.h> -#define MLOG_MASK_PREFIX ML_INODE  #include <cluster/masklog.h>  #include "ocfs2.h" @@ -23,8 +23,11 @@  #include "ioctl.h"  #include "resize.h"  #include "refcounttree.h" - -#include <linux/ext2_fs.h> +#include "sysfile.h" +#include "dir.h" +#include "buffer_head_io.h" +#include "suballoc.h" +#include "move_extents.h"  #define o2info_from_user(a, b)	\  		copy_from_user(&(a), (b), sizeof(a)) @@ -36,15 +39,27 @@   * be -EFAULT.  The error will be returned from the ioctl(2) call.  It's   * just a best-effort to tell userspace that this request caused the error.   */ -static inline void __o2info_set_request_error(struct ocfs2_info_request *kreq, +static inline void o2info_set_request_error(struct ocfs2_info_request *kreq,  					struct ocfs2_info_request __user *req)  {  	kreq->ir_flags |= OCFS2_INFO_FL_ERROR;  	(void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags));  } -#define o2info_set_request_error(a, b) \ -		__o2info_set_request_error((struct ocfs2_info_request *)&(a), b) +static inline void o2info_set_request_filled(struct ocfs2_info_request *req) +{ +	req->ir_flags |= OCFS2_INFO_FL_FILLED; +} + +static inline void o2info_clear_request_filled(struct ocfs2_info_request *req) +{ +	req->ir_flags &= ~OCFS2_INFO_FL_FILLED; +} + +static inline int o2info_coherent(struct ocfs2_info_request *req) +{ +	return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT)); +}  static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)  { @@ -59,7 +74,6 @@ static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)  	*flags = OCFS2_I(inode)->ip_attr;  	ocfs2_inode_unlock(inode, 0); -	mlog_exit(status);  	return status;  } @@ -82,19 +96,12 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,  	}  	status = -EACCES; -	if (!is_owner_or_cap(inode)) +	if (!inode_owner_or_capable(inode))  		goto bail_unlock;  	if (!S_ISDIR(inode->i_mode))  		flags &= ~OCFS2_DIRSYNC_FL; -	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); -	if (IS_ERR(handle)) { -		status = PTR_ERR(handle); -		mlog_errno(status); -		goto bail_unlock; -	} -  	oldflags = ocfs2_inode->ip_attr;  	flags = flags & mask;  	flags |= oldflags & ~mask; @@ -110,6 +117,13 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,  			goto bail_unlock;  	} +	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); +	if (IS_ERR(handle)) { +		status = PTR_ERR(handle); +		mlog_errno(status); +		goto bail_unlock; +	} +  	ocfs2_inode->ip_attr = flags;  	ocfs2_set_inode_flags(inode); @@ -118,6 +132,7 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,  		mlog_errno(status);  	ocfs2_commit_trans(osb, handle); +  bail_unlock:  	ocfs2_inode_unlock(inode, 1);  bail: @@ -125,12 +140,11 @@ bail:  	brelse(bh); -	mlog_exit(status);  	return status;  } -int ocfs2_info_handle_blocksize(struct inode *inode, -				struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_blocksize(struct inode *inode, +				       struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_blocksize oib; @@ -139,7 +153,8 @@ int ocfs2_info_handle_blocksize(struct inode *inode,  		goto bail;  	oib.ib_blocksize = inode->i_sb->s_blocksize; -	oib.ib_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oib.ib_req);  	if (o2info_to_user(oib, req))  		goto bail; @@ -147,13 +162,13 @@ int ocfs2_info_handle_blocksize(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oib, req); +		o2info_set_request_error(&oib.ib_req, req);  	return status;  } -int ocfs2_info_handle_clustersize(struct inode *inode, -				  struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_clustersize(struct inode *inode, +					 struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_clustersize oic; @@ -163,7 +178,8 @@ int ocfs2_info_handle_clustersize(struct inode *inode,  		goto bail;  	oic.ic_clustersize = osb->s_clustersize; -	oic.ic_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oic.ic_req);  	if (o2info_to_user(oic, req))  		goto bail; @@ -171,13 +187,13 @@ int ocfs2_info_handle_clustersize(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oic, req); +		o2info_set_request_error(&oic.ic_req, req);  	return status;  } -int ocfs2_info_handle_maxslots(struct inode *inode, -			       struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_maxslots(struct inode *inode, +				      struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_maxslots oim; @@ -187,7 +203,8 @@ int ocfs2_info_handle_maxslots(struct inode *inode,  		goto bail;  	oim.im_max_slots = osb->max_slots; -	oim.im_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oim.im_req);  	if (o2info_to_user(oim, req))  		goto bail; @@ -195,13 +212,13 @@ int ocfs2_info_handle_maxslots(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oim, req); +		o2info_set_request_error(&oim.im_req, req);  	return status;  } -int ocfs2_info_handle_label(struct inode *inode, -			    struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_label(struct inode *inode, +				   struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_label oil; @@ -211,7 +228,8 @@ int ocfs2_info_handle_label(struct inode *inode,  		goto bail;  	memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN); -	oil.il_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oil.il_req);  	if (o2info_to_user(oil, req))  		goto bail; @@ -219,13 +237,13 @@ int ocfs2_info_handle_label(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oil, req); +		o2info_set_request_error(&oil.il_req, req);  	return status;  } -int ocfs2_info_handle_uuid(struct inode *inode, -			   struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_uuid(struct inode *inode, +				  struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_uuid oiu; @@ -235,7 +253,8 @@ int ocfs2_info_handle_uuid(struct inode *inode,  		goto bail;  	memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1); -	oiu.iu_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oiu.iu_req);  	if (o2info_to_user(oiu, req))  		goto bail; @@ -243,13 +262,13 @@ int ocfs2_info_handle_uuid(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oiu, req); +		o2info_set_request_error(&oiu.iu_req, req);  	return status;  } -int ocfs2_info_handle_fs_features(struct inode *inode, -				  struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_fs_features(struct inode *inode, +					 struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_fs_features oif; @@ -261,7 +280,8 @@ int ocfs2_info_handle_fs_features(struct inode *inode,  	oif.if_compat_features = osb->s_feature_compat;  	oif.if_incompat_features = osb->s_feature_incompat;  	oif.if_ro_compat_features = osb->s_feature_ro_compat; -	oif.if_req.ir_flags |= OCFS2_INFO_FL_FILLED; + +	o2info_set_request_filled(&oif.if_req);  	if (o2info_to_user(oif, req))  		goto bail; @@ -269,13 +289,13 @@ int ocfs2_info_handle_fs_features(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oif, req); +		o2info_set_request_error(&oif.if_req, req);  	return status;  } -int ocfs2_info_handle_journal_size(struct inode *inode, -				   struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_journal_size(struct inode *inode, +					  struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_journal_size oij; @@ -284,9 +304,9 @@ int ocfs2_info_handle_journal_size(struct inode *inode,  	if (o2info_from_user(oij, req))  		goto bail; -	oij.ij_journal_size = osb->journal->j_inode->i_size; +	oij.ij_journal_size = i_size_read(osb->journal->j_inode); -	oij.ij_req.ir_flags |= OCFS2_INFO_FL_FILLED; +	o2info_set_request_filled(&oij.ij_req);  	if (o2info_to_user(oij, req))  		goto bail; @@ -294,13 +314,418 @@ int ocfs2_info_handle_journal_size(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oij, req); +		o2info_set_request_error(&oij.ij_req, req); + +	return status; +} + +static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb, +				       struct inode *inode_alloc, u64 blkno, +				       struct ocfs2_info_freeinode *fi, +				       u32 slot) +{ +	int status = 0, unlock = 0; + +	struct buffer_head *bh = NULL; +	struct ocfs2_dinode *dinode_alloc = NULL; + +	if (inode_alloc) +		mutex_lock(&inode_alloc->i_mutex); + +	if (o2info_coherent(&fi->ifi_req)) { +		status = ocfs2_inode_lock(inode_alloc, &bh, 0); +		if (status < 0) { +			mlog_errno(status); +			goto bail; +		} +		unlock = 1; +	} else { +		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); +		if (status < 0) { +			mlog_errno(status); +			goto bail; +		} +	} + +	dinode_alloc = (struct ocfs2_dinode *)bh->b_data; + +	fi->ifi_stat[slot].lfi_total = +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total); +	fi->ifi_stat[slot].lfi_free = +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) - +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used); + +bail: +	if (unlock) +		ocfs2_inode_unlock(inode_alloc, 0); + +	if (inode_alloc) +		mutex_unlock(&inode_alloc->i_mutex); + +	brelse(bh); + +	return status; +} + +static int ocfs2_info_handle_freeinode(struct inode *inode, +				       struct ocfs2_info_request __user *req) +{ +	u32 i; +	u64 blkno = -1; +	char namebuf[40]; +	int status = -EFAULT, type = INODE_ALLOC_SYSTEM_INODE; +	struct ocfs2_info_freeinode *oifi = NULL; +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); +	struct inode *inode_alloc = NULL; + +	oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL); +	if (!oifi) { +		status = -ENOMEM; +		mlog_errno(status); +		goto out_err; +	} + +	if (o2info_from_user(*oifi, req)) +		goto bail; + +	oifi->ifi_slotnum = osb->max_slots; + +	for (i = 0; i < oifi->ifi_slotnum; i++) { +		if (o2info_coherent(&oifi->ifi_req)) { +			inode_alloc = ocfs2_get_system_file_inode(osb, type, i); +			if (!inode_alloc) { +				mlog(ML_ERROR, "unable to get alloc inode in " +				    "slot %u\n", i); +				status = -EIO; +				goto bail; +			} +		} else { +			ocfs2_sprintf_system_inode_name(namebuf, +							sizeof(namebuf), +							type, i); +			status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, +							    namebuf, +							    strlen(namebuf), +							    &blkno); +			if (status < 0) { +				status = -ENOENT; +				goto bail; +			} +		} + +		status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i); + +		iput(inode_alloc); +		inode_alloc = NULL; + +		if (status < 0) +			goto bail; +	} + +	o2info_set_request_filled(&oifi->ifi_req); + +	if (o2info_to_user(*oifi, req)) +		goto bail; + +	status = 0; +bail: +	if (status) +		o2info_set_request_error(&oifi->ifi_req, req); + +	kfree(oifi); +out_err: +	return status; +} + +static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist, +				   unsigned int chunksize) +{ +	int index; + +	index = __ilog2_u32(chunksize); +	if (index >= OCFS2_INFO_MAX_HIST) +		index = OCFS2_INFO_MAX_HIST - 1; + +	hist->fc_chunks[index]++; +	hist->fc_clusters[index] += chunksize; +} + +static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats, +			       unsigned int chunksize) +{ +	if (chunksize > stats->ffs_max) +		stats->ffs_max = chunksize; + +	if (chunksize < stats->ffs_min) +		stats->ffs_min = chunksize; + +	stats->ffs_avg += chunksize; +	stats->ffs_free_chunks_real++; +} + +static void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg, +				  unsigned int chunksize) +{ +	o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize); +	o2ffg_update_stats(&(ffg->iff_ffs), chunksize); +} + +static int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, +					  struct inode *gb_inode, +					  struct ocfs2_dinode *gb_dinode, +					  struct ocfs2_chain_rec *rec, +					  struct ocfs2_info_freefrag *ffg, +					  u32 chunks_in_group) +{ +	int status = 0, used; +	u64 blkno; + +	struct buffer_head *bh = NULL; +	struct ocfs2_group_desc *bg = NULL; + +	unsigned int max_bits, num_clusters; +	unsigned int offset = 0, cluster, chunk; +	unsigned int chunk_free, last_chunksize = 0; + +	if (!le32_to_cpu(rec->c_free)) +		goto bail; + +	do { +		if (!bg) +			blkno = le64_to_cpu(rec->c_blkno); +		else +			blkno = le64_to_cpu(bg->bg_next_group); + +		if (bh) { +			brelse(bh); +			bh = NULL; +		} + +		if (o2info_coherent(&ffg->iff_req)) +			status = ocfs2_read_group_descriptor(gb_inode, +							     gb_dinode, +							     blkno, &bh); +		else +			status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); + +		if (status < 0) { +			mlog(ML_ERROR, "Can't read the group descriptor # " +			     "%llu from device.", (unsigned long long)blkno); +			status = -EIO; +			goto bail; +		} + +		bg = (struct ocfs2_group_desc *)bh->b_data; + +		if (!le16_to_cpu(bg->bg_free_bits_count)) +			continue; + +		max_bits = le16_to_cpu(bg->bg_bits); +		offset = 0; + +		for (chunk = 0; chunk < chunks_in_group; chunk++) { +			/* +			 * last chunk may be not an entire one. +			 */ +			if ((offset + ffg->iff_chunksize) > max_bits) +				num_clusters = max_bits - offset; +			else +				num_clusters = ffg->iff_chunksize; + +			chunk_free = 0; +			for (cluster = 0; cluster < num_clusters; cluster++) { +				used = ocfs2_test_bit(offset, +						(unsigned long *)bg->bg_bitmap); +				/* +				 * - chunk_free counts free clusters in #N chunk. +				 * - last_chunksize records the size(in) clusters +				 *   for the last real free chunk being counted. +				 */ +				if (!used) { +					last_chunksize++; +					chunk_free++; +				} + +				if (used && last_chunksize) { +					ocfs2_info_update_ffg(ffg, +							      last_chunksize); +					last_chunksize = 0; +				} + +				offset++; +			} + +			if (chunk_free == ffg->iff_chunksize) +				ffg->iff_ffs.ffs_free_chunks++; +		} + +		/* +		 * need to update the info for last free chunk. +		 */ +		if (last_chunksize) +			ocfs2_info_update_ffg(ffg, last_chunksize); + +	} while (le64_to_cpu(bg->bg_next_group)); + +bail: +	brelse(bh); + +	return status; +} + +static int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb, +					   struct inode *gb_inode, u64 blkno, +					   struct ocfs2_info_freefrag *ffg) +{ +	u32 chunks_in_group; +	int status = 0, unlock = 0, i; + +	struct buffer_head *bh = NULL; +	struct ocfs2_chain_list *cl = NULL; +	struct ocfs2_chain_rec *rec = NULL; +	struct ocfs2_dinode *gb_dinode = NULL; + +	if (gb_inode) +		mutex_lock(&gb_inode->i_mutex); + +	if (o2info_coherent(&ffg->iff_req)) { +		status = ocfs2_inode_lock(gb_inode, &bh, 0); +		if (status < 0) { +			mlog_errno(status); +			goto bail; +		} +		unlock = 1; +	} else { +		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); +		if (status < 0) { +			mlog_errno(status); +			goto bail; +		} +	} + +	gb_dinode = (struct ocfs2_dinode *)bh->b_data; +	cl = &(gb_dinode->id2.i_chain); + +	/* +	 * Chunksize(in) clusters from userspace should be +	 * less than clusters in a group. +	 */ +	if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) { +		status = -EINVAL; +		goto bail; +	} + +	memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats)); + +	ffg->iff_ffs.ffs_min = ~0U; +	ffg->iff_ffs.ffs_clusters = +			le32_to_cpu(gb_dinode->id1.bitmap1.i_total); +	ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters - +			le32_to_cpu(gb_dinode->id1.bitmap1.i_used); + +	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1; + +	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { +		rec = &(cl->cl_recs[i]); +		status = ocfs2_info_freefrag_scan_chain(osb, gb_inode, +							gb_dinode, +							rec, ffg, +							chunks_in_group); +		if (status) +			goto bail; +	} + +	if (ffg->iff_ffs.ffs_free_chunks_real) +		ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg / +					ffg->iff_ffs.ffs_free_chunks_real); +bail: +	if (unlock) +		ocfs2_inode_unlock(gb_inode, 0); + +	if (gb_inode) +		mutex_unlock(&gb_inode->i_mutex); + +	if (gb_inode) +		iput(gb_inode); + +	brelse(bh); + +	return status; +} + +static int ocfs2_info_handle_freefrag(struct inode *inode, +				      struct ocfs2_info_request __user *req) +{ +	u64 blkno = -1; +	char namebuf[40]; +	int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE; + +	struct ocfs2_info_freefrag *oiff; +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); +	struct inode *gb_inode = NULL; + +	oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL); +	if (!oiff) { +		status = -ENOMEM; +		mlog_errno(status); +		goto out_err; +	} + +	if (o2info_from_user(*oiff, req)) +		goto bail; +	/* +	 * chunksize from userspace should be power of 2. +	 */ +	if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) || +	    (!oiff->iff_chunksize)) { +		status = -EINVAL; +		goto bail; +	} + +	if (o2info_coherent(&oiff->iff_req)) { +		gb_inode = ocfs2_get_system_file_inode(osb, type, +						       OCFS2_INVALID_SLOT); +		if (!gb_inode) { +			mlog(ML_ERROR, "unable to get global_bitmap inode\n"); +			status = -EIO; +			goto bail; +		} +	} else { +		ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, +						OCFS2_INVALID_SLOT); +		status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, +						    namebuf, +						    strlen(namebuf), +						    &blkno); +		if (status < 0) { +			status = -ENOENT; +			goto bail; +		} +	} + +	status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff); +	if (status < 0) +		goto bail; + +	o2info_set_request_filled(&oiff->iff_req); + +	if (o2info_to_user(*oiff, req)) { +		status = -EFAULT; +		goto bail; +	} + +	status = 0; +bail: +	if (status) +		o2info_set_request_error(&oiff->iff_req, req); +	kfree(oiff); +out_err:  	return status;  } -int ocfs2_info_handle_unknown(struct inode *inode, -			      struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_unknown(struct inode *inode, +				     struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_request oir; @@ -308,7 +733,7 @@ int ocfs2_info_handle_unknown(struct inode *inode,  	if (o2info_from_user(oir, req))  		goto bail; -	oir.ir_flags &= ~OCFS2_INFO_FL_FILLED; +	o2info_clear_request_filled(&oir);  	if (o2info_to_user(oir, req))  		goto bail; @@ -316,7 +741,7 @@ int ocfs2_info_handle_unknown(struct inode *inode,  	status = 0;  bail:  	if (status) -		o2info_set_request_error(oir, req); +		o2info_set_request_error(&oir, req);  	return status;  } @@ -328,8 +753,8 @@ bail:   * - distinguish different requests.   * - validate size of different requests.   */ -int ocfs2_info_handle_request(struct inode *inode, -			      struct ocfs2_info_request __user *req) +static int ocfs2_info_handle_request(struct inode *inode, +				     struct ocfs2_info_request __user *req)  {  	int status = -EFAULT;  	struct ocfs2_info_request oir; @@ -370,6 +795,14 @@ int ocfs2_info_handle_request(struct inode *inode,  		if (oir.ir_size == sizeof(struct ocfs2_info_journal_size))  			status = ocfs2_info_handle_journal_size(inode, req);  		break; +	case OCFS2_INFO_FREEINODE: +		if (oir.ir_size == sizeof(struct ocfs2_info_freeinode)) +			status = ocfs2_info_handle_freeinode(inode, req); +		break; +	case OCFS2_INFO_FREEFRAG: +		if (oir.ir_size == sizeof(struct ocfs2_info_freefrag)) +			status = ocfs2_info_handle_freefrag(inode, req); +		break;  	default:  		status = ocfs2_info_handle_unknown(inode, req);  		break; @@ -379,8 +812,8 @@ bail:  	return status;  } -int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx, -			  u64 *req_addr, int compat_flag) +static int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx, +				 u64 *req_addr, int compat_flag)  {  	int status = -EFAULT;  	u64 __user *bp = NULL; @@ -417,8 +850,8 @@ bail:   * a better backward&forward compatibility, since a small piece of   * request will be less likely to be broken if disk layout get changed.   */ -int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, -		      int compat_flag) +static int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, +			     int compat_flag)  {  	int i, status = 0;  	u64 req_addr; @@ -436,7 +869,7 @@ int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,  		if (status)  			break; -		reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; +		reqp = (struct ocfs2_info_request __user *)(unsigned long)req_addr;  		if (!reqp) {  			status = -EINVAL;  			goto bail; @@ -453,16 +886,18 @@ bail:  long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  { -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct inode *inode = file_inode(filp);  	unsigned int flags;  	int new_clusters;  	int status;  	struct ocfs2_space_resv sr;  	struct ocfs2_new_group_input input;  	struct reflink_arguments args; -	const char *old_path, *new_path; +	const char __user *old_path; +	const char __user *new_path;  	bool preserve;  	struct ocfs2_info info; +	void __user *argp = (void __user *)arg;  	switch (cmd) {  	case OCFS2_IOC_GETFLAGS: @@ -476,12 +911,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		if (get_user(flags, (int __user *) arg))  			return -EFAULT; -		status = mnt_want_write(filp->f_path.mnt); +		status = mnt_want_write_file(filp);  		if (status)  			return status;  		status = ocfs2_set_inode_attr(inode, flags,  			OCFS2_FL_MODIFIABLE); -		mnt_drop_write(filp->f_path.mnt); +		mnt_drop_write_file(filp);  		return status;  	case OCFS2_IOC_RESVSP:  	case OCFS2_IOC_RESVSP64: @@ -498,7 +933,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		if (get_user(new_clusters, (int __user *)arg))  			return -EFAULT; -		return ocfs2_group_extend(inode, new_clusters); +		status = mnt_want_write_file(filp); +		if (status) +			return status; +		status = ocfs2_group_extend(inode, new_clusters); +		mnt_drop_write_file(filp); +		return status;  	case OCFS2_IOC_GROUP_ADD:  	case OCFS2_IOC_GROUP_ADD64:  		if (!capable(CAP_SYS_RESOURCE)) @@ -507,22 +947,54 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  		if (copy_from_user(&input, (int __user *) arg, sizeof(input)))  			return -EFAULT; -		return ocfs2_group_add(inode, &input); +		status = mnt_want_write_file(filp); +		if (status) +			return status; +		status = ocfs2_group_add(inode, &input); +		mnt_drop_write_file(filp); +		return status;  	case OCFS2_IOC_REFLINK: -		if (copy_from_user(&args, (struct reflink_arguments *)arg, -				   sizeof(args))) +		if (copy_from_user(&args, argp, sizeof(args)))  			return -EFAULT; -		old_path = (const char *)(unsigned long)args.old_path; -		new_path = (const char *)(unsigned long)args.new_path; +		old_path = (const char __user *)(unsigned long)args.old_path; +		new_path = (const char __user *)(unsigned long)args.new_path;  		preserve = (args.preserve != 0);  		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);  	case OCFS2_IOC_INFO: -		if (copy_from_user(&info, (struct ocfs2_info __user *)arg, -				   sizeof(struct ocfs2_info))) +		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))  			return -EFAULT;  		return ocfs2_info_handle(inode, &info, 0); +	case FITRIM: +	{ +		struct super_block *sb = inode->i_sb; +		struct request_queue *q = bdev_get_queue(sb->s_bdev); +		struct fstrim_range range; +		int ret = 0; + +		if (!capable(CAP_SYS_ADMIN)) +			return -EPERM; + +		if (!blk_queue_discard(q)) +			return -EOPNOTSUPP; + +		if (copy_from_user(&range, argp, sizeof(range))) +			return -EFAULT; + +		range.minlen = max_t(u64, q->limits.discard_granularity, +				     range.minlen); +		ret = ocfs2_trim_fs(sb, &range); +		if (ret < 0) +			return ret; + +		if (copy_to_user(argp, &range, sizeof(range))) +			return -EFAULT; + +		return 0; +	} +	case OCFS2_IOC_MOVE_EXT: +		return ocfs2_ioctl_move_extents(filp, argp);  	default:  		return -ENOTTY;  	} @@ -533,8 +1005,9 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)  {  	bool preserve;  	struct reflink_arguments args; -	struct inode *inode = file->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file);  	struct ocfs2_info info; +	void __user *argp = (void __user *)arg;  	switch (cmd) {  	case OCFS2_IOC32_GETFLAGS: @@ -550,21 +1023,22 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)  	case OCFS2_IOC_GROUP_EXTEND:  	case OCFS2_IOC_GROUP_ADD:  	case OCFS2_IOC_GROUP_ADD64: +	case FITRIM:  		break;  	case OCFS2_IOC_REFLINK: -		if (copy_from_user(&args, (struct reflink_arguments *)arg, -				   sizeof(args))) +		if (copy_from_user(&args, argp, sizeof(args)))  			return -EFAULT;  		preserve = (args.preserve != 0);  		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),  					   compat_ptr(args.new_path), preserve);  	case OCFS2_IOC_INFO: -		if (copy_from_user(&info, (struct ocfs2_info __user *)arg, -				   sizeof(struct ocfs2_info))) +		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))  			return -EFAULT;  		return ocfs2_info_handle(inode, &info, 1); +	case OCFS2_IOC_MOVE_EXT: +		break;  	default:  		return -ENOIOCTLCMD;  	}  | 
