xref: /linux/fs/ocfs2/ioctl.c (revision a561be7100cd610bd2e082f3211c1dfb45835817)
1ca4d147eSHerbert Poetzl /*
2ca4d147eSHerbert Poetzl  * linux/fs/ocfs2/ioctl.c
3ca4d147eSHerbert Poetzl  *
4ca4d147eSHerbert Poetzl  * Copyright (C) 2006 Herbert Poetzl
5ca4d147eSHerbert Poetzl  * adapted from Remy Card's ext2/ioctl.c
6ca4d147eSHerbert Poetzl  */
7ca4d147eSHerbert Poetzl 
8ca4d147eSHerbert Poetzl #include <linux/fs.h>
9ca4d147eSHerbert Poetzl #include <linux/mount.h>
1034e6c59aSTao Ma #include <linux/compat.h>
11ca4d147eSHerbert Poetzl 
12ca4d147eSHerbert Poetzl #include <cluster/masklog.h>
13ca4d147eSHerbert Poetzl 
14ca4d147eSHerbert Poetzl #include "ocfs2.h"
15ca4d147eSHerbert Poetzl #include "alloc.h"
16ca4d147eSHerbert Poetzl #include "dlmglue.h"
17b2580103SMark Fasheh #include "file.h"
18ca4d147eSHerbert Poetzl #include "inode.h"
19ca4d147eSHerbert Poetzl #include "journal.h"
20ca4d147eSHerbert Poetzl 
21ca4d147eSHerbert Poetzl #include "ocfs2_fs.h"
222d562518SAdrian Bunk #include "ioctl.h"
23d659072fSTao Ma #include "resize.h"
24bd50873dSTao Ma #include "refcounttree.h"
253e5db17dSTristan Ye #include "sysfile.h"
263e5db17dSTristan Ye #include "dir.h"
273e5db17dSTristan Ye #include "buffer_head_io.h"
28d24a10b9STristan Ye #include "suballoc.h"
2953069d4eSTristan Ye #include "move_extents.h"
302d562518SAdrian Bunk 
31ca4d147eSHerbert Poetzl #include <linux/ext2_fs.h>
32ca4d147eSHerbert Poetzl 
33ddee5cdbSTristan Ye #define o2info_from_user(a, b)	\
34ddee5cdbSTristan Ye 		copy_from_user(&(a), (b), sizeof(a))
35ddee5cdbSTristan Ye #define o2info_to_user(a, b)	\
36ddee5cdbSTristan Ye 		copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
37ddee5cdbSTristan Ye 
38ddee5cdbSTristan Ye /*
39ddee5cdbSTristan Ye  * This call is void because we are already reporting an error that may
40ddee5cdbSTristan Ye  * be -EFAULT.  The error will be returned from the ioctl(2) call.  It's
41ddee5cdbSTristan Ye  * just a best-effort to tell userspace that this request caused the error.
42ddee5cdbSTristan Ye  */
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 
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 
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 
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 
65ca4d147eSHerbert Poetzl static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
66ca4d147eSHerbert Poetzl {
67ca4d147eSHerbert Poetzl 	int status;
68ca4d147eSHerbert Poetzl 
69e63aecb6SMark Fasheh 	status = ocfs2_inode_lock(inode, NULL, 0);
70ca4d147eSHerbert Poetzl 	if (status < 0) {
71ca4d147eSHerbert Poetzl 		mlog_errno(status);
72ca4d147eSHerbert Poetzl 		return status;
73ca4d147eSHerbert Poetzl 	}
746e4b0d56SJan Kara 	ocfs2_get_inode_flags(OCFS2_I(inode));
75ca4d147eSHerbert Poetzl 	*flags = OCFS2_I(inode)->ip_attr;
76e63aecb6SMark Fasheh 	ocfs2_inode_unlock(inode, 0);
77ca4d147eSHerbert Poetzl 
78ca4d147eSHerbert Poetzl 	return status;
79ca4d147eSHerbert Poetzl }
80ca4d147eSHerbert Poetzl 
81ca4d147eSHerbert Poetzl static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
82ca4d147eSHerbert Poetzl 				unsigned mask)
83ca4d147eSHerbert Poetzl {
84ca4d147eSHerbert Poetzl 	struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode);
85ca4d147eSHerbert Poetzl 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
861fabe148SMark Fasheh 	handle_t *handle = NULL;
87ca4d147eSHerbert Poetzl 	struct buffer_head *bh = NULL;
88ca4d147eSHerbert Poetzl 	unsigned oldflags;
89ca4d147eSHerbert Poetzl 	int status;
90ca4d147eSHerbert Poetzl 
91ca4d147eSHerbert Poetzl 	mutex_lock(&inode->i_mutex);
92ca4d147eSHerbert Poetzl 
93e63aecb6SMark Fasheh 	status = ocfs2_inode_lock(inode, &bh, 1);
94ca4d147eSHerbert Poetzl 	if (status < 0) {
95ca4d147eSHerbert Poetzl 		mlog_errno(status);
96ca4d147eSHerbert Poetzl 		goto bail;
97ca4d147eSHerbert Poetzl 	}
98ca4d147eSHerbert Poetzl 
99ca4d147eSHerbert Poetzl 	status = -EACCES;
1002e149670SSerge E. Hallyn 	if (!inode_owner_or_capable(inode))
101ca4d147eSHerbert Poetzl 		goto bail_unlock;
102ca4d147eSHerbert Poetzl 
103ca4d147eSHerbert Poetzl 	if (!S_ISDIR(inode->i_mode))
104ca4d147eSHerbert Poetzl 		flags &= ~OCFS2_DIRSYNC_FL;
105ca4d147eSHerbert Poetzl 
10665eff9ccSMark Fasheh 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
107ca4d147eSHerbert Poetzl 	if (IS_ERR(handle)) {
108ca4d147eSHerbert Poetzl 		status = PTR_ERR(handle);
109ca4d147eSHerbert Poetzl 		mlog_errno(status);
110ca4d147eSHerbert Poetzl 		goto bail_unlock;
111ca4d147eSHerbert Poetzl 	}
112ca4d147eSHerbert Poetzl 
113ca4d147eSHerbert Poetzl 	oldflags = ocfs2_inode->ip_attr;
114ca4d147eSHerbert Poetzl 	flags = flags & mask;
115ca4d147eSHerbert Poetzl 	flags |= oldflags & ~mask;
116ca4d147eSHerbert Poetzl 
117ca4d147eSHerbert Poetzl 	/*
118ca4d147eSHerbert Poetzl 	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
119ca4d147eSHerbert Poetzl 	 * the relevant capability.
120ca4d147eSHerbert Poetzl 	 */
121ca4d147eSHerbert Poetzl 	status = -EPERM;
122ca4d147eSHerbert Poetzl 	if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
123ca4d147eSHerbert Poetzl 		(OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
124ca4d147eSHerbert Poetzl 		if (!capable(CAP_LINUX_IMMUTABLE))
125b8a0ae57SWengang Wang 			goto bail_commit;
126ca4d147eSHerbert Poetzl 	}
127ca4d147eSHerbert Poetzl 
128ca4d147eSHerbert Poetzl 	ocfs2_inode->ip_attr = flags;
129ca4d147eSHerbert Poetzl 	ocfs2_set_inode_flags(inode);
130ca4d147eSHerbert Poetzl 
131ca4d147eSHerbert Poetzl 	status = ocfs2_mark_inode_dirty(handle, inode, bh);
132ca4d147eSHerbert Poetzl 	if (status < 0)
133ca4d147eSHerbert Poetzl 		mlog_errno(status);
134ca4d147eSHerbert Poetzl 
135b8a0ae57SWengang Wang bail_commit:
13602dc1af4SMark Fasheh 	ocfs2_commit_trans(osb, handle);
137ca4d147eSHerbert Poetzl bail_unlock:
138e63aecb6SMark Fasheh 	ocfs2_inode_unlock(inode, 1);
139ca4d147eSHerbert Poetzl bail:
140ca4d147eSHerbert Poetzl 	mutex_unlock(&inode->i_mutex);
141ca4d147eSHerbert Poetzl 
142ca4d147eSHerbert Poetzl 	brelse(bh);
143ca4d147eSHerbert Poetzl 
144ca4d147eSHerbert Poetzl 	return status;
145ca4d147eSHerbert Poetzl }
146ca4d147eSHerbert Poetzl 
147ddee5cdbSTristan Ye int ocfs2_info_handle_blocksize(struct inode *inode,
148ddee5cdbSTristan Ye 				struct ocfs2_info_request __user *req)
149ddee5cdbSTristan Ye {
150ddee5cdbSTristan Ye 	int status = -EFAULT;
151ddee5cdbSTristan Ye 	struct ocfs2_info_blocksize oib;
152ddee5cdbSTristan Ye 
153ddee5cdbSTristan Ye 	if (o2info_from_user(oib, req))
154ddee5cdbSTristan Ye 		goto bail;
155ddee5cdbSTristan Ye 
156ddee5cdbSTristan Ye 	oib.ib_blocksize = inode->i_sb->s_blocksize;
1571936a267STristan Ye 
1588aa1fa36STristan Ye 	o2info_set_request_filled(&oib.ib_req);
159ddee5cdbSTristan Ye 
160ddee5cdbSTristan Ye 	if (o2info_to_user(oib, req))
161ddee5cdbSTristan Ye 		goto bail;
162ddee5cdbSTristan Ye 
163ddee5cdbSTristan Ye 	status = 0;
164ddee5cdbSTristan Ye bail:
165ddee5cdbSTristan Ye 	if (status)
1668aa1fa36STristan Ye 		o2info_set_request_error(&oib.ib_req, req);
167ddee5cdbSTristan Ye 
168ddee5cdbSTristan Ye 	return status;
169ddee5cdbSTristan Ye }
170ddee5cdbSTristan Ye 
171ddee5cdbSTristan Ye int ocfs2_info_handle_clustersize(struct inode *inode,
172ddee5cdbSTristan Ye 				  struct ocfs2_info_request __user *req)
173ddee5cdbSTristan Ye {
174ddee5cdbSTristan Ye 	int status = -EFAULT;
175ddee5cdbSTristan Ye 	struct ocfs2_info_clustersize oic;
176ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
177ddee5cdbSTristan Ye 
178ddee5cdbSTristan Ye 	if (o2info_from_user(oic, req))
179ddee5cdbSTristan Ye 		goto bail;
180ddee5cdbSTristan Ye 
181ddee5cdbSTristan Ye 	oic.ic_clustersize = osb->s_clustersize;
1821936a267STristan Ye 
1838aa1fa36STristan Ye 	o2info_set_request_filled(&oic.ic_req);
184ddee5cdbSTristan Ye 
185ddee5cdbSTristan Ye 	if (o2info_to_user(oic, req))
186ddee5cdbSTristan Ye 		goto bail;
187ddee5cdbSTristan Ye 
188ddee5cdbSTristan Ye 	status = 0;
189ddee5cdbSTristan Ye bail:
190ddee5cdbSTristan Ye 	if (status)
1918aa1fa36STristan Ye 		o2info_set_request_error(&oic.ic_req, req);
192ddee5cdbSTristan Ye 
193ddee5cdbSTristan Ye 	return status;
194ddee5cdbSTristan Ye }
195ddee5cdbSTristan Ye 
196ddee5cdbSTristan Ye int ocfs2_info_handle_maxslots(struct inode *inode,
197ddee5cdbSTristan Ye 			       struct ocfs2_info_request __user *req)
198ddee5cdbSTristan Ye {
199ddee5cdbSTristan Ye 	int status = -EFAULT;
200ddee5cdbSTristan Ye 	struct ocfs2_info_maxslots oim;
201ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
202ddee5cdbSTristan Ye 
203ddee5cdbSTristan Ye 	if (o2info_from_user(oim, req))
204ddee5cdbSTristan Ye 		goto bail;
205ddee5cdbSTristan Ye 
206ddee5cdbSTristan Ye 	oim.im_max_slots = osb->max_slots;
2071936a267STristan Ye 
2088aa1fa36STristan Ye 	o2info_set_request_filled(&oim.im_req);
209ddee5cdbSTristan Ye 
210ddee5cdbSTristan Ye 	if (o2info_to_user(oim, req))
211ddee5cdbSTristan Ye 		goto bail;
212ddee5cdbSTristan Ye 
213ddee5cdbSTristan Ye 	status = 0;
214ddee5cdbSTristan Ye bail:
215ddee5cdbSTristan Ye 	if (status)
2168aa1fa36STristan Ye 		o2info_set_request_error(&oim.im_req, req);
217ddee5cdbSTristan Ye 
218ddee5cdbSTristan Ye 	return status;
219ddee5cdbSTristan Ye }
220ddee5cdbSTristan Ye 
221ddee5cdbSTristan Ye int ocfs2_info_handle_label(struct inode *inode,
222ddee5cdbSTristan Ye 			    struct ocfs2_info_request __user *req)
223ddee5cdbSTristan Ye {
224ddee5cdbSTristan Ye 	int status = -EFAULT;
225ddee5cdbSTristan Ye 	struct ocfs2_info_label oil;
226ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
227ddee5cdbSTristan Ye 
228ddee5cdbSTristan Ye 	if (o2info_from_user(oil, req))
229ddee5cdbSTristan Ye 		goto bail;
230ddee5cdbSTristan Ye 
231ddee5cdbSTristan Ye 	memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
2321936a267STristan Ye 
2338aa1fa36STristan Ye 	o2info_set_request_filled(&oil.il_req);
234ddee5cdbSTristan Ye 
235ddee5cdbSTristan Ye 	if (o2info_to_user(oil, req))
236ddee5cdbSTristan Ye 		goto bail;
237ddee5cdbSTristan Ye 
238ddee5cdbSTristan Ye 	status = 0;
239ddee5cdbSTristan Ye bail:
240ddee5cdbSTristan Ye 	if (status)
2418aa1fa36STristan Ye 		o2info_set_request_error(&oil.il_req, req);
242ddee5cdbSTristan Ye 
243ddee5cdbSTristan Ye 	return status;
244ddee5cdbSTristan Ye }
245ddee5cdbSTristan Ye 
246ddee5cdbSTristan Ye int ocfs2_info_handle_uuid(struct inode *inode,
247ddee5cdbSTristan Ye 			   struct ocfs2_info_request __user *req)
248ddee5cdbSTristan Ye {
249ddee5cdbSTristan Ye 	int status = -EFAULT;
250ddee5cdbSTristan Ye 	struct ocfs2_info_uuid oiu;
251ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
252ddee5cdbSTristan Ye 
253ddee5cdbSTristan Ye 	if (o2info_from_user(oiu, req))
254ddee5cdbSTristan Ye 		goto bail;
255ddee5cdbSTristan Ye 
256ddee5cdbSTristan Ye 	memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1);
2571936a267STristan Ye 
2588aa1fa36STristan Ye 	o2info_set_request_filled(&oiu.iu_req);
259ddee5cdbSTristan Ye 
260ddee5cdbSTristan Ye 	if (o2info_to_user(oiu, req))
261ddee5cdbSTristan Ye 		goto bail;
262ddee5cdbSTristan Ye 
263ddee5cdbSTristan Ye 	status = 0;
264ddee5cdbSTristan Ye bail:
265ddee5cdbSTristan Ye 	if (status)
2668aa1fa36STristan Ye 		o2info_set_request_error(&oiu.iu_req, req);
267ddee5cdbSTristan Ye 
268ddee5cdbSTristan Ye 	return status;
269ddee5cdbSTristan Ye }
270ddee5cdbSTristan Ye 
271ddee5cdbSTristan Ye int ocfs2_info_handle_fs_features(struct inode *inode,
272ddee5cdbSTristan Ye 				  struct ocfs2_info_request __user *req)
273ddee5cdbSTristan Ye {
274ddee5cdbSTristan Ye 	int status = -EFAULT;
275ddee5cdbSTristan Ye 	struct ocfs2_info_fs_features oif;
276ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
277ddee5cdbSTristan Ye 
278ddee5cdbSTristan Ye 	if (o2info_from_user(oif, req))
279ddee5cdbSTristan Ye 		goto bail;
280ddee5cdbSTristan Ye 
281ddee5cdbSTristan Ye 	oif.if_compat_features = osb->s_feature_compat;
282ddee5cdbSTristan Ye 	oif.if_incompat_features = osb->s_feature_incompat;
283ddee5cdbSTristan Ye 	oif.if_ro_compat_features = osb->s_feature_ro_compat;
2841936a267STristan Ye 
2858aa1fa36STristan Ye 	o2info_set_request_filled(&oif.if_req);
286ddee5cdbSTristan Ye 
287ddee5cdbSTristan Ye 	if (o2info_to_user(oif, req))
288ddee5cdbSTristan Ye 		goto bail;
289ddee5cdbSTristan Ye 
290ddee5cdbSTristan Ye 	status = 0;
291ddee5cdbSTristan Ye bail:
292ddee5cdbSTristan Ye 	if (status)
2938aa1fa36STristan Ye 		o2info_set_request_error(&oif.if_req, req);
294ddee5cdbSTristan Ye 
295ddee5cdbSTristan Ye 	return status;
296ddee5cdbSTristan Ye }
297ddee5cdbSTristan Ye 
298ddee5cdbSTristan Ye int ocfs2_info_handle_journal_size(struct inode *inode,
299ddee5cdbSTristan Ye 				   struct ocfs2_info_request __user *req)
300ddee5cdbSTristan Ye {
301ddee5cdbSTristan Ye 	int status = -EFAULT;
302ddee5cdbSTristan Ye 	struct ocfs2_info_journal_size oij;
303ddee5cdbSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
304ddee5cdbSTristan Ye 
305ddee5cdbSTristan Ye 	if (o2info_from_user(oij, req))
306ddee5cdbSTristan Ye 		goto bail;
307ddee5cdbSTristan Ye 
308ddee5cdbSTristan Ye 	oij.ij_journal_size = osb->journal->j_inode->i_size;
309ddee5cdbSTristan Ye 
3108aa1fa36STristan Ye 	o2info_set_request_filled(&oij.ij_req);
311ddee5cdbSTristan Ye 
312ddee5cdbSTristan Ye 	if (o2info_to_user(oij, req))
313ddee5cdbSTristan Ye 		goto bail;
314ddee5cdbSTristan Ye 
315ddee5cdbSTristan Ye 	status = 0;
316ddee5cdbSTristan Ye bail:
317ddee5cdbSTristan Ye 	if (status)
3188aa1fa36STristan Ye 		o2info_set_request_error(&oij.ij_req, req);
319ddee5cdbSTristan Ye 
320ddee5cdbSTristan Ye 	return status;
321ddee5cdbSTristan Ye }
322ddee5cdbSTristan Ye 
3233e5db17dSTristan Ye int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
3243e5db17dSTristan Ye 				struct inode *inode_alloc, u64 blkno,
3253e5db17dSTristan Ye 				struct ocfs2_info_freeinode *fi, u32 slot)
3263e5db17dSTristan Ye {
3273e5db17dSTristan Ye 	int status = 0, unlock = 0;
3283e5db17dSTristan Ye 
3293e5db17dSTristan Ye 	struct buffer_head *bh = NULL;
3303e5db17dSTristan Ye 	struct ocfs2_dinode *dinode_alloc = NULL;
3313e5db17dSTristan Ye 
3323e5db17dSTristan Ye 	if (inode_alloc)
3333e5db17dSTristan Ye 		mutex_lock(&inode_alloc->i_mutex);
3343e5db17dSTristan Ye 
3353e5db17dSTristan Ye 	if (o2info_coherent(&fi->ifi_req)) {
3363e5db17dSTristan Ye 		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
3373e5db17dSTristan Ye 		if (status < 0) {
3383e5db17dSTristan Ye 			mlog_errno(status);
3393e5db17dSTristan Ye 			goto bail;
3403e5db17dSTristan Ye 		}
3413e5db17dSTristan Ye 		unlock = 1;
3423e5db17dSTristan Ye 	} else {
3433e5db17dSTristan Ye 		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
3443e5db17dSTristan Ye 		if (status < 0) {
3453e5db17dSTristan Ye 			mlog_errno(status);
3463e5db17dSTristan Ye 			goto bail;
3473e5db17dSTristan Ye 		}
3483e5db17dSTristan Ye 	}
3493e5db17dSTristan Ye 
3503e5db17dSTristan Ye 	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
3513e5db17dSTristan Ye 
3523e5db17dSTristan Ye 	fi->ifi_stat[slot].lfi_total =
3533e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
3543e5db17dSTristan Ye 	fi->ifi_stat[slot].lfi_free =
3553e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
3563e5db17dSTristan Ye 		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
3573e5db17dSTristan Ye 
3583e5db17dSTristan Ye bail:
3593e5db17dSTristan Ye 	if (unlock)
3603e5db17dSTristan Ye 		ocfs2_inode_unlock(inode_alloc, 0);
3613e5db17dSTristan Ye 
3623e5db17dSTristan Ye 	if (inode_alloc)
3633e5db17dSTristan Ye 		mutex_unlock(&inode_alloc->i_mutex);
3643e5db17dSTristan Ye 
3653e5db17dSTristan Ye 	brelse(bh);
3663e5db17dSTristan Ye 
3673e5db17dSTristan Ye 	return status;
3683e5db17dSTristan Ye }
3693e5db17dSTristan Ye 
3703e5db17dSTristan Ye int ocfs2_info_handle_freeinode(struct inode *inode,
3713e5db17dSTristan Ye 				struct ocfs2_info_request __user *req)
3723e5db17dSTristan Ye {
3733e5db17dSTristan Ye 	u32 i;
3743e5db17dSTristan Ye 	u64 blkno = -1;
3753e5db17dSTristan Ye 	char namebuf[40];
3763e5db17dSTristan Ye 	int status = -EFAULT, type = INODE_ALLOC_SYSTEM_INODE;
3773e5db17dSTristan Ye 	struct ocfs2_info_freeinode *oifi = NULL;
3783e5db17dSTristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
3793e5db17dSTristan Ye 	struct inode *inode_alloc = NULL;
3803e5db17dSTristan Ye 
3813e5db17dSTristan Ye 	oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL);
3823e5db17dSTristan Ye 	if (!oifi) {
3833e5db17dSTristan Ye 		status = -ENOMEM;
3843e5db17dSTristan Ye 		mlog_errno(status);
38587f0d5c8SDan Carpenter 		goto out_err;
3863e5db17dSTristan Ye 	}
3873e5db17dSTristan Ye 
3883e5db17dSTristan Ye 	if (o2info_from_user(*oifi, req))
3893e5db17dSTristan Ye 		goto bail;
3903e5db17dSTristan Ye 
3913e5db17dSTristan Ye 	oifi->ifi_slotnum = osb->max_slots;
3923e5db17dSTristan Ye 
3933e5db17dSTristan Ye 	for (i = 0; i < oifi->ifi_slotnum; i++) {
3943e5db17dSTristan Ye 		if (o2info_coherent(&oifi->ifi_req)) {
3953e5db17dSTristan Ye 			inode_alloc = ocfs2_get_system_file_inode(osb, type, i);
3963e5db17dSTristan Ye 			if (!inode_alloc) {
3973e5db17dSTristan Ye 				mlog(ML_ERROR, "unable to get alloc inode in "
3983e5db17dSTristan Ye 				    "slot %u\n", i);
3993e5db17dSTristan Ye 				status = -EIO;
4003e5db17dSTristan Ye 				goto bail;
4013e5db17dSTristan Ye 			}
4023e5db17dSTristan Ye 		} else {
4033e5db17dSTristan Ye 			ocfs2_sprintf_system_inode_name(namebuf,
4043e5db17dSTristan Ye 							sizeof(namebuf),
4053e5db17dSTristan Ye 							type, i);
4063e5db17dSTristan Ye 			status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
4073e5db17dSTristan Ye 							    namebuf,
4083e5db17dSTristan Ye 							    strlen(namebuf),
4093e5db17dSTristan Ye 							    &blkno);
4103e5db17dSTristan Ye 			if (status < 0) {
4113e5db17dSTristan Ye 				status = -ENOENT;
4123e5db17dSTristan Ye 				goto bail;
4133e5db17dSTristan Ye 			}
4143e5db17dSTristan Ye 		}
4153e5db17dSTristan Ye 
4163e5db17dSTristan Ye 		status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i);
4173e5db17dSTristan Ye 		if (status < 0)
4183e5db17dSTristan Ye 			goto bail;
4193e5db17dSTristan Ye 
4203e5db17dSTristan Ye 		iput(inode_alloc);
4213e5db17dSTristan Ye 		inode_alloc = NULL;
4223e5db17dSTristan Ye 	}
4233e5db17dSTristan Ye 
4243e5db17dSTristan Ye 	o2info_set_request_filled(&oifi->ifi_req);
4253e5db17dSTristan Ye 
4263e5db17dSTristan Ye 	if (o2info_to_user(*oifi, req))
4273e5db17dSTristan Ye 		goto bail;
4283e5db17dSTristan Ye 
4293e5db17dSTristan Ye 	status = 0;
4303e5db17dSTristan Ye bail:
4313e5db17dSTristan Ye 	if (status)
4323e5db17dSTristan Ye 		o2info_set_request_error(&oifi->ifi_req, req);
4333e5db17dSTristan Ye 
4343e5db17dSTristan Ye 	kfree(oifi);
43587f0d5c8SDan Carpenter out_err:
4363e5db17dSTristan Ye 	return status;
4373e5db17dSTristan Ye }
4383e5db17dSTristan Ye 
439d24a10b9STristan Ye static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist,
440d24a10b9STristan Ye 				   unsigned int chunksize)
441d24a10b9STristan Ye {
442d24a10b9STristan Ye 	int index;
443d24a10b9STristan Ye 
444d24a10b9STristan Ye 	index = __ilog2_u32(chunksize);
445d24a10b9STristan Ye 	if (index >= OCFS2_INFO_MAX_HIST)
446d24a10b9STristan Ye 		index = OCFS2_INFO_MAX_HIST - 1;
447d24a10b9STristan Ye 
448d24a10b9STristan Ye 	hist->fc_chunks[index]++;
449d24a10b9STristan Ye 	hist->fc_clusters[index] += chunksize;
450d24a10b9STristan Ye }
451d24a10b9STristan Ye 
452d24a10b9STristan Ye static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats,
453d24a10b9STristan Ye 			       unsigned int chunksize)
454d24a10b9STristan Ye {
455d24a10b9STristan Ye 	if (chunksize > stats->ffs_max)
456d24a10b9STristan Ye 		stats->ffs_max = chunksize;
457d24a10b9STristan Ye 
458d24a10b9STristan Ye 	if (chunksize < stats->ffs_min)
459d24a10b9STristan Ye 		stats->ffs_min = chunksize;
460d24a10b9STristan Ye 
461d24a10b9STristan Ye 	stats->ffs_avg += chunksize;
462d24a10b9STristan Ye 	stats->ffs_free_chunks_real++;
463d24a10b9STristan Ye }
464d24a10b9STristan Ye 
465d24a10b9STristan Ye void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
466d24a10b9STristan Ye 			   unsigned int chunksize)
467d24a10b9STristan Ye {
468d24a10b9STristan Ye 	o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize);
469d24a10b9STristan Ye 	o2ffg_update_stats(&(ffg->iff_ffs), chunksize);
470d24a10b9STristan Ye }
471d24a10b9STristan Ye 
472d24a10b9STristan Ye int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb,
473d24a10b9STristan Ye 				   struct inode *gb_inode,
474d24a10b9STristan Ye 				   struct ocfs2_dinode *gb_dinode,
475d24a10b9STristan Ye 				   struct ocfs2_chain_rec *rec,
476d24a10b9STristan Ye 				   struct ocfs2_info_freefrag *ffg,
477d24a10b9STristan Ye 				   u32 chunks_in_group)
478d24a10b9STristan Ye {
479d24a10b9STristan Ye 	int status = 0, used;
480d24a10b9STristan Ye 	u64 blkno;
481d24a10b9STristan Ye 
482d24a10b9STristan Ye 	struct buffer_head *bh = NULL;
483d24a10b9STristan Ye 	struct ocfs2_group_desc *bg = NULL;
484d24a10b9STristan Ye 
485d24a10b9STristan Ye 	unsigned int max_bits, num_clusters;
486d24a10b9STristan Ye 	unsigned int offset = 0, cluster, chunk;
487d24a10b9STristan Ye 	unsigned int chunk_free, last_chunksize = 0;
488d24a10b9STristan Ye 
489d24a10b9STristan Ye 	if (!le32_to_cpu(rec->c_free))
490d24a10b9STristan Ye 		goto bail;
491d24a10b9STristan Ye 
492d24a10b9STristan Ye 	do {
493d24a10b9STristan Ye 		if (!bg)
494d24a10b9STristan Ye 			blkno = le64_to_cpu(rec->c_blkno);
495d24a10b9STristan Ye 		else
496d24a10b9STristan Ye 			blkno = le64_to_cpu(bg->bg_next_group);
497d24a10b9STristan Ye 
498d24a10b9STristan Ye 		if (bh) {
499d24a10b9STristan Ye 			brelse(bh);
500d24a10b9STristan Ye 			bh = NULL;
501d24a10b9STristan Ye 		}
502d24a10b9STristan Ye 
503d24a10b9STristan Ye 		if (o2info_coherent(&ffg->iff_req))
504d24a10b9STristan Ye 			status = ocfs2_read_group_descriptor(gb_inode,
505d24a10b9STristan Ye 							     gb_dinode,
506d24a10b9STristan Ye 							     blkno, &bh);
507d24a10b9STristan Ye 		else
508d24a10b9STristan Ye 			status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
509d24a10b9STristan Ye 
510d24a10b9STristan Ye 		if (status < 0) {
511d24a10b9STristan Ye 			mlog(ML_ERROR, "Can't read the group descriptor # "
512d24a10b9STristan Ye 			     "%llu from device.", (unsigned long long)blkno);
513d24a10b9STristan Ye 			status = -EIO;
514d24a10b9STristan Ye 			goto bail;
515d24a10b9STristan Ye 		}
516d24a10b9STristan Ye 
517d24a10b9STristan Ye 		bg = (struct ocfs2_group_desc *)bh->b_data;
518d24a10b9STristan Ye 
519d24a10b9STristan Ye 		if (!le16_to_cpu(bg->bg_free_bits_count))
520d24a10b9STristan Ye 			continue;
521d24a10b9STristan Ye 
522d24a10b9STristan Ye 		max_bits = le16_to_cpu(bg->bg_bits);
523d24a10b9STristan Ye 		offset = 0;
524d24a10b9STristan Ye 
525d24a10b9STristan Ye 		for (chunk = 0; chunk < chunks_in_group; chunk++) {
526d24a10b9STristan Ye 			/*
527d24a10b9STristan Ye 			 * last chunk may be not an entire one.
528d24a10b9STristan Ye 			 */
529d24a10b9STristan Ye 			if ((offset + ffg->iff_chunksize) > max_bits)
530d24a10b9STristan Ye 				num_clusters = max_bits - offset;
531d24a10b9STristan Ye 			else
532d24a10b9STristan Ye 				num_clusters = ffg->iff_chunksize;
533d24a10b9STristan Ye 
534d24a10b9STristan Ye 			chunk_free = 0;
535d24a10b9STristan Ye 			for (cluster = 0; cluster < num_clusters; cluster++) {
536d24a10b9STristan Ye 				used = ocfs2_test_bit(offset,
537d24a10b9STristan Ye 						(unsigned long *)bg->bg_bitmap);
538d24a10b9STristan Ye 				/*
539d24a10b9STristan Ye 				 * - chunk_free counts free clusters in #N chunk.
540d24a10b9STristan Ye 				 * - last_chunksize records the size(in) clusters
541d24a10b9STristan Ye 				 *   for the last real free chunk being counted.
542d24a10b9STristan Ye 				 */
543d24a10b9STristan Ye 				if (!used) {
544d24a10b9STristan Ye 					last_chunksize++;
545d24a10b9STristan Ye 					chunk_free++;
546d24a10b9STristan Ye 				}
547d24a10b9STristan Ye 
548d24a10b9STristan Ye 				if (used && last_chunksize) {
549d24a10b9STristan Ye 					ocfs2_info_update_ffg(ffg,
550d24a10b9STristan Ye 							      last_chunksize);
551d24a10b9STristan Ye 					last_chunksize = 0;
552d24a10b9STristan Ye 				}
553d24a10b9STristan Ye 
554d24a10b9STristan Ye 				offset++;
555d24a10b9STristan Ye 			}
556d24a10b9STristan Ye 
557d24a10b9STristan Ye 			if (chunk_free == ffg->iff_chunksize)
558d24a10b9STristan Ye 				ffg->iff_ffs.ffs_free_chunks++;
559d24a10b9STristan Ye 		}
560d24a10b9STristan Ye 
561d24a10b9STristan Ye 		/*
562d24a10b9STristan Ye 		 * need to update the info for last free chunk.
563d24a10b9STristan Ye 		 */
564d24a10b9STristan Ye 		if (last_chunksize)
565d24a10b9STristan Ye 			ocfs2_info_update_ffg(ffg, last_chunksize);
566d24a10b9STristan Ye 
567d24a10b9STristan Ye 	} while (le64_to_cpu(bg->bg_next_group));
568d24a10b9STristan Ye 
569d24a10b9STristan Ye bail:
570d24a10b9STristan Ye 	brelse(bh);
571d24a10b9STristan Ye 
572d24a10b9STristan Ye 	return status;
573d24a10b9STristan Ye }
574d24a10b9STristan Ye 
575d24a10b9STristan Ye int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb,
576d24a10b9STristan Ye 				    struct inode *gb_inode, u64 blkno,
577d24a10b9STristan Ye 				    struct ocfs2_info_freefrag *ffg)
578d24a10b9STristan Ye {
579d24a10b9STristan Ye 	u32 chunks_in_group;
580d24a10b9STristan Ye 	int status = 0, unlock = 0, i;
581d24a10b9STristan Ye 
582d24a10b9STristan Ye 	struct buffer_head *bh = NULL;
583d24a10b9STristan Ye 	struct ocfs2_chain_list *cl = NULL;
584d24a10b9STristan Ye 	struct ocfs2_chain_rec *rec = NULL;
585d24a10b9STristan Ye 	struct ocfs2_dinode *gb_dinode = NULL;
586d24a10b9STristan Ye 
587d24a10b9STristan Ye 	if (gb_inode)
588d24a10b9STristan Ye 		mutex_lock(&gb_inode->i_mutex);
589d24a10b9STristan Ye 
590d24a10b9STristan Ye 	if (o2info_coherent(&ffg->iff_req)) {
591d24a10b9STristan Ye 		status = ocfs2_inode_lock(gb_inode, &bh, 0);
592d24a10b9STristan Ye 		if (status < 0) {
593d24a10b9STristan Ye 			mlog_errno(status);
594d24a10b9STristan Ye 			goto bail;
595d24a10b9STristan Ye 		}
596d24a10b9STristan Ye 		unlock = 1;
597d24a10b9STristan Ye 	} else {
598d24a10b9STristan Ye 		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
599d24a10b9STristan Ye 		if (status < 0) {
600d24a10b9STristan Ye 			mlog_errno(status);
601d24a10b9STristan Ye 			goto bail;
602d24a10b9STristan Ye 		}
603d24a10b9STristan Ye 	}
604d24a10b9STristan Ye 
605d24a10b9STristan Ye 	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
606d24a10b9STristan Ye 	cl = &(gb_dinode->id2.i_chain);
607d24a10b9STristan Ye 
608d24a10b9STristan Ye 	/*
609d24a10b9STristan Ye 	 * Chunksize(in) clusters from userspace should be
610d24a10b9STristan Ye 	 * less than clusters in a group.
611d24a10b9STristan Ye 	 */
612d24a10b9STristan Ye 	if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) {
613d24a10b9STristan Ye 		status = -EINVAL;
614d24a10b9STristan Ye 		goto bail;
615d24a10b9STristan Ye 	}
616d24a10b9STristan Ye 
617d24a10b9STristan Ye 	memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats));
618d24a10b9STristan Ye 
619d24a10b9STristan Ye 	ffg->iff_ffs.ffs_min = ~0U;
620d24a10b9STristan Ye 	ffg->iff_ffs.ffs_clusters =
621d24a10b9STristan Ye 			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
622d24a10b9STristan Ye 	ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters -
623d24a10b9STristan Ye 			le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
624d24a10b9STristan Ye 
625d24a10b9STristan Ye 	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1;
626d24a10b9STristan Ye 
627d24a10b9STristan Ye 	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
628d24a10b9STristan Ye 		rec = &(cl->cl_recs[i]);
629d24a10b9STristan Ye 		status = ocfs2_info_freefrag_scan_chain(osb, gb_inode,
630d24a10b9STristan Ye 							gb_dinode,
631d24a10b9STristan Ye 							rec, ffg,
632d24a10b9STristan Ye 							chunks_in_group);
633d24a10b9STristan Ye 		if (status)
634d24a10b9STristan Ye 			goto bail;
635d24a10b9STristan Ye 	}
636d24a10b9STristan Ye 
637d24a10b9STristan Ye 	if (ffg->iff_ffs.ffs_free_chunks_real)
638d24a10b9STristan Ye 		ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg /
639d24a10b9STristan Ye 					ffg->iff_ffs.ffs_free_chunks_real);
640d24a10b9STristan Ye bail:
641d24a10b9STristan Ye 	if (unlock)
642d24a10b9STristan Ye 		ocfs2_inode_unlock(gb_inode, 0);
643d24a10b9STristan Ye 
644d24a10b9STristan Ye 	if (gb_inode)
645d24a10b9STristan Ye 		mutex_unlock(&gb_inode->i_mutex);
646d24a10b9STristan Ye 
647d24a10b9STristan Ye 	if (gb_inode)
648d24a10b9STristan Ye 		iput(gb_inode);
649d24a10b9STristan Ye 
650d24a10b9STristan Ye 	brelse(bh);
651d24a10b9STristan Ye 
652d24a10b9STristan Ye 	return status;
653d24a10b9STristan Ye }
654d24a10b9STristan Ye 
655d24a10b9STristan Ye int ocfs2_info_handle_freefrag(struct inode *inode,
656d24a10b9STristan Ye 			       struct ocfs2_info_request __user *req)
657d24a10b9STristan Ye {
658d24a10b9STristan Ye 	u64 blkno = -1;
659d24a10b9STristan Ye 	char namebuf[40];
660d24a10b9STristan Ye 	int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE;
661d24a10b9STristan Ye 
662d24a10b9STristan Ye 	struct ocfs2_info_freefrag *oiff;
663d24a10b9STristan Ye 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
664d24a10b9STristan Ye 	struct inode *gb_inode = NULL;
665d24a10b9STristan Ye 
666d24a10b9STristan Ye 	oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL);
667d24a10b9STristan Ye 	if (!oiff) {
668d24a10b9STristan Ye 		status = -ENOMEM;
669d24a10b9STristan Ye 		mlog_errno(status);
67087f0d5c8SDan Carpenter 		goto out_err;
671d24a10b9STristan Ye 	}
672d24a10b9STristan Ye 
673d24a10b9STristan Ye 	if (o2info_from_user(*oiff, req))
674d24a10b9STristan Ye 		goto bail;
675d24a10b9STristan Ye 	/*
676d24a10b9STristan Ye 	 * chunksize from userspace should be power of 2.
677d24a10b9STristan Ye 	 */
678d24a10b9STristan Ye 	if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) ||
679d24a10b9STristan Ye 	    (!oiff->iff_chunksize)) {
680d24a10b9STristan Ye 		status = -EINVAL;
681d24a10b9STristan Ye 		goto bail;
682d24a10b9STristan Ye 	}
683d24a10b9STristan Ye 
684d24a10b9STristan Ye 	if (o2info_coherent(&oiff->iff_req)) {
685d24a10b9STristan Ye 		gb_inode = ocfs2_get_system_file_inode(osb, type,
686d24a10b9STristan Ye 						       OCFS2_INVALID_SLOT);
687d24a10b9STristan Ye 		if (!gb_inode) {
688d24a10b9STristan Ye 			mlog(ML_ERROR, "unable to get global_bitmap inode\n");
689d24a10b9STristan Ye 			status = -EIO;
690d24a10b9STristan Ye 			goto bail;
691d24a10b9STristan Ye 		}
692d24a10b9STristan Ye 	} else {
693d24a10b9STristan Ye 		ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type,
694d24a10b9STristan Ye 						OCFS2_INVALID_SLOT);
695d24a10b9STristan Ye 		status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
696d24a10b9STristan Ye 						    namebuf,
697d24a10b9STristan Ye 						    strlen(namebuf),
698d24a10b9STristan Ye 						    &blkno);
699d24a10b9STristan Ye 		if (status < 0) {
700d24a10b9STristan Ye 			status = -ENOENT;
701d24a10b9STristan Ye 			goto bail;
702d24a10b9STristan Ye 		}
703d24a10b9STristan Ye 	}
704d24a10b9STristan Ye 
705d24a10b9STristan Ye 	status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff);
706d24a10b9STristan Ye 	if (status < 0)
707d24a10b9STristan Ye 		goto bail;
708d24a10b9STristan Ye 
709d24a10b9STristan Ye 	o2info_set_request_filled(&oiff->iff_req);
710d24a10b9STristan Ye 
711d24a10b9STristan Ye 	if (o2info_to_user(*oiff, req))
712d24a10b9STristan Ye 		goto bail;
713d24a10b9STristan Ye 
714d24a10b9STristan Ye 	status = 0;
715d24a10b9STristan Ye bail:
716d24a10b9STristan Ye 	if (status)
717d24a10b9STristan Ye 		o2info_set_request_error(&oiff->iff_req, req);
718d24a10b9STristan Ye 
719d24a10b9STristan Ye 	kfree(oiff);
72087f0d5c8SDan Carpenter out_err:
721ddee5cdbSTristan Ye 	return status;
722ddee5cdbSTristan Ye }
723ddee5cdbSTristan Ye 
724ddee5cdbSTristan Ye int ocfs2_info_handle_unknown(struct inode *inode,
725ddee5cdbSTristan Ye 			      struct ocfs2_info_request __user *req)
726ddee5cdbSTristan Ye {
727ddee5cdbSTristan Ye 	int status = -EFAULT;
728ddee5cdbSTristan Ye 	struct ocfs2_info_request oir;
729ddee5cdbSTristan Ye 
730ddee5cdbSTristan Ye 	if (o2info_from_user(oir, req))
731ddee5cdbSTristan Ye 		goto bail;
732ddee5cdbSTristan Ye 
7338aa1fa36STristan Ye 	o2info_clear_request_filled(&oir);
734ddee5cdbSTristan Ye 
735ddee5cdbSTristan Ye 	if (o2info_to_user(oir, req))
736ddee5cdbSTristan Ye 		goto bail;
737ddee5cdbSTristan Ye 
738ddee5cdbSTristan Ye 	status = 0;
739ddee5cdbSTristan Ye bail:
740ddee5cdbSTristan Ye 	if (status)
7418aa1fa36STristan Ye 		o2info_set_request_error(&oir, req);
742ddee5cdbSTristan Ye 
743ddee5cdbSTristan Ye 	return status;
744ddee5cdbSTristan Ye }
745ddee5cdbSTristan Ye 
746ddee5cdbSTristan Ye /*
747ddee5cdbSTristan Ye  * Validate and distinguish OCFS2_IOC_INFO requests.
748ddee5cdbSTristan Ye  *
749ddee5cdbSTristan Ye  * - validate the magic number.
750ddee5cdbSTristan Ye  * - distinguish different requests.
751ddee5cdbSTristan Ye  * - validate size of different requests.
752ddee5cdbSTristan Ye  */
753ddee5cdbSTristan Ye int ocfs2_info_handle_request(struct inode *inode,
754ddee5cdbSTristan Ye 			      struct ocfs2_info_request __user *req)
755ddee5cdbSTristan Ye {
756ddee5cdbSTristan Ye 	int status = -EFAULT;
757ddee5cdbSTristan Ye 	struct ocfs2_info_request oir;
758ddee5cdbSTristan Ye 
759ddee5cdbSTristan Ye 	if (o2info_from_user(oir, req))
760ddee5cdbSTristan Ye 		goto bail;
761ddee5cdbSTristan Ye 
762ddee5cdbSTristan Ye 	status = -EINVAL;
763ddee5cdbSTristan Ye 	if (oir.ir_magic != OCFS2_INFO_MAGIC)
764ddee5cdbSTristan Ye 		goto bail;
765ddee5cdbSTristan Ye 
766ddee5cdbSTristan Ye 	switch (oir.ir_code) {
767ddee5cdbSTristan Ye 	case OCFS2_INFO_BLOCKSIZE:
768ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_blocksize))
769ddee5cdbSTristan Ye 			status = ocfs2_info_handle_blocksize(inode, req);
770ddee5cdbSTristan Ye 		break;
771ddee5cdbSTristan Ye 	case OCFS2_INFO_CLUSTERSIZE:
772ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_clustersize))
773ddee5cdbSTristan Ye 			status = ocfs2_info_handle_clustersize(inode, req);
774ddee5cdbSTristan Ye 		break;
775ddee5cdbSTristan Ye 	case OCFS2_INFO_MAXSLOTS:
776ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_maxslots))
777ddee5cdbSTristan Ye 			status = ocfs2_info_handle_maxslots(inode, req);
778ddee5cdbSTristan Ye 		break;
779ddee5cdbSTristan Ye 	case OCFS2_INFO_LABEL:
780ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_label))
781ddee5cdbSTristan Ye 			status = ocfs2_info_handle_label(inode, req);
782ddee5cdbSTristan Ye 		break;
783ddee5cdbSTristan Ye 	case OCFS2_INFO_UUID:
784ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_uuid))
785ddee5cdbSTristan Ye 			status = ocfs2_info_handle_uuid(inode, req);
786ddee5cdbSTristan Ye 		break;
787ddee5cdbSTristan Ye 	case OCFS2_INFO_FS_FEATURES:
788ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_fs_features))
789ddee5cdbSTristan Ye 			status = ocfs2_info_handle_fs_features(inode, req);
790ddee5cdbSTristan Ye 		break;
791ddee5cdbSTristan Ye 	case OCFS2_INFO_JOURNAL_SIZE:
792ddee5cdbSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_journal_size))
793ddee5cdbSTristan Ye 			status = ocfs2_info_handle_journal_size(inode, req);
794ddee5cdbSTristan Ye 		break;
7953e5db17dSTristan Ye 	case OCFS2_INFO_FREEINODE:
7963e5db17dSTristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_freeinode))
7973e5db17dSTristan Ye 			status = ocfs2_info_handle_freeinode(inode, req);
7983e5db17dSTristan Ye 		break;
799d24a10b9STristan Ye 	case OCFS2_INFO_FREEFRAG:
800d24a10b9STristan Ye 		if (oir.ir_size == sizeof(struct ocfs2_info_freefrag))
801d24a10b9STristan Ye 			status = ocfs2_info_handle_freefrag(inode, req);
802d24a10b9STristan Ye 		break;
803ddee5cdbSTristan Ye 	default:
804ddee5cdbSTristan Ye 		status = ocfs2_info_handle_unknown(inode, req);
805ddee5cdbSTristan Ye 		break;
806ddee5cdbSTristan Ye 	}
807ddee5cdbSTristan Ye 
808ddee5cdbSTristan Ye bail:
809ddee5cdbSTristan Ye 	return status;
810ddee5cdbSTristan Ye }
811ddee5cdbSTristan Ye 
812ddee5cdbSTristan Ye int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx,
813ddee5cdbSTristan Ye 			  u64 *req_addr, int compat_flag)
814ddee5cdbSTristan Ye {
815ddee5cdbSTristan Ye 	int status = -EFAULT;
816ddee5cdbSTristan Ye 	u64 __user *bp = NULL;
817ddee5cdbSTristan Ye 
818ddee5cdbSTristan Ye 	if (compat_flag) {
819ddee5cdbSTristan Ye #ifdef CONFIG_COMPAT
820ddee5cdbSTristan Ye 		/*
821ddee5cdbSTristan Ye 		 * pointer bp stores the base address of a pointers array,
822ddee5cdbSTristan Ye 		 * which collects all addresses of separate request.
823ddee5cdbSTristan Ye 		 */
824ddee5cdbSTristan Ye 		bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests);
825ddee5cdbSTristan Ye #else
826ddee5cdbSTristan Ye 		BUG();
827ddee5cdbSTristan Ye #endif
828ddee5cdbSTristan Ye 	} else
829ddee5cdbSTristan Ye 		bp = (u64 __user *)(unsigned long)(info->oi_requests);
830ddee5cdbSTristan Ye 
831ddee5cdbSTristan Ye 	if (o2info_from_user(*req_addr, bp + idx))
832ddee5cdbSTristan Ye 		goto bail;
833ddee5cdbSTristan Ye 
834ddee5cdbSTristan Ye 	status = 0;
835ddee5cdbSTristan Ye bail:
836ddee5cdbSTristan Ye 	return status;
837ddee5cdbSTristan Ye }
838ddee5cdbSTristan Ye 
839ddee5cdbSTristan Ye /*
840ddee5cdbSTristan Ye  * OCFS2_IOC_INFO handles an array of requests passed from userspace.
841ddee5cdbSTristan Ye  *
842ddee5cdbSTristan Ye  * ocfs2_info_handle() recevies a large info aggregation, grab and
843ddee5cdbSTristan Ye  * validate the request count from header, then break it into small
844ddee5cdbSTristan Ye  * pieces, later specific handlers can handle them one by one.
845ddee5cdbSTristan Ye  *
846ddee5cdbSTristan Ye  * Idea here is to make each separate request small enough to ensure
847ddee5cdbSTristan Ye  * a better backward&forward compatibility, since a small piece of
848ddee5cdbSTristan Ye  * request will be less likely to be broken if disk layout get changed.
849ddee5cdbSTristan Ye  */
850ddee5cdbSTristan Ye int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
851ddee5cdbSTristan Ye 		      int compat_flag)
852ddee5cdbSTristan Ye {
853ddee5cdbSTristan Ye 	int i, status = 0;
854ddee5cdbSTristan Ye 	u64 req_addr;
855ddee5cdbSTristan Ye 	struct ocfs2_info_request __user *reqp;
856ddee5cdbSTristan Ye 
857ddee5cdbSTristan Ye 	if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) ||
858ddee5cdbSTristan Ye 	    (!info->oi_requests)) {
859ddee5cdbSTristan Ye 		status = -EINVAL;
860ddee5cdbSTristan Ye 		goto bail;
861ddee5cdbSTristan Ye 	}
862ddee5cdbSTristan Ye 
863ddee5cdbSTristan Ye 	for (i = 0; i < info->oi_count; i++) {
864ddee5cdbSTristan Ye 
865ddee5cdbSTristan Ye 		status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag);
866ddee5cdbSTristan Ye 		if (status)
867ddee5cdbSTristan Ye 			break;
868ddee5cdbSTristan Ye 
869ddee5cdbSTristan Ye 		reqp = (struct ocfs2_info_request *)(unsigned long)req_addr;
870ddee5cdbSTristan Ye 		if (!reqp) {
871ddee5cdbSTristan Ye 			status = -EINVAL;
872ddee5cdbSTristan Ye 			goto bail;
873ddee5cdbSTristan Ye 		}
874ddee5cdbSTristan Ye 
875ddee5cdbSTristan Ye 		status = ocfs2_info_handle_request(inode, reqp);
876ddee5cdbSTristan Ye 		if (status)
877ddee5cdbSTristan Ye 			break;
878ddee5cdbSTristan Ye 	}
879ddee5cdbSTristan Ye 
880ddee5cdbSTristan Ye bail:
881ddee5cdbSTristan Ye 	return status;
882ddee5cdbSTristan Ye }
883ddee5cdbSTristan Ye 
884c9ec1488SAndi Kleen long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
885ca4d147eSHerbert Poetzl {
886c9ec1488SAndi Kleen 	struct inode *inode = filp->f_path.dentry->d_inode;
887ca4d147eSHerbert Poetzl 	unsigned int flags;
888d659072fSTao Ma 	int new_clusters;
889ca4d147eSHerbert Poetzl 	int status;
890b2580103SMark Fasheh 	struct ocfs2_space_resv sr;
8917909f2bfSTao Ma 	struct ocfs2_new_group_input input;
892bd50873dSTao Ma 	struct reflink_arguments args;
893bd50873dSTao Ma 	const char *old_path, *new_path;
894bd50873dSTao Ma 	bool preserve;
895ddee5cdbSTristan Ye 	struct ocfs2_info info;
896ca4d147eSHerbert Poetzl 
897ca4d147eSHerbert Poetzl 	switch (cmd) {
898ca4d147eSHerbert Poetzl 	case OCFS2_IOC_GETFLAGS:
899ca4d147eSHerbert Poetzl 		status = ocfs2_get_inode_attr(inode, &flags);
900ca4d147eSHerbert Poetzl 		if (status < 0)
901ca4d147eSHerbert Poetzl 			return status;
902ca4d147eSHerbert Poetzl 
903ca4d147eSHerbert Poetzl 		flags &= OCFS2_FL_VISIBLE;
904ca4d147eSHerbert Poetzl 		return put_user(flags, (int __user *) arg);
905ca4d147eSHerbert Poetzl 	case OCFS2_IOC_SETFLAGS:
906ca4d147eSHerbert Poetzl 		if (get_user(flags, (int __user *) arg))
907ca4d147eSHerbert Poetzl 			return -EFAULT;
908ca4d147eSHerbert Poetzl 
909*a561be71SAl Viro 		status = mnt_want_write_file(filp);
91042a74f20SDave Hansen 		if (status)
91142a74f20SDave Hansen 			return status;
91242a74f20SDave Hansen 		status = ocfs2_set_inode_attr(inode, flags,
913ca4d147eSHerbert Poetzl 			OCFS2_FL_MODIFIABLE);
91442a74f20SDave Hansen 		mnt_drop_write(filp->f_path.mnt);
91542a74f20SDave Hansen 		return status;
916b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP:
917b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP64:
918b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP:
919b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP64:
920b2580103SMark Fasheh 		if (copy_from_user(&sr, (int __user *) arg, sizeof(sr)))
921b2580103SMark Fasheh 			return -EFAULT;
922b2580103SMark Fasheh 
923b2580103SMark Fasheh 		return ocfs2_change_file_space(filp, cmd, &sr);
924d659072fSTao Ma 	case OCFS2_IOC_GROUP_EXTEND:
9250957f007SMark Fasheh 		if (!capable(CAP_SYS_RESOURCE))
9260957f007SMark Fasheh 			return -EPERM;
9270957f007SMark Fasheh 
928d659072fSTao Ma 		if (get_user(new_clusters, (int __user *)arg))
929d659072fSTao Ma 			return -EFAULT;
930d659072fSTao Ma 
931d659072fSTao Ma 		return ocfs2_group_extend(inode, new_clusters);
9327909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD:
9337909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD64:
9340957f007SMark Fasheh 		if (!capable(CAP_SYS_RESOURCE))
9350957f007SMark Fasheh 			return -EPERM;
9360957f007SMark Fasheh 
9377909f2bfSTao Ma 		if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
9387909f2bfSTao Ma 			return -EFAULT;
9397909f2bfSTao Ma 
9407909f2bfSTao Ma 		return ocfs2_group_add(inode, &input);
941bd50873dSTao Ma 	case OCFS2_IOC_REFLINK:
942bd50873dSTao Ma 		if (copy_from_user(&args, (struct reflink_arguments *)arg,
943bd50873dSTao Ma 				   sizeof(args)))
944bd50873dSTao Ma 			return -EFAULT;
945bd50873dSTao Ma 		old_path = (const char *)(unsigned long)args.old_path;
946bd50873dSTao Ma 		new_path = (const char *)(unsigned long)args.new_path;
947bd50873dSTao Ma 		preserve = (args.preserve != 0);
948bd50873dSTao Ma 
949bd50873dSTao Ma 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
950ddee5cdbSTristan Ye 	case OCFS2_IOC_INFO:
951ddee5cdbSTristan Ye 		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
952ddee5cdbSTristan Ye 				   sizeof(struct ocfs2_info)))
953ddee5cdbSTristan Ye 			return -EFAULT;
954ddee5cdbSTristan Ye 
955ddee5cdbSTristan Ye 		return ocfs2_info_handle(inode, &info, 0);
95655e67872STao Ma 	case FITRIM:
95755e67872STao Ma 	{
95855e67872STao Ma 		struct super_block *sb = inode->i_sb;
95955e67872STao Ma 		struct fstrim_range range;
96055e67872STao Ma 		int ret = 0;
96155e67872STao Ma 
96255e67872STao Ma 		if (!capable(CAP_SYS_ADMIN))
96355e67872STao Ma 			return -EPERM;
96455e67872STao Ma 
96555e67872STao Ma 		if (copy_from_user(&range, (struct fstrim_range *)arg,
96655e67872STao Ma 		    sizeof(range)))
96755e67872STao Ma 			return -EFAULT;
96855e67872STao Ma 
96955e67872STao Ma 		ret = ocfs2_trim_fs(sb, &range);
97055e67872STao Ma 		if (ret < 0)
97155e67872STao Ma 			return ret;
97255e67872STao Ma 
97355e67872STao Ma 		if (copy_to_user((struct fstrim_range *)arg, &range,
97455e67872STao Ma 		    sizeof(range)))
97555e67872STao Ma 			return -EFAULT;
97655e67872STao Ma 
97755e67872STao Ma 		return 0;
97855e67872STao Ma 	}
97953069d4eSTristan Ye 	case OCFS2_IOC_MOVE_EXT:
98053069d4eSTristan Ye 		return ocfs2_ioctl_move_extents(filp, (void __user *)arg);
981ca4d147eSHerbert Poetzl 	default:
982ca4d147eSHerbert Poetzl 		return -ENOTTY;
983ca4d147eSHerbert Poetzl 	}
984ca4d147eSHerbert Poetzl }
985ca4d147eSHerbert Poetzl 
986586d232bSMark Fasheh #ifdef CONFIG_COMPAT
987586d232bSMark Fasheh long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
988586d232bSMark Fasheh {
98934e6c59aSTao Ma 	bool preserve;
99034e6c59aSTao Ma 	struct reflink_arguments args;
99134e6c59aSTao Ma 	struct inode *inode = file->f_path.dentry->d_inode;
992ddee5cdbSTristan Ye 	struct ocfs2_info info;
99334e6c59aSTao Ma 
994586d232bSMark Fasheh 	switch (cmd) {
995586d232bSMark Fasheh 	case OCFS2_IOC32_GETFLAGS:
996586d232bSMark Fasheh 		cmd = OCFS2_IOC_GETFLAGS;
997586d232bSMark Fasheh 		break;
998586d232bSMark Fasheh 	case OCFS2_IOC32_SETFLAGS:
999586d232bSMark Fasheh 		cmd = OCFS2_IOC_SETFLAGS;
1000586d232bSMark Fasheh 		break;
1001b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP:
1002b2580103SMark Fasheh 	case OCFS2_IOC_RESVSP64:
1003b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP:
1004b2580103SMark Fasheh 	case OCFS2_IOC_UNRESVSP64:
1005d659072fSTao Ma 	case OCFS2_IOC_GROUP_EXTEND:
10067909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD:
10077909f2bfSTao Ma 	case OCFS2_IOC_GROUP_ADD64:
100855e67872STao Ma 	case FITRIM:
1009b2580103SMark Fasheh 		break;
101034e6c59aSTao Ma 	case OCFS2_IOC_REFLINK:
101134e6c59aSTao Ma 		if (copy_from_user(&args, (struct reflink_arguments *)arg,
101234e6c59aSTao Ma 				   sizeof(args)))
101334e6c59aSTao Ma 			return -EFAULT;
101434e6c59aSTao Ma 		preserve = (args.preserve != 0);
101534e6c59aSTao Ma 
101634e6c59aSTao Ma 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
101734e6c59aSTao Ma 					   compat_ptr(args.new_path), preserve);
1018ddee5cdbSTristan Ye 	case OCFS2_IOC_INFO:
1019ddee5cdbSTristan Ye 		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
1020ddee5cdbSTristan Ye 				   sizeof(struct ocfs2_info)))
1021ddee5cdbSTristan Ye 			return -EFAULT;
1022ddee5cdbSTristan Ye 
1023ddee5cdbSTristan Ye 		return ocfs2_info_handle(inode, &info, 1);
102453069d4eSTristan Ye 	case OCFS2_IOC_MOVE_EXT:
102553069d4eSTristan Ye 		break;
1026586d232bSMark Fasheh 	default:
1027586d232bSMark Fasheh 		return -ENOIOCTLCMD;
1028586d232bSMark Fasheh 	}
1029586d232bSMark Fasheh 
1030c9ec1488SAndi Kleen 	return ocfs2_ioctl(file, cmd, arg);
1031586d232bSMark Fasheh }
1032586d232bSMark Fasheh #endif
1033