xref: /openbmc/linux/fs/nilfs2/ioctl.c (revision e2c7617a)
17942b919SKoji Sato /*
27942b919SKoji Sato  * ioctl.c - NILFS ioctl operations.
37942b919SKoji Sato  *
47942b919SKoji Sato  * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation.
57942b919SKoji Sato  *
67942b919SKoji Sato  * This program is free software; you can redistribute it and/or modify
77942b919SKoji Sato  * it under the terms of the GNU General Public License as published by
87942b919SKoji Sato  * the Free Software Foundation; either version 2 of the License, or
97942b919SKoji Sato  * (at your option) any later version.
107942b919SKoji Sato  *
117942b919SKoji Sato  * This program is distributed in the hope that it will be useful,
127942b919SKoji Sato  * but WITHOUT ANY WARRANTY; without even the implied warranty of
137942b919SKoji Sato  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147942b919SKoji Sato  * GNU General Public License for more details.
157942b919SKoji Sato  *
167942b919SKoji Sato  * You should have received a copy of the GNU General Public License
177942b919SKoji Sato  * along with this program; if not, write to the Free Software
187942b919SKoji Sato  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
197942b919SKoji Sato  *
207942b919SKoji Sato  * Written by Koji Sato <koji@osrg.net>.
217942b919SKoji Sato  */
227942b919SKoji Sato 
237942b919SKoji Sato #include <linux/fs.h>
247942b919SKoji Sato #include <linux/wait.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
267942b919SKoji Sato #include <linux/capability.h>	/* capable() */
277942b919SKoji Sato #include <linux/uaccess.h>	/* copy_from_user(), copy_to_user() */
284f6b8288SRyusuke Konishi #include <linux/vmalloc.h>
29828b1c50SRyusuke Konishi #include <linux/compat.h>	/* compat_ptr() */
302a79f17eSAl Viro #include <linux/mount.h>	/* mnt_want_write_file(), mnt_drop_write_file() */
31ae191838SRyusuke Konishi #include <linux/buffer_head.h>
327942b919SKoji Sato #include <linux/nilfs2_fs.h>
337942b919SKoji Sato #include "nilfs.h"
347942b919SKoji Sato #include "segment.h"
357942b919SKoji Sato #include "bmap.h"
367942b919SKoji Sato #include "cpfile.h"
377942b919SKoji Sato #include "sufile.h"
387942b919SKoji Sato #include "dat.h"
397942b919SKoji Sato 
40d623a942SVyacheslav Dubeyko /**
41d623a942SVyacheslav Dubeyko  * nilfs_ioctl_wrap_copy - wrapping function of get/set metadata info
42d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
43d623a942SVyacheslav Dubeyko  * @argv: vector of arguments from userspace
44d623a942SVyacheslav Dubeyko  * @dir: set of direction flags
45d623a942SVyacheslav Dubeyko  * @dofunc: concrete function of get/set metadata info
46d623a942SVyacheslav Dubeyko  *
47d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_wrap_copy() gets/sets metadata info by means of
48d623a942SVyacheslav Dubeyko  * calling dofunc() function on the basis of @argv argument.
49d623a942SVyacheslav Dubeyko  *
50d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned and requested metadata info
51d623a942SVyacheslav Dubeyko  * is copied into userspace. On error, one of the following
52d623a942SVyacheslav Dubeyko  * negative error codes is returned.
53d623a942SVyacheslav Dubeyko  *
54d623a942SVyacheslav Dubeyko  * %-EINVAL - Invalid arguments from userspace.
55d623a942SVyacheslav Dubeyko  *
56d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
57d623a942SVyacheslav Dubeyko  *
58d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during execution of requested operation.
59d623a942SVyacheslav Dubeyko  */
607942b919SKoji Sato static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
617942b919SKoji Sato 				 struct nilfs_argv *argv, int dir,
627942b919SKoji Sato 				 ssize_t (*dofunc)(struct the_nilfs *,
63b028fcfcSRyusuke Konishi 						   __u64 *, int,
647942b919SKoji Sato 						   void *, size_t, size_t))
657942b919SKoji Sato {
667942b919SKoji Sato 	void *buf;
67dc498d09SRyusuke Konishi 	void __user *base = (void __user *)(unsigned long)argv->v_base;
683358b4aaSRyusuke Konishi 	size_t maxmembs, total, n;
697942b919SKoji Sato 	ssize_t nr;
707942b919SKoji Sato 	int ret, i;
71b028fcfcSRyusuke Konishi 	__u64 pos, ppos;
727942b919SKoji Sato 
737942b919SKoji Sato 	if (argv->v_nmembs == 0)
747942b919SKoji Sato 		return 0;
757942b919SKoji Sato 
763358b4aaSRyusuke Konishi 	if (argv->v_size > PAGE_SIZE)
773358b4aaSRyusuke Konishi 		return -EINVAL;
783358b4aaSRyusuke Konishi 
794b15d617SWenliang Fan 	/*
804b15d617SWenliang Fan 	 * Reject pairs of a start item position (argv->v_index) and a
814b15d617SWenliang Fan 	 * total count (argv->v_nmembs) which leads position 'pos' to
824b15d617SWenliang Fan 	 * overflow by the increment at the end of the loop.
834b15d617SWenliang Fan 	 */
844b15d617SWenliang Fan 	if (argv->v_index > ~(__u64)0 - argv->v_nmembs)
854b15d617SWenliang Fan 		return -EINVAL;
864b15d617SWenliang Fan 
873358b4aaSRyusuke Konishi 	buf = (void *)__get_free_pages(GFP_NOFS, 0);
883358b4aaSRyusuke Konishi 	if (unlikely(!buf))
897942b919SKoji Sato 		return -ENOMEM;
903358b4aaSRyusuke Konishi 	maxmembs = PAGE_SIZE / argv->v_size;
917942b919SKoji Sato 
927942b919SKoji Sato 	ret = 0;
937942b919SKoji Sato 	total = 0;
94b028fcfcSRyusuke Konishi 	pos = argv->v_index;
957942b919SKoji Sato 	for (i = 0; i < argv->v_nmembs; i += n) {
967942b919SKoji Sato 		n = (argv->v_nmembs - i < maxmembs) ?
977942b919SKoji Sato 			argv->v_nmembs - i : maxmembs;
987942b919SKoji Sato 		if ((dir & _IOC_WRITE) &&
99dc498d09SRyusuke Konishi 		    copy_from_user(buf, base + argv->v_size * i,
1007942b919SKoji Sato 				   argv->v_size * n)) {
1017942b919SKoji Sato 			ret = -EFAULT;
1027942b919SKoji Sato 			break;
1037942b919SKoji Sato 		}
104b028fcfcSRyusuke Konishi 		ppos = pos;
1058acfbf09SPekka Enberg 		nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size,
106b028fcfcSRyusuke Konishi 			       n);
1077942b919SKoji Sato 		if (nr < 0) {
1087942b919SKoji Sato 			ret = nr;
1097942b919SKoji Sato 			break;
1107942b919SKoji Sato 		}
1117942b919SKoji Sato 		if ((dir & _IOC_READ) &&
112dc498d09SRyusuke Konishi 		    copy_to_user(base + argv->v_size * i, buf,
113dc498d09SRyusuke Konishi 				 argv->v_size * nr)) {
1147942b919SKoji Sato 			ret = -EFAULT;
1157942b919SKoji Sato 			break;
1167942b919SKoji Sato 		}
1177942b919SKoji Sato 		total += nr;
118b028fcfcSRyusuke Konishi 		if ((size_t)nr < n)
119b028fcfcSRyusuke Konishi 			break;
120b028fcfcSRyusuke Konishi 		if (pos == ppos)
121b028fcfcSRyusuke Konishi 			pos += n;
1227942b919SKoji Sato 	}
1237942b919SKoji Sato 	argv->v_nmembs = total;
1247942b919SKoji Sato 
1253358b4aaSRyusuke Konishi 	free_pages((unsigned long)buf, 0);
1267942b919SKoji Sato 	return ret;
1277942b919SKoji Sato }
1287942b919SKoji Sato 
129d623a942SVyacheslav Dubeyko /**
130d623a942SVyacheslav Dubeyko  * nilfs_ioctl_getflags - ioctl to support lsattr
131d623a942SVyacheslav Dubeyko  */
132cde98f0fSRyusuke Konishi static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp)
133cde98f0fSRyusuke Konishi {
134cde98f0fSRyusuke Konishi 	unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE;
135cde98f0fSRyusuke Konishi 
136cde98f0fSRyusuke Konishi 	return put_user(flags, (int __user *)argp);
137cde98f0fSRyusuke Konishi }
138cde98f0fSRyusuke Konishi 
139d623a942SVyacheslav Dubeyko /**
140d623a942SVyacheslav Dubeyko  * nilfs_ioctl_setflags - ioctl to support chattr
141d623a942SVyacheslav Dubeyko  */
142cde98f0fSRyusuke Konishi static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
143cde98f0fSRyusuke Konishi 				void __user *argp)
144cde98f0fSRyusuke Konishi {
145cde98f0fSRyusuke Konishi 	struct nilfs_transaction_info ti;
146cde98f0fSRyusuke Konishi 	unsigned int flags, oldflags;
147cde98f0fSRyusuke Konishi 	int ret;
148cde98f0fSRyusuke Konishi 
1492e149670SSerge E. Hallyn 	if (!inode_owner_or_capable(inode))
150cde98f0fSRyusuke Konishi 		return -EACCES;
151cde98f0fSRyusuke Konishi 
152cde98f0fSRyusuke Konishi 	if (get_user(flags, (int __user *)argp))
153cde98f0fSRyusuke Konishi 		return -EFAULT;
154cde98f0fSRyusuke Konishi 
155a561be71SAl Viro 	ret = mnt_want_write_file(filp);
156cde98f0fSRyusuke Konishi 	if (ret)
157cde98f0fSRyusuke Konishi 		return ret;
158cde98f0fSRyusuke Konishi 
159cde98f0fSRyusuke Konishi 	flags = nilfs_mask_flags(inode->i_mode, flags);
160cde98f0fSRyusuke Konishi 
161cde98f0fSRyusuke Konishi 	mutex_lock(&inode->i_mutex);
162cde98f0fSRyusuke Konishi 
163cde98f0fSRyusuke Konishi 	oldflags = NILFS_I(inode)->i_flags;
164cde98f0fSRyusuke Konishi 
165cde98f0fSRyusuke Konishi 	/*
166cde98f0fSRyusuke Konishi 	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
167cde98f0fSRyusuke Konishi 	 * relevant capability.
168cde98f0fSRyusuke Konishi 	 */
169cde98f0fSRyusuke Konishi 	ret = -EPERM;
170cde98f0fSRyusuke Konishi 	if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
171cde98f0fSRyusuke Konishi 	    !capable(CAP_LINUX_IMMUTABLE))
172cde98f0fSRyusuke Konishi 		goto out;
173cde98f0fSRyusuke Konishi 
174cde98f0fSRyusuke Konishi 	ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
175cde98f0fSRyusuke Konishi 	if (ret)
176cde98f0fSRyusuke Konishi 		goto out;
177cde98f0fSRyusuke Konishi 
178cde98f0fSRyusuke Konishi 	NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) |
179cde98f0fSRyusuke Konishi 		(flags & FS_FL_USER_MODIFIABLE);
180cde98f0fSRyusuke Konishi 
181cde98f0fSRyusuke Konishi 	nilfs_set_inode_flags(inode);
182cde98f0fSRyusuke Konishi 	inode->i_ctime = CURRENT_TIME;
183cde98f0fSRyusuke Konishi 	if (IS_SYNC(inode))
184cde98f0fSRyusuke Konishi 		nilfs_set_transaction_flag(NILFS_TI_SYNC);
185cde98f0fSRyusuke Konishi 
186cde98f0fSRyusuke Konishi 	nilfs_mark_inode_dirty(inode);
187cde98f0fSRyusuke Konishi 	ret = nilfs_transaction_commit(inode->i_sb);
188cde98f0fSRyusuke Konishi out:
189cde98f0fSRyusuke Konishi 	mutex_unlock(&inode->i_mutex);
1902a79f17eSAl Viro 	mnt_drop_write_file(filp);
191cde98f0fSRyusuke Konishi 	return ret;
192cde98f0fSRyusuke Konishi }
193cde98f0fSRyusuke Konishi 
194d623a942SVyacheslav Dubeyko /**
195d623a942SVyacheslav Dubeyko  * nilfs_ioctl_getversion - get info about a file's version (generation number)
196d623a942SVyacheslav Dubeyko  */
197cde98f0fSRyusuke Konishi static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp)
198cde98f0fSRyusuke Konishi {
199cde98f0fSRyusuke Konishi 	return put_user(inode->i_generation, (int __user *)argp);
200cde98f0fSRyusuke Konishi }
201cde98f0fSRyusuke Konishi 
202d623a942SVyacheslav Dubeyko /**
203d623a942SVyacheslav Dubeyko  * nilfs_ioctl_change_cpmode - change checkpoint mode (checkpoint/snapshot)
204d623a942SVyacheslav Dubeyko  * @inode: inode object
205d623a942SVyacheslav Dubeyko  * @filp: file object
206d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
207d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
208d623a942SVyacheslav Dubeyko  *
209d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_change_cpmode() function changes mode of
210d623a942SVyacheslav Dubeyko  * given checkpoint between checkpoint and snapshot state. This ioctl
211d623a942SVyacheslav Dubeyko  * is used in chcp and mkcp utilities.
212d623a942SVyacheslav Dubeyko  *
213d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned and mode of a checkpoint is
214d623a942SVyacheslav Dubeyko  * changed. On error, one of the following negative error codes
215d623a942SVyacheslav Dubeyko  * is returned.
216d623a942SVyacheslav Dubeyko  *
217d623a942SVyacheslav Dubeyko  * %-EPERM - Operation not permitted.
218d623a942SVyacheslav Dubeyko  *
219d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during checkpoint mode changing.
220d623a942SVyacheslav Dubeyko  */
2217942b919SKoji Sato static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
2227942b919SKoji Sato 				     unsigned int cmd, void __user *argp)
2237942b919SKoji Sato {
224e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
2257942b919SKoji Sato 	struct nilfs_transaction_info ti;
2267942b919SKoji Sato 	struct nilfs_cpmode cpmode;
2277942b919SKoji Sato 	int ret;
2287942b919SKoji Sato 
2297942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
2307942b919SKoji Sato 		return -EPERM;
2317512487eSRyusuke Konishi 
232a561be71SAl Viro 	ret = mnt_want_write_file(filp);
2337512487eSRyusuke Konishi 	if (ret)
2347512487eSRyusuke Konishi 		return ret;
2357512487eSRyusuke Konishi 
2367512487eSRyusuke Konishi 	ret = -EFAULT;
2377942b919SKoji Sato 	if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
2387512487eSRyusuke Konishi 		goto out;
2397942b919SKoji Sato 
240572d8b39SRyusuke Konishi 	mutex_lock(&nilfs->ns_snapshot_mount_mutex);
2417512487eSRyusuke Konishi 
2427942b919SKoji Sato 	nilfs_transaction_begin(inode->i_sb, &ti, 0);
2437942b919SKoji Sato 	ret = nilfs_cpfile_change_cpmode(
244e3154e97SRyusuke Konishi 		nilfs->ns_cpfile, cpmode.cm_cno, cpmode.cm_mode);
2457512487eSRyusuke Konishi 	if (unlikely(ret < 0))
24647420c79SRyusuke Konishi 		nilfs_transaction_abort(inode->i_sb);
2477512487eSRyusuke Konishi 	else
24847420c79SRyusuke Konishi 		nilfs_transaction_commit(inode->i_sb); /* never fails */
2497512487eSRyusuke Konishi 
250572d8b39SRyusuke Konishi 	mutex_unlock(&nilfs->ns_snapshot_mount_mutex);
2517512487eSRyusuke Konishi out:
2522a79f17eSAl Viro 	mnt_drop_write_file(filp);
2537942b919SKoji Sato 	return ret;
2547942b919SKoji Sato }
2557942b919SKoji Sato 
256d623a942SVyacheslav Dubeyko /**
257d623a942SVyacheslav Dubeyko  * nilfs_ioctl_delete_checkpoint - remove checkpoint
258d623a942SVyacheslav Dubeyko  * @inode: inode object
259d623a942SVyacheslav Dubeyko  * @filp: file object
260d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
261d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
262d623a942SVyacheslav Dubeyko  *
263d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_delete_checkpoint() function removes
264d623a942SVyacheslav Dubeyko  * checkpoint from NILFS2 file system. This ioctl is used in rmcp
265d623a942SVyacheslav Dubeyko  * utility.
266d623a942SVyacheslav Dubeyko  *
267d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned and a checkpoint is
268d623a942SVyacheslav Dubeyko  * removed. On error, one of the following negative error codes
269d623a942SVyacheslav Dubeyko  * is returned.
270d623a942SVyacheslav Dubeyko  *
271d623a942SVyacheslav Dubeyko  * %-EPERM - Operation not permitted.
272d623a942SVyacheslav Dubeyko  *
273d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during checkpoint removing.
274d623a942SVyacheslav Dubeyko  */
2757942b919SKoji Sato static int
2767942b919SKoji Sato nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
2777942b919SKoji Sato 			      unsigned int cmd, void __user *argp)
2787942b919SKoji Sato {
279e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
2807942b919SKoji Sato 	struct nilfs_transaction_info ti;
2817942b919SKoji Sato 	__u64 cno;
2827942b919SKoji Sato 	int ret;
2837942b919SKoji Sato 
2847942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
2857942b919SKoji Sato 		return -EPERM;
2867512487eSRyusuke Konishi 
287a561be71SAl Viro 	ret = mnt_want_write_file(filp);
2887512487eSRyusuke Konishi 	if (ret)
2897512487eSRyusuke Konishi 		return ret;
2907512487eSRyusuke Konishi 
2917512487eSRyusuke Konishi 	ret = -EFAULT;
2927942b919SKoji Sato 	if (copy_from_user(&cno, argp, sizeof(cno)))
2937512487eSRyusuke Konishi 		goto out;
2947942b919SKoji Sato 
2957942b919SKoji Sato 	nilfs_transaction_begin(inode->i_sb, &ti, 0);
296e3154e97SRyusuke Konishi 	ret = nilfs_cpfile_delete_checkpoint(nilfs->ns_cpfile, cno);
2977512487eSRyusuke Konishi 	if (unlikely(ret < 0))
29847420c79SRyusuke Konishi 		nilfs_transaction_abort(inode->i_sb);
2997512487eSRyusuke Konishi 	else
30047420c79SRyusuke Konishi 		nilfs_transaction_commit(inode->i_sb); /* never fails */
3017512487eSRyusuke Konishi out:
3022a79f17eSAl Viro 	mnt_drop_write_file(filp);
3037942b919SKoji Sato 	return ret;
3047942b919SKoji Sato }
3057942b919SKoji Sato 
306d623a942SVyacheslav Dubeyko /**
307d623a942SVyacheslav Dubeyko  * nilfs_ioctl_do_get_cpinfo - callback method getting info about checkpoints
308d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
309d623a942SVyacheslav Dubeyko  * @posp: pointer on array of checkpoint's numbers
310d623a942SVyacheslav Dubeyko  * @flags: checkpoint mode (checkpoint or snapshot)
311d623a942SVyacheslav Dubeyko  * @buf: buffer for storing checkponts' info
312d623a942SVyacheslav Dubeyko  * @size: size in bytes of one checkpoint info item in array
313d623a942SVyacheslav Dubeyko  * @nmembs: number of checkpoints in array (numbers and infos)
314d623a942SVyacheslav Dubeyko  *
315d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_do_get_cpinfo() function returns info about
316d623a942SVyacheslav Dubeyko  * requested checkpoints. The NILFS_IOCTL_GET_CPINFO ioctl is used in
317d623a942SVyacheslav Dubeyko  * lscp utility and by nilfs_cleanerd daemon.
318d623a942SVyacheslav Dubeyko  *
319d623a942SVyacheslav Dubeyko  * Return value: count of nilfs_cpinfo structures in output buffer.
320d623a942SVyacheslav Dubeyko  */
3217942b919SKoji Sato static ssize_t
322b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
3237942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
3247942b919SKoji Sato {
3257942b919SKoji Sato 	int ret;
3267942b919SKoji Sato 
32747420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
32847eb6b9cSRyusuke Konishi 	ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
329003ff182SRyusuke Konishi 				      size, nmembs);
33047420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
3317942b919SKoji Sato 	return ret;
3327942b919SKoji Sato }
3337942b919SKoji Sato 
334d623a942SVyacheslav Dubeyko /**
335d623a942SVyacheslav Dubeyko  * nilfs_ioctl_get_cpstat - get checkpoints statistics
336d623a942SVyacheslav Dubeyko  * @inode: inode object
337d623a942SVyacheslav Dubeyko  * @filp: file object
338d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
339d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
340d623a942SVyacheslav Dubeyko  *
341d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_get_cpstat() returns information about checkpoints.
342d623a942SVyacheslav Dubeyko  * The NILFS_IOCTL_GET_CPSTAT ioctl is used by lscp, rmcp utilities
343d623a942SVyacheslav Dubeyko  * and by nilfs_cleanerd daemon.
344d623a942SVyacheslav Dubeyko  *
345d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned, and checkpoints information is
346d623a942SVyacheslav Dubeyko  * copied into userspace pointer @argp. On error, one of the following
347d623a942SVyacheslav Dubeyko  * negative error codes is returned.
348d623a942SVyacheslav Dubeyko  *
349d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
350d623a942SVyacheslav Dubeyko  *
351d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
352d623a942SVyacheslav Dubeyko  *
353d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during getting checkpoints statistics.
354d623a942SVyacheslav Dubeyko  */
3557942b919SKoji Sato static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp,
3567942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
3577942b919SKoji Sato {
358e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
3597942b919SKoji Sato 	struct nilfs_cpstat cpstat;
3607942b919SKoji Sato 	int ret;
3617942b919SKoji Sato 
36247420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
36347420c79SRyusuke Konishi 	ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat);
36447420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
3657942b919SKoji Sato 	if (ret < 0)
3667942b919SKoji Sato 		return ret;
3677942b919SKoji Sato 
3687942b919SKoji Sato 	if (copy_to_user(argp, &cpstat, sizeof(cpstat)))
3697942b919SKoji Sato 		ret = -EFAULT;
3707942b919SKoji Sato 	return ret;
3717942b919SKoji Sato }
3727942b919SKoji Sato 
373d623a942SVyacheslav Dubeyko /**
374d623a942SVyacheslav Dubeyko  * nilfs_ioctl_do_get_suinfo - callback method getting segment usage info
375d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
376d623a942SVyacheslav Dubeyko  * @posp: pointer on array of segment numbers
377d623a942SVyacheslav Dubeyko  * @flags: *not used*
378d623a942SVyacheslav Dubeyko  * @buf: buffer for storing suinfo array
379d623a942SVyacheslav Dubeyko  * @size: size in bytes of one suinfo item in array
380d623a942SVyacheslav Dubeyko  * @nmembs: count of segment numbers and suinfos in array
381d623a942SVyacheslav Dubeyko  *
382d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_do_get_suinfo() function returns segment usage
383d623a942SVyacheslav Dubeyko  * info about requested segments. The NILFS_IOCTL_GET_SUINFO ioctl is used
384d623a942SVyacheslav Dubeyko  * in lssu, nilfs_resize utilities and by nilfs_cleanerd daemon.
385d623a942SVyacheslav Dubeyko  *
386d623a942SVyacheslav Dubeyko  * Return value: count of nilfs_suinfo structures in output buffer.
387d623a942SVyacheslav Dubeyko  */
3887942b919SKoji Sato static ssize_t
389b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
3907942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
3917942b919SKoji Sato {
3927942b919SKoji Sato 	int ret;
3937942b919SKoji Sato 
39447420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
395003ff182SRyusuke Konishi 	ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
396003ff182SRyusuke Konishi 				      nmembs);
39747420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
3987942b919SKoji Sato 	return ret;
3997942b919SKoji Sato }
4007942b919SKoji Sato 
401d623a942SVyacheslav Dubeyko /**
402d623a942SVyacheslav Dubeyko  * nilfs_ioctl_get_sustat - get segment usage statistics
403d623a942SVyacheslav Dubeyko  * @inode: inode object
404d623a942SVyacheslav Dubeyko  * @filp: file object
405d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
406d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
407d623a942SVyacheslav Dubeyko  *
408d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_get_sustat() returns segment usage statistics.
409d623a942SVyacheslav Dubeyko  * The NILFS_IOCTL_GET_SUSTAT ioctl is used in lssu, nilfs_resize utilities
410d623a942SVyacheslav Dubeyko  * and by nilfs_cleanerd daemon.
411d623a942SVyacheslav Dubeyko  *
412d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned, and segment usage information is
413d623a942SVyacheslav Dubeyko  * copied into userspace pointer @argp. On error, one of the following
414d623a942SVyacheslav Dubeyko  * negative error codes is returned.
415d623a942SVyacheslav Dubeyko  *
416d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
417d623a942SVyacheslav Dubeyko  *
418d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
419d623a942SVyacheslav Dubeyko  *
420d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during getting segment usage statistics.
421d623a942SVyacheslav Dubeyko  */
4227942b919SKoji Sato static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp,
4237942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
4247942b919SKoji Sato {
425e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
4267942b919SKoji Sato 	struct nilfs_sustat sustat;
4277942b919SKoji Sato 	int ret;
4287942b919SKoji Sato 
42947420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
43047420c79SRyusuke Konishi 	ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat);
43147420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
4327942b919SKoji Sato 	if (ret < 0)
4337942b919SKoji Sato 		return ret;
4347942b919SKoji Sato 
4357942b919SKoji Sato 	if (copy_to_user(argp, &sustat, sizeof(sustat)))
4367942b919SKoji Sato 		ret = -EFAULT;
4377942b919SKoji Sato 	return ret;
4387942b919SKoji Sato }
4397942b919SKoji Sato 
440d623a942SVyacheslav Dubeyko /**
441d623a942SVyacheslav Dubeyko  * nilfs_ioctl_do_get_vinfo - callback method getting virtual blocks info
442d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
443d623a942SVyacheslav Dubeyko  * @posp: *not used*
444d623a942SVyacheslav Dubeyko  * @flags: *not used*
445d623a942SVyacheslav Dubeyko  * @buf: buffer for storing array of nilfs_vinfo structures
446d623a942SVyacheslav Dubeyko  * @size: size in bytes of one vinfo item in array
447d623a942SVyacheslav Dubeyko  * @nmembs: count of vinfos in array
448d623a942SVyacheslav Dubeyko  *
449d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_do_get_vinfo() function returns information
450d623a942SVyacheslav Dubeyko  * on virtual block addresses. The NILFS_IOCTL_GET_VINFO ioctl is used
451d623a942SVyacheslav Dubeyko  * by nilfs_cleanerd daemon.
452d623a942SVyacheslav Dubeyko  *
453d623a942SVyacheslav Dubeyko  * Return value: count of nilfs_vinfo structures in output buffer.
454d623a942SVyacheslav Dubeyko  */
4557942b919SKoji Sato static ssize_t
456b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
4577942b919SKoji Sato 			 void *buf, size_t size, size_t nmembs)
4587942b919SKoji Sato {
4597942b919SKoji Sato 	int ret;
4607942b919SKoji Sato 
46147420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
462365e215cSRyusuke Konishi 	ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs);
46347420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
4647942b919SKoji Sato 	return ret;
4657942b919SKoji Sato }
4667942b919SKoji Sato 
467d623a942SVyacheslav Dubeyko /**
468d623a942SVyacheslav Dubeyko  * nilfs_ioctl_do_get_bdescs - callback method getting disk block descriptors
469d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
470d623a942SVyacheslav Dubeyko  * @posp: *not used*
471d623a942SVyacheslav Dubeyko  * @flags: *not used*
472d623a942SVyacheslav Dubeyko  * @buf: buffer for storing array of nilfs_bdesc structures
473d623a942SVyacheslav Dubeyko  * @size: size in bytes of one bdesc item in array
474d623a942SVyacheslav Dubeyko  * @nmembs: count of bdescs in array
475d623a942SVyacheslav Dubeyko  *
476d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_do_get_bdescs() function returns information
477d623a942SVyacheslav Dubeyko  * about descriptors of disk block numbers. The NILFS_IOCTL_GET_BDESCS ioctl
478d623a942SVyacheslav Dubeyko  * is used by nilfs_cleanerd daemon.
479d623a942SVyacheslav Dubeyko  *
480d623a942SVyacheslav Dubeyko  * Return value: count of nilfs_bdescs structures in output buffer.
481d623a942SVyacheslav Dubeyko  */
4827942b919SKoji Sato static ssize_t
483b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags,
4847942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
4857942b919SKoji Sato {
486365e215cSRyusuke Konishi 	struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
4877942b919SKoji Sato 	struct nilfs_bdesc *bdescs = buf;
4887942b919SKoji Sato 	int ret, i;
4897942b919SKoji Sato 
49047eb6b9cSRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
4917942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
4927942b919SKoji Sato 		ret = nilfs_bmap_lookup_at_level(bmap,
4937942b919SKoji Sato 						 bdescs[i].bd_offset,
4947942b919SKoji Sato 						 bdescs[i].bd_level + 1,
4957942b919SKoji Sato 						 &bdescs[i].bd_blocknr);
4967942b919SKoji Sato 		if (ret < 0) {
49747eb6b9cSRyusuke Konishi 			if (ret != -ENOENT) {
49847eb6b9cSRyusuke Konishi 				up_read(&nilfs->ns_segctor_sem);
4997942b919SKoji Sato 				return ret;
50047eb6b9cSRyusuke Konishi 			}
5017942b919SKoji Sato 			bdescs[i].bd_blocknr = 0;
5027942b919SKoji Sato 		}
5037942b919SKoji Sato 	}
50447eb6b9cSRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
5057942b919SKoji Sato 	return nmembs;
5067942b919SKoji Sato }
5077942b919SKoji Sato 
508d623a942SVyacheslav Dubeyko /**
509d623a942SVyacheslav Dubeyko  * nilfs_ioctl_get_bdescs - get disk block descriptors
510d623a942SVyacheslav Dubeyko  * @inode: inode object
511d623a942SVyacheslav Dubeyko  * @filp: file object
512d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
513d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
514d623a942SVyacheslav Dubeyko  *
515d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_do_get_bdescs() function returns information
516d623a942SVyacheslav Dubeyko  * about descriptors of disk block numbers. The NILFS_IOCTL_GET_BDESCS ioctl
517d623a942SVyacheslav Dubeyko  * is used by nilfs_cleanerd daemon.
518d623a942SVyacheslav Dubeyko  *
519d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned, and disk block descriptors are
520d623a942SVyacheslav Dubeyko  * copied into userspace pointer @argp. On error, one of the following
521d623a942SVyacheslav Dubeyko  * negative error codes is returned.
522d623a942SVyacheslav Dubeyko  *
523d623a942SVyacheslav Dubeyko  * %-EINVAL - Invalid arguments from userspace.
524d623a942SVyacheslav Dubeyko  *
525d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
526d623a942SVyacheslav Dubeyko  *
527d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
528d623a942SVyacheslav Dubeyko  *
529d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during getting disk block descriptors.
530d623a942SVyacheslav Dubeyko  */
5317942b919SKoji Sato static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,
5327942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
5337942b919SKoji Sato {
534e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
5357942b919SKoji Sato 	struct nilfs_argv argv;
5367942b919SKoji Sato 	int ret;
5377942b919SKoji Sato 
5387942b919SKoji Sato 	if (copy_from_user(&argv, argp, sizeof(argv)))
5397942b919SKoji Sato 		return -EFAULT;
5407942b919SKoji Sato 
54183aca8f4SRyusuke Konishi 	if (argv.v_size != sizeof(struct nilfs_bdesc))
54283aca8f4SRyusuke Konishi 		return -EINVAL;
54383aca8f4SRyusuke Konishi 
5447942b919SKoji Sato 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
5457942b919SKoji Sato 				    nilfs_ioctl_do_get_bdescs);
54647420c79SRyusuke Konishi 	if (ret < 0)
54747420c79SRyusuke Konishi 		return ret;
5487942b919SKoji Sato 
5497942b919SKoji Sato 	if (copy_to_user(argp, &argv, sizeof(argv)))
5507942b919SKoji Sato 		ret = -EFAULT;
5517942b919SKoji Sato 	return ret;
5527942b919SKoji Sato }
5537942b919SKoji Sato 
554d623a942SVyacheslav Dubeyko /**
555d623a942SVyacheslav Dubeyko  * nilfs_ioctl_move_inode_block - prepare data/node block for moving by GC
556d623a942SVyacheslav Dubeyko  * @inode: inode object
557d623a942SVyacheslav Dubeyko  * @vdesc: descriptor of virtual block number
558d623a942SVyacheslav Dubeyko  * @buffers: list of moving buffers
559d623a942SVyacheslav Dubeyko  *
560d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_move_inode_block() function registers data/node
561d623a942SVyacheslav Dubeyko  * buffer in the GC pagecache and submit read request.
562d623a942SVyacheslav Dubeyko  *
563d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned. On error, one of the following
564d623a942SVyacheslav Dubeyko  * negative error codes is returned.
565d623a942SVyacheslav Dubeyko  *
566d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
567d623a942SVyacheslav Dubeyko  *
568d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
569d623a942SVyacheslav Dubeyko  *
570d623a942SVyacheslav Dubeyko  * %-ENOENT - Requested block doesn't exist.
571d623a942SVyacheslav Dubeyko  *
572d623a942SVyacheslav Dubeyko  * %-EEXIST - Blocks conflict is detected.
573d623a942SVyacheslav Dubeyko  */
5747942b919SKoji Sato static int nilfs_ioctl_move_inode_block(struct inode *inode,
5757942b919SKoji Sato 					struct nilfs_vdesc *vdesc,
5767942b919SKoji Sato 					struct list_head *buffers)
5777942b919SKoji Sato {
5787942b919SKoji Sato 	struct buffer_head *bh;
5797942b919SKoji Sato 	int ret;
5807942b919SKoji Sato 
5817942b919SKoji Sato 	if (vdesc->vd_flags == 0)
5827942b919SKoji Sato 		ret = nilfs_gccache_submit_read_data(
5837942b919SKoji Sato 			inode, vdesc->vd_offset, vdesc->vd_blocknr,
5847942b919SKoji Sato 			vdesc->vd_vblocknr, &bh);
5857942b919SKoji Sato 	else
5867942b919SKoji Sato 		ret = nilfs_gccache_submit_read_node(
5877942b919SKoji Sato 			inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
5887942b919SKoji Sato 
5897942b919SKoji Sato 	if (unlikely(ret < 0)) {
5907942b919SKoji Sato 		if (ret == -ENOENT)
5917942b919SKoji Sato 			printk(KERN_CRIT
5927942b919SKoji Sato 			       "%s: invalid virtual block address (%s): "
5937942b919SKoji Sato 			       "ino=%llu, cno=%llu, offset=%llu, "
5947942b919SKoji Sato 			       "blocknr=%llu, vblocknr=%llu\n",
5957942b919SKoji Sato 			       __func__, vdesc->vd_flags ? "node" : "data",
5967942b919SKoji Sato 			       (unsigned long long)vdesc->vd_ino,
5977942b919SKoji Sato 			       (unsigned long long)vdesc->vd_cno,
5987942b919SKoji Sato 			       (unsigned long long)vdesc->vd_offset,
5997942b919SKoji Sato 			       (unsigned long long)vdesc->vd_blocknr,
6007942b919SKoji Sato 			       (unsigned long long)vdesc->vd_vblocknr);
6017942b919SKoji Sato 		return ret;
6027942b919SKoji Sato 	}
6035399dd1fSRyusuke Konishi 	if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
6045399dd1fSRyusuke Konishi 		printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
6055399dd1fSRyusuke Konishi 		       "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
6065399dd1fSRyusuke Konishi 		       __func__, vdesc->vd_flags ? "node" : "data",
6075399dd1fSRyusuke Konishi 		       (unsigned long long)vdesc->vd_ino,
6085399dd1fSRyusuke Konishi 		       (unsigned long long)vdesc->vd_cno,
6095399dd1fSRyusuke Konishi 		       (unsigned long long)vdesc->vd_offset,
6105399dd1fSRyusuke Konishi 		       (unsigned long long)vdesc->vd_blocknr,
6115399dd1fSRyusuke Konishi 		       (unsigned long long)vdesc->vd_vblocknr);
6125399dd1fSRyusuke Konishi 		brelse(bh);
6135399dd1fSRyusuke Konishi 		return -EEXIST;
6145399dd1fSRyusuke Konishi 	}
6157942b919SKoji Sato 	list_add_tail(&bh->b_assoc_buffers, buffers);
6167942b919SKoji Sato 	return 0;
6177942b919SKoji Sato }
6187942b919SKoji Sato 
619d623a942SVyacheslav Dubeyko /**
620d623a942SVyacheslav Dubeyko  * nilfs_ioctl_move_blocks - move valid inode's blocks during garbage collection
621d623a942SVyacheslav Dubeyko  * @sb: superblock object
622d623a942SVyacheslav Dubeyko  * @argv: vector of arguments from userspace
623d623a942SVyacheslav Dubeyko  * @buf: array of nilfs_vdesc structures
624d623a942SVyacheslav Dubeyko  *
625d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_move_blocks() function reads valid data/node
626d623a942SVyacheslav Dubeyko  * blocks that garbage collector specified with the array of nilfs_vdesc
627d623a942SVyacheslav Dubeyko  * structures and stores them into page caches of GC inodes.
628d623a942SVyacheslav Dubeyko  *
629d623a942SVyacheslav Dubeyko  * Return Value: Number of processed nilfs_vdesc structures or
630d623a942SVyacheslav Dubeyko  * error code, otherwise.
631d623a942SVyacheslav Dubeyko  */
632263d90ceSRyusuke Konishi static int nilfs_ioctl_move_blocks(struct super_block *sb,
6334f6b8288SRyusuke Konishi 				   struct nilfs_argv *argv, void *buf)
6347942b919SKoji Sato {
6354f6b8288SRyusuke Konishi 	size_t nmembs = argv->v_nmembs;
636e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = sb->s_fs_info;
6377942b919SKoji Sato 	struct inode *inode;
6387942b919SKoji Sato 	struct nilfs_vdesc *vdesc;
6397942b919SKoji Sato 	struct buffer_head *bh, *n;
6407942b919SKoji Sato 	LIST_HEAD(buffers);
6417942b919SKoji Sato 	ino_t ino;
6427942b919SKoji Sato 	__u64 cno;
6437942b919SKoji Sato 	int i, ret;
6447942b919SKoji Sato 
6457942b919SKoji Sato 	for (i = 0, vdesc = buf; i < nmembs; ) {
6467942b919SKoji Sato 		ino = vdesc->vd_ino;
6477942b919SKoji Sato 		cno = vdesc->vd_cno;
648263d90ceSRyusuke Konishi 		inode = nilfs_iget_for_gc(sb, ino, cno);
649103cfcf5SDan Carpenter 		if (IS_ERR(inode)) {
650103cfcf5SDan Carpenter 			ret = PTR_ERR(inode);
6517942b919SKoji Sato 			goto failed;
6527942b919SKoji Sato 		}
653947b10aeSRyusuke Konishi 		if (list_empty(&NILFS_I(inode)->i_dirty)) {
654947b10aeSRyusuke Konishi 			/*
655947b10aeSRyusuke Konishi 			 * Add the inode to GC inode list. Garbage Collection
656947b10aeSRyusuke Konishi 			 * is serialized and no two processes manipulate the
657947b10aeSRyusuke Konishi 			 * list simultaneously.
658947b10aeSRyusuke Konishi 			 */
659947b10aeSRyusuke Konishi 			igrab(inode);
660947b10aeSRyusuke Konishi 			list_add(&NILFS_I(inode)->i_dirty,
661947b10aeSRyusuke Konishi 				 &nilfs->ns_gc_inodes);
662947b10aeSRyusuke Konishi 		}
663947b10aeSRyusuke Konishi 
6647942b919SKoji Sato 		do {
6657942b919SKoji Sato 			ret = nilfs_ioctl_move_inode_block(inode, vdesc,
6667942b919SKoji Sato 							   &buffers);
667263d90ceSRyusuke Konishi 			if (unlikely(ret < 0)) {
668263d90ceSRyusuke Konishi 				iput(inode);
6697942b919SKoji Sato 				goto failed;
670263d90ceSRyusuke Konishi 			}
6717942b919SKoji Sato 			vdesc++;
6727942b919SKoji Sato 		} while (++i < nmembs &&
6737942b919SKoji Sato 			 vdesc->vd_ino == ino && vdesc->vd_cno == cno);
674263d90ceSRyusuke Konishi 
675263d90ceSRyusuke Konishi 		iput(inode); /* The inode still remains in GC inode list */
6767942b919SKoji Sato 	}
6777942b919SKoji Sato 
6787942b919SKoji Sato 	list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
6797942b919SKoji Sato 		ret = nilfs_gccache_wait_and_mark_dirty(bh);
6807942b919SKoji Sato 		if (unlikely(ret < 0)) {
6815399dd1fSRyusuke Konishi 			WARN_ON(ret == -EEXIST);
6827942b919SKoji Sato 			goto failed;
6837942b919SKoji Sato 		}
6847942b919SKoji Sato 		list_del_init(&bh->b_assoc_buffers);
6857942b919SKoji Sato 		brelse(bh);
6867942b919SKoji Sato 	}
6877942b919SKoji Sato 	return nmembs;
6887942b919SKoji Sato 
6897942b919SKoji Sato  failed:
6907942b919SKoji Sato 	list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
6917942b919SKoji Sato 		list_del_init(&bh->b_assoc_buffers);
6927942b919SKoji Sato 		brelse(bh);
6937942b919SKoji Sato 	}
6947942b919SKoji Sato 	return ret;
6957942b919SKoji Sato }
6967942b919SKoji Sato 
697d623a942SVyacheslav Dubeyko /**
698d623a942SVyacheslav Dubeyko  * nilfs_ioctl_delete_checkpoints - delete checkpoints
699d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
700d623a942SVyacheslav Dubeyko  * @argv: vector of arguments from userspace
701d623a942SVyacheslav Dubeyko  * @buf: array of periods of checkpoints numbers
702d623a942SVyacheslav Dubeyko  *
703d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_delete_checkpoints() function deletes checkpoints
704d623a942SVyacheslav Dubeyko  * in the period from p_start to p_end, excluding p_end itself. The checkpoints
705d623a942SVyacheslav Dubeyko  * which have been already deleted are ignored.
706d623a942SVyacheslav Dubeyko  *
707d623a942SVyacheslav Dubeyko  * Return Value: Number of processed nilfs_period structures or
708d623a942SVyacheslav Dubeyko  * error code, otherwise.
709d623a942SVyacheslav Dubeyko  *
710d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
711d623a942SVyacheslav Dubeyko  *
712d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
713d623a942SVyacheslav Dubeyko  *
714d623a942SVyacheslav Dubeyko  * %-EINVAL - invalid checkpoints.
715d623a942SVyacheslav Dubeyko  */
7164f6b8288SRyusuke Konishi static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs,
7174f6b8288SRyusuke Konishi 					  struct nilfs_argv *argv, void *buf)
7187942b919SKoji Sato {
7194f6b8288SRyusuke Konishi 	size_t nmembs = argv->v_nmembs;
7207942b919SKoji Sato 	struct inode *cpfile = nilfs->ns_cpfile;
7217942b919SKoji Sato 	struct nilfs_period *periods = buf;
7227942b919SKoji Sato 	int ret, i;
7237942b919SKoji Sato 
7247942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
7257942b919SKoji Sato 		ret = nilfs_cpfile_delete_checkpoints(
7267942b919SKoji Sato 			cpfile, periods[i].p_start, periods[i].p_end);
7277942b919SKoji Sato 		if (ret < 0)
7287942b919SKoji Sato 			return ret;
7297942b919SKoji Sato 	}
7307942b919SKoji Sato 	return nmembs;
7317942b919SKoji Sato }
7327942b919SKoji Sato 
733d623a942SVyacheslav Dubeyko /**
734d623a942SVyacheslav Dubeyko  * nilfs_ioctl_free_vblocknrs - free virtual block numbers
735d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
736d623a942SVyacheslav Dubeyko  * @argv: vector of arguments from userspace
737d623a942SVyacheslav Dubeyko  * @buf: array of virtual block numbers
738d623a942SVyacheslav Dubeyko  *
739d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_free_vblocknrs() function frees
740d623a942SVyacheslav Dubeyko  * the virtual block numbers specified by @buf and @argv->v_nmembs.
741d623a942SVyacheslav Dubeyko  *
742d623a942SVyacheslav Dubeyko  * Return Value: Number of processed virtual block numbers or
743d623a942SVyacheslav Dubeyko  * error code, otherwise.
744d623a942SVyacheslav Dubeyko  *
745d623a942SVyacheslav Dubeyko  * %-EIO - I/O error.
746d623a942SVyacheslav Dubeyko  *
747d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
748d623a942SVyacheslav Dubeyko  *
749d623a942SVyacheslav Dubeyko  * %-ENOENT - The virtual block number have not been allocated.
750d623a942SVyacheslav Dubeyko  */
7514f6b8288SRyusuke Konishi static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs,
7524f6b8288SRyusuke Konishi 				      struct nilfs_argv *argv, void *buf)
7537942b919SKoji Sato {
7544f6b8288SRyusuke Konishi 	size_t nmembs = argv->v_nmembs;
7554f6b8288SRyusuke Konishi 	int ret;
7567942b919SKoji Sato 
757365e215cSRyusuke Konishi 	ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs);
7587942b919SKoji Sato 
7597942b919SKoji Sato 	return (ret < 0) ? ret : nmembs;
7607942b919SKoji Sato }
7617942b919SKoji Sato 
762d623a942SVyacheslav Dubeyko /**
763d623a942SVyacheslav Dubeyko  * nilfs_ioctl_mark_blocks_dirty - mark blocks dirty
764d623a942SVyacheslav Dubeyko  * @nilfs: nilfs object
765d623a942SVyacheslav Dubeyko  * @argv: vector of arguments from userspace
766d623a942SVyacheslav Dubeyko  * @buf: array of block descriptors
767d623a942SVyacheslav Dubeyko  *
768d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_mark_blocks_dirty() function marks
769d623a942SVyacheslav Dubeyko  * metadata file or data blocks as dirty.
770d623a942SVyacheslav Dubeyko  *
771d623a942SVyacheslav Dubeyko  * Return Value: Number of processed block descriptors or
772d623a942SVyacheslav Dubeyko  * error code, otherwise.
773d623a942SVyacheslav Dubeyko  *
774d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient memory available.
775d623a942SVyacheslav Dubeyko  *
776d623a942SVyacheslav Dubeyko  * %-EIO - I/O error
777d623a942SVyacheslav Dubeyko  *
778d623a942SVyacheslav Dubeyko  * %-ENOENT - the specified block does not exist (hole block)
779d623a942SVyacheslav Dubeyko  */
7804f6b8288SRyusuke Konishi static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
7814f6b8288SRyusuke Konishi 					 struct nilfs_argv *argv, void *buf)
7827942b919SKoji Sato {
7834f6b8288SRyusuke Konishi 	size_t nmembs = argv->v_nmembs;
784365e215cSRyusuke Konishi 	struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
7857942b919SKoji Sato 	struct nilfs_bdesc *bdescs = buf;
7867942b919SKoji Sato 	int ret, i;
7877942b919SKoji Sato 
7887942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
7897942b919SKoji Sato 		/* XXX: use macro or inline func to check liveness */
7907942b919SKoji Sato 		ret = nilfs_bmap_lookup_at_level(bmap,
7917942b919SKoji Sato 						 bdescs[i].bd_offset,
7927942b919SKoji Sato 						 bdescs[i].bd_level + 1,
7937942b919SKoji Sato 						 &bdescs[i].bd_blocknr);
7947942b919SKoji Sato 		if (ret < 0) {
7957942b919SKoji Sato 			if (ret != -ENOENT)
7967942b919SKoji Sato 				return ret;
7977942b919SKoji Sato 			bdescs[i].bd_blocknr = 0;
7987942b919SKoji Sato 		}
7997942b919SKoji Sato 		if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr)
8007942b919SKoji Sato 			/* skip dead block */
8017942b919SKoji Sato 			continue;
8027942b919SKoji Sato 		if (bdescs[i].bd_level == 0) {
803365e215cSRyusuke Konishi 			ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat,
8047942b919SKoji Sato 							 bdescs[i].bd_offset);
8057942b919SKoji Sato 			if (ret < 0) {
8061f5abe7eSRyusuke Konishi 				WARN_ON(ret == -ENOENT);
8077942b919SKoji Sato 				return ret;
8087942b919SKoji Sato 			}
8097942b919SKoji Sato 		} else {
8107942b919SKoji Sato 			ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset,
8117942b919SKoji Sato 					      bdescs[i].bd_level);
8127942b919SKoji Sato 			if (ret < 0) {
8131f5abe7eSRyusuke Konishi 				WARN_ON(ret == -ENOENT);
8147942b919SKoji Sato 				return ret;
8157942b919SKoji Sato 			}
8167942b919SKoji Sato 		}
8177942b919SKoji Sato 	}
8187942b919SKoji Sato 	return nmembs;
8197942b919SKoji Sato }
8207942b919SKoji Sato 
8217942b919SKoji Sato int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
8224f6b8288SRyusuke Konishi 				       struct nilfs_argv *argv, void **kbufs)
8237942b919SKoji Sato {
8241f5abe7eSRyusuke Konishi 	const char *msg;
8254f6b8288SRyusuke Konishi 	int ret;
8267942b919SKoji Sato 
8274f6b8288SRyusuke Konishi 	ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]);
8281f5abe7eSRyusuke Konishi 	if (ret < 0) {
8291f5abe7eSRyusuke Konishi 		/*
8301f5abe7eSRyusuke Konishi 		 * can safely abort because checkpoints can be removed
8311f5abe7eSRyusuke Konishi 		 * independently.
8321f5abe7eSRyusuke Konishi 		 */
8331f5abe7eSRyusuke Konishi 		msg = "cannot delete checkpoints";
8341f5abe7eSRyusuke Konishi 		goto failed;
8351f5abe7eSRyusuke Konishi 	}
8364f6b8288SRyusuke Konishi 	ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]);
8371f5abe7eSRyusuke Konishi 	if (ret < 0) {
8381f5abe7eSRyusuke Konishi 		/*
8391f5abe7eSRyusuke Konishi 		 * can safely abort because DAT file is updated atomically
8401f5abe7eSRyusuke Konishi 		 * using a copy-on-write technique.
8411f5abe7eSRyusuke Konishi 		 */
8421f5abe7eSRyusuke Konishi 		msg = "cannot delete virtual blocks from DAT file";
8431f5abe7eSRyusuke Konishi 		goto failed;
8441f5abe7eSRyusuke Konishi 	}
8454f6b8288SRyusuke Konishi 	ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]);
8461f5abe7eSRyusuke Konishi 	if (ret < 0) {
8471f5abe7eSRyusuke Konishi 		/*
8481f5abe7eSRyusuke Konishi 		 * can safely abort because the operation is nondestructive.
8491f5abe7eSRyusuke Konishi 		 */
8501f5abe7eSRyusuke Konishi 		msg = "cannot mark copying blocks dirty";
8511f5abe7eSRyusuke Konishi 		goto failed;
8521f5abe7eSRyusuke Konishi 	}
8537942b919SKoji Sato 	return 0;
8547942b919SKoji Sato 
8551f5abe7eSRyusuke Konishi  failed:
8561f5abe7eSRyusuke Konishi 	printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n",
8571f5abe7eSRyusuke Konishi 	       msg, ret);
8587942b919SKoji Sato 	return ret;
8597942b919SKoji Sato }
8607942b919SKoji Sato 
861d623a942SVyacheslav Dubeyko /**
862d623a942SVyacheslav Dubeyko  * nilfs_ioctl_clean_segments - clean segments
863d623a942SVyacheslav Dubeyko  * @inode: inode object
864d623a942SVyacheslav Dubeyko  * @filp: file object
865d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
866d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
867d623a942SVyacheslav Dubeyko  *
868d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_clean_segments() function makes garbage
869d623a942SVyacheslav Dubeyko  * collection operation in the environment of requested parameters
870d623a942SVyacheslav Dubeyko  * from userspace. The NILFS_IOCTL_CLEAN_SEGMENTS ioctl is used by
871d623a942SVyacheslav Dubeyko  * nilfs_cleanerd daemon.
872d623a942SVyacheslav Dubeyko  *
873d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned or error code, otherwise.
874d623a942SVyacheslav Dubeyko  */
8757942b919SKoji Sato static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
8767942b919SKoji Sato 				      unsigned int cmd, void __user *argp)
8777942b919SKoji Sato {
8784f6b8288SRyusuke Konishi 	struct nilfs_argv argv[5];
87933e189bdSTobias Klauser 	static const size_t argsz[5] = {
8804f6b8288SRyusuke Konishi 		sizeof(struct nilfs_vdesc),
8814f6b8288SRyusuke Konishi 		sizeof(struct nilfs_period),
8824f6b8288SRyusuke Konishi 		sizeof(__u64),
8834f6b8288SRyusuke Konishi 		sizeof(struct nilfs_bdesc),
8844f6b8288SRyusuke Konishi 		sizeof(__u64),
8854f6b8288SRyusuke Konishi 	};
8864f6b8288SRyusuke Konishi 	void __user *base;
8874f6b8288SRyusuke Konishi 	void *kbufs[5];
8884f6b8288SRyusuke Konishi 	struct the_nilfs *nilfs;
8894f6b8288SRyusuke Konishi 	size_t len, nsegs;
8904f6b8288SRyusuke Konishi 	int n, ret;
8914f6b8288SRyusuke Konishi 
8927942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
8937942b919SKoji Sato 		return -EPERM;
8944f6b8288SRyusuke Konishi 
895a561be71SAl Viro 	ret = mnt_want_write_file(filp);
8967512487eSRyusuke Konishi 	if (ret)
8977512487eSRyusuke Konishi 		return ret;
8984f6b8288SRyusuke Konishi 
8997512487eSRyusuke Konishi 	ret = -EFAULT;
9007512487eSRyusuke Konishi 	if (copy_from_user(argv, argp, sizeof(argv)))
9017512487eSRyusuke Konishi 		goto out;
9027512487eSRyusuke Konishi 
9037512487eSRyusuke Konishi 	ret = -EINVAL;
9044f6b8288SRyusuke Konishi 	nsegs = argv[4].v_nmembs;
9054f6b8288SRyusuke Konishi 	if (argv[4].v_size != argsz[4])
9067512487eSRyusuke Konishi 		goto out;
9071ecd3c7eSXi Wang 	if (nsegs > UINT_MAX / sizeof(__u64))
9081ecd3c7eSXi Wang 		goto out;
9097512487eSRyusuke Konishi 
9104f6b8288SRyusuke Konishi 	/*
9114f6b8288SRyusuke Konishi 	 * argv[4] points to segment numbers this ioctl cleans.  We
9124f6b8288SRyusuke Konishi 	 * use kmalloc() for its buffer because memory used for the
9134f6b8288SRyusuke Konishi 	 * segment numbers is enough small.
9144f6b8288SRyusuke Konishi 	 */
9154f6b8288SRyusuke Konishi 	kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base,
9164f6b8288SRyusuke Konishi 			       nsegs * sizeof(__u64));
9177512487eSRyusuke Konishi 	if (IS_ERR(kbufs[4])) {
9187512487eSRyusuke Konishi 		ret = PTR_ERR(kbufs[4]);
9197512487eSRyusuke Konishi 		goto out;
9207512487eSRyusuke Konishi 	}
921e3154e97SRyusuke Konishi 	nilfs = inode->i_sb->s_fs_info;
9224f6b8288SRyusuke Konishi 
9234f6b8288SRyusuke Konishi 	for (n = 0; n < 4; n++) {
9244f6b8288SRyusuke Konishi 		ret = -EINVAL;
9254f6b8288SRyusuke Konishi 		if (argv[n].v_size != argsz[n])
9264f6b8288SRyusuke Konishi 			goto out_free;
9274f6b8288SRyusuke Konishi 
9284f6b8288SRyusuke Konishi 		if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment)
9294f6b8288SRyusuke Konishi 			goto out_free;
9304f6b8288SRyusuke Konishi 
931481fe17eSHaogang Chen 		if (argv[n].v_nmembs >= UINT_MAX / argv[n].v_size)
932481fe17eSHaogang Chen 			goto out_free;
933481fe17eSHaogang Chen 
9344f6b8288SRyusuke Konishi 		len = argv[n].v_size * argv[n].v_nmembs;
9354f6b8288SRyusuke Konishi 		base = (void __user *)(unsigned long)argv[n].v_base;
9364f6b8288SRyusuke Konishi 		if (len == 0) {
9374f6b8288SRyusuke Konishi 			kbufs[n] = NULL;
9384f6b8288SRyusuke Konishi 			continue;
9394f6b8288SRyusuke Konishi 		}
9404f6b8288SRyusuke Konishi 
9414f6b8288SRyusuke Konishi 		kbufs[n] = vmalloc(len);
9424f6b8288SRyusuke Konishi 		if (!kbufs[n]) {
9434f6b8288SRyusuke Konishi 			ret = -ENOMEM;
9444f6b8288SRyusuke Konishi 			goto out_free;
9454f6b8288SRyusuke Konishi 		}
9464f6b8288SRyusuke Konishi 		if (copy_from_user(kbufs[n], base, len)) {
9474f6b8288SRyusuke Konishi 			ret = -EFAULT;
9484f6b8288SRyusuke Konishi 			vfree(kbufs[n]);
9494f6b8288SRyusuke Konishi 			goto out_free;
9504f6b8288SRyusuke Konishi 		}
9514f6b8288SRyusuke Konishi 	}
9524f6b8288SRyusuke Konishi 
9531cf58fa8SJiro SEKIBA 	/*
954263d90ceSRyusuke Konishi 	 * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(),
9551cf58fa8SJiro SEKIBA 	 * which will operates an inode list without blocking.
9561cf58fa8SJiro SEKIBA 	 * To protect the list from concurrent operations,
9571cf58fa8SJiro SEKIBA 	 * nilfs_ioctl_move_blocks should be atomic operation.
9581cf58fa8SJiro SEKIBA 	 */
9591cf58fa8SJiro SEKIBA 	if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) {
9601cf58fa8SJiro SEKIBA 		ret = -EBUSY;
9611cf58fa8SJiro SEKIBA 		goto out_free;
9621cf58fa8SJiro SEKIBA 	}
9631cf58fa8SJiro SEKIBA 
964263d90ceSRyusuke Konishi 	ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]);
9651cf58fa8SJiro SEKIBA 	if (ret < 0)
9661cf58fa8SJiro SEKIBA 		printk(KERN_ERR "NILFS: GC failed during preparation: "
9671cf58fa8SJiro SEKIBA 			"cannot read source blocks: err=%d\n", ret);
968a9bae189SVyacheslav Dubeyko 	else {
969a9bae189SVyacheslav Dubeyko 		if (nilfs_sb_need_update(nilfs))
970a9bae189SVyacheslav Dubeyko 			set_nilfs_discontinued(nilfs);
9714f6b8288SRyusuke Konishi 		ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
972a9bae189SVyacheslav Dubeyko 	}
9734f6b8288SRyusuke Konishi 
974263d90ceSRyusuke Konishi 	nilfs_remove_all_gcinodes(nilfs);
9751cf58fa8SJiro SEKIBA 	clear_nilfs_gc_running(nilfs);
9761cf58fa8SJiro SEKIBA 
9774f6b8288SRyusuke Konishi out_free:
978d5046853SRyusuke Konishi 	while (--n >= 0)
9794f6b8288SRyusuke Konishi 		vfree(kbufs[n]);
9804f6b8288SRyusuke Konishi 	kfree(kbufs[4]);
9817512487eSRyusuke Konishi out:
9822a79f17eSAl Viro 	mnt_drop_write_file(filp);
9834f6b8288SRyusuke Konishi 	return ret;
9847942b919SKoji Sato }
9857942b919SKoji Sato 
986d623a942SVyacheslav Dubeyko /**
987d623a942SVyacheslav Dubeyko  * nilfs_ioctl_sync - make a checkpoint
988d623a942SVyacheslav Dubeyko  * @inode: inode object
989d623a942SVyacheslav Dubeyko  * @filp: file object
990d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
991d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
992d623a942SVyacheslav Dubeyko  *
993d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_sync() function constructs a logical segment
994d623a942SVyacheslav Dubeyko  * for checkpointing.  This function guarantees that all modified data
995d623a942SVyacheslav Dubeyko  * and metadata are written out to the device when it successfully
996d623a942SVyacheslav Dubeyko  * returned.
997d623a942SVyacheslav Dubeyko  *
998d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is retured. On errors, one of the following
999d623a942SVyacheslav Dubeyko  * negative error code is returned.
1000d623a942SVyacheslav Dubeyko  *
1001d623a942SVyacheslav Dubeyko  * %-EROFS - Read only filesystem.
1002d623a942SVyacheslav Dubeyko  *
1003d623a942SVyacheslav Dubeyko  * %-EIO - I/O error
1004d623a942SVyacheslav Dubeyko  *
1005d623a942SVyacheslav Dubeyko  * %-ENOSPC - No space left on device (only in a panic state).
1006d623a942SVyacheslav Dubeyko  *
1007d623a942SVyacheslav Dubeyko  * %-ERESTARTSYS - Interrupted.
1008d623a942SVyacheslav Dubeyko  *
1009d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient memory available.
1010d623a942SVyacheslav Dubeyko  *
1011d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during execution of requested operation.
1012d623a942SVyacheslav Dubeyko  */
10137942b919SKoji Sato static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
10147942b919SKoji Sato 			    unsigned int cmd, void __user *argp)
10157942b919SKoji Sato {
10167942b919SKoji Sato 	__u64 cno;
10177942b919SKoji Sato 	int ret;
10180d561f12SJiro SEKIBA 	struct the_nilfs *nilfs;
10197942b919SKoji Sato 
10207942b919SKoji Sato 	ret = nilfs_construct_segment(inode->i_sb);
10217942b919SKoji Sato 	if (ret < 0)
10227942b919SKoji Sato 		return ret;
10237942b919SKoji Sato 
1024e3154e97SRyusuke Konishi 	nilfs = inode->i_sb->s_fs_info;
1025e2c7617aSAndreas Rohner 	ret = nilfs_flush_device(nilfs);
1026e2c7617aSAndreas Rohner 	if (ret < 0)
102711475975SRyusuke Konishi 		return ret;
102811475975SRyusuke Konishi 
102911475975SRyusuke Konishi 	if (argp != NULL) {
10300d561f12SJiro SEKIBA 		down_read(&nilfs->ns_segctor_sem);
10310d561f12SJiro SEKIBA 		cno = nilfs->ns_cno - 1;
10320d561f12SJiro SEKIBA 		up_read(&nilfs->ns_segctor_sem);
10337942b919SKoji Sato 		if (copy_to_user(argp, &cno, sizeof(cno)))
10347942b919SKoji Sato 			return -EFAULT;
10357942b919SKoji Sato 	}
10367942b919SKoji Sato 	return 0;
10377942b919SKoji Sato }
10387942b919SKoji Sato 
1039d623a942SVyacheslav Dubeyko /**
1040d623a942SVyacheslav Dubeyko  * nilfs_ioctl_resize - resize NILFS2 volume
1041d623a942SVyacheslav Dubeyko  * @inode: inode object
1042d623a942SVyacheslav Dubeyko  * @filp: file object
1043d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
1044d623a942SVyacheslav Dubeyko  *
1045d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned or error code, otherwise.
1046d623a942SVyacheslav Dubeyko  */
10474e33f9eaSRyusuke Konishi static int nilfs_ioctl_resize(struct inode *inode, struct file *filp,
10484e33f9eaSRyusuke Konishi 			      void __user *argp)
10494e33f9eaSRyusuke Konishi {
10504e33f9eaSRyusuke Konishi 	__u64 newsize;
10514e33f9eaSRyusuke Konishi 	int ret = -EPERM;
10524e33f9eaSRyusuke Konishi 
10534e33f9eaSRyusuke Konishi 	if (!capable(CAP_SYS_ADMIN))
10544e33f9eaSRyusuke Konishi 		goto out;
10554e33f9eaSRyusuke Konishi 
1056a561be71SAl Viro 	ret = mnt_want_write_file(filp);
10574e33f9eaSRyusuke Konishi 	if (ret)
10584e33f9eaSRyusuke Konishi 		goto out;
10594e33f9eaSRyusuke Konishi 
10604e33f9eaSRyusuke Konishi 	ret = -EFAULT;
10614e33f9eaSRyusuke Konishi 	if (copy_from_user(&newsize, argp, sizeof(newsize)))
10624e33f9eaSRyusuke Konishi 		goto out_drop_write;
10634e33f9eaSRyusuke Konishi 
10644e33f9eaSRyusuke Konishi 	ret = nilfs_resize_fs(inode->i_sb, newsize);
10654e33f9eaSRyusuke Konishi 
10664e33f9eaSRyusuke Konishi out_drop_write:
10672a79f17eSAl Viro 	mnt_drop_write_file(filp);
10684e33f9eaSRyusuke Konishi out:
10694e33f9eaSRyusuke Konishi 	return ret;
10704e33f9eaSRyusuke Konishi }
10714e33f9eaSRyusuke Konishi 
1072d623a942SVyacheslav Dubeyko /**
1073f9f32c44SAndreas Rohner  * nilfs_ioctl_trim_fs() - trim ioctl handle function
1074f9f32c44SAndreas Rohner  * @inode: inode object
1075f9f32c44SAndreas Rohner  * @argp: pointer on argument from userspace
1076f9f32c44SAndreas Rohner  *
1077f9f32c44SAndreas Rohner  * Decription: nilfs_ioctl_trim_fs is the FITRIM ioctl handle function. It
1078f9f32c44SAndreas Rohner  * checks the arguments from userspace and calls nilfs_sufile_trim_fs, which
1079f9f32c44SAndreas Rohner  * performs the actual trim operation.
1080f9f32c44SAndreas Rohner  *
1081f9f32c44SAndreas Rohner  * Return Value: On success, 0 is returned or negative error code, otherwise.
1082f9f32c44SAndreas Rohner  */
1083f9f32c44SAndreas Rohner static int nilfs_ioctl_trim_fs(struct inode *inode, void __user *argp)
1084f9f32c44SAndreas Rohner {
1085f9f32c44SAndreas Rohner 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
1086f9f32c44SAndreas Rohner 	struct request_queue *q = bdev_get_queue(nilfs->ns_bdev);
1087f9f32c44SAndreas Rohner 	struct fstrim_range range;
1088f9f32c44SAndreas Rohner 	int ret;
1089f9f32c44SAndreas Rohner 
1090f9f32c44SAndreas Rohner 	if (!capable(CAP_SYS_ADMIN))
1091f9f32c44SAndreas Rohner 		return -EPERM;
1092f9f32c44SAndreas Rohner 
1093f9f32c44SAndreas Rohner 	if (!blk_queue_discard(q))
1094f9f32c44SAndreas Rohner 		return -EOPNOTSUPP;
1095f9f32c44SAndreas Rohner 
1096f9f32c44SAndreas Rohner 	if (copy_from_user(&range, argp, sizeof(range)))
1097f9f32c44SAndreas Rohner 		return -EFAULT;
1098f9f32c44SAndreas Rohner 
1099f9f32c44SAndreas Rohner 	range.minlen = max_t(u64, range.minlen, q->limits.discard_granularity);
1100f9f32c44SAndreas Rohner 
1101f9f32c44SAndreas Rohner 	down_read(&nilfs->ns_segctor_sem);
1102f9f32c44SAndreas Rohner 	ret = nilfs_sufile_trim_fs(nilfs->ns_sufile, &range);
1103f9f32c44SAndreas Rohner 	up_read(&nilfs->ns_segctor_sem);
1104f9f32c44SAndreas Rohner 
1105f9f32c44SAndreas Rohner 	if (ret < 0)
1106f9f32c44SAndreas Rohner 		return ret;
1107f9f32c44SAndreas Rohner 
1108f9f32c44SAndreas Rohner 	if (copy_to_user(argp, &range, sizeof(range)))
1109f9f32c44SAndreas Rohner 		return -EFAULT;
1110f9f32c44SAndreas Rohner 
1111f9f32c44SAndreas Rohner 	return 0;
1112f9f32c44SAndreas Rohner }
1113f9f32c44SAndreas Rohner 
1114f9f32c44SAndreas Rohner /**
1115d623a942SVyacheslav Dubeyko  * nilfs_ioctl_set_alloc_range - limit range of segments to be allocated
1116d623a942SVyacheslav Dubeyko  * @inode: inode object
1117d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
1118d623a942SVyacheslav Dubeyko  *
1119d623a942SVyacheslav Dubeyko  * Decription: nilfs_ioctl_set_alloc_range() function defines lower limit
1120d623a942SVyacheslav Dubeyko  * of segments in bytes and upper limit of segments in bytes.
1121d623a942SVyacheslav Dubeyko  * The NILFS_IOCTL_SET_ALLOC_RANGE is used by nilfs_resize utility.
1122d623a942SVyacheslav Dubeyko  *
1123d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned or error code, otherwise.
1124d623a942SVyacheslav Dubeyko  */
1125619205daSRyusuke Konishi static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp)
1126619205daSRyusuke Konishi {
1127619205daSRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
1128619205daSRyusuke Konishi 	__u64 range[2];
1129619205daSRyusuke Konishi 	__u64 minseg, maxseg;
1130619205daSRyusuke Konishi 	unsigned long segbytes;
1131619205daSRyusuke Konishi 	int ret = -EPERM;
1132619205daSRyusuke Konishi 
1133619205daSRyusuke Konishi 	if (!capable(CAP_SYS_ADMIN))
1134619205daSRyusuke Konishi 		goto out;
1135619205daSRyusuke Konishi 
1136619205daSRyusuke Konishi 	ret = -EFAULT;
1137619205daSRyusuke Konishi 	if (copy_from_user(range, argp, sizeof(__u64[2])))
1138619205daSRyusuke Konishi 		goto out;
1139619205daSRyusuke Konishi 
1140619205daSRyusuke Konishi 	ret = -ERANGE;
1141619205daSRyusuke Konishi 	if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode))
1142619205daSRyusuke Konishi 		goto out;
1143619205daSRyusuke Konishi 
1144619205daSRyusuke Konishi 	segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize;
1145619205daSRyusuke Konishi 
1146619205daSRyusuke Konishi 	minseg = range[0] + segbytes - 1;
1147619205daSRyusuke Konishi 	do_div(minseg, segbytes);
1148619205daSRyusuke Konishi 	maxseg = NILFS_SB2_OFFSET_BYTES(range[1]);
1149619205daSRyusuke Konishi 	do_div(maxseg, segbytes);
1150619205daSRyusuke Konishi 	maxseg--;
1151619205daSRyusuke Konishi 
1152619205daSRyusuke Konishi 	ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg);
1153619205daSRyusuke Konishi out:
1154619205daSRyusuke Konishi 	return ret;
1155619205daSRyusuke Konishi }
1156619205daSRyusuke Konishi 
1157d623a942SVyacheslav Dubeyko /**
1158d623a942SVyacheslav Dubeyko  * nilfs_ioctl_get_info - wrapping function of get metadata info
1159d623a942SVyacheslav Dubeyko  * @inode: inode object
1160d623a942SVyacheslav Dubeyko  * @filp: file object
1161d623a942SVyacheslav Dubeyko  * @cmd: ioctl's request code
1162d623a942SVyacheslav Dubeyko  * @argp: pointer on argument from userspace
1163d623a942SVyacheslav Dubeyko  * @membsz: size of an item in bytes
1164d623a942SVyacheslav Dubeyko  * @dofunc: concrete function of getting metadata info
1165d623a942SVyacheslav Dubeyko  *
1166d623a942SVyacheslav Dubeyko  * Description: nilfs_ioctl_get_info() gets metadata info by means of
1167d623a942SVyacheslav Dubeyko  * calling dofunc() function.
1168d623a942SVyacheslav Dubeyko  *
1169d623a942SVyacheslav Dubeyko  * Return Value: On success, 0 is returned and requested metadata info
1170d623a942SVyacheslav Dubeyko  * is copied into userspace. On error, one of the following
1171d623a942SVyacheslav Dubeyko  * negative error codes is returned.
1172d623a942SVyacheslav Dubeyko  *
1173d623a942SVyacheslav Dubeyko  * %-EINVAL - Invalid arguments from userspace.
1174d623a942SVyacheslav Dubeyko  *
1175d623a942SVyacheslav Dubeyko  * %-ENOMEM - Insufficient amount of memory available.
1176d623a942SVyacheslav Dubeyko  *
1177d623a942SVyacheslav Dubeyko  * %-EFAULT - Failure during execution of requested operation.
1178d623a942SVyacheslav Dubeyko  */
117947eb6b9cSRyusuke Konishi static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
118047eb6b9cSRyusuke Konishi 				unsigned int cmd, void __user *argp,
118183aca8f4SRyusuke Konishi 				size_t membsz,
118247eb6b9cSRyusuke Konishi 				ssize_t (*dofunc)(struct the_nilfs *,
118347eb6b9cSRyusuke Konishi 						  __u64 *, int,
118447eb6b9cSRyusuke Konishi 						  void *, size_t, size_t))
118547eb6b9cSRyusuke Konishi 
118647eb6b9cSRyusuke Konishi {
1187e3154e97SRyusuke Konishi 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
118847eb6b9cSRyusuke Konishi 	struct nilfs_argv argv;
118947eb6b9cSRyusuke Konishi 	int ret;
119047eb6b9cSRyusuke Konishi 
119147eb6b9cSRyusuke Konishi 	if (copy_from_user(&argv, argp, sizeof(argv)))
119247eb6b9cSRyusuke Konishi 		return -EFAULT;
119347eb6b9cSRyusuke Konishi 
1194003ff182SRyusuke Konishi 	if (argv.v_size < membsz)
119583aca8f4SRyusuke Konishi 		return -EINVAL;
119683aca8f4SRyusuke Konishi 
119747eb6b9cSRyusuke Konishi 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);
119847eb6b9cSRyusuke Konishi 	if (ret < 0)
119947eb6b9cSRyusuke Konishi 		return ret;
120047eb6b9cSRyusuke Konishi 
120147eb6b9cSRyusuke Konishi 	if (copy_to_user(argp, &argv, sizeof(argv)))
120247eb6b9cSRyusuke Konishi 		ret = -EFAULT;
120347eb6b9cSRyusuke Konishi 	return ret;
120447eb6b9cSRyusuke Konishi }
120547eb6b9cSRyusuke Konishi 
12062cc88f3aSAndreas Rohner /**
12072cc88f3aSAndreas Rohner  * nilfs_ioctl_set_suinfo - set segment usage info
12082cc88f3aSAndreas Rohner  * @inode: inode object
12092cc88f3aSAndreas Rohner  * @filp: file object
12102cc88f3aSAndreas Rohner  * @cmd: ioctl's request code
12112cc88f3aSAndreas Rohner  * @argp: pointer on argument from userspace
12122cc88f3aSAndreas Rohner  *
12132cc88f3aSAndreas Rohner  * Description: Expects an array of nilfs_suinfo_update structures
12142cc88f3aSAndreas Rohner  * encapsulated in nilfs_argv and updates the segment usage info
12152cc88f3aSAndreas Rohner  * according to the flags in nilfs_suinfo_update.
12162cc88f3aSAndreas Rohner  *
12172cc88f3aSAndreas Rohner  * Return Value: On success, 0 is returned. On error, one of the
12182cc88f3aSAndreas Rohner  * following negative error codes is returned.
12192cc88f3aSAndreas Rohner  *
12202cc88f3aSAndreas Rohner  * %-EPERM - Not enough permissions
12212cc88f3aSAndreas Rohner  *
12222cc88f3aSAndreas Rohner  * %-EFAULT - Error copying input data
12232cc88f3aSAndreas Rohner  *
12242cc88f3aSAndreas Rohner  * %-EIO - I/O error.
12252cc88f3aSAndreas Rohner  *
12262cc88f3aSAndreas Rohner  * %-ENOMEM - Insufficient amount of memory available.
12272cc88f3aSAndreas Rohner  *
12282cc88f3aSAndreas Rohner  * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
12292cc88f3aSAndreas Rohner  */
12302cc88f3aSAndreas Rohner static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp,
12312cc88f3aSAndreas Rohner 				unsigned int cmd, void __user *argp)
12322cc88f3aSAndreas Rohner {
12332cc88f3aSAndreas Rohner 	struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
12342cc88f3aSAndreas Rohner 	struct nilfs_transaction_info ti;
12352cc88f3aSAndreas Rohner 	struct nilfs_argv argv;
12362cc88f3aSAndreas Rohner 	size_t len;
12372cc88f3aSAndreas Rohner 	void __user *base;
12382cc88f3aSAndreas Rohner 	void *kbuf;
12392cc88f3aSAndreas Rohner 	int ret;
12402cc88f3aSAndreas Rohner 
12412cc88f3aSAndreas Rohner 	if (!capable(CAP_SYS_ADMIN))
12422cc88f3aSAndreas Rohner 		return -EPERM;
12432cc88f3aSAndreas Rohner 
12442cc88f3aSAndreas Rohner 	ret = mnt_want_write_file(filp);
12452cc88f3aSAndreas Rohner 	if (ret)
12462cc88f3aSAndreas Rohner 		return ret;
12472cc88f3aSAndreas Rohner 
12482cc88f3aSAndreas Rohner 	ret = -EFAULT;
12492cc88f3aSAndreas Rohner 	if (copy_from_user(&argv, argp, sizeof(argv)))
12502cc88f3aSAndreas Rohner 		goto out;
12512cc88f3aSAndreas Rohner 
12522cc88f3aSAndreas Rohner 	ret = -EINVAL;
12532cc88f3aSAndreas Rohner 	if (argv.v_size < sizeof(struct nilfs_suinfo_update))
12542cc88f3aSAndreas Rohner 		goto out;
12552cc88f3aSAndreas Rohner 
12562cc88f3aSAndreas Rohner 	if (argv.v_nmembs > nilfs->ns_nsegments)
12572cc88f3aSAndreas Rohner 		goto out;
12582cc88f3aSAndreas Rohner 
12592cc88f3aSAndreas Rohner 	if (argv.v_nmembs >= UINT_MAX / argv.v_size)
12602cc88f3aSAndreas Rohner 		goto out;
12612cc88f3aSAndreas Rohner 
12622cc88f3aSAndreas Rohner 	len = argv.v_size * argv.v_nmembs;
12632cc88f3aSAndreas Rohner 	if (!len) {
12642cc88f3aSAndreas Rohner 		ret = 0;
12652cc88f3aSAndreas Rohner 		goto out;
12662cc88f3aSAndreas Rohner 	}
12672cc88f3aSAndreas Rohner 
12682cc88f3aSAndreas Rohner 	base = (void __user *)(unsigned long)argv.v_base;
12692cc88f3aSAndreas Rohner 	kbuf = vmalloc(len);
12702cc88f3aSAndreas Rohner 	if (!kbuf) {
12712cc88f3aSAndreas Rohner 		ret = -ENOMEM;
12722cc88f3aSAndreas Rohner 		goto out;
12732cc88f3aSAndreas Rohner 	}
12742cc88f3aSAndreas Rohner 
12752cc88f3aSAndreas Rohner 	if (copy_from_user(kbuf, base, len)) {
12762cc88f3aSAndreas Rohner 		ret = -EFAULT;
12772cc88f3aSAndreas Rohner 		goto out_free;
12782cc88f3aSAndreas Rohner 	}
12792cc88f3aSAndreas Rohner 
12802cc88f3aSAndreas Rohner 	nilfs_transaction_begin(inode->i_sb, &ti, 0);
12812cc88f3aSAndreas Rohner 	ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size,
12822cc88f3aSAndreas Rohner 			argv.v_nmembs);
12832cc88f3aSAndreas Rohner 	if (unlikely(ret < 0))
12842cc88f3aSAndreas Rohner 		nilfs_transaction_abort(inode->i_sb);
12852cc88f3aSAndreas Rohner 	else
12862cc88f3aSAndreas Rohner 		nilfs_transaction_commit(inode->i_sb); /* never fails */
12872cc88f3aSAndreas Rohner 
12882cc88f3aSAndreas Rohner out_free:
12892cc88f3aSAndreas Rohner 	vfree(kbuf);
12902cc88f3aSAndreas Rohner out:
12912cc88f3aSAndreas Rohner 	mnt_drop_write_file(filp);
12922cc88f3aSAndreas Rohner 	return ret;
12932cc88f3aSAndreas Rohner }
12942cc88f3aSAndreas Rohner 
12957a946193SRyusuke Konishi long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
12967942b919SKoji Sato {
1297496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
129875323400SLi Hong 	void __user *argp = (void __user *)arg;
12997942b919SKoji Sato 
13007942b919SKoji Sato 	switch (cmd) {
1301cde98f0fSRyusuke Konishi 	case FS_IOC_GETFLAGS:
1302cde98f0fSRyusuke Konishi 		return nilfs_ioctl_getflags(inode, argp);
1303cde98f0fSRyusuke Konishi 	case FS_IOC_SETFLAGS:
1304cde98f0fSRyusuke Konishi 		return nilfs_ioctl_setflags(inode, filp, argp);
1305cde98f0fSRyusuke Konishi 	case FS_IOC_GETVERSION:
1306cde98f0fSRyusuke Konishi 		return nilfs_ioctl_getversion(inode, argp);
13077942b919SKoji Sato 	case NILFS_IOCTL_CHANGE_CPMODE:
13087942b919SKoji Sato 		return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
13097942b919SKoji Sato 	case NILFS_IOCTL_DELETE_CHECKPOINT:
13107942b919SKoji Sato 		return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
13117942b919SKoji Sato 	case NILFS_IOCTL_GET_CPINFO:
131247eb6b9cSRyusuke Konishi 		return nilfs_ioctl_get_info(inode, filp, cmd, argp,
131383aca8f4SRyusuke Konishi 					    sizeof(struct nilfs_cpinfo),
131447eb6b9cSRyusuke Konishi 					    nilfs_ioctl_do_get_cpinfo);
13157942b919SKoji Sato 	case NILFS_IOCTL_GET_CPSTAT:
13167942b919SKoji Sato 		return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
13177942b919SKoji Sato 	case NILFS_IOCTL_GET_SUINFO:
131847eb6b9cSRyusuke Konishi 		return nilfs_ioctl_get_info(inode, filp, cmd, argp,
131983aca8f4SRyusuke Konishi 					    sizeof(struct nilfs_suinfo),
132047eb6b9cSRyusuke Konishi 					    nilfs_ioctl_do_get_suinfo);
13212cc88f3aSAndreas Rohner 	case NILFS_IOCTL_SET_SUINFO:
13222cc88f3aSAndreas Rohner 		return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp);
13237942b919SKoji Sato 	case NILFS_IOCTL_GET_SUSTAT:
13247942b919SKoji Sato 		return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
13257942b919SKoji Sato 	case NILFS_IOCTL_GET_VINFO:
132647eb6b9cSRyusuke Konishi 		return nilfs_ioctl_get_info(inode, filp, cmd, argp,
132783aca8f4SRyusuke Konishi 					    sizeof(struct nilfs_vinfo),
132847eb6b9cSRyusuke Konishi 					    nilfs_ioctl_do_get_vinfo);
13297942b919SKoji Sato 	case NILFS_IOCTL_GET_BDESCS:
13307942b919SKoji Sato 		return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
13317942b919SKoji Sato 	case NILFS_IOCTL_CLEAN_SEGMENTS:
13327942b919SKoji Sato 		return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
13337942b919SKoji Sato 	case NILFS_IOCTL_SYNC:
13347942b919SKoji Sato 		return nilfs_ioctl_sync(inode, filp, cmd, argp);
13354e33f9eaSRyusuke Konishi 	case NILFS_IOCTL_RESIZE:
13364e33f9eaSRyusuke Konishi 		return nilfs_ioctl_resize(inode, filp, argp);
1337619205daSRyusuke Konishi 	case NILFS_IOCTL_SET_ALLOC_RANGE:
1338619205daSRyusuke Konishi 		return nilfs_ioctl_set_alloc_range(inode, argp);
1339f9f32c44SAndreas Rohner 	case FITRIM:
1340f9f32c44SAndreas Rohner 		return nilfs_ioctl_trim_fs(inode, argp);
13417942b919SKoji Sato 	default:
13427942b919SKoji Sato 		return -ENOTTY;
13437942b919SKoji Sato 	}
13447942b919SKoji Sato }
1345828b1c50SRyusuke Konishi 
1346828b1c50SRyusuke Konishi #ifdef CONFIG_COMPAT
1347828b1c50SRyusuke Konishi long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1348828b1c50SRyusuke Konishi {
1349828b1c50SRyusuke Konishi 	switch (cmd) {
1350828b1c50SRyusuke Konishi 	case FS_IOC32_GETFLAGS:
1351828b1c50SRyusuke Konishi 		cmd = FS_IOC_GETFLAGS;
1352828b1c50SRyusuke Konishi 		break;
1353828b1c50SRyusuke Konishi 	case FS_IOC32_SETFLAGS:
1354828b1c50SRyusuke Konishi 		cmd = FS_IOC_SETFLAGS;
1355828b1c50SRyusuke Konishi 		break;
1356828b1c50SRyusuke Konishi 	case FS_IOC32_GETVERSION:
1357828b1c50SRyusuke Konishi 		cmd = FS_IOC_GETVERSION;
1358828b1c50SRyusuke Konishi 		break;
1359695c60f2SThomas Meyer 	case NILFS_IOCTL_CHANGE_CPMODE:
1360695c60f2SThomas Meyer 	case NILFS_IOCTL_DELETE_CHECKPOINT:
1361695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_CPINFO:
1362695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_CPSTAT:
1363695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_SUINFO:
13642cc88f3aSAndreas Rohner 	case NILFS_IOCTL_SET_SUINFO:
1365695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_SUSTAT:
1366695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_VINFO:
1367695c60f2SThomas Meyer 	case NILFS_IOCTL_GET_BDESCS:
1368695c60f2SThomas Meyer 	case NILFS_IOCTL_CLEAN_SEGMENTS:
1369695c60f2SThomas Meyer 	case NILFS_IOCTL_SYNC:
1370695c60f2SThomas Meyer 	case NILFS_IOCTL_RESIZE:
1371695c60f2SThomas Meyer 	case NILFS_IOCTL_SET_ALLOC_RANGE:
1372f9f32c44SAndreas Rohner 	case FITRIM:
1373695c60f2SThomas Meyer 		break;
1374828b1c50SRyusuke Konishi 	default:
1375828b1c50SRyusuke Konishi 		return -ENOIOCTLCMD;
1376828b1c50SRyusuke Konishi 	}
1377828b1c50SRyusuke Konishi 	return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
1378828b1c50SRyusuke Konishi }
1379828b1c50SRyusuke Konishi #endif
1380