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