xref: /openbmc/linux/fs/ocfs2/ioctl.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ca4d147eSHerbert Poetzl /*
3ca4d147eSHerbert Poetzl  * linux/fs/ocfs2/ioctl.c
4ca4d147eSHerbert Poetzl  *
5ca4d147eSHerbert Poetzl  * Copyright (C) 2006 Herbert Poetzl
6ca4d147eSHerbert Poetzl  * adapted from Remy Card's ext2/ioctl.c
7ca4d147eSHerbert Poetzl  */
8ca4d147eSHerbert Poetzl 
9ca4d147eSHerbert Poetzl #include <linux/fs.h>
10ca4d147eSHerbert Poetzl #include <linux/mount.h>
1119e8ac27SJie Liu #include <linux/blkdev.h>
1234e6c59aSTao Ma #include <linux/compat.h>
132b5f52c5SMiklos Szeredi #include <linux/fileattr.h>
14ca4d147eSHerbert Poetzl 
15ca4d147eSHerbert Poetzl #include <cluster/masklog.h>
16ca4d147eSHerbert Poetzl 
17ca4d147eSHerbert Poetzl #include "ocfs2.h"
18ca4d147eSHerbert Poetzl #include "alloc.h"
19ca4d147eSHerbert Poetzl #include "dlmglue.h"
20b2580103SMark Fasheh #include "file.h"
21ca4d147eSHerbert Poetzl #include "inode.h"
22ca4d147eSHerbert Poetzl #include "journal.h"
23ca4d147eSHerbert Poetzl 
24ca4d147eSHerbert Poetzl #include "ocfs2_fs.h"
252d562518SAdrian Bunk #include "ioctl.h"
26d659072fSTao Ma #include "resize.h"
27bd50873dSTao Ma #include "refcounttree.h"
283e5db17dSTristan Ye #include "sysfile.h"
293e5db17dSTristan Ye #include "dir.h"
303e5db17dSTristan Ye #include "buffer_head_io.h"
31d24a10b9STristan Ye #include "suballoc.h"
3253069d4eSTristan Ye #include "move_extents.h"
332d562518SAdrian Bunk 
34ddee5cdbSTristan Ye #define o2info_from_user(a, b)	\
35ddee5cdbSTristan Ye 		copy_from_user(&(a), (b), sizeof(a))
36ddee5cdbSTristan Ye #define o2info_to_user(a, b)	\
37ddee5cdbSTristan Ye 		copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
38ddee5cdbSTristan Ye 
39ddee5cdbSTristan Ye /*
402b462638SBen Hutchings  * This is just a best-effort to tell userspace that this request
412b462638SBen Hutchings  * caused the error.
42ddee5cdbSTristan Ye  */
o2info_set_request_error(struct ocfs2_info_request * kreq,struct ocfs2_info_request __user * req)438aa1fa36STristan Ye static inline void o2info_set_request_error(struct ocfs2_info_request *kreq,
44ddee5cdbSTristan Ye 					struct ocfs2_info_request __user *req)
45ddee5cdbSTristan Ye {
46ddee5cdbSTristan Ye 	kreq->ir_flags |= OCFS2_INFO_FL_ERROR;
47ddee5cdbSTristan Ye 	(void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags));
48ddee5cdbSTristan Ye }
49ddee5cdbSTristan Ye 
o2info_set_request_filled(struct ocfs2_info_request * req)508aa1fa36STristan Ye static inline void o2info_set_request_filled(struct ocfs2_info_request *req)
511936a267STristan Ye {
521936a267STristan Ye 	req->ir_flags |= OCFS2_INFO_FL_FILLED;
531936a267STristan Ye }
541936a267STristan Ye 
o2info_clear_request_filled(struct ocfs2_info_request * req)558aa1fa36STristan Ye static inline void o2info_clear_request_filled(struct ocfs2_info_request *req)
561936a267STristan Ye {
571936a267STristan Ye 	req->ir_flags &= ~OCFS2_INFO_FL_FILLED;
581936a267STristan Ye }
591936a267STristan Ye 
o2info_coherent(struct ocfs2_info_request * req)603e5db17dSTristan Ye static inline int o2info_coherent(struct ocfs2_info_request *req)
613e5db17dSTristan Ye {
623e5db17dSTristan Ye 	return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT));
633e5db17dSTristan Ye }
641936a267STristan Ye 
ocfs2_fileattr_get(struct dentry * dentry,struct fileattr * fa)652b5f52c5SMiklos Szeredi int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
66ca4d147eSHerbert Poetzl {
672b5f52c5SMiklos Szeredi 	struct inode *inode = d_inode(dentry);
682b5f52c5SMiklos Szeredi 	unsigned int flags;
69ca4d147eSHerbert Poetzl 	int status;
70ca4d147eSHerbert Poetzl 
71e63aecb6SMark Fasheh 	status = ocfs2_inode_lock(inode, NULL, 0);
72ca4d147eSHerbert Poetzl 	if (status < 0) {
73ca4d147eSHerbert Poetzl 		mlog_errno(status);
74ca4d147eSHerbert Poetzl 		return status;
75ca4d147eSHerbert Poetzl 	}
766e4b0d56SJan Kara 	ocfs2_get_inode_flags(OCFS2_I(inode));
772b5f52c5SMiklos Szeredi 	flags = OCFS2_I(inode)->ip_attr;
78e63aecb6SMark Fasheh 	ocfs2_inode_unlock(inode, 0);
79ca4d147eSHerbert Poetzl 
802b5f52c5SMiklos Szeredi 	fileattr_fill_flags(fa, flags & OCFS2_FL_VISIBLE);
812b5f52c5SMiklos Szeredi 
82ca4d147eSHerbert Poetzl 	return status;
83ca4d147eSHerbert Poetzl }
84ca4d147eSHerbert Poetzl 
ocfs2_fileattr_set(struct mnt_idmap * idmap,struct dentry * dentry,struct fileattr * fa)858782a9aeSChristian Brauner int ocfs2_fileattr_set(struct mnt_idmap *idmap,
862b5f52c5SMiklos Szeredi 		       struct dentry *dentry, struct fileattr *fa)
87ca4d147eSHerbert Poetzl {
882b5f52c5SMiklos Szeredi 	struct inode *inode = d_inode(dentry);
892b5f52c5SMiklos Szeredi 	unsigned int flags = fa->flags;
90ca4d147eSHerbert Poetzl 	struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode);
91ca4d147eSHerbert Poetzl 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
921fabe148SMark Fasheh 	handle_t *handle = NULL;
93ca4d147eSHerbert Poetzl 	struct buffer_head *bh = NULL;
94ca4d147eSHerbert Poetzl 	unsigned oldflags;
95ca4d147eSHerbert Poetzl 	int status;
96ca4d147eSHerbert Poetzl 
972b5f52c5SMiklos Szeredi 	if (fileattr_has_fsx(fa))
982b5f52c5SMiklos Szeredi 		return -EOPNOTSUPP;
99ca4d147eSHerbert Poetzl 
100e63aecb6SMark Fasheh 	status = ocfs2_inode_lock(inode, &bh, 1);
101ca4d147eSHerbert Poetzl 	if (status < 0) {
102ca4d147eSHerbert Poetzl 		mlog_errno(status);
103ca4d147eSHerbert Poetzl 		goto bail;
104ca4d147eSHerbert Poetzl 	}
105ca4d147eSHerbert Poetzl 
106ca4d147eSHerbert Poetzl 	if (!S_ISDIR(inode->i_mode))
107ca4d147eSHerbert Poetzl 		flags &= ~OCFS2_DIRSYNC_FL;
108ca4d147eSHerbert Poetzl 
109ca4d147eSHerbert Poetzl 	oldflags = ocfs2_inode->ip_attr;
1102b5f52c5SMiklos Szeredi 	flags = flags & OCFS2_FL_MODIFIABLE;
1112b5f52c5SMiklos Szeredi 	flags |= oldflags & ~OCFS2_FL_MODIFIABLE;
112ca4d147eSHerbert Poetzl 
1132b5f52c5SMiklos Szeredi 	/* Check already done by VFS, but repeat with ocfs lock */
1142b5f52c5SMiklos Szeredi 	status = -EPERM;
1152b5f52c5SMiklos Szeredi 	if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
1162b5f52c5SMiklos Szeredi 	    !capable(CAP_LINUX_IMMUTABLE))
117b3e0767aSJeff Liu 		goto bail_unlock;
118b3e0767aSJeff Liu 
119b3e0767aSJeff Liu 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
120b3e0767aSJeff Liu 	if (IS_ERR(handle)) {
121b3e0767aSJeff Liu 		status = PTR_ERR(handle);
122b3e0767aSJeff Liu 		mlog_errno(status);
123b3e0767aSJeff Liu 		goto bail_unlock;
124ca4d147eSHerbert Poetzl 	}
125ca4d147eSHerbert Poetzl 
126ca4d147eSHerbert Poetzl 	ocfs2_inode->ip_attr = flags;
127ca4d147eSHerbert Poetzl 	ocfs2_set_inode_flags(inode);
128ca4d147eSHerbert Poetzl 
129ca4d147eSHerbert Poetzl 	status = ocfs2_mark_inode_dirty(handle, inode, bh);
130ca4d147eSHerbert Poetzl 	if (status < 0)
131ca4d147eSHerbert Poetzl 		mlog_errno(status);
132ca4d147eSHerbert Poetzl 
13302dc1af4SMark Fasheh 	ocfs2_commit_trans(osb, handle);
134b3e0767aSJeff Liu 
135ca4d147eSHerbert Poetzl bail_unlock:
136e63aecb6SMark Fasheh 	ocfs2_inode_unlock(inode, 1);
137ca4d147eSHerbert Poetzl bail:
138ca4d147eSHerbert Poetzl 	brelse(bh);
139ca4d147eSHerbert Poetzl 
140ca4d147eSHerbert Poetzl 	return status;
141ca4d147eSHerbert Poetzl }
142ca4d147eSHerbert Poetzl 
ocfs2_info_handle_blocksize(struct inode * inode,struct ocfs2_info_request __user * req)143c253ed1fSFabian Frederick static int ocfs2_info_handle_blocksize(struct inode *inode,
144ddee5cdbSTristan Ye 				       struct ocfs2_info_request __user *req)
145ddee5cdbSTristan Ye {
146ddee5cdbSTristan Ye 	struct ocfs2_info_blocksize oib;
147ddee5cdbSTristan Ye 
148ddee5cdbSTristan Ye 	if (o2info_from_user(oib, req))
1492b462638SBen Hutchings 		return -EFAULT;
150ddee5cdbSTristan Ye 
151ddee5cdbSTristan Ye 	oib.ib_blocksize = inode->i_sb->s_blocksize;
1521936a267STristan Ye 
1538aa1fa36STristan Ye 	o2info_set_request_filled(&oib.ib_req);
154ddee5cdbSTristan Ye 
155ddee5cdbSTristan Ye 	if (o2info_to_user(oib, req))
1562b462638SBen Hutchings 		return -EFAULT;
157ddee5cdbSTristan Ye 
1582b462638SBen Hutchings 	return 0;
159ddee5cdbSTristan Ye }
160ddee5cdbSTristan Ye 
ocfs2_info_handle_clustersize(struct inode * inode,struct ocfs2_info_request __user * req)161c253ed1fSFabian Frederick static int ocfs2_info_handle_clustersize(struct inode *inode,
162ddee5cdbSTristan Ye 					 struct ocfs2_info_request __user *req)
163ddee5cdbSTristan Ye {
164ddee5cdbSTristan Ye 	struct ocfs2_info_clustersize oic;
165ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
166ddee5cdbSTristan Ye 
167ddee5cdbSTristan Ye 	if (o2info_from_user(oic, req))
1682b462638SBen Hutchings 		return -EFAULT;
169ddee5cdbSTristan Ye 
170ddee5cdbSTristan Ye 	oic.ic_clustersize = osb->s_clustersize;
1711936a267STristan Ye 
1728aa1fa36STristan Ye 	o2info_set_request_filled(&oic.ic_req);
173ddee5cdbSTristan Ye 
174ddee5cdbSTristan Ye 	if (o2info_to_user(oic, req))
1752b462638SBen Hutchings 		return -EFAULT;
176ddee5cdbSTristan Ye 
1772b462638SBen Hutchings 	return 0;
178ddee5cdbSTristan Ye }
179ddee5cdbSTristan Ye 
ocfs2_info_handle_maxslots(struct inode * inode,struct ocfs2_info_request __user * req)180c253ed1fSFabian Frederick static int ocfs2_info_handle_maxslots(struct inode *inode,
181ddee5cdbSTristan Ye 				      struct ocfs2_info_request __user *req)
182ddee5cdbSTristan Ye {
183ddee5cdbSTristan Ye 	struct ocfs2_info_maxslots oim;
184ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
185ddee5cdbSTristan Ye 
186ddee5cdbSTristan Ye 	if (o2info_from_user(oim, req))
1872b462638SBen Hutchings 		return -EFAULT;
188ddee5cdbSTristan Ye 
189ddee5cdbSTristan Ye 	oim.im_max_slots = osb->max_slots;
1901936a267STristan Ye 
1918aa1fa36STristan Ye 	o2info_set_request_filled(&oim.im_req);
192ddee5cdbSTristan Ye 
193ddee5cdbSTristan Ye 	if (o2info_to_user(oim, req))
1942b462638SBen Hutchings 		return -EFAULT;
195ddee5cdbSTristan Ye 
1962b462638SBen Hutchings 	return 0;
197ddee5cdbSTristan Ye }
198ddee5cdbSTristan Ye 
ocfs2_info_handle_label(struct inode * inode,struct ocfs2_info_request __user * req)199c253ed1fSFabian Frederick static int ocfs2_info_handle_label(struct inode *inode,
200ddee5cdbSTristan Ye 				   struct ocfs2_info_request __user *req)
201ddee5cdbSTristan Ye {
202ddee5cdbSTristan Ye 	struct ocfs2_info_label oil;
203ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
204ddee5cdbSTristan Ye 
205ddee5cdbSTristan Ye 	if (o2info_from_user(oil, req))
2062b462638SBen Hutchings 		return -EFAULT;
207ddee5cdbSTristan Ye 
208ddee5cdbSTristan Ye 	memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
2091936a267STristan Ye 
2108aa1fa36STristan Ye 	o2info_set_request_filled(&oil.il_req);
211ddee5cdbSTristan Ye 
212ddee5cdbSTristan Ye 	if (o2info_to_user(oil, req))
2132b462638SBen Hutchings 		return -EFAULT;
214ddee5cdbSTristan Ye 
2152b462638SBen Hutchings 	return 0;
216ddee5cdbSTristan Ye }
217ddee5cdbSTristan Ye 
ocfs2_info_handle_uuid(struct inode * inode,struct ocfs2_info_request __user * req)218c253ed1fSFabian Frederick static int ocfs2_info_handle_uuid(struct inode *inode,
219ddee5cdbSTristan Ye 				  struct ocfs2_info_request __user *req)
220ddee5cdbSTristan Ye {
221ddee5cdbSTristan Ye 	struct ocfs2_info_uuid oiu;
222ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
223ddee5cdbSTristan Ye 
224ddee5cdbSTristan Ye 	if (o2info_from_user(oiu, req))
2252b462638SBen Hutchings 		return -EFAULT;
226ddee5cdbSTristan Ye 
227ddee5cdbSTristan Ye 	memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1);
2281936a267STristan Ye 
2298aa1fa36STristan Ye 	o2info_set_request_filled(&oiu.iu_req);
230ddee5cdbSTristan Ye 
231ddee5cdbSTristan Ye 	if (o2info_to_user(oiu, req))
2322b462638SBen Hutchings 		return -EFAULT;
233ddee5cdbSTristan Ye 
2342b462638SBen Hutchings 	return 0;
235ddee5cdbSTristan Ye }
236ddee5cdbSTristan Ye 
ocfs2_info_handle_fs_features(struct inode * inode,struct ocfs2_info_request __user * req)237c253ed1fSFabian Frederick static int ocfs2_info_handle_fs_features(struct inode *inode,
238ddee5cdbSTristan Ye 					 struct ocfs2_info_request __user *req)
239ddee5cdbSTristan Ye {
240ddee5cdbSTristan Ye 	struct ocfs2_info_fs_features oif;
241ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
242ddee5cdbSTristan Ye 
243ddee5cdbSTristan Ye 	if (o2info_from_user(oif, req))
2442b462638SBen Hutchings 		return -EFAULT;
245ddee5cdbSTristan Ye 
246ddee5cdbSTristan Ye 	oif.if_compat_features = osb->s_feature_compat;
247ddee5cdbSTristan Ye 	oif.if_incompat_features = osb->s_feature_incompat;
248ddee5cdbSTristan Ye 	oif.if_ro_compat_features = osb->s_feature_ro_compat;
2491936a267STristan Ye 
2508aa1fa36STristan Ye 	o2info_set_request_filled(&oif.if_req);
251ddee5cdbSTristan Ye 
252ddee5cdbSTristan Ye 	if (o2info_to_user(oif, req))
2532b462638SBen Hutchings 		return -EFAULT;
254ddee5cdbSTristan Ye 
2552b462638SBen Hutchings 	return 0;
256ddee5cdbSTristan Ye }
257ddee5cdbSTristan Ye 
ocfs2_info_handle_journal_size(struct inode * inode,struct ocfs2_info_request __user * req)258c253ed1fSFabian Frederick static int ocfs2_info_handle_journal_size(struct inode *inode,
259ddee5cdbSTristan Ye 					  struct ocfs2_info_request __user *req)
260ddee5cdbSTristan Ye {
261ddee5cdbSTristan Ye 	struct ocfs2_info_journal_size oij;
262ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
263ddee5cdbSTristan Ye 
264ddee5cdbSTristan Ye 	if (o2info_from_user(oij, req))
2652b462638SBen Hutchings 		return -EFAULT;
266ddee5cdbSTristan Ye 
267f17c20ddSJunxiao Bi 	oij.ij_journal_size = i_size_read(osb->journal->j_inode);
268ddee5cdbSTristan Ye 
2698aa1fa36STristan Ye 	o2info_set_request_filled(&oij.ij_req);
270ddee5cdbSTristan Ye 
271ddee5cdbSTristan Ye 	if (o2info_to_user(oij, req))
2722b462638SBen Hutchings 		return -EFAULT;
273ddee5cdbSTristan Ye 
2742b462638SBen Hutchings 	return 0;
275ddee5cdbSTristan Ye }
276ddee5cdbSTristan Ye 
ocfs2_info_scan_inode_alloc(struct ocfs2_super * osb,struct inode * inode_alloc,u64 blkno,struct ocfs2_info_freeinode * fi,u32 slot)277c253ed1fSFabian Frederick static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
2783e5db17dSTristan Ye 				       struct inode *inode_alloc, u64 blkno,
279c253ed1fSFabian Frederick 				       struct ocfs2_info_freeinode *fi,
280c253ed1fSFabian Frederick 				       u32 slot)
2813e5db17dSTristan Ye {
2823e5db17dSTristan Ye 	int status = 0, unlock = 0;
2833e5db17dSTristan Ye 
2843e5db17dSTristan Ye 	struct buffer_head *bh = NULL;
2853e5db17dSTristan Ye 	struct ocfs2_dinode *dinode_alloc = NULL;
2863e5db17dSTristan Ye 
2873e5db17dSTristan Ye 	if (inode_alloc)
2885955102cSAl Viro 		inode_lock(inode_alloc);
2893e5db17dSTristan Ye 
2902abb7d3bSJia-Ju Bai 	if (inode_alloc && o2info_coherent(&fi->ifi_req)) {
2913e5db17dSTristan Ye 		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
2923e5db17dSTristan Ye 		if (status < 0) {
2933e5db17dSTristan Ye 			mlog_errno(status);
2943e5db17dSTristan Ye 			goto bail;
2953e5db17dSTristan Ye 		}
2963e5db17dSTristan Ye 		unlock = 1;
2973e5db17dSTristan Ye 	} else {
2983e5db17dSTristan Ye 		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
2993e5db17dSTristan Ye 		if (status < 0) {
3003e5db17dSTristan Ye 			mlog_errno(status);
3013e5db17dSTristan Ye 			goto bail;
3023e5db17dSTristan Ye 		}
3033e5db17dSTristan Ye 	}
3043e5db17dSTristan Ye 
3053e5db17dSTristan Ye 	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
3063e5db17dSTristan Ye 
3073e5db17dSTristan Ye 	fi->ifi_stat[slot].lfi_total =
3083e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
3093e5db17dSTristan Ye 	fi->ifi_stat[slot].lfi_free =
3103e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
3113e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
3123e5db17dSTristan Ye 
3133e5db17dSTristan Ye bail:
3143e5db17dSTristan Ye 	if (unlock)
3153e5db17dSTristan Ye 		ocfs2_inode_unlock(inode_alloc, 0);
3163e5db17dSTristan Ye 
3173e5db17dSTristan Ye 	if (inode_alloc)
3185955102cSAl Viro 		inode_unlock(inode_alloc);
3193e5db17dSTristan Ye 
3203e5db17dSTristan Ye 	brelse(bh);
3213e5db17dSTristan Ye 
3223e5db17dSTristan Ye 	return status;
3233e5db17dSTristan Ye }
3243e5db17dSTristan Ye 
ocfs2_info_handle_freeinode(struct inode * inode,struct ocfs2_info_request __user * req)325c253ed1fSFabian Frederick static int ocfs2_info_handle_freeinode(struct inode *inode,
3263e5db17dSTristan Ye 				       struct ocfs2_info_request __user *req)
3273e5db17dSTristan Ye {
3283e5db17dSTristan Ye 	u32 i;
3293e5db17dSTristan Ye 	u64 blkno = -1;
3303e5db17dSTristan Ye 	char namebuf[40];
3312b462638SBen Hutchings 	int status, type = INODE_ALLOC_SYSTEM_INODE;
3323e5db17dSTristan Ye 	struct ocfs2_info_freeinode *oifi = NULL;
3333e5db17dSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
3343e5db17dSTristan Ye 	struct inode *inode_alloc = NULL;
3353e5db17dSTristan Ye 
3363e5db17dSTristan Ye 	oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL);
3373e5db17dSTristan Ye 	if (!oifi) {
3383e5db17dSTristan Ye 		status = -ENOMEM;
3393e5db17dSTristan Ye 		mlog_errno(status);
34087f0d5c8SDan Carpenter 		goto out_err;
3413e5db17dSTristan Ye 	}
3423e5db17dSTristan Ye 
3432b462638SBen Hutchings 	if (o2info_from_user(*oifi, req)) {
3442b462638SBen Hutchings 		status = -EFAULT;
3452b462638SBen Hutchings 		goto out_free;
3462b462638SBen Hutchings 	}
3473e5db17dSTristan Ye 
3483e5db17dSTristan Ye 	oifi->ifi_slotnum = osb->max_slots;
3493e5db17dSTristan Ye 
3503e5db17dSTristan Ye 	for (i = 0; i < oifi->ifi_slotnum; i++) {
3513e5db17dSTristan Ye 		if (o2info_coherent(&oifi->ifi_req)) {
3523e5db17dSTristan Ye 			inode_alloc = ocfs2_get_system_file_inode(osb, type, i);
3533e5db17dSTristan Ye 			if (!inode_alloc) {
3543e5db17dSTristan Ye 				mlog(ML_ERROR, "unable to get alloc inode in "
3553e5db17dSTristan Ye 				    "slot %u\n", i);
3563e5db17dSTristan Ye 				status = -EIO;
3573e5db17dSTristan Ye 				goto bail;
3583e5db17dSTristan Ye 			}
3593e5db17dSTristan Ye 		} else {
3603e5db17dSTristan Ye 			ocfs2_sprintf_system_inode_name(namebuf,
3613e5db17dSTristan Ye 							sizeof(namebuf),
3623e5db17dSTristan Ye 							type, i);
3633e5db17dSTristan Ye 			status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
3643e5db17dSTristan Ye 							    namebuf,
3653e5db17dSTristan Ye 							    strlen(namebuf),
3663e5db17dSTristan Ye 							    &blkno);
3673e5db17dSTristan Ye 			if (status < 0) {
3683e5db17dSTristan Ye 				status = -ENOENT;
3693e5db17dSTristan Ye 				goto bail;
3703e5db17dSTristan Ye 			}
3713e5db17dSTristan Ye 		}
3723e5db17dSTristan Ye 
3733e5db17dSTristan Ye 		status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i);
3743e5db17dSTristan Ye 
3753e5db17dSTristan Ye 		iput(inode_alloc);
3763e5db17dSTristan Ye 		inode_alloc = NULL;
3777dc3e839Sjiangyiwen 
3787dc3e839Sjiangyiwen 		if (status < 0)
3797dc3e839Sjiangyiwen 			goto bail;
3803e5db17dSTristan Ye 	}
3813e5db17dSTristan Ye 
3823e5db17dSTristan Ye 	o2info_set_request_filled(&oifi->ifi_req);
3833e5db17dSTristan Ye 
3842b462638SBen Hutchings 	if (o2info_to_user(*oifi, req)) {
3852b462638SBen Hutchings 		status = -EFAULT;
3862b462638SBen Hutchings 		goto out_free;
3872b462638SBen Hutchings 	}
3883e5db17dSTristan Ye 
3893e5db17dSTristan Ye 	status = 0;
3903e5db17dSTristan Ye bail:
3913e5db17dSTristan Ye 	if (status)
3923e5db17dSTristan Ye 		o2info_set_request_error(&oifi->ifi_req, req);
3932b462638SBen Hutchings out_free:
3943e5db17dSTristan Ye 	kfree(oifi);
39587f0d5c8SDan Carpenter out_err:
3963e5db17dSTristan Ye 	return status;
3973e5db17dSTristan Ye }
3983e5db17dSTristan Ye 
o2ffg_update_histogram(struct ocfs2_info_free_chunk_list * hist,unsigned int chunksize)399d24a10b9STristan Ye static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist,
400d24a10b9STristan Ye 				   unsigned int chunksize)
401d24a10b9STristan Ye {
402731a40faSZhen Lei 	u32 index;
403d24a10b9STristan Ye 
404d24a10b9STristan Ye 	index = __ilog2_u32(chunksize);
405d24a10b9STristan Ye 	if (index >= OCFS2_INFO_MAX_HIST)
406d24a10b9STristan Ye 		index = OCFS2_INFO_MAX_HIST - 1;
407d24a10b9STristan Ye 
408d24a10b9STristan Ye 	hist->fc_chunks[index]++;
409d24a10b9STristan Ye 	hist->fc_clusters[index] += chunksize;
410d24a10b9STristan Ye }
411d24a10b9STristan Ye 
o2ffg_update_stats(struct ocfs2_info_freefrag_stats * stats,unsigned int chunksize)412d24a10b9STristan Ye static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats,
413d24a10b9STristan Ye 			       unsigned int chunksize)
414d24a10b9STristan Ye {
415d24a10b9STristan Ye 	if (chunksize > stats->ffs_max)
416d24a10b9STristan Ye 		stats->ffs_max = chunksize;
417d24a10b9STristan Ye 
418d24a10b9STristan Ye 	if (chunksize < stats->ffs_min)
419d24a10b9STristan Ye 		stats->ffs_min = chunksize;
420d24a10b9STristan Ye 
421d24a10b9STristan Ye 	stats->ffs_avg += chunksize;
422d24a10b9STristan Ye 	stats->ffs_free_chunks_real++;
423d24a10b9STristan Ye }
424d24a10b9STristan Ye 
ocfs2_info_update_ffg(struct ocfs2_info_freefrag * ffg,unsigned int chunksize)425c253ed1fSFabian Frederick static void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
426d24a10b9STristan Ye 				  unsigned int chunksize)
427d24a10b9STristan Ye {
428d24a10b9STristan Ye 	o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize);
429d24a10b9STristan Ye 	o2ffg_update_stats(&(ffg->iff_ffs), chunksize);
430d24a10b9STristan Ye }
431d24a10b9STristan Ye 
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)432c253ed1fSFabian Frederick static int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb,
433d24a10b9STristan Ye 					  struct inode *gb_inode,
434d24a10b9STristan Ye 					  struct ocfs2_dinode *gb_dinode,
435d24a10b9STristan Ye 					  struct ocfs2_chain_rec *rec,
436d24a10b9STristan Ye 					  struct ocfs2_info_freefrag *ffg,
437d24a10b9STristan Ye 					  u32 chunks_in_group)
438d24a10b9STristan Ye {
439d24a10b9STristan Ye 	int status = 0, used;
440d24a10b9STristan Ye 	u64 blkno;
441d24a10b9STristan Ye 
442d24a10b9STristan Ye 	struct buffer_head *bh = NULL;
443d24a10b9STristan Ye 	struct ocfs2_group_desc *bg = NULL;
444d24a10b9STristan Ye 
445d24a10b9STristan Ye 	unsigned int max_bits, num_clusters;
446d24a10b9STristan Ye 	unsigned int offset = 0, cluster, chunk;
447d24a10b9STristan Ye 	unsigned int chunk_free, last_chunksize = 0;
448d24a10b9STristan Ye 
449d24a10b9STristan Ye 	if (!le32_to_cpu(rec->c_free))
450d24a10b9STristan Ye 		goto bail;
451d24a10b9STristan Ye 
452d24a10b9STristan Ye 	do {
453d24a10b9STristan Ye 		if (!bg)
454d24a10b9STristan Ye 			blkno = le64_to_cpu(rec->c_blkno);
455d24a10b9STristan Ye 		else
456d24a10b9STristan Ye 			blkno = le64_to_cpu(bg->bg_next_group);
457d24a10b9STristan Ye 
458d24a10b9STristan Ye 		if (bh) {
459d24a10b9STristan Ye 			brelse(bh);
460d24a10b9STristan Ye 			bh = NULL;
461d24a10b9STristan Ye 		}
462d24a10b9STristan Ye 
463d24a10b9STristan Ye 		if (o2info_coherent(&ffg->iff_req))
464d24a10b9STristan Ye 			status = ocfs2_read_group_descriptor(gb_inode,
465d24a10b9STristan Ye 							     gb_dinode,
466d24a10b9STristan Ye 							     blkno, &bh);
467d24a10b9STristan Ye 		else
468d24a10b9STristan Ye 			status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
469d24a10b9STristan Ye 
470d24a10b9STristan Ye 		if (status < 0) {
471d24a10b9STristan Ye 			mlog(ML_ERROR, "Can't read the group descriptor # "
472d24a10b9STristan Ye 			     "%llu from device.", (unsigned long long)blkno);
473d24a10b9STristan Ye 			status = -EIO;
474d24a10b9STristan Ye 			goto bail;
475d24a10b9STristan Ye 		}
476d24a10b9STristan Ye 
477d24a10b9STristan Ye 		bg = (struct ocfs2_group_desc *)bh->b_data;
478d24a10b9STristan Ye 
479d24a10b9STristan Ye 		if (!le16_to_cpu(bg->bg_free_bits_count))
480d24a10b9STristan Ye 			continue;
481d24a10b9STristan Ye 
482d24a10b9STristan Ye 		max_bits = le16_to_cpu(bg->bg_bits);
483d24a10b9STristan Ye 		offset = 0;
484d24a10b9STristan Ye 
485d24a10b9STristan Ye 		for (chunk = 0; chunk < chunks_in_group; chunk++) {
486d24a10b9STristan Ye 			/*
487d24a10b9STristan Ye 			 * last chunk may be not an entire one.
488d24a10b9STristan Ye 			 */
489d24a10b9STristan Ye 			if ((offset + ffg->iff_chunksize) > max_bits)
490d24a10b9STristan Ye 				num_clusters = max_bits - offset;
491d24a10b9STristan Ye 			else
492d24a10b9STristan Ye 				num_clusters = ffg->iff_chunksize;
493d24a10b9STristan Ye 
494d24a10b9STristan Ye 			chunk_free = 0;
495d24a10b9STristan Ye 			for (cluster = 0; cluster < num_clusters; cluster++) {
496d24a10b9STristan Ye 				used = ocfs2_test_bit(offset,
497d24a10b9STristan Ye 						(unsigned long *)bg->bg_bitmap);
498d24a10b9STristan Ye 				/*
499d24a10b9STristan Ye 				 * - chunk_free counts free clusters in #N chunk.
500d24a10b9STristan Ye 				 * - last_chunksize records the size(in) clusters
501d24a10b9STristan Ye 				 *   for the last real free chunk being counted.
502d24a10b9STristan Ye 				 */
503d24a10b9STristan Ye 				if (!used) {
504d24a10b9STristan Ye 					last_chunksize++;
505d24a10b9STristan Ye 					chunk_free++;
506d24a10b9STristan Ye 				}
507d24a10b9STristan Ye 
508d24a10b9STristan Ye 				if (used && last_chunksize) {
509d24a10b9STristan Ye 					ocfs2_info_update_ffg(ffg,
510d24a10b9STristan Ye 							      last_chunksize);
511d24a10b9STristan Ye 					last_chunksize = 0;
512d24a10b9STristan Ye 				}
513d24a10b9STristan Ye 
514d24a10b9STristan Ye 				offset++;
515d24a10b9STristan Ye 			}
516d24a10b9STristan Ye 
517d24a10b9STristan Ye 			if (chunk_free == ffg->iff_chunksize)
518d24a10b9STristan Ye 				ffg->iff_ffs.ffs_free_chunks++;
519d24a10b9STristan Ye 		}
520d24a10b9STristan Ye 
521d24a10b9STristan Ye 		/*
522d24a10b9STristan Ye 		 * need to update the info for last free chunk.
523d24a10b9STristan Ye 		 */
524d24a10b9STristan Ye 		if (last_chunksize)
525d24a10b9STristan Ye 			ocfs2_info_update_ffg(ffg, last_chunksize);
526d24a10b9STristan Ye 
527d24a10b9STristan Ye 	} while (le64_to_cpu(bg->bg_next_group));
528d24a10b9STristan Ye 
529d24a10b9STristan Ye bail:
530d24a10b9STristan Ye 	brelse(bh);
531d24a10b9STristan Ye 
532d24a10b9STristan Ye 	return status;
533d24a10b9STristan Ye }
534d24a10b9STristan Ye 
ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super * osb,struct inode * gb_inode,u64 blkno,struct ocfs2_info_freefrag * ffg)535c253ed1fSFabian Frederick static int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb,
536d24a10b9STristan Ye 					   struct inode *gb_inode, u64 blkno,
537d24a10b9STristan Ye 					   struct ocfs2_info_freefrag *ffg)
538d24a10b9STristan Ye {
539d24a10b9STristan Ye 	u32 chunks_in_group;
540d24a10b9STristan Ye 	int status = 0, unlock = 0, i;
541d24a10b9STristan Ye 
542d24a10b9STristan Ye 	struct buffer_head *bh = NULL;
543d24a10b9STristan Ye 	struct ocfs2_chain_list *cl = NULL;
544d24a10b9STristan Ye 	struct ocfs2_chain_rec *rec = NULL;
545d24a10b9STristan Ye 	struct ocfs2_dinode *gb_dinode = NULL;
546d24a10b9STristan Ye 
547d24a10b9STristan Ye 	if (gb_inode)
5485955102cSAl Viro 		inode_lock(gb_inode);
549d24a10b9STristan Ye 
550d24a10b9STristan Ye 	if (o2info_coherent(&ffg->iff_req)) {
551d24a10b9STristan Ye 		status = ocfs2_inode_lock(gb_inode, &bh, 0);
552d24a10b9STristan Ye 		if (status < 0) {
553d24a10b9STristan Ye 			mlog_errno(status);
554d24a10b9STristan Ye 			goto bail;
555d24a10b9STristan Ye 		}
556d24a10b9STristan Ye 		unlock = 1;
557d24a10b9STristan Ye 	} else {
558d24a10b9STristan Ye 		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
559d24a10b9STristan Ye 		if (status < 0) {
560d24a10b9STristan Ye 			mlog_errno(status);
561d24a10b9STristan Ye 			goto bail;
562d24a10b9STristan Ye 		}
563d24a10b9STristan Ye 	}
564d24a10b9STristan Ye 
565d24a10b9STristan Ye 	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
566d24a10b9STristan Ye 	cl = &(gb_dinode->id2.i_chain);
567d24a10b9STristan Ye 
568d24a10b9STristan Ye 	/*
569d24a10b9STristan Ye 	 * Chunksize(in) clusters from userspace should be
570d24a10b9STristan Ye 	 * less than clusters in a group.
571d24a10b9STristan Ye 	 */
572d24a10b9STristan Ye 	if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) {
573d24a10b9STristan Ye 		status = -EINVAL;
574d24a10b9STristan Ye 		goto bail;
575d24a10b9STristan Ye 	}
576d24a10b9STristan Ye 
577d24a10b9STristan Ye 	memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats));
578d24a10b9STristan Ye 
579d24a10b9STristan Ye 	ffg->iff_ffs.ffs_min = ~0U;
580d24a10b9STristan Ye 	ffg->iff_ffs.ffs_clusters =
581d24a10b9STristan Ye 			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
582d24a10b9STristan Ye 	ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters -
583d24a10b9STristan Ye 			le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
584d24a10b9STristan Ye 
585d24a10b9STristan Ye 	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1;
586d24a10b9STristan Ye 
587d24a10b9STristan Ye 	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
588d24a10b9STristan Ye 		rec = &(cl->cl_recs[i]);
589d24a10b9STristan Ye 		status = ocfs2_info_freefrag_scan_chain(osb, gb_inode,
590d24a10b9STristan Ye 							gb_dinode,
591d24a10b9STristan Ye 							rec, ffg,
592d24a10b9STristan Ye 							chunks_in_group);
593d24a10b9STristan Ye 		if (status)
594d24a10b9STristan Ye 			goto bail;
595d24a10b9STristan Ye 	}
596d24a10b9STristan Ye 
597d24a10b9STristan Ye 	if (ffg->iff_ffs.ffs_free_chunks_real)
598d24a10b9STristan Ye 		ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg /
599d24a10b9STristan Ye 					ffg->iff_ffs.ffs_free_chunks_real);
600d24a10b9STristan Ye bail:
601d24a10b9STristan Ye 	if (unlock)
602d24a10b9STristan Ye 		ocfs2_inode_unlock(gb_inode, 0);
603d24a10b9STristan Ye 
604d24a10b9STristan Ye 	if (gb_inode)
6055955102cSAl Viro 		inode_unlock(gb_inode);
606d24a10b9STristan Ye 
607d24a10b9STristan Ye 	iput(gb_inode);
608d24a10b9STristan Ye 	brelse(bh);
609d24a10b9STristan Ye 
610d24a10b9STristan Ye 	return status;
611d24a10b9STristan Ye }
612d24a10b9STristan Ye 
ocfs2_info_handle_freefrag(struct inode * inode,struct ocfs2_info_request __user * req)613c253ed1fSFabian Frederick static int ocfs2_info_handle_freefrag(struct inode *inode,
614d24a10b9STristan Ye 				      struct ocfs2_info_request __user *req)
615d24a10b9STristan Ye {
616d24a10b9STristan Ye 	u64 blkno = -1;
617d24a10b9STristan Ye 	char namebuf[40];
6182b462638SBen Hutchings 	int status, type = GLOBAL_BITMAP_SYSTEM_INODE;
619d24a10b9STristan Ye 
620d24a10b9STristan Ye 	struct ocfs2_info_freefrag *oiff;
621d24a10b9STristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
622d24a10b9STristan Ye 	struct inode *gb_inode = NULL;
623d24a10b9STristan Ye 
624d24a10b9STristan Ye 	oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL);
625d24a10b9STristan Ye 	if (!oiff) {
626d24a10b9STristan Ye 		status = -ENOMEM;
627d24a10b9STristan Ye 		mlog_errno(status);
62887f0d5c8SDan Carpenter 		goto out_err;
629d24a10b9STristan Ye 	}
630d24a10b9STristan Ye 
6312b462638SBen Hutchings 	if (o2info_from_user(*oiff, req)) {
6322b462638SBen Hutchings 		status = -EFAULT;
6332b462638SBen Hutchings 		goto out_free;
6342b462638SBen Hutchings 	}
635d24a10b9STristan Ye 	/*
636d24a10b9STristan Ye 	 * chunksize from userspace should be power of 2.
637d24a10b9STristan Ye 	 */
638d24a10b9STristan Ye 	if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) ||
639d24a10b9STristan Ye 	    (!oiff->iff_chunksize)) {
640d24a10b9STristan Ye 		status = -EINVAL;
641d24a10b9STristan Ye 		goto bail;
642d24a10b9STristan Ye 	}
643d24a10b9STristan Ye 
644d24a10b9STristan Ye 	if (o2info_coherent(&oiff->iff_req)) {
645d24a10b9STristan Ye 		gb_inode = ocfs2_get_system_file_inode(osb, type,
646d24a10b9STristan Ye 						       OCFS2_INVALID_SLOT);
647d24a10b9STristan Ye 		if (!gb_inode) {
648d24a10b9STristan Ye 			mlog(ML_ERROR, "unable to get global_bitmap inode\n");
649d24a10b9STristan Ye 			status = -EIO;
650d24a10b9STristan Ye 			goto bail;
651d24a10b9STristan Ye 		}
652d24a10b9STristan Ye 	} else {
653d24a10b9STristan Ye 		ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type,
654d24a10b9STristan Ye 						OCFS2_INVALID_SLOT);
655d24a10b9STristan Ye 		status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
656d24a10b9STristan Ye 						    namebuf,
657d24a10b9STristan Ye 						    strlen(namebuf),
658d24a10b9STristan Ye 						    &blkno);
659d24a10b9STristan Ye 		if (status < 0) {
660d24a10b9STristan Ye 			status = -ENOENT;
661d24a10b9STristan Ye 			goto bail;
662d24a10b9STristan Ye 		}
663d24a10b9STristan Ye 	}
664d24a10b9STristan Ye 
665d24a10b9STristan Ye 	status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff);
666d24a10b9STristan Ye 	if (status < 0)
667d24a10b9STristan Ye 		goto bail;
668d24a10b9STristan Ye 
669d24a10b9STristan Ye 	o2info_set_request_filled(&oiff->iff_req);
670d24a10b9STristan Ye 
6717ebab453SWei Yongjun 	if (o2info_to_user(*oiff, req)) {
6727ebab453SWei Yongjun 		status = -EFAULT;
6732b462638SBen Hutchings 		goto out_free;
6747ebab453SWei Yongjun 	}
675d24a10b9STristan Ye 
676d24a10b9STristan Ye 	status = 0;
677d24a10b9STristan Ye bail:
678d24a10b9STristan Ye 	if (status)
679d24a10b9STristan Ye 		o2info_set_request_error(&oiff->iff_req, req);
6802b462638SBen Hutchings out_free:
681d24a10b9STristan Ye 	kfree(oiff);
68287f0d5c8SDan Carpenter out_err:
683ddee5cdbSTristan Ye 	return status;
684ddee5cdbSTristan Ye }
685ddee5cdbSTristan Ye 
ocfs2_info_handle_unknown(struct inode * inode,struct ocfs2_info_request __user * req)686c253ed1fSFabian Frederick static int ocfs2_info_handle_unknown(struct inode *inode,
687ddee5cdbSTristan Ye 				     struct ocfs2_info_request __user *req)
688ddee5cdbSTristan Ye {
689ddee5cdbSTristan Ye 	struct ocfs2_info_request oir;
690ddee5cdbSTristan Ye 
691ddee5cdbSTristan Ye 	if (o2info_from_user(oir, req))
6922b462638SBen Hutchings 		return -EFAULT;
693ddee5cdbSTristan Ye 
6948aa1fa36STristan Ye 	o2info_clear_request_filled(&oir);
695ddee5cdbSTristan Ye 
696ddee5cdbSTristan Ye 	if (o2info_to_user(oir, req))
6972b462638SBen Hutchings 		return -EFAULT;
698ddee5cdbSTristan Ye 
6992b462638SBen Hutchings 	return 0;
700ddee5cdbSTristan Ye }
701ddee5cdbSTristan Ye 
702ddee5cdbSTristan Ye /*
703ddee5cdbSTristan Ye  * Validate and distinguish OCFS2_IOC_INFO requests.
704ddee5cdbSTristan Ye  *
705ddee5cdbSTristan Ye  * - validate the magic number.
706ddee5cdbSTristan Ye  * - distinguish different requests.
707ddee5cdbSTristan Ye  * - validate size of different requests.
708ddee5cdbSTristan Ye  */
ocfs2_info_handle_request(struct inode * inode,struct ocfs2_info_request __user * req)709c253ed1fSFabian Frederick static int ocfs2_info_handle_request(struct inode *inode,
710ddee5cdbSTristan Ye 				     struct ocfs2_info_request __user *req)
711ddee5cdbSTristan Ye {
712ddee5cdbSTristan Ye 	int status = -EFAULT;
713ddee5cdbSTristan Ye 	struct ocfs2_info_request oir;
714ddee5cdbSTristan Ye 
715ddee5cdbSTristan Ye 	if (o2info_from_user(oir, req))
716ddee5cdbSTristan Ye 		goto bail;
717ddee5cdbSTristan Ye 
718ddee5cdbSTristan Ye 	status = -EINVAL;
719ddee5cdbSTristan Ye 	if (oir.ir_magic != OCFS2_INFO_MAGIC)
720ddee5cdbSTristan Ye 		goto bail;
721ddee5cdbSTristan Ye 
722ddee5cdbSTristan Ye 	switch (oir.ir_code) {
723ddee5cdbSTristan Ye 	case OCFS2_INFO_BLOCKSIZE:
724ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_blocksize))
725ddee5cdbSTristan Ye 			status = ocfs2_info_handle_blocksize(inode, req);
726ddee5cdbSTristan Ye 		break;
727ddee5cdbSTristan Ye 	case OCFS2_INFO_CLUSTERSIZE:
728ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_clustersize))
729ddee5cdbSTristan Ye 			status = ocfs2_info_handle_clustersize(inode, req);
730ddee5cdbSTristan Ye 		break;
731ddee5cdbSTristan Ye 	case OCFS2_INFO_MAXSLOTS:
732ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_maxslots))
733ddee5cdbSTristan Ye 			status = ocfs2_info_handle_maxslots(inode, req);
734ddee5cdbSTristan Ye 		break;
735ddee5cdbSTristan Ye 	case OCFS2_INFO_LABEL:
736ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_label))
737ddee5cdbSTristan Ye 			status = ocfs2_info_handle_label(inode, req);
738ddee5cdbSTristan Ye 		break;
739ddee5cdbSTristan Ye 	case OCFS2_INFO_UUID:
740ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_uuid))
741ddee5cdbSTristan Ye 			status = ocfs2_info_handle_uuid(inode, req);
742ddee5cdbSTristan Ye 		break;
743ddee5cdbSTristan Ye 	case OCFS2_INFO_FS_FEATURES:
744ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_fs_features))
745ddee5cdbSTristan Ye 			status = ocfs2_info_handle_fs_features(inode, req);
746ddee5cdbSTristan Ye 		break;
747ddee5cdbSTristan Ye 	case OCFS2_INFO_JOURNAL_SIZE:
748ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_journal_size))
749ddee5cdbSTristan Ye 			status = ocfs2_info_handle_journal_size(inode, req);
750ddee5cdbSTristan Ye 		break;
7513e5db17dSTristan Ye 	case OCFS2_INFO_FREEINODE:
7523e5db17dSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_freeinode))
7533e5db17dSTristan Ye 			status = ocfs2_info_handle_freeinode(inode, req);
7543e5db17dSTristan Ye 		break;
755d24a10b9STristan Ye 	case OCFS2_INFO_FREEFRAG:
756d24a10b9STristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_freefrag))
757d24a10b9STristan Ye 			status = ocfs2_info_handle_freefrag(inode, req);
758d24a10b9STristan Ye 		break;
759ddee5cdbSTristan Ye 	default:
760ddee5cdbSTristan Ye 		status = ocfs2_info_handle_unknown(inode, req);
761ddee5cdbSTristan Ye 		break;
762ddee5cdbSTristan Ye 	}
763ddee5cdbSTristan Ye 
764ddee5cdbSTristan Ye bail:
765ddee5cdbSTristan Ye 	return status;
766ddee5cdbSTristan Ye }
767ddee5cdbSTristan Ye 
ocfs2_get_request_ptr(struct ocfs2_info * info,int idx,u64 * req_addr,int compat_flag)768c253ed1fSFabian Frederick static int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx,
769ddee5cdbSTristan Ye 				 u64 *req_addr, int compat_flag)
770ddee5cdbSTristan Ye {
771ddee5cdbSTristan Ye 	int status = -EFAULT;
772ddee5cdbSTristan Ye 	u64 __user *bp = NULL;
773ddee5cdbSTristan Ye 
774ddee5cdbSTristan Ye 	if (compat_flag) {
775ddee5cdbSTristan Ye #ifdef CONFIG_COMPAT
776ddee5cdbSTristan Ye 		/*
777ddee5cdbSTristan Ye 		 * pointer bp stores the base address of a pointers array,
778ddee5cdbSTristan Ye 		 * which collects all addresses of separate request.
779ddee5cdbSTristan Ye 		 */
780ddee5cdbSTristan Ye 		bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests);
781ddee5cdbSTristan Ye #else
782ddee5cdbSTristan Ye 		BUG();
783ddee5cdbSTristan Ye #endif
784ddee5cdbSTristan Ye 	} else
785ddee5cdbSTristan Ye 		bp = (u64 __user *)(unsigned long)(info->oi_requests);
786ddee5cdbSTristan Ye 
787ddee5cdbSTristan Ye 	if (o2info_from_user(*req_addr, bp + idx))
788ddee5cdbSTristan Ye 		goto bail;
789ddee5cdbSTristan Ye 
790ddee5cdbSTristan Ye 	status = 0;
791ddee5cdbSTristan Ye bail:
792ddee5cdbSTristan Ye 	return status;
793ddee5cdbSTristan Ye }
794ddee5cdbSTristan Ye 
795ddee5cdbSTristan Ye /*
796ddee5cdbSTristan Ye  * OCFS2_IOC_INFO handles an array of requests passed from userspace.
797ddee5cdbSTristan Ye  *
798ddee5cdbSTristan Ye  * ocfs2_info_handle() recevies a large info aggregation, grab and
799ddee5cdbSTristan Ye  * validate the request count from header, then break it into small
800ddee5cdbSTristan Ye  * pieces, later specific handlers can handle them one by one.
801ddee5cdbSTristan Ye  *
802ddee5cdbSTristan Ye  * Idea here is to make each separate request small enough to ensure
803ddee5cdbSTristan Ye  * a better backward&forward compatibility, since a small piece of
804ddee5cdbSTristan Ye  * request will be less likely to be broken if disk layout get changed.
805ddee5cdbSTristan Ye  */
806*09d49eb9SArnd Bergmann static noinline_for_stack int
ocfs2_info_handle(struct inode * inode,struct ocfs2_info * info,int compat_flag)807*09d49eb9SArnd Bergmann ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, int compat_flag)
808ddee5cdbSTristan Ye {
809ddee5cdbSTristan Ye 	int i, status = 0;
810ddee5cdbSTristan Ye 	u64 req_addr;
811ddee5cdbSTristan Ye 	struct ocfs2_info_request __user *reqp;
812ddee5cdbSTristan Ye 
813ddee5cdbSTristan Ye 	if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) ||
814ddee5cdbSTristan Ye 	    (!info->oi_requests)) {
815ddee5cdbSTristan Ye 		status = -EINVAL;
816ddee5cdbSTristan Ye 		goto bail;
817ddee5cdbSTristan Ye 	}
818ddee5cdbSTristan Ye 
819ddee5cdbSTristan Ye 	for (i = 0; i < info->oi_count; i++) {
820ddee5cdbSTristan Ye 
821ddee5cdbSTristan Ye 		status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag);
822ddee5cdbSTristan Ye 		if (status)
823ddee5cdbSTristan Ye 			break;
824ddee5cdbSTristan Ye 
825f6a56903SAl Viro 		reqp = (struct ocfs2_info_request __user *)(unsigned long)req_addr;
826ddee5cdbSTristan Ye 		if (!reqp) {
827ddee5cdbSTristan Ye 			status = -EINVAL;
828ddee5cdbSTristan Ye 			goto bail;
829ddee5cdbSTristan Ye 		}
830ddee5cdbSTristan Ye 
831ddee5cdbSTristan Ye 		status = ocfs2_info_handle_request(inode, reqp);
832ddee5cdbSTristan Ye 		if (status)
833ddee5cdbSTristan Ye 			break;
834ddee5cdbSTristan Ye 	}
835ddee5cdbSTristan Ye 
836ddee5cdbSTristan Ye bail:
837ddee5cdbSTristan Ye 	return status;
838ddee5cdbSTristan Ye }
839ddee5cdbSTristan Ye 
ocfs2_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)840c9ec1488SAndi Kleen long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
841ca4d147eSHerbert Poetzl {
842496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
843f6a56903SAl Viro 	void __user *argp = (void __user *)arg;
844*09d49eb9SArnd Bergmann 	int status;
845ca4d147eSHerbert Poetzl 
846ca4d147eSHerbert Poetzl 	switch (cmd) {
847b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP:
848b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP64:
849b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP:
850b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP64:
851*09d49eb9SArnd Bergmann 	{
852*09d49eb9SArnd Bergmann 		struct ocfs2_space_resv sr;
853*09d49eb9SArnd Bergmann 
854b2580103SMark Fasheh 		if (copy_from_user(&sr, (int __user *) arg, sizeof(sr)))
855b2580103SMark Fasheh 			return -EFAULT;
856b2580103SMark Fasheh 
857b2580103SMark Fasheh 		return ocfs2_change_file_space(filp, cmd, &sr);
858*09d49eb9SArnd Bergmann 	}
859d659072fSTao Ma 	case OCFS2_IOC_GROUP_EXTEND:
860*09d49eb9SArnd Bergmann 	{
861*09d49eb9SArnd Bergmann 		int new_clusters;
862*09d49eb9SArnd Bergmann 
8630957f007SMark Fasheh 		if (!capable(CAP_SYS_RESOURCE))
8640957f007SMark Fasheh 			return -EPERM;
8650957f007SMark Fasheh 
866d659072fSTao Ma 		if (get_user(new_clusters, (int __user *)arg))
867d659072fSTao Ma 			return -EFAULT;
868d659072fSTao Ma 
869fef6925cSJan Kara 		status = mnt_want_write_file(filp);
870fef6925cSJan Kara 		if (status)
871fef6925cSJan Kara 			return status;
872fef6925cSJan Kara 		status = ocfs2_group_extend(inode, new_clusters);
873fef6925cSJan Kara 		mnt_drop_write_file(filp);
874fef6925cSJan Kara 		return status;
875*09d49eb9SArnd Bergmann 	}
8767909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD:
8777909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD64:
878*09d49eb9SArnd Bergmann 	{
879*09d49eb9SArnd Bergmann 		struct ocfs2_new_group_input input;
880*09d49eb9SArnd Bergmann 
8810957f007SMark Fasheh 		if (!capable(CAP_SYS_RESOURCE))
8820957f007SMark Fasheh 			return -EPERM;
8830957f007SMark Fasheh 
8847909f2bfSTao Ma 		if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
8857909f2bfSTao Ma 			return -EFAULT;
8867909f2bfSTao Ma 
887fef6925cSJan Kara 		status = mnt_want_write_file(filp);
888fef6925cSJan Kara 		if (status)
889fef6925cSJan Kara 			return status;
890fef6925cSJan Kara 		status = ocfs2_group_add(inode, &input);
891fef6925cSJan Kara 		mnt_drop_write_file(filp);
892fef6925cSJan Kara 		return status;
893*09d49eb9SArnd Bergmann 	}
894bd50873dSTao Ma 	case OCFS2_IOC_REFLINK:
895*09d49eb9SArnd Bergmann 	{
896*09d49eb9SArnd Bergmann 		struct reflink_arguments args;
897*09d49eb9SArnd Bergmann 		const char __user *old_path;
898*09d49eb9SArnd Bergmann 		const char __user *new_path;
899*09d49eb9SArnd Bergmann 		bool preserve;
900*09d49eb9SArnd Bergmann 
901f6a56903SAl Viro 		if (copy_from_user(&args, argp, sizeof(args)))
902bd50873dSTao Ma 			return -EFAULT;
903f6a56903SAl Viro 		old_path = (const char __user *)(unsigned long)args.old_path;
904f6a56903SAl Viro 		new_path = (const char __user *)(unsigned long)args.new_path;
905bd50873dSTao Ma 		preserve = (args.preserve != 0);
906bd50873dSTao Ma 
907bd50873dSTao Ma 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
908*09d49eb9SArnd Bergmann 	}
909ddee5cdbSTristan Ye 	case OCFS2_IOC_INFO:
910*09d49eb9SArnd Bergmann 	{
911*09d49eb9SArnd Bergmann 		struct ocfs2_info info;
912*09d49eb9SArnd Bergmann 
913f6a56903SAl Viro 		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
914ddee5cdbSTristan Ye 			return -EFAULT;
915ddee5cdbSTristan Ye 
916ddee5cdbSTristan Ye 		return ocfs2_info_handle(inode, &info, 0);
917*09d49eb9SArnd Bergmann 	}
91855e67872STao Ma 	case FITRIM:
91955e67872STao Ma 	{
92055e67872STao Ma 		struct super_block *sb = inode->i_sb;
92155e67872STao Ma 		struct fstrim_range range;
92255e67872STao Ma 		int ret = 0;
92355e67872STao Ma 
92455e67872STao Ma 		if (!capable(CAP_SYS_ADMIN))
92555e67872STao Ma 			return -EPERM;
92655e67872STao Ma 
92770200574SChristoph Hellwig 		if (!bdev_max_discard_sectors(sb->s_bdev))
92819e8ac27SJie Liu 			return -EOPNOTSUPP;
92919e8ac27SJie Liu 
930f6a56903SAl Viro 		if (copy_from_user(&range, argp, sizeof(range)))
93155e67872STao Ma 			return -EFAULT;
93255e67872STao Ma 
9337b47ef52SChristoph Hellwig 		range.minlen = max_t(u64, bdev_discard_granularity(sb->s_bdev),
9341ba2212bSJie Liu 				     range.minlen);
93555e67872STao Ma 		ret = ocfs2_trim_fs(sb, &range);
93655e67872STao Ma 		if (ret < 0)
93755e67872STao Ma 			return ret;
93855e67872STao Ma 
939f6a56903SAl Viro 		if (copy_to_user(argp, &range, sizeof(range)))
94055e67872STao Ma 			return -EFAULT;
94155e67872STao Ma 
94255e67872STao Ma 		return 0;
94355e67872STao Ma 	}
94453069d4eSTristan Ye 	case OCFS2_IOC_MOVE_EXT:
945f6a56903SAl Viro 		return ocfs2_ioctl_move_extents(filp, argp);
946ca4d147eSHerbert Poetzl 	default:
947ca4d147eSHerbert Poetzl 		return -ENOTTY;
948ca4d147eSHerbert Poetzl 	}
949ca4d147eSHerbert Poetzl }
950ca4d147eSHerbert Poetzl 
951586d232bSMark Fasheh #ifdef CONFIG_COMPAT
ocfs2_compat_ioctl(struct file * file,unsigned cmd,unsigned long arg)952586d232bSMark Fasheh long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
953586d232bSMark Fasheh {
95434e6c59aSTao Ma 	bool preserve;
95534e6c59aSTao Ma 	struct reflink_arguments args;
956496ad9aaSAl Viro 	struct inode *inode = file_inode(file);
957ddee5cdbSTristan Ye 	struct ocfs2_info info;
958f6a56903SAl Viro 	void __user *argp = (void __user *)arg;
95934e6c59aSTao Ma 
960586d232bSMark Fasheh 	switch (cmd) {
961b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP:
962b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP64:
963b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP:
964b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP64:
965d659072fSTao Ma 	case OCFS2_IOC_GROUP_EXTEND:
9667909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD:
9677909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD64:
968b2580103SMark Fasheh 		break;
96934e6c59aSTao Ma 	case OCFS2_IOC_REFLINK:
970f6a56903SAl Viro 		if (copy_from_user(&args, argp, sizeof(args)))
97134e6c59aSTao Ma 			return -EFAULT;
97234e6c59aSTao Ma 		preserve = (args.preserve != 0);
97334e6c59aSTao Ma 
97434e6c59aSTao Ma 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
97534e6c59aSTao Ma 					   compat_ptr(args.new_path), preserve);
976ddee5cdbSTristan Ye 	case OCFS2_IOC_INFO:
977f6a56903SAl Viro 		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
978ddee5cdbSTristan Ye 			return -EFAULT;
979ddee5cdbSTristan Ye 
980ddee5cdbSTristan Ye 		return ocfs2_info_handle(inode, &info, 1);
981314999dcSArnd Bergmann 	case FITRIM:
98253069d4eSTristan Ye 	case OCFS2_IOC_MOVE_EXT:
98353069d4eSTristan Ye 		break;
984586d232bSMark Fasheh 	default:
985586d232bSMark Fasheh 		return -ENOIOCTLCMD;
986586d232bSMark Fasheh 	}
987586d232bSMark Fasheh 
988c9ec1488SAndi Kleen 	return ocfs2_ioctl(file, cmd, arg);
989586d232bSMark Fasheh }
990586d232bSMark Fasheh #endif
991