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