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