xref: /openbmc/linux/fs/nilfs2/ioctl.c (revision 8acfbf09)
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>
257942b919SKoji Sato #include <linux/smp_lock.h>	/* lock_kernel(), unlock_kernel() */
267942b919SKoji Sato #include <linux/capability.h>	/* capable() */
277942b919SKoji Sato #include <linux/uaccess.h>	/* copy_from_user(), copy_to_user() */
287942b919SKoji Sato #include <linux/nilfs2_fs.h>
297942b919SKoji Sato #include "nilfs.h"
307942b919SKoji Sato #include "segment.h"
317942b919SKoji Sato #include "bmap.h"
327942b919SKoji Sato #include "cpfile.h"
337942b919SKoji Sato #include "sufile.h"
347942b919SKoji Sato #include "dat.h"
357942b919SKoji Sato 
367942b919SKoji Sato 
377942b919SKoji Sato static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
387942b919SKoji Sato 				 struct nilfs_argv *argv, int dir,
397942b919SKoji Sato 				 ssize_t (*dofunc)(struct the_nilfs *,
40b028fcfcSRyusuke Konishi 						   __u64 *, int,
417942b919SKoji Sato 						   void *, size_t, size_t))
427942b919SKoji Sato {
437942b919SKoji Sato 	void *buf;
443358b4aaSRyusuke Konishi 	size_t maxmembs, total, n;
457942b919SKoji Sato 	ssize_t nr;
467942b919SKoji Sato 	int ret, i;
47b028fcfcSRyusuke Konishi 	__u64 pos, ppos;
487942b919SKoji Sato 
497942b919SKoji Sato 	if (argv->v_nmembs == 0)
507942b919SKoji Sato 		return 0;
517942b919SKoji Sato 
523358b4aaSRyusuke Konishi 	if (argv->v_size > PAGE_SIZE)
533358b4aaSRyusuke Konishi 		return -EINVAL;
543358b4aaSRyusuke Konishi 
553358b4aaSRyusuke Konishi 	buf = (void *)__get_free_pages(GFP_NOFS, 0);
563358b4aaSRyusuke Konishi 	if (unlikely(!buf))
577942b919SKoji Sato 		return -ENOMEM;
583358b4aaSRyusuke Konishi 	maxmembs = PAGE_SIZE / argv->v_size;
597942b919SKoji Sato 
607942b919SKoji Sato 	ret = 0;
617942b919SKoji Sato 	total = 0;
62b028fcfcSRyusuke Konishi 	pos = argv->v_index;
637942b919SKoji Sato 	for (i = 0; i < argv->v_nmembs; i += n) {
647942b919SKoji Sato 		n = (argv->v_nmembs - i < maxmembs) ?
657942b919SKoji Sato 			argv->v_nmembs - i : maxmembs;
667942b919SKoji Sato 		if ((dir & _IOC_WRITE) &&
677942b919SKoji Sato 		    copy_from_user(buf,
687942b919SKoji Sato 			    (void __user *)argv->v_base + argv->v_size * i,
697942b919SKoji Sato 			    argv->v_size * n)) {
707942b919SKoji Sato 			ret = -EFAULT;
717942b919SKoji Sato 			break;
727942b919SKoji Sato 		}
73b028fcfcSRyusuke Konishi 		ppos = pos;
748acfbf09SPekka Enberg 		nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size,
75b028fcfcSRyusuke Konishi 			       n);
767942b919SKoji Sato 		if (nr < 0) {
777942b919SKoji Sato 			ret = nr;
787942b919SKoji Sato 			break;
797942b919SKoji Sato 		}
807942b919SKoji Sato 		if ((dir & _IOC_READ) &&
817942b919SKoji Sato 		    copy_to_user(
827942b919SKoji Sato 			    (void __user *)argv->v_base + argv->v_size * i,
837942b919SKoji Sato 			    buf, argv->v_size * nr)) {
847942b919SKoji Sato 			ret = -EFAULT;
857942b919SKoji Sato 			break;
867942b919SKoji Sato 		}
877942b919SKoji Sato 		total += nr;
88b028fcfcSRyusuke Konishi 		if ((size_t)nr < n)
89b028fcfcSRyusuke Konishi 			break;
90b028fcfcSRyusuke Konishi 		if (pos == ppos)
91b028fcfcSRyusuke Konishi 			pos += n;
927942b919SKoji Sato 	}
937942b919SKoji Sato 	argv->v_nmembs = total;
947942b919SKoji Sato 
953358b4aaSRyusuke Konishi 	free_pages((unsigned long)buf, 0);
967942b919SKoji Sato 	return ret;
977942b919SKoji Sato }
987942b919SKoji Sato 
997942b919SKoji Sato static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
1007942b919SKoji Sato 				     unsigned int cmd, void __user *argp)
1017942b919SKoji Sato {
1027942b919SKoji Sato 	struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
1037942b919SKoji Sato 	struct nilfs_transaction_info ti;
1047942b919SKoji Sato 	struct nilfs_cpmode cpmode;
1057942b919SKoji Sato 	int ret;
1067942b919SKoji Sato 
1077942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
1087942b919SKoji Sato 		return -EPERM;
1097942b919SKoji Sato 	if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
1107942b919SKoji Sato 		return -EFAULT;
1117942b919SKoji Sato 
1127942b919SKoji Sato 	nilfs_transaction_begin(inode->i_sb, &ti, 0);
1137942b919SKoji Sato 	ret = nilfs_cpfile_change_cpmode(
1147942b919SKoji Sato 		cpfile, cpmode.cm_cno, cpmode.cm_mode);
11547420c79SRyusuke Konishi 	if (unlikely(ret < 0)) {
11647420c79SRyusuke Konishi 		nilfs_transaction_abort(inode->i_sb);
11747420c79SRyusuke Konishi 		return ret;
11847420c79SRyusuke Konishi 	}
11947420c79SRyusuke Konishi 	nilfs_transaction_commit(inode->i_sb); /* never fails */
1207942b919SKoji Sato 	return ret;
1217942b919SKoji Sato }
1227942b919SKoji Sato 
1237942b919SKoji Sato static int
1247942b919SKoji Sato nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
1257942b919SKoji Sato 			      unsigned int cmd, void __user *argp)
1267942b919SKoji Sato {
1277942b919SKoji Sato 	struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
1287942b919SKoji Sato 	struct nilfs_transaction_info ti;
1297942b919SKoji Sato 	__u64 cno;
1307942b919SKoji Sato 	int ret;
1317942b919SKoji Sato 
1327942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
1337942b919SKoji Sato 		return -EPERM;
1347942b919SKoji Sato 	if (copy_from_user(&cno, argp, sizeof(cno)))
1357942b919SKoji Sato 		return -EFAULT;
1367942b919SKoji Sato 
1377942b919SKoji Sato 	nilfs_transaction_begin(inode->i_sb, &ti, 0);
1387942b919SKoji Sato 	ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
13947420c79SRyusuke Konishi 	if (unlikely(ret < 0)) {
14047420c79SRyusuke Konishi 		nilfs_transaction_abort(inode->i_sb);
14147420c79SRyusuke Konishi 		return ret;
14247420c79SRyusuke Konishi 	}
14347420c79SRyusuke Konishi 	nilfs_transaction_commit(inode->i_sb); /* never fails */
1447942b919SKoji Sato 	return ret;
1457942b919SKoji Sato }
1467942b919SKoji Sato 
1477942b919SKoji Sato static ssize_t
148b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
1497942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
1507942b919SKoji Sato {
151b028fcfcSRyusuke Konishi 	return nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
1527942b919SKoji Sato 				       nmembs);
1537942b919SKoji Sato }
1547942b919SKoji Sato 
1557942b919SKoji Sato static int nilfs_ioctl_get_cpinfo(struct inode *inode, struct file *filp,
1567942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
1577942b919SKoji Sato {
1587942b919SKoji Sato 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
1597942b919SKoji Sato 	struct nilfs_argv argv;
1607942b919SKoji Sato 	int ret;
1617942b919SKoji Sato 
1627942b919SKoji Sato 	if (copy_from_user(&argv, argp, sizeof(argv)))
1637942b919SKoji Sato 		return -EFAULT;
1647942b919SKoji Sato 
16547420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
1667942b919SKoji Sato 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
1677942b919SKoji Sato 				    nilfs_ioctl_do_get_cpinfo);
16847420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
16947420c79SRyusuke Konishi 	if (ret < 0)
17047420c79SRyusuke Konishi 		return ret;
1717942b919SKoji Sato 
1727942b919SKoji Sato 	if (copy_to_user(argp, &argv, sizeof(argv)))
1737942b919SKoji Sato 		ret = -EFAULT;
1747942b919SKoji Sato 	return ret;
1757942b919SKoji Sato }
1767942b919SKoji Sato 
1777942b919SKoji Sato static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp,
1787942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
1797942b919SKoji Sato {
18047420c79SRyusuke Konishi 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
1817942b919SKoji Sato 	struct nilfs_cpstat cpstat;
1827942b919SKoji Sato 	int ret;
1837942b919SKoji Sato 
18447420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
18547420c79SRyusuke Konishi 	ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat);
18647420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
1877942b919SKoji Sato 	if (ret < 0)
1887942b919SKoji Sato 		return ret;
1897942b919SKoji Sato 
1907942b919SKoji Sato 	if (copy_to_user(argp, &cpstat, sizeof(cpstat)))
1917942b919SKoji Sato 		ret = -EFAULT;
1927942b919SKoji Sato 	return ret;
1937942b919SKoji Sato }
1947942b919SKoji Sato 
1957942b919SKoji Sato static ssize_t
196b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
1977942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
1987942b919SKoji Sato {
199b028fcfcSRyusuke Konishi 	return nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs);
2007942b919SKoji Sato }
2017942b919SKoji Sato 
2027942b919SKoji Sato static int nilfs_ioctl_get_suinfo(struct inode *inode, struct file *filp,
2037942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
2047942b919SKoji Sato {
2057942b919SKoji Sato 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
2067942b919SKoji Sato 	struct nilfs_argv argv;
2077942b919SKoji Sato 	int ret;
2087942b919SKoji Sato 
2097942b919SKoji Sato 	if (copy_from_user(&argv, argp, sizeof(argv)))
2107942b919SKoji Sato 		return -EFAULT;
2117942b919SKoji Sato 
21247420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
2137942b919SKoji Sato 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
2147942b919SKoji Sato 				    nilfs_ioctl_do_get_suinfo);
21547420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
21647420c79SRyusuke Konishi 	if (ret < 0)
21747420c79SRyusuke Konishi 		return ret;
2187942b919SKoji Sato 
2197942b919SKoji Sato 	if (copy_to_user(argp, &argv, sizeof(argv)))
2207942b919SKoji Sato 		ret = -EFAULT;
2217942b919SKoji Sato 	return ret;
2227942b919SKoji Sato }
2237942b919SKoji Sato 
2247942b919SKoji Sato static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp,
2257942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
2267942b919SKoji Sato {
22747420c79SRyusuke Konishi 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
2287942b919SKoji Sato 	struct nilfs_sustat sustat;
2297942b919SKoji Sato 	int ret;
2307942b919SKoji Sato 
23147420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
23247420c79SRyusuke Konishi 	ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat);
23347420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
2347942b919SKoji Sato 	if (ret < 0)
2357942b919SKoji Sato 		return ret;
2367942b919SKoji Sato 
2377942b919SKoji Sato 	if (copy_to_user(argp, &sustat, sizeof(sustat)))
2387942b919SKoji Sato 		ret = -EFAULT;
2397942b919SKoji Sato 	return ret;
2407942b919SKoji Sato }
2417942b919SKoji Sato 
2427942b919SKoji Sato static ssize_t
243b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
2447942b919SKoji Sato 			 void *buf, size_t size, size_t nmembs)
2457942b919SKoji Sato {
2467942b919SKoji Sato 	return nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs);
2477942b919SKoji Sato }
2487942b919SKoji Sato 
2497942b919SKoji Sato static int nilfs_ioctl_get_vinfo(struct inode *inode, struct file *filp,
2507942b919SKoji Sato 				 unsigned int cmd, void __user *argp)
2517942b919SKoji Sato {
2527942b919SKoji Sato 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
2537942b919SKoji Sato 	struct nilfs_argv argv;
2547942b919SKoji Sato 	int ret;
2557942b919SKoji Sato 
2567942b919SKoji Sato 	if (copy_from_user(&argv, argp, sizeof(argv)))
2577942b919SKoji Sato 		return -EFAULT;
2587942b919SKoji Sato 
25947420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
2607942b919SKoji Sato 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
2617942b919SKoji Sato 				    nilfs_ioctl_do_get_vinfo);
26247420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
26347420c79SRyusuke Konishi 	if (ret < 0)
26447420c79SRyusuke Konishi 		return ret;
2657942b919SKoji Sato 
2667942b919SKoji Sato 	if (copy_to_user(argp, &argv, sizeof(argv)))
2677942b919SKoji Sato 		ret = -EFAULT;
2687942b919SKoji Sato 	return ret;
2697942b919SKoji Sato }
2707942b919SKoji Sato 
2717942b919SKoji Sato static ssize_t
272b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags,
2737942b919SKoji Sato 			  void *buf, size_t size, size_t nmembs)
2747942b919SKoji Sato {
2757942b919SKoji Sato 	struct inode *dat = nilfs_dat_inode(nilfs);
2767942b919SKoji Sato 	struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap;
2777942b919SKoji Sato 	struct nilfs_bdesc *bdescs = buf;
2787942b919SKoji Sato 	int ret, i;
2797942b919SKoji Sato 
2807942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
2817942b919SKoji Sato 		ret = nilfs_bmap_lookup_at_level(bmap,
2827942b919SKoji Sato 						 bdescs[i].bd_offset,
2837942b919SKoji Sato 						 bdescs[i].bd_level + 1,
2847942b919SKoji Sato 						 &bdescs[i].bd_blocknr);
2857942b919SKoji Sato 		if (ret < 0) {
2867942b919SKoji Sato 			if (ret != -ENOENT)
2877942b919SKoji Sato 				return ret;
2887942b919SKoji Sato 			bdescs[i].bd_blocknr = 0;
2897942b919SKoji Sato 		}
2907942b919SKoji Sato 	}
2917942b919SKoji Sato 	return nmembs;
2927942b919SKoji Sato }
2937942b919SKoji Sato 
2947942b919SKoji Sato static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,
2957942b919SKoji Sato 				  unsigned int cmd, void __user *argp)
2967942b919SKoji Sato {
2977942b919SKoji Sato 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
2987942b919SKoji Sato 	struct nilfs_argv argv;
2997942b919SKoji Sato 	int ret;
3007942b919SKoji Sato 
3017942b919SKoji Sato 	if (copy_from_user(&argv, argp, sizeof(argv)))
3027942b919SKoji Sato 		return -EFAULT;
3037942b919SKoji Sato 
30447420c79SRyusuke Konishi 	down_read(&nilfs->ns_segctor_sem);
3057942b919SKoji Sato 	ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
3067942b919SKoji Sato 				    nilfs_ioctl_do_get_bdescs);
30747420c79SRyusuke Konishi 	up_read(&nilfs->ns_segctor_sem);
30847420c79SRyusuke Konishi 	if (ret < 0)
30947420c79SRyusuke Konishi 		return ret;
3107942b919SKoji Sato 
3117942b919SKoji Sato 	if (copy_to_user(argp, &argv, sizeof(argv)))
3127942b919SKoji Sato 		ret = -EFAULT;
3137942b919SKoji Sato 	return ret;
3147942b919SKoji Sato }
3157942b919SKoji Sato 
3167942b919SKoji Sato static int nilfs_ioctl_move_inode_block(struct inode *inode,
3177942b919SKoji Sato 					struct nilfs_vdesc *vdesc,
3187942b919SKoji Sato 					struct list_head *buffers)
3197942b919SKoji Sato {
3207942b919SKoji Sato 	struct buffer_head *bh;
3217942b919SKoji Sato 	int ret;
3227942b919SKoji Sato 
3237942b919SKoji Sato 	if (vdesc->vd_flags == 0)
3247942b919SKoji Sato 		ret = nilfs_gccache_submit_read_data(
3257942b919SKoji Sato 			inode, vdesc->vd_offset, vdesc->vd_blocknr,
3267942b919SKoji Sato 			vdesc->vd_vblocknr, &bh);
3277942b919SKoji Sato 	else
3287942b919SKoji Sato 		ret = nilfs_gccache_submit_read_node(
3297942b919SKoji Sato 			inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
3307942b919SKoji Sato 
3317942b919SKoji Sato 	if (unlikely(ret < 0)) {
3327942b919SKoji Sato 		if (ret == -ENOENT)
3337942b919SKoji Sato 			printk(KERN_CRIT
3347942b919SKoji Sato 			       "%s: invalid virtual block address (%s): "
3357942b919SKoji Sato 			       "ino=%llu, cno=%llu, offset=%llu, "
3367942b919SKoji Sato 			       "blocknr=%llu, vblocknr=%llu\n",
3377942b919SKoji Sato 			       __func__, vdesc->vd_flags ? "node" : "data",
3387942b919SKoji Sato 			       (unsigned long long)vdesc->vd_ino,
3397942b919SKoji Sato 			       (unsigned long long)vdesc->vd_cno,
3407942b919SKoji Sato 			       (unsigned long long)vdesc->vd_offset,
3417942b919SKoji Sato 			       (unsigned long long)vdesc->vd_blocknr,
3427942b919SKoji Sato 			       (unsigned long long)vdesc->vd_vblocknr);
3437942b919SKoji Sato 		return ret;
3447942b919SKoji Sato 	}
3457942b919SKoji Sato 	bh->b_private = vdesc;
3467942b919SKoji Sato 	list_add_tail(&bh->b_assoc_buffers, buffers);
3477942b919SKoji Sato 	return 0;
3487942b919SKoji Sato }
3497942b919SKoji Sato 
3507942b919SKoji Sato static ssize_t
351b028fcfcSRyusuke Konishi nilfs_ioctl_do_move_blocks(struct the_nilfs *nilfs, __u64 *posp, int flags,
3527942b919SKoji Sato 			   void *buf, size_t size, size_t nmembs)
3537942b919SKoji Sato {
3547942b919SKoji Sato 	struct inode *inode;
3557942b919SKoji Sato 	struct nilfs_vdesc *vdesc;
3567942b919SKoji Sato 	struct buffer_head *bh, *n;
3577942b919SKoji Sato 	LIST_HEAD(buffers);
3587942b919SKoji Sato 	ino_t ino;
3597942b919SKoji Sato 	__u64 cno;
3607942b919SKoji Sato 	int i, ret;
3617942b919SKoji Sato 
3627942b919SKoji Sato 	for (i = 0, vdesc = buf; i < nmembs; ) {
3637942b919SKoji Sato 		ino = vdesc->vd_ino;
3647942b919SKoji Sato 		cno = vdesc->vd_cno;
3657942b919SKoji Sato 		inode = nilfs_gc_iget(nilfs, ino, cno);
3667942b919SKoji Sato 		if (unlikely(inode == NULL)) {
3677942b919SKoji Sato 			ret = -ENOMEM;
3687942b919SKoji Sato 			goto failed;
3697942b919SKoji Sato 		}
3707942b919SKoji Sato 		do {
3717942b919SKoji Sato 			ret = nilfs_ioctl_move_inode_block(inode, vdesc,
3727942b919SKoji Sato 							   &buffers);
3737942b919SKoji Sato 			if (unlikely(ret < 0))
3747942b919SKoji Sato 				goto failed;
3757942b919SKoji Sato 			vdesc++;
3767942b919SKoji Sato 		} while (++i < nmembs &&
3777942b919SKoji Sato 			 vdesc->vd_ino == ino && vdesc->vd_cno == cno);
3787942b919SKoji Sato 	}
3797942b919SKoji Sato 
3807942b919SKoji Sato 	list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
3817942b919SKoji Sato 		ret = nilfs_gccache_wait_and_mark_dirty(bh);
3827942b919SKoji Sato 		if (unlikely(ret < 0)) {
3837942b919SKoji Sato 			if (ret == -EEXIST) {
3847942b919SKoji Sato 				vdesc = bh->b_private;
3857942b919SKoji Sato 				printk(KERN_CRIT
3867942b919SKoji Sato 				       "%s: conflicting %s buffer: "
3877942b919SKoji Sato 				       "ino=%llu, cno=%llu, offset=%llu, "
3887942b919SKoji Sato 				       "blocknr=%llu, vblocknr=%llu\n",
3897942b919SKoji Sato 				       __func__,
3907942b919SKoji Sato 				       vdesc->vd_flags ? "node" : "data",
3917942b919SKoji Sato 				       (unsigned long long)vdesc->vd_ino,
3927942b919SKoji Sato 				       (unsigned long long)vdesc->vd_cno,
3937942b919SKoji Sato 				       (unsigned long long)vdesc->vd_offset,
3947942b919SKoji Sato 				       (unsigned long long)vdesc->vd_blocknr,
3957942b919SKoji Sato 				       (unsigned long long)vdesc->vd_vblocknr);
3967942b919SKoji Sato 			}
3977942b919SKoji Sato 			goto failed;
3987942b919SKoji Sato 		}
3997942b919SKoji Sato 		list_del_init(&bh->b_assoc_buffers);
4007942b919SKoji Sato 		bh->b_private = NULL;
4017942b919SKoji Sato 		brelse(bh);
4027942b919SKoji Sato 	}
4037942b919SKoji Sato 	return nmembs;
4047942b919SKoji Sato 
4057942b919SKoji Sato  failed:
4067942b919SKoji Sato 	list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
4077942b919SKoji Sato 		list_del_init(&bh->b_assoc_buffers);
4087942b919SKoji Sato 		bh->b_private = NULL;
4097942b919SKoji Sato 		brelse(bh);
4107942b919SKoji Sato 	}
4117942b919SKoji Sato 	return ret;
4127942b919SKoji Sato }
4137942b919SKoji Sato 
4147942b919SKoji Sato static inline int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
4157942b919SKoji Sato 					  struct nilfs_argv *argv,
4167942b919SKoji Sato 					  int dir)
4177942b919SKoji Sato {
4187942b919SKoji Sato 	return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
4197942b919SKoji Sato 				     nilfs_ioctl_do_move_blocks);
4207942b919SKoji Sato }
4217942b919SKoji Sato 
4227942b919SKoji Sato static ssize_t
423b028fcfcSRyusuke Konishi nilfs_ioctl_do_delete_checkpoints(struct the_nilfs *nilfs, __u64 *posp,
4247942b919SKoji Sato 				  int flags, void *buf, size_t size,
4257942b919SKoji Sato 				  size_t nmembs)
4267942b919SKoji Sato {
4277942b919SKoji Sato 	struct inode *cpfile = nilfs->ns_cpfile;
4287942b919SKoji Sato 	struct nilfs_period *periods = buf;
4297942b919SKoji Sato 	int ret, i;
4307942b919SKoji Sato 
4317942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
4327942b919SKoji Sato 		ret = nilfs_cpfile_delete_checkpoints(
4337942b919SKoji Sato 			cpfile, periods[i].p_start, periods[i].p_end);
4347942b919SKoji Sato 		if (ret < 0)
4357942b919SKoji Sato 			return ret;
4367942b919SKoji Sato 	}
4377942b919SKoji Sato 	return nmembs;
4387942b919SKoji Sato }
4397942b919SKoji Sato 
4407942b919SKoji Sato static inline int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs,
4417942b919SKoji Sato 						 struct nilfs_argv *argv,
4427942b919SKoji Sato 						 int dir)
4437942b919SKoji Sato {
4447942b919SKoji Sato 	return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
4457942b919SKoji Sato 				     nilfs_ioctl_do_delete_checkpoints);
4467942b919SKoji Sato }
4477942b919SKoji Sato 
4487942b919SKoji Sato static ssize_t
449b028fcfcSRyusuke Konishi nilfs_ioctl_do_free_vblocknrs(struct the_nilfs *nilfs, __u64 *posp, int flags,
4507942b919SKoji Sato 			      void *buf, size_t size, size_t nmembs)
4517942b919SKoji Sato {
4527942b919SKoji Sato 	int ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs);
4537942b919SKoji Sato 
4547942b919SKoji Sato 	return (ret < 0) ? ret : nmembs;
4557942b919SKoji Sato }
4567942b919SKoji Sato 
4577942b919SKoji Sato static inline int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs,
4587942b919SKoji Sato 					     struct nilfs_argv *argv,
4597942b919SKoji Sato 					     int dir)
4607942b919SKoji Sato {
4617942b919SKoji Sato 	return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
4627942b919SKoji Sato 				     nilfs_ioctl_do_free_vblocknrs);
4637942b919SKoji Sato }
4647942b919SKoji Sato 
4657942b919SKoji Sato static ssize_t
466b028fcfcSRyusuke Konishi nilfs_ioctl_do_mark_blocks_dirty(struct the_nilfs *nilfs, __u64 *posp,
467b028fcfcSRyusuke Konishi 				 int flags, void *buf, size_t size,
468b028fcfcSRyusuke Konishi 				 size_t nmembs)
4697942b919SKoji Sato {
4707942b919SKoji Sato 	struct inode *dat = nilfs_dat_inode(nilfs);
4717942b919SKoji Sato 	struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap;
4727942b919SKoji Sato 	struct nilfs_bdesc *bdescs = buf;
4737942b919SKoji Sato 	int ret, i;
4747942b919SKoji Sato 
4757942b919SKoji Sato 	for (i = 0; i < nmembs; i++) {
4767942b919SKoji Sato 		/* XXX: use macro or inline func to check liveness */
4777942b919SKoji Sato 		ret = nilfs_bmap_lookup_at_level(bmap,
4787942b919SKoji Sato 						 bdescs[i].bd_offset,
4797942b919SKoji Sato 						 bdescs[i].bd_level + 1,
4807942b919SKoji Sato 						 &bdescs[i].bd_blocknr);
4817942b919SKoji Sato 		if (ret < 0) {
4827942b919SKoji Sato 			if (ret != -ENOENT)
4837942b919SKoji Sato 				return ret;
4847942b919SKoji Sato 			bdescs[i].bd_blocknr = 0;
4857942b919SKoji Sato 		}
4867942b919SKoji Sato 		if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr)
4877942b919SKoji Sato 			/* skip dead block */
4887942b919SKoji Sato 			continue;
4897942b919SKoji Sato 		if (bdescs[i].bd_level == 0) {
4907942b919SKoji Sato 			ret = nilfs_mdt_mark_block_dirty(dat,
4917942b919SKoji Sato 							 bdescs[i].bd_offset);
4927942b919SKoji Sato 			if (ret < 0) {
4937942b919SKoji Sato 				BUG_ON(ret == -ENOENT);
4947942b919SKoji Sato 				return ret;
4957942b919SKoji Sato 			}
4967942b919SKoji Sato 		} else {
4977942b919SKoji Sato 			ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset,
4987942b919SKoji Sato 					      bdescs[i].bd_level);
4997942b919SKoji Sato 			if (ret < 0) {
5007942b919SKoji Sato 				BUG_ON(ret == -ENOENT);
5017942b919SKoji Sato 				return ret;
5027942b919SKoji Sato 			}
5037942b919SKoji Sato 		}
5047942b919SKoji Sato 	}
5057942b919SKoji Sato 	return nmembs;
5067942b919SKoji Sato }
5077942b919SKoji Sato 
5087942b919SKoji Sato static inline int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
5097942b919SKoji Sato 						struct nilfs_argv *argv,
5107942b919SKoji Sato 						int dir)
5117942b919SKoji Sato {
5127942b919SKoji Sato 	return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
5137942b919SKoji Sato 				     nilfs_ioctl_do_mark_blocks_dirty);
5147942b919SKoji Sato }
5157942b919SKoji Sato 
5167942b919SKoji Sato static ssize_t
517b028fcfcSRyusuke Konishi nilfs_ioctl_do_free_segments(struct the_nilfs *nilfs, __u64 *posp, int flags,
5187942b919SKoji Sato 			     void *buf, size_t size, size_t nmembs)
5197942b919SKoji Sato {
5207942b919SKoji Sato 	struct nilfs_sb_info *sbi = nilfs_get_writer(nilfs);
5217942b919SKoji Sato 	int ret;
5227942b919SKoji Sato 
5237942b919SKoji Sato 	BUG_ON(!sbi);
5247942b919SKoji Sato 	ret = nilfs_segctor_add_segments_to_be_freed(
5257942b919SKoji Sato 		NILFS_SC(sbi), buf, nmembs);
5267942b919SKoji Sato 	nilfs_put_writer(nilfs);
5277942b919SKoji Sato 
5287942b919SKoji Sato 	return (ret < 0) ? ret : nmembs;
5297942b919SKoji Sato }
5307942b919SKoji Sato 
5317942b919SKoji Sato static inline int nilfs_ioctl_free_segments(struct the_nilfs *nilfs,
5327942b919SKoji Sato 					     struct nilfs_argv *argv,
5337942b919SKoji Sato 					     int dir)
5347942b919SKoji Sato {
5357942b919SKoji Sato 	return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
5367942b919SKoji Sato 				     nilfs_ioctl_do_free_segments);
5377942b919SKoji Sato }
5387942b919SKoji Sato 
5397942b919SKoji Sato int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
5407942b919SKoji Sato 				       void __user *argp)
5417942b919SKoji Sato {
5427942b919SKoji Sato 	struct nilfs_argv argv[5];
5437942b919SKoji Sato 	int dir, ret;
5447942b919SKoji Sato 
5457942b919SKoji Sato 	if (copy_from_user(argv, argp, sizeof(argv)))
5467942b919SKoji Sato 		return -EFAULT;
5477942b919SKoji Sato 
5487942b919SKoji Sato 	dir = _IOC_WRITE;
5497942b919SKoji Sato 	ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], dir);
5507942b919SKoji Sato 	if (ret < 0)
5517942b919SKoji Sato 		goto out_move_blks;
5527942b919SKoji Sato 	ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], dir);
5537942b919SKoji Sato 	if (ret < 0)
5547942b919SKoji Sato 		goto out_del_cps;
5557942b919SKoji Sato 	ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], dir);
5567942b919SKoji Sato 	if (ret < 0)
5577942b919SKoji Sato 		goto out_free_vbns;
5587942b919SKoji Sato 	ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], dir);
5597942b919SKoji Sato 	if (ret < 0)
5607942b919SKoji Sato 		goto out_free_vbns;
5617942b919SKoji Sato 	ret = nilfs_ioctl_free_segments(nilfs, &argv[4], dir);
5627942b919SKoji Sato 	if (ret < 0)
5637942b919SKoji Sato 		goto out_free_segs;
5647942b919SKoji Sato 
5657942b919SKoji Sato 	return 0;
5667942b919SKoji Sato 
5677942b919SKoji Sato  out_free_segs:
5687942b919SKoji Sato 	BUG(); /* XXX: not implemented yet */
5697942b919SKoji Sato  out_free_vbns:
5707942b919SKoji Sato 	BUG();/* XXX: not implemented yet */
5717942b919SKoji Sato  out_del_cps:
5727942b919SKoji Sato 	BUG();/* XXX: not implemented yet */
5737942b919SKoji Sato  out_move_blks:
5747942b919SKoji Sato 	nilfs_remove_all_gcinode(nilfs);
5757942b919SKoji Sato 	return ret;
5767942b919SKoji Sato }
5777942b919SKoji Sato 
5787942b919SKoji Sato static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
5797942b919SKoji Sato 				      unsigned int cmd, void __user *argp)
5807942b919SKoji Sato {
5817942b919SKoji Sato 	int ret;
5827942b919SKoji Sato 
5837942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
5847942b919SKoji Sato 		return -EPERM;
5857942b919SKoji Sato 
5867942b919SKoji Sato 	ret = nilfs_clean_segments(inode->i_sb, argp);
5877942b919SKoji Sato 	clear_nilfs_cond_nongc_write(NILFS_SB(inode->i_sb)->s_nilfs);
5887942b919SKoji Sato 	return ret;
5897942b919SKoji Sato }
5907942b919SKoji Sato 
5917942b919SKoji Sato static int nilfs_ioctl_test_cond(struct the_nilfs *nilfs, int cond)
5927942b919SKoji Sato {
5937942b919SKoji Sato 	return (cond & NILFS_TIMEDWAIT_SEG_WRITE) &&
5947942b919SKoji Sato 		nilfs_cond_nongc_write(nilfs);
5957942b919SKoji Sato }
5967942b919SKoji Sato 
5977942b919SKoji Sato static void nilfs_ioctl_clear_cond(struct the_nilfs *nilfs, int cond)
5987942b919SKoji Sato {
5997942b919SKoji Sato 	if (cond & NILFS_TIMEDWAIT_SEG_WRITE)
6007942b919SKoji Sato 		clear_nilfs_cond_nongc_write(nilfs);
6017942b919SKoji Sato }
6027942b919SKoji Sato 
6037942b919SKoji Sato static int nilfs_ioctl_timedwait(struct inode *inode, struct file *filp,
6047942b919SKoji Sato 				 unsigned int cmd, void __user *argp)
6057942b919SKoji Sato {
6067942b919SKoji Sato 	struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
6077942b919SKoji Sato 	struct nilfs_wait_cond wc;
6087942b919SKoji Sato 	long ret;
6097942b919SKoji Sato 
6107942b919SKoji Sato 	if (!capable(CAP_SYS_ADMIN))
6117942b919SKoji Sato 		return -EPERM;
6127942b919SKoji Sato 	if (copy_from_user(&wc, argp, sizeof(wc)))
6137942b919SKoji Sato 		return -EFAULT;
6147942b919SKoji Sato 
6157942b919SKoji Sato 	unlock_kernel();
6167942b919SKoji Sato 	ret = wc.wc_flags ?
6177942b919SKoji Sato 		wait_event_interruptible_timeout(
6187942b919SKoji Sato 			nilfs->ns_cleanerd_wq,
6197942b919SKoji Sato 			nilfs_ioctl_test_cond(nilfs, wc.wc_cond),
6207942b919SKoji Sato 			timespec_to_jiffies(&wc.wc_timeout)) :
6217942b919SKoji Sato 		wait_event_interruptible(
6227942b919SKoji Sato 			nilfs->ns_cleanerd_wq,
6237942b919SKoji Sato 			nilfs_ioctl_test_cond(nilfs, wc.wc_cond));
6247942b919SKoji Sato 	lock_kernel();
6257942b919SKoji Sato 	nilfs_ioctl_clear_cond(nilfs, wc.wc_cond);
6267942b919SKoji Sato 
6277942b919SKoji Sato 	if (ret > 0) {
6287942b919SKoji Sato 		jiffies_to_timespec(ret, &wc.wc_timeout);
6297942b919SKoji Sato 		if (copy_to_user(argp, &wc, sizeof(wc)))
6307942b919SKoji Sato 			return -EFAULT;
6317942b919SKoji Sato 		return 0;
6327942b919SKoji Sato 	}
6337942b919SKoji Sato 	if (ret != 0)
6347942b919SKoji Sato 		return -EINTR;
6357942b919SKoji Sato 
6367942b919SKoji Sato 	return wc.wc_flags ? -ETIME : 0;
6377942b919SKoji Sato }
6387942b919SKoji Sato 
6397942b919SKoji Sato static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
6407942b919SKoji Sato 			    unsigned int cmd, void __user *argp)
6417942b919SKoji Sato {
6427942b919SKoji Sato 	__u64 cno;
6437942b919SKoji Sato 	int ret;
6447942b919SKoji Sato 
6457942b919SKoji Sato 	ret = nilfs_construct_segment(inode->i_sb);
6467942b919SKoji Sato 	if (ret < 0)
6477942b919SKoji Sato 		return ret;
6487942b919SKoji Sato 
6497942b919SKoji Sato 	if (argp != NULL) {
6507942b919SKoji Sato 		cno = NILFS_SB(inode->i_sb)->s_nilfs->ns_cno - 1;
6517942b919SKoji Sato 		if (copy_to_user(argp, &cno, sizeof(cno)))
6527942b919SKoji Sato 			return -EFAULT;
6537942b919SKoji Sato 	}
6547942b919SKoji Sato 	return 0;
6557942b919SKoji Sato }
6567942b919SKoji Sato 
6577942b919SKoji Sato int nilfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
6587942b919SKoji Sato 		unsigned long arg)
6597942b919SKoji Sato {
6607942b919SKoji Sato 	void __user *argp = (void * __user *)arg;
6617942b919SKoji Sato 
6627942b919SKoji Sato 	switch (cmd) {
6637942b919SKoji Sato 	case NILFS_IOCTL_CHANGE_CPMODE:
6647942b919SKoji Sato 		return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
6657942b919SKoji Sato 	case NILFS_IOCTL_DELETE_CHECKPOINT:
6667942b919SKoji Sato 		return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
6677942b919SKoji Sato 	case NILFS_IOCTL_GET_CPINFO:
6687942b919SKoji Sato 		return nilfs_ioctl_get_cpinfo(inode, filp, cmd, argp);
6697942b919SKoji Sato 	case NILFS_IOCTL_GET_CPSTAT:
6707942b919SKoji Sato 		return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
6717942b919SKoji Sato 	case NILFS_IOCTL_GET_SUINFO:
6727942b919SKoji Sato 		return nilfs_ioctl_get_suinfo(inode, filp, cmd, argp);
6737942b919SKoji Sato 	case NILFS_IOCTL_GET_SUSTAT:
6747942b919SKoji Sato 		return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
6757942b919SKoji Sato 	case NILFS_IOCTL_GET_VINFO:
6767942b919SKoji Sato 		/* XXX: rename to ??? */
6777942b919SKoji Sato 		return nilfs_ioctl_get_vinfo(inode, filp, cmd, argp);
6787942b919SKoji Sato 	case NILFS_IOCTL_GET_BDESCS:
6797942b919SKoji Sato 		return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
6807942b919SKoji Sato 	case NILFS_IOCTL_CLEAN_SEGMENTS:
6817942b919SKoji Sato 		return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
6827942b919SKoji Sato 	case NILFS_IOCTL_TIMEDWAIT:
6837942b919SKoji Sato 		return nilfs_ioctl_timedwait(inode, filp, cmd, argp);
6847942b919SKoji Sato 	case NILFS_IOCTL_SYNC:
6857942b919SKoji Sato 		return nilfs_ioctl_sync(inode, filp, cmd, argp);
6867942b919SKoji Sato 	default:
6877942b919SKoji Sato 		return -ENOTTY;
6887942b919SKoji Sato 	}
6897942b919SKoji Sato }
6907942b919SKoji Sato 
6917942b919SKoji Sato /* compat_ioctl */
6927942b919SKoji Sato #ifdef CONFIG_COMPAT
6937942b919SKoji Sato #include <linux/compat.h>
6947942b919SKoji Sato 
6957942b919SKoji Sato static int nilfs_compat_locked_ioctl(struct inode *inode, struct file *filp,
6967942b919SKoji Sato 				     unsigned int cmd, unsigned long arg)
6977942b919SKoji Sato {
6987942b919SKoji Sato 	int ret;
6997942b919SKoji Sato 
7007942b919SKoji Sato 	lock_kernel();
7017942b919SKoji Sato 	ret = nilfs_ioctl(inode, filp, cmd, arg);
7027942b919SKoji Sato 	unlock_kernel();
7037942b919SKoji Sato 	return ret;
7047942b919SKoji Sato }
7057942b919SKoji Sato 
7067942b919SKoji Sato static int
7077942b919SKoji Sato nilfs_compat_ioctl_uargv32_to_uargv(struct nilfs_argv32 __user *uargv32,
7087942b919SKoji Sato 				    struct nilfs_argv __user *uargv)
7097942b919SKoji Sato {
7107942b919SKoji Sato 	compat_uptr_t base;
7117942b919SKoji Sato 	compat_size_t nmembs, size;
7127942b919SKoji Sato 	compat_int_t index, flags;
7137942b919SKoji Sato 
7147942b919SKoji Sato 	if (get_user(base, &uargv32->v_base) ||
7157942b919SKoji Sato 	    put_user(compat_ptr(base), &uargv->v_base) ||
7167942b919SKoji Sato 	    get_user(nmembs, &uargv32->v_nmembs) ||
7177942b919SKoji Sato 	    put_user(nmembs, &uargv->v_nmembs) ||
7187942b919SKoji Sato 	    get_user(size, &uargv32->v_size) ||
7197942b919SKoji Sato 	    put_user(size, &uargv->v_size) ||
7207942b919SKoji Sato 	    get_user(index, &uargv32->v_index) ||
7217942b919SKoji Sato 	    put_user(index, &uargv->v_index) ||
7227942b919SKoji Sato 	    get_user(flags, &uargv32->v_flags) ||
7237942b919SKoji Sato 	    put_user(flags, &uargv->v_flags))
7247942b919SKoji Sato 		return -EFAULT;
7257942b919SKoji Sato 	return 0;
7267942b919SKoji Sato }
7277942b919SKoji Sato 
7287942b919SKoji Sato static int
7297942b919SKoji Sato nilfs_compat_ioctl_uargv_to_uargv32(struct nilfs_argv __user *uargv,
7307942b919SKoji Sato 				    struct nilfs_argv32 __user *uargv32)
7317942b919SKoji Sato {
7327942b919SKoji Sato 	size_t nmembs;
7337942b919SKoji Sato 
7347942b919SKoji Sato 	if (get_user(nmembs, &uargv->v_nmembs) ||
7357942b919SKoji Sato 	    put_user(nmembs, &uargv32->v_nmembs))
7367942b919SKoji Sato 		return -EFAULT;
7377942b919SKoji Sato 	return 0;
7387942b919SKoji Sato }
7397942b919SKoji Sato 
7407942b919SKoji Sato static int
7417942b919SKoji Sato nilfs_compat_ioctl_get_by_argv(struct inode *inode, struct file *filp,
7427942b919SKoji Sato 			       unsigned int cmd, unsigned long arg)
7437942b919SKoji Sato {
7447942b919SKoji Sato 	struct nilfs_argv __user *uargv;
7457942b919SKoji Sato 	struct nilfs_argv32 __user *uargv32;
7467942b919SKoji Sato 	int ret;
7477942b919SKoji Sato 
7487942b919SKoji Sato 	uargv = compat_alloc_user_space(sizeof(struct nilfs_argv));
7497942b919SKoji Sato 	uargv32 = compat_ptr(arg);
7507942b919SKoji Sato 	ret = nilfs_compat_ioctl_uargv32_to_uargv(uargv32, uargv);
7517942b919SKoji Sato 	if (ret < 0)
7527942b919SKoji Sato 		return ret;
7537942b919SKoji Sato 
7547942b919SKoji Sato 	ret = nilfs_compat_locked_ioctl(inode, filp, cmd, (unsigned long)uargv);
7557942b919SKoji Sato 	if (ret < 0)
7567942b919SKoji Sato 		return ret;
7577942b919SKoji Sato 
7587942b919SKoji Sato 	return nilfs_compat_ioctl_uargv_to_uargv32(uargv, uargv32);
7597942b919SKoji Sato }
7607942b919SKoji Sato 
7617942b919SKoji Sato static int
7627942b919SKoji Sato nilfs_compat_ioctl_change_cpmode(struct inode *inode, struct file *filp,
7637942b919SKoji Sato 				 unsigned int cmd, unsigned long arg)
7647942b919SKoji Sato {
7657942b919SKoji Sato 	struct nilfs_cpmode __user *ucpmode;
7667942b919SKoji Sato 	struct nilfs_cpmode32 __user *ucpmode32;
7677942b919SKoji Sato 	int mode;
7687942b919SKoji Sato 
7697942b919SKoji Sato 	ucpmode = compat_alloc_user_space(sizeof(struct nilfs_cpmode));
7707942b919SKoji Sato 	ucpmode32 = compat_ptr(arg);
7717942b919SKoji Sato 	if (copy_in_user(&ucpmode->cm_cno, &ucpmode32->cm_cno,
7727942b919SKoji Sato 			 sizeof(__u64)) ||
7737942b919SKoji Sato 	    get_user(mode, &ucpmode32->cm_mode) ||
7747942b919SKoji Sato 	    put_user(mode, &ucpmode->cm_mode))
7757942b919SKoji Sato 		return -EFAULT;
7767942b919SKoji Sato 
7777942b919SKoji Sato 	return nilfs_compat_locked_ioctl(
7787942b919SKoji Sato 		inode, filp, cmd, (unsigned long)ucpmode);
7797942b919SKoji Sato }
7807942b919SKoji Sato 
7817942b919SKoji Sato 
7827942b919SKoji Sato static inline int
7837942b919SKoji Sato nilfs_compat_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
7847942b919SKoji Sato 				     unsigned int cmd, unsigned long arg)
7857942b919SKoji Sato {
7867942b919SKoji Sato 	return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
7877942b919SKoji Sato }
7887942b919SKoji Sato 
7897942b919SKoji Sato static inline int
7907942b919SKoji Sato nilfs_compat_ioctl_get_cpinfo(struct inode *inode, struct file *filp,
7917942b919SKoji Sato 			      unsigned int cmd, unsigned long arg)
7927942b919SKoji Sato {
7937942b919SKoji Sato 	return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
7947942b919SKoji Sato }
7957942b919SKoji Sato 
7967942b919SKoji Sato static inline int
7977942b919SKoji Sato nilfs_compat_ioctl_get_cpstat(struct inode *inode, struct file *filp,
7987942b919SKoji Sato 			      unsigned int cmd, unsigned long arg)
7997942b919SKoji Sato {
8007942b919SKoji Sato 	return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
8017942b919SKoji Sato }
8027942b919SKoji Sato 
8037942b919SKoji Sato static inline int
8047942b919SKoji Sato nilfs_compat_ioctl_get_suinfo(struct inode *inode, struct file *filp,
8057942b919SKoji Sato 			      unsigned int cmd, unsigned long arg)
8067942b919SKoji Sato {
8077942b919SKoji Sato 	return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
8087942b919SKoji Sato }
8097942b919SKoji Sato 
8107942b919SKoji Sato static int
8117942b919SKoji Sato nilfs_compat_ioctl_get_sustat(struct inode *inode, struct file *filp,
8127942b919SKoji Sato 			      unsigned int cmd, unsigned long arg)
8137942b919SKoji Sato {
8147942b919SKoji Sato 	struct nilfs_sustat __user *usustat;
8157942b919SKoji Sato 	struct nilfs_sustat32 __user *usustat32;
8167942b919SKoji Sato 	time_t ctime, nongc_ctime;
8177942b919SKoji Sato 	int ret;
8187942b919SKoji Sato 
8197942b919SKoji Sato 	usustat = compat_alloc_user_space(sizeof(struct nilfs_sustat));
8207942b919SKoji Sato 	ret = nilfs_compat_locked_ioctl(inode, filp, cmd,
8217942b919SKoji Sato 					(unsigned long)usustat);
8227942b919SKoji Sato 	if (ret < 0)
8237942b919SKoji Sato 		return ret;
8247942b919SKoji Sato 
8257942b919SKoji Sato 	usustat32 = compat_ptr(arg);
8267942b919SKoji Sato 	if (copy_in_user(&usustat32->ss_nsegs, &usustat->ss_nsegs,
8277942b919SKoji Sato 			 sizeof(__u64)) ||
8287942b919SKoji Sato 	    copy_in_user(&usustat32->ss_ncleansegs, &usustat->ss_ncleansegs,
8297942b919SKoji Sato 			 sizeof(__u64)) ||
8307942b919SKoji Sato 	    copy_in_user(&usustat32->ss_ndirtysegs, &usustat->ss_ndirtysegs,
8317942b919SKoji Sato 			 sizeof(__u64)) ||
8327942b919SKoji Sato 	    get_user(ctime, &usustat->ss_ctime) ||
8337942b919SKoji Sato 	    put_user(ctime, &usustat32->ss_ctime) ||
8347942b919SKoji Sato 	    get_user(nongc_ctime, &usustat->ss_nongc_ctime) ||
8357942b919SKoji Sato 	    put_user(nongc_ctime, &usustat32->ss_nongc_ctime))
8367942b919SKoji Sato 		return -EFAULT;
8377942b919SKoji Sato 	return 0;
8387942b919SKoji Sato }
8397942b919SKoji Sato 
8407942b919SKoji Sato static inline int
8417942b919SKoji Sato nilfs_compat_ioctl_get_vinfo(struct inode *inode, struct file *filp,
8427942b919SKoji Sato 			      unsigned int cmd, unsigned long arg)
8437942b919SKoji Sato {
8447942b919SKoji Sato 	return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
8457942b919SKoji Sato }
8467942b919SKoji Sato 
8477942b919SKoji Sato static inline int
8487942b919SKoji Sato nilfs_compat_ioctl_get_bdescs(struct inode *inode, struct file *filp,
8497942b919SKoji Sato 			     unsigned int cmd, unsigned long arg)
8507942b919SKoji Sato {
8517942b919SKoji Sato 	return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
8527942b919SKoji Sato }
8537942b919SKoji Sato 
8547942b919SKoji Sato static int
8557942b919SKoji Sato nilfs_compat_ioctl_clean_segments(struct inode *inode, struct file *filp,
8567942b919SKoji Sato 				  unsigned int cmd, unsigned long arg)
8577942b919SKoji Sato {
8587942b919SKoji Sato 	struct nilfs_argv __user *uargv;
8597942b919SKoji Sato 	struct nilfs_argv32 __user *uargv32;
8607942b919SKoji Sato 	int i, ret;
8617942b919SKoji Sato 
8627942b919SKoji Sato 	uargv = compat_alloc_user_space(sizeof(struct nilfs_argv) * 5);
8637942b919SKoji Sato 	uargv32 = compat_ptr(arg);
8647942b919SKoji Sato 	for (i = 0; i < 5; i++) {
8657942b919SKoji Sato 		ret = nilfs_compat_ioctl_uargv32_to_uargv(&uargv32[i],
8667942b919SKoji Sato 							  &uargv[i]);
8677942b919SKoji Sato 		if (ret < 0)
8687942b919SKoji Sato 			return ret;
8697942b919SKoji Sato 	}
8707942b919SKoji Sato 	return nilfs_compat_locked_ioctl(
8717942b919SKoji Sato 		inode, filp, cmd, (unsigned long)uargv);
8727942b919SKoji Sato }
8737942b919SKoji Sato 
8747942b919SKoji Sato static int
8757942b919SKoji Sato nilfs_compat_ioctl_timedwait(struct inode *inode, struct file *filp,
8767942b919SKoji Sato 			     unsigned int cmd, unsigned long arg)
8777942b919SKoji Sato {
8787942b919SKoji Sato 	struct nilfs_wait_cond __user *uwcond;
8797942b919SKoji Sato 	struct nilfs_wait_cond32 __user *uwcond32;
8807942b919SKoji Sato 	struct timespec ts;
8817942b919SKoji Sato 	int cond, flags, ret;
8827942b919SKoji Sato 
8837942b919SKoji Sato 	uwcond = compat_alloc_user_space(sizeof(struct nilfs_wait_cond));
8847942b919SKoji Sato 	uwcond32 = compat_ptr(arg);
8857942b919SKoji Sato 	if (get_user(cond, &uwcond32->wc_cond) ||
8867942b919SKoji Sato 	    put_user(cond, &uwcond->wc_cond) ||
8877942b919SKoji Sato 	    get_user(flags, &uwcond32->wc_flags) ||
8887942b919SKoji Sato 	    put_user(flags, &uwcond->wc_flags) ||
8897942b919SKoji Sato 	    get_user(ts.tv_sec, &uwcond32->wc_timeout.tv_sec) ||
8907942b919SKoji Sato 	    get_user(ts.tv_nsec, &uwcond32->wc_timeout.tv_nsec) ||
8917942b919SKoji Sato 	    put_user(ts.tv_sec, &uwcond->wc_timeout.tv_sec) ||
8927942b919SKoji Sato 	    put_user(ts.tv_nsec, &uwcond->wc_timeout.tv_nsec))
8937942b919SKoji Sato 		return -EFAULT;
8947942b919SKoji Sato 
8957942b919SKoji Sato 	ret = nilfs_compat_locked_ioctl(inode, filp, cmd,
8967942b919SKoji Sato 					(unsigned long)uwcond);
8977942b919SKoji Sato 	if (ret < 0)
8987942b919SKoji Sato 		return ret;
8997942b919SKoji Sato 
9007942b919SKoji Sato 	if (get_user(ts.tv_sec, &uwcond->wc_timeout.tv_sec) ||
9017942b919SKoji Sato 	    get_user(ts.tv_nsec, &uwcond->wc_timeout.tv_nsec) ||
9027942b919SKoji Sato 	    put_user(ts.tv_sec, &uwcond32->wc_timeout.tv_sec) ||
9037942b919SKoji Sato 	    put_user(ts.tv_nsec, &uwcond32->wc_timeout.tv_nsec))
9047942b919SKoji Sato 		return -EFAULT;
9057942b919SKoji Sato 
9067942b919SKoji Sato 	return 0;
9077942b919SKoji Sato }
9087942b919SKoji Sato 
9097942b919SKoji Sato static int nilfs_compat_ioctl_sync(struct inode *inode, struct file *filp,
9107942b919SKoji Sato 				   unsigned int cmd, unsigned long arg)
9117942b919SKoji Sato {
9127942b919SKoji Sato 	return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
9137942b919SKoji Sato }
9147942b919SKoji Sato 
9157942b919SKoji Sato long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
9167942b919SKoji Sato {
9177942b919SKoji Sato 	struct inode *inode = filp->f_dentry->d_inode;
9187942b919SKoji Sato 
9197942b919SKoji Sato 	switch (cmd) {
9207942b919SKoji Sato 	case NILFS_IOCTL32_CHANGE_CPMODE:
9217942b919SKoji Sato 		return nilfs_compat_ioctl_change_cpmode(
9227942b919SKoji Sato 			inode, filp, NILFS_IOCTL_CHANGE_CPMODE, arg);
9237942b919SKoji Sato 	case NILFS_IOCTL_DELETE_CHECKPOINT:
9247942b919SKoji Sato 		return nilfs_compat_ioctl_delete_checkpoint(
9257942b919SKoji Sato 			inode, filp, cmd, arg);
9267942b919SKoji Sato 	case NILFS_IOCTL32_GET_CPINFO:
9277942b919SKoji Sato 		return nilfs_compat_ioctl_get_cpinfo(
9287942b919SKoji Sato 			inode, filp, NILFS_IOCTL_GET_CPINFO, arg);
9297942b919SKoji Sato 	case NILFS_IOCTL_GET_CPSTAT:
9307942b919SKoji Sato 		return nilfs_compat_ioctl_get_cpstat(inode, filp, cmd, arg);
9317942b919SKoji Sato 	case NILFS_IOCTL32_GET_SUINFO:
9327942b919SKoji Sato 		return nilfs_compat_ioctl_get_suinfo(
9337942b919SKoji Sato 			inode, filp, NILFS_IOCTL_GET_SUINFO, arg);
9347942b919SKoji Sato 	case NILFS_IOCTL32_GET_SUSTAT:
9357942b919SKoji Sato 		return nilfs_compat_ioctl_get_sustat(
9367942b919SKoji Sato 			inode, filp, NILFS_IOCTL_GET_SUSTAT, arg);
9377942b919SKoji Sato 	case NILFS_IOCTL32_GET_VINFO:
9387942b919SKoji Sato 		return nilfs_compat_ioctl_get_vinfo(
9397942b919SKoji Sato 			inode, filp, NILFS_IOCTL_GET_VINFO, arg);
9407942b919SKoji Sato 	case NILFS_IOCTL32_GET_BDESCS:
9417942b919SKoji Sato 		return nilfs_compat_ioctl_get_bdescs(
9427942b919SKoji Sato 			inode, filp, NILFS_IOCTL_GET_BDESCS, arg);
9437942b919SKoji Sato 	case NILFS_IOCTL32_CLEAN_SEGMENTS:
9447942b919SKoji Sato 		return nilfs_compat_ioctl_clean_segments(
9457942b919SKoji Sato 			inode, filp, NILFS_IOCTL_CLEAN_SEGMENTS, arg);
9467942b919SKoji Sato 	case NILFS_IOCTL32_TIMEDWAIT:
9477942b919SKoji Sato 		return nilfs_compat_ioctl_timedwait(
9487942b919SKoji Sato 			inode, filp, NILFS_IOCTL_TIMEDWAIT, arg);
9497942b919SKoji Sato 	case NILFS_IOCTL_SYNC:
9507942b919SKoji Sato 		return nilfs_compat_ioctl_sync(inode, filp, cmd, arg);
9517942b919SKoji Sato 	default:
9527942b919SKoji Sato 		return -ENOIOCTLCMD;
9537942b919SKoji Sato 	}
9547942b919SKoji Sato }
9557942b919SKoji Sato #endif
956