17942b919SKoji Sato /* 27942b919SKoji Sato * ioctl.c - NILFS ioctl operations. 37942b919SKoji Sato * 47942b919SKoji Sato * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation. 57942b919SKoji Sato * 67942b919SKoji Sato * This program is free software; you can redistribute it and/or modify 77942b919SKoji Sato * it under the terms of the GNU General Public License as published by 87942b919SKoji Sato * the Free Software Foundation; either version 2 of the License, or 97942b919SKoji Sato * (at your option) any later version. 107942b919SKoji Sato * 117942b919SKoji Sato * This program is distributed in the hope that it will be useful, 127942b919SKoji Sato * but WITHOUT ANY WARRANTY; without even the implied warranty of 137942b919SKoji Sato * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 147942b919SKoji Sato * GNU General Public License for more details. 157942b919SKoji Sato * 167942b919SKoji Sato * You should have received a copy of the GNU General Public License 177942b919SKoji Sato * along with this program; if not, write to the Free Software 187942b919SKoji Sato * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 197942b919SKoji Sato * 207942b919SKoji Sato * Written by Koji Sato <koji@osrg.net>. 217942b919SKoji Sato */ 227942b919SKoji Sato 237942b919SKoji Sato #include <linux/fs.h> 247942b919SKoji Sato #include <linux/wait.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 267942b919SKoji Sato #include <linux/capability.h> /* capable() */ 277942b919SKoji Sato #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ 284f6b8288SRyusuke Konishi #include <linux/vmalloc.h> 29828b1c50SRyusuke Konishi #include <linux/compat.h> /* compat_ptr() */ 302a79f17eSAl Viro #include <linux/mount.h> /* mnt_want_write_file(), mnt_drop_write_file() */ 31ae191838SRyusuke Konishi #include <linux/buffer_head.h> 327942b919SKoji Sato #include <linux/nilfs2_fs.h> 337942b919SKoji Sato #include "nilfs.h" 347942b919SKoji Sato #include "segment.h" 357942b919SKoji Sato #include "bmap.h" 367942b919SKoji Sato #include "cpfile.h" 377942b919SKoji Sato #include "sufile.h" 387942b919SKoji Sato #include "dat.h" 397942b919SKoji Sato 407942b919SKoji Sato 417942b919SKoji Sato static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, 427942b919SKoji Sato struct nilfs_argv *argv, int dir, 437942b919SKoji Sato ssize_t (*dofunc)(struct the_nilfs *, 44b028fcfcSRyusuke Konishi __u64 *, int, 457942b919SKoji Sato void *, size_t, size_t)) 467942b919SKoji Sato { 477942b919SKoji Sato void *buf; 48dc498d09SRyusuke Konishi void __user *base = (void __user *)(unsigned long)argv->v_base; 493358b4aaSRyusuke Konishi size_t maxmembs, total, n; 507942b919SKoji Sato ssize_t nr; 517942b919SKoji Sato int ret, i; 52b028fcfcSRyusuke Konishi __u64 pos, ppos; 537942b919SKoji Sato 547942b919SKoji Sato if (argv->v_nmembs == 0) 557942b919SKoji Sato return 0; 567942b919SKoji Sato 573358b4aaSRyusuke Konishi if (argv->v_size > PAGE_SIZE) 583358b4aaSRyusuke Konishi return -EINVAL; 593358b4aaSRyusuke Konishi 603358b4aaSRyusuke Konishi buf = (void *)__get_free_pages(GFP_NOFS, 0); 613358b4aaSRyusuke Konishi if (unlikely(!buf)) 627942b919SKoji Sato return -ENOMEM; 633358b4aaSRyusuke Konishi maxmembs = PAGE_SIZE / argv->v_size; 647942b919SKoji Sato 657942b919SKoji Sato ret = 0; 667942b919SKoji Sato total = 0; 67b028fcfcSRyusuke Konishi pos = argv->v_index; 687942b919SKoji Sato for (i = 0; i < argv->v_nmembs; i += n) { 697942b919SKoji Sato n = (argv->v_nmembs - i < maxmembs) ? 707942b919SKoji Sato argv->v_nmembs - i : maxmembs; 717942b919SKoji Sato if ((dir & _IOC_WRITE) && 72dc498d09SRyusuke Konishi copy_from_user(buf, base + argv->v_size * i, 737942b919SKoji Sato argv->v_size * n)) { 747942b919SKoji Sato ret = -EFAULT; 757942b919SKoji Sato break; 767942b919SKoji Sato } 77b028fcfcSRyusuke Konishi ppos = pos; 788acfbf09SPekka Enberg nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, 79b028fcfcSRyusuke Konishi n); 807942b919SKoji Sato if (nr < 0) { 817942b919SKoji Sato ret = nr; 827942b919SKoji Sato break; 837942b919SKoji Sato } 847942b919SKoji Sato if ((dir & _IOC_READ) && 85dc498d09SRyusuke Konishi copy_to_user(base + argv->v_size * i, buf, 86dc498d09SRyusuke Konishi argv->v_size * nr)) { 877942b919SKoji Sato ret = -EFAULT; 887942b919SKoji Sato break; 897942b919SKoji Sato } 907942b919SKoji Sato total += nr; 91b028fcfcSRyusuke Konishi if ((size_t)nr < n) 92b028fcfcSRyusuke Konishi break; 93b028fcfcSRyusuke Konishi if (pos == ppos) 94b028fcfcSRyusuke Konishi pos += n; 957942b919SKoji Sato } 967942b919SKoji Sato argv->v_nmembs = total; 977942b919SKoji Sato 983358b4aaSRyusuke Konishi free_pages((unsigned long)buf, 0); 997942b919SKoji Sato return ret; 1007942b919SKoji Sato } 1017942b919SKoji Sato 102cde98f0fSRyusuke Konishi static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp) 103cde98f0fSRyusuke Konishi { 104cde98f0fSRyusuke Konishi unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE; 105cde98f0fSRyusuke Konishi 106cde98f0fSRyusuke Konishi return put_user(flags, (int __user *)argp); 107cde98f0fSRyusuke Konishi } 108cde98f0fSRyusuke Konishi 109cde98f0fSRyusuke Konishi static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp, 110cde98f0fSRyusuke Konishi void __user *argp) 111cde98f0fSRyusuke Konishi { 112cde98f0fSRyusuke Konishi struct nilfs_transaction_info ti; 113cde98f0fSRyusuke Konishi unsigned int flags, oldflags; 114cde98f0fSRyusuke Konishi int ret; 115cde98f0fSRyusuke Konishi 1162e149670SSerge E. Hallyn if (!inode_owner_or_capable(inode)) 117cde98f0fSRyusuke Konishi return -EACCES; 118cde98f0fSRyusuke Konishi 119cde98f0fSRyusuke Konishi if (get_user(flags, (int __user *)argp)) 120cde98f0fSRyusuke Konishi return -EFAULT; 121cde98f0fSRyusuke Konishi 122a561be71SAl Viro ret = mnt_want_write_file(filp); 123cde98f0fSRyusuke Konishi if (ret) 124cde98f0fSRyusuke Konishi return ret; 125cde98f0fSRyusuke Konishi 126cde98f0fSRyusuke Konishi flags = nilfs_mask_flags(inode->i_mode, flags); 127cde98f0fSRyusuke Konishi 128cde98f0fSRyusuke Konishi mutex_lock(&inode->i_mutex); 129cde98f0fSRyusuke Konishi 130cde98f0fSRyusuke Konishi oldflags = NILFS_I(inode)->i_flags; 131cde98f0fSRyusuke Konishi 132cde98f0fSRyusuke Konishi /* 133cde98f0fSRyusuke Konishi * The IMMUTABLE and APPEND_ONLY flags can only be changed by the 134cde98f0fSRyusuke Konishi * relevant capability. 135cde98f0fSRyusuke Konishi */ 136cde98f0fSRyusuke Konishi ret = -EPERM; 137cde98f0fSRyusuke Konishi if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && 138cde98f0fSRyusuke Konishi !capable(CAP_LINUX_IMMUTABLE)) 139cde98f0fSRyusuke Konishi goto out; 140cde98f0fSRyusuke Konishi 141cde98f0fSRyusuke Konishi ret = nilfs_transaction_begin(inode->i_sb, &ti, 0); 142cde98f0fSRyusuke Konishi if (ret) 143cde98f0fSRyusuke Konishi goto out; 144cde98f0fSRyusuke Konishi 145cde98f0fSRyusuke Konishi NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) | 146cde98f0fSRyusuke Konishi (flags & FS_FL_USER_MODIFIABLE); 147cde98f0fSRyusuke Konishi 148cde98f0fSRyusuke Konishi nilfs_set_inode_flags(inode); 149cde98f0fSRyusuke Konishi inode->i_ctime = CURRENT_TIME; 150cde98f0fSRyusuke Konishi if (IS_SYNC(inode)) 151cde98f0fSRyusuke Konishi nilfs_set_transaction_flag(NILFS_TI_SYNC); 152cde98f0fSRyusuke Konishi 153cde98f0fSRyusuke Konishi nilfs_mark_inode_dirty(inode); 154cde98f0fSRyusuke Konishi ret = nilfs_transaction_commit(inode->i_sb); 155cde98f0fSRyusuke Konishi out: 156cde98f0fSRyusuke Konishi mutex_unlock(&inode->i_mutex); 1572a79f17eSAl Viro mnt_drop_write_file(filp); 158cde98f0fSRyusuke Konishi return ret; 159cde98f0fSRyusuke Konishi } 160cde98f0fSRyusuke Konishi 161cde98f0fSRyusuke Konishi static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp) 162cde98f0fSRyusuke Konishi { 163cde98f0fSRyusuke Konishi return put_user(inode->i_generation, (int __user *)argp); 164cde98f0fSRyusuke Konishi } 165cde98f0fSRyusuke Konishi 1667942b919SKoji Sato static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, 1677942b919SKoji Sato unsigned int cmd, void __user *argp) 1687942b919SKoji Sato { 169e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 1707942b919SKoji Sato struct nilfs_transaction_info ti; 1717942b919SKoji Sato struct nilfs_cpmode cpmode; 1727942b919SKoji Sato int ret; 1737942b919SKoji Sato 1747942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 1757942b919SKoji Sato return -EPERM; 1767512487eSRyusuke Konishi 177a561be71SAl Viro ret = mnt_want_write_file(filp); 1787512487eSRyusuke Konishi if (ret) 1797512487eSRyusuke Konishi return ret; 1807512487eSRyusuke Konishi 1817512487eSRyusuke Konishi ret = -EFAULT; 1827942b919SKoji Sato if (copy_from_user(&cpmode, argp, sizeof(cpmode))) 1837512487eSRyusuke Konishi goto out; 1847942b919SKoji Sato 185348fe8daSRyusuke Konishi down_read(&inode->i_sb->s_umount); 1867512487eSRyusuke Konishi 1877942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 1887942b919SKoji Sato ret = nilfs_cpfile_change_cpmode( 189e3154e97SRyusuke Konishi nilfs->ns_cpfile, cpmode.cm_cno, cpmode.cm_mode); 1907512487eSRyusuke Konishi if (unlikely(ret < 0)) 19147420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 1927512487eSRyusuke Konishi else 19347420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 1947512487eSRyusuke Konishi 195348fe8daSRyusuke Konishi up_read(&inode->i_sb->s_umount); 1967512487eSRyusuke Konishi out: 1972a79f17eSAl Viro mnt_drop_write_file(filp); 1987942b919SKoji Sato return ret; 1997942b919SKoji Sato } 2007942b919SKoji Sato 2017942b919SKoji Sato static int 2027942b919SKoji Sato nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, 2037942b919SKoji Sato unsigned int cmd, void __user *argp) 2047942b919SKoji Sato { 205e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 2067942b919SKoji Sato struct nilfs_transaction_info ti; 2077942b919SKoji Sato __u64 cno; 2087942b919SKoji Sato int ret; 2097942b919SKoji Sato 2107942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 2117942b919SKoji Sato return -EPERM; 2127512487eSRyusuke Konishi 213a561be71SAl Viro ret = mnt_want_write_file(filp); 2147512487eSRyusuke Konishi if (ret) 2157512487eSRyusuke Konishi return ret; 2167512487eSRyusuke Konishi 2177512487eSRyusuke Konishi ret = -EFAULT; 2187942b919SKoji Sato if (copy_from_user(&cno, argp, sizeof(cno))) 2197512487eSRyusuke Konishi goto out; 2207942b919SKoji Sato 2217942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 222e3154e97SRyusuke Konishi ret = nilfs_cpfile_delete_checkpoint(nilfs->ns_cpfile, cno); 2237512487eSRyusuke Konishi if (unlikely(ret < 0)) 22447420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 2257512487eSRyusuke Konishi else 22647420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 2277512487eSRyusuke Konishi out: 2282a79f17eSAl Viro mnt_drop_write_file(filp); 2297942b919SKoji Sato return ret; 2307942b919SKoji Sato } 2317942b919SKoji Sato 2327942b919SKoji Sato static ssize_t 233b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2347942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2357942b919SKoji Sato { 2367942b919SKoji Sato int ret; 2377942b919SKoji Sato 23847420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 23947eb6b9cSRyusuke Konishi ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, 240003ff182SRyusuke Konishi size, nmembs); 24147420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2427942b919SKoji Sato return ret; 2437942b919SKoji Sato } 2447942b919SKoji Sato 2457942b919SKoji Sato static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, 2467942b919SKoji Sato unsigned int cmd, void __user *argp) 2477942b919SKoji Sato { 248e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 2497942b919SKoji Sato struct nilfs_cpstat cpstat; 2507942b919SKoji Sato int ret; 2517942b919SKoji Sato 25247420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 25347420c79SRyusuke Konishi ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); 25447420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2557942b919SKoji Sato if (ret < 0) 2567942b919SKoji Sato return ret; 2577942b919SKoji Sato 2587942b919SKoji Sato if (copy_to_user(argp, &cpstat, sizeof(cpstat))) 2597942b919SKoji Sato ret = -EFAULT; 2607942b919SKoji Sato return ret; 2617942b919SKoji Sato } 2627942b919SKoji Sato 2637942b919SKoji Sato static ssize_t 264b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2657942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2667942b919SKoji Sato { 2677942b919SKoji Sato int ret; 2687942b919SKoji Sato 26947420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 270003ff182SRyusuke Konishi ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, 271003ff182SRyusuke Konishi nmembs); 27247420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2737942b919SKoji Sato return ret; 2747942b919SKoji Sato } 2757942b919SKoji Sato 2767942b919SKoji Sato static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, 2777942b919SKoji Sato unsigned int cmd, void __user *argp) 2787942b919SKoji Sato { 279e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 2807942b919SKoji Sato struct nilfs_sustat sustat; 2817942b919SKoji Sato int ret; 2827942b919SKoji Sato 28347420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 28447420c79SRyusuke Konishi ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); 28547420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2867942b919SKoji Sato if (ret < 0) 2877942b919SKoji Sato return ret; 2887942b919SKoji Sato 2897942b919SKoji Sato if (copy_to_user(argp, &sustat, sizeof(sustat))) 2907942b919SKoji Sato ret = -EFAULT; 2917942b919SKoji Sato return ret; 2927942b919SKoji Sato } 2937942b919SKoji Sato 2947942b919SKoji Sato static ssize_t 295b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2967942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2977942b919SKoji Sato { 2987942b919SKoji Sato int ret; 2997942b919SKoji Sato 30047420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 301365e215cSRyusuke Konishi ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs); 30247420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 3037942b919SKoji Sato return ret; 3047942b919SKoji Sato } 3057942b919SKoji Sato 3067942b919SKoji Sato static ssize_t 307b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, 3087942b919SKoji Sato void *buf, size_t size, size_t nmembs) 3097942b919SKoji Sato { 310365e215cSRyusuke Konishi struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; 3117942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 3127942b919SKoji Sato int ret, i; 3137942b919SKoji Sato 31447eb6b9cSRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 3157942b919SKoji Sato for (i = 0; i < nmembs; i++) { 3167942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 3177942b919SKoji Sato bdescs[i].bd_offset, 3187942b919SKoji Sato bdescs[i].bd_level + 1, 3197942b919SKoji Sato &bdescs[i].bd_blocknr); 3207942b919SKoji Sato if (ret < 0) { 32147eb6b9cSRyusuke Konishi if (ret != -ENOENT) { 32247eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 3237942b919SKoji Sato return ret; 32447eb6b9cSRyusuke Konishi } 3257942b919SKoji Sato bdescs[i].bd_blocknr = 0; 3267942b919SKoji Sato } 3277942b919SKoji Sato } 32847eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 3297942b919SKoji Sato return nmembs; 3307942b919SKoji Sato } 3317942b919SKoji Sato 3327942b919SKoji Sato static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, 3337942b919SKoji Sato unsigned int cmd, void __user *argp) 3347942b919SKoji Sato { 335e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 3367942b919SKoji Sato struct nilfs_argv argv; 3377942b919SKoji Sato int ret; 3387942b919SKoji Sato 3397942b919SKoji Sato if (copy_from_user(&argv, argp, sizeof(argv))) 3407942b919SKoji Sato return -EFAULT; 3417942b919SKoji Sato 34283aca8f4SRyusuke Konishi if (argv.v_size != sizeof(struct nilfs_bdesc)) 34383aca8f4SRyusuke Konishi return -EINVAL; 34483aca8f4SRyusuke Konishi 3457942b919SKoji Sato ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), 3467942b919SKoji Sato nilfs_ioctl_do_get_bdescs); 34747420c79SRyusuke Konishi if (ret < 0) 34847420c79SRyusuke Konishi return ret; 3497942b919SKoji Sato 3507942b919SKoji Sato if (copy_to_user(argp, &argv, sizeof(argv))) 3517942b919SKoji Sato ret = -EFAULT; 3527942b919SKoji Sato return ret; 3537942b919SKoji Sato } 3547942b919SKoji Sato 3557942b919SKoji Sato static int nilfs_ioctl_move_inode_block(struct inode *inode, 3567942b919SKoji Sato struct nilfs_vdesc *vdesc, 3577942b919SKoji Sato struct list_head *buffers) 3587942b919SKoji Sato { 3597942b919SKoji Sato struct buffer_head *bh; 3607942b919SKoji Sato int ret; 3617942b919SKoji Sato 3627942b919SKoji Sato if (vdesc->vd_flags == 0) 3637942b919SKoji Sato ret = nilfs_gccache_submit_read_data( 3647942b919SKoji Sato inode, vdesc->vd_offset, vdesc->vd_blocknr, 3657942b919SKoji Sato vdesc->vd_vblocknr, &bh); 3667942b919SKoji Sato else 3677942b919SKoji Sato ret = nilfs_gccache_submit_read_node( 3687942b919SKoji Sato inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); 3697942b919SKoji Sato 3707942b919SKoji Sato if (unlikely(ret < 0)) { 3717942b919SKoji Sato if (ret == -ENOENT) 3727942b919SKoji Sato printk(KERN_CRIT 3737942b919SKoji Sato "%s: invalid virtual block address (%s): " 3747942b919SKoji Sato "ino=%llu, cno=%llu, offset=%llu, " 3757942b919SKoji Sato "blocknr=%llu, vblocknr=%llu\n", 3767942b919SKoji Sato __func__, vdesc->vd_flags ? "node" : "data", 3777942b919SKoji Sato (unsigned long long)vdesc->vd_ino, 3787942b919SKoji Sato (unsigned long long)vdesc->vd_cno, 3797942b919SKoji Sato (unsigned long long)vdesc->vd_offset, 3807942b919SKoji Sato (unsigned long long)vdesc->vd_blocknr, 3817942b919SKoji Sato (unsigned long long)vdesc->vd_vblocknr); 3827942b919SKoji Sato return ret; 3837942b919SKoji Sato } 3845399dd1fSRyusuke Konishi if (unlikely(!list_empty(&bh->b_assoc_buffers))) { 3855399dd1fSRyusuke Konishi printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " 3865399dd1fSRyusuke Konishi "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", 3875399dd1fSRyusuke Konishi __func__, vdesc->vd_flags ? "node" : "data", 3885399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_ino, 3895399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_cno, 3905399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_offset, 3915399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_blocknr, 3925399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_vblocknr); 3935399dd1fSRyusuke Konishi brelse(bh); 3945399dd1fSRyusuke Konishi return -EEXIST; 3955399dd1fSRyusuke Konishi } 3967942b919SKoji Sato list_add_tail(&bh->b_assoc_buffers, buffers); 3977942b919SKoji Sato return 0; 3987942b919SKoji Sato } 3997942b919SKoji Sato 400263d90ceSRyusuke Konishi static int nilfs_ioctl_move_blocks(struct super_block *sb, 4014f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4027942b919SKoji Sato { 4034f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 404e3154e97SRyusuke Konishi struct the_nilfs *nilfs = sb->s_fs_info; 4057942b919SKoji Sato struct inode *inode; 4067942b919SKoji Sato struct nilfs_vdesc *vdesc; 4077942b919SKoji Sato struct buffer_head *bh, *n; 4087942b919SKoji Sato LIST_HEAD(buffers); 4097942b919SKoji Sato ino_t ino; 4107942b919SKoji Sato __u64 cno; 4117942b919SKoji Sato int i, ret; 4127942b919SKoji Sato 4137942b919SKoji Sato for (i = 0, vdesc = buf; i < nmembs; ) { 4147942b919SKoji Sato ino = vdesc->vd_ino; 4157942b919SKoji Sato cno = vdesc->vd_cno; 416263d90ceSRyusuke Konishi inode = nilfs_iget_for_gc(sb, ino, cno); 417103cfcf5SDan Carpenter if (IS_ERR(inode)) { 418103cfcf5SDan Carpenter ret = PTR_ERR(inode); 4197942b919SKoji Sato goto failed; 4207942b919SKoji Sato } 421947b10aeSRyusuke Konishi if (list_empty(&NILFS_I(inode)->i_dirty)) { 422947b10aeSRyusuke Konishi /* 423947b10aeSRyusuke Konishi * Add the inode to GC inode list. Garbage Collection 424947b10aeSRyusuke Konishi * is serialized and no two processes manipulate the 425947b10aeSRyusuke Konishi * list simultaneously. 426947b10aeSRyusuke Konishi */ 427947b10aeSRyusuke Konishi igrab(inode); 428947b10aeSRyusuke Konishi list_add(&NILFS_I(inode)->i_dirty, 429947b10aeSRyusuke Konishi &nilfs->ns_gc_inodes); 430947b10aeSRyusuke Konishi } 431947b10aeSRyusuke Konishi 4327942b919SKoji Sato do { 4337942b919SKoji Sato ret = nilfs_ioctl_move_inode_block(inode, vdesc, 4347942b919SKoji Sato &buffers); 435263d90ceSRyusuke Konishi if (unlikely(ret < 0)) { 436263d90ceSRyusuke Konishi iput(inode); 4377942b919SKoji Sato goto failed; 438263d90ceSRyusuke Konishi } 4397942b919SKoji Sato vdesc++; 4407942b919SKoji Sato } while (++i < nmembs && 4417942b919SKoji Sato vdesc->vd_ino == ino && vdesc->vd_cno == cno); 442263d90ceSRyusuke Konishi 443263d90ceSRyusuke Konishi iput(inode); /* The inode still remains in GC inode list */ 4447942b919SKoji Sato } 4457942b919SKoji Sato 4467942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 4477942b919SKoji Sato ret = nilfs_gccache_wait_and_mark_dirty(bh); 4487942b919SKoji Sato if (unlikely(ret < 0)) { 4495399dd1fSRyusuke Konishi WARN_ON(ret == -EEXIST); 4507942b919SKoji Sato goto failed; 4517942b919SKoji Sato } 4527942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 4537942b919SKoji Sato brelse(bh); 4547942b919SKoji Sato } 4557942b919SKoji Sato return nmembs; 4567942b919SKoji Sato 4577942b919SKoji Sato failed: 4587942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 4597942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 4607942b919SKoji Sato brelse(bh); 4617942b919SKoji Sato } 4627942b919SKoji Sato return ret; 4637942b919SKoji Sato } 4647942b919SKoji Sato 4654f6b8288SRyusuke Konishi static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, 4664f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4677942b919SKoji Sato { 4684f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 4697942b919SKoji Sato struct inode *cpfile = nilfs->ns_cpfile; 4707942b919SKoji Sato struct nilfs_period *periods = buf; 4717942b919SKoji Sato int ret, i; 4727942b919SKoji Sato 4737942b919SKoji Sato for (i = 0; i < nmembs; i++) { 4747942b919SKoji Sato ret = nilfs_cpfile_delete_checkpoints( 4757942b919SKoji Sato cpfile, periods[i].p_start, periods[i].p_end); 4767942b919SKoji Sato if (ret < 0) 4777942b919SKoji Sato return ret; 4787942b919SKoji Sato } 4797942b919SKoji Sato return nmembs; 4807942b919SKoji Sato } 4817942b919SKoji Sato 4824f6b8288SRyusuke Konishi static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, 4834f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4847942b919SKoji Sato { 4854f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 4864f6b8288SRyusuke Konishi int ret; 4877942b919SKoji Sato 488365e215cSRyusuke Konishi ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs); 4897942b919SKoji Sato 4907942b919SKoji Sato return (ret < 0) ? ret : nmembs; 4917942b919SKoji Sato } 4927942b919SKoji Sato 4934f6b8288SRyusuke Konishi static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, 4944f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4957942b919SKoji Sato { 4964f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 497365e215cSRyusuke Konishi struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap; 4987942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 4997942b919SKoji Sato int ret, i; 5007942b919SKoji Sato 5017942b919SKoji Sato for (i = 0; i < nmembs; i++) { 5027942b919SKoji Sato /* XXX: use macro or inline func to check liveness */ 5037942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 5047942b919SKoji Sato bdescs[i].bd_offset, 5057942b919SKoji Sato bdescs[i].bd_level + 1, 5067942b919SKoji Sato &bdescs[i].bd_blocknr); 5077942b919SKoji Sato if (ret < 0) { 5087942b919SKoji Sato if (ret != -ENOENT) 5097942b919SKoji Sato return ret; 5107942b919SKoji Sato bdescs[i].bd_blocknr = 0; 5117942b919SKoji Sato } 5127942b919SKoji Sato if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) 5137942b919SKoji Sato /* skip dead block */ 5147942b919SKoji Sato continue; 5157942b919SKoji Sato if (bdescs[i].bd_level == 0) { 516365e215cSRyusuke Konishi ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat, 5177942b919SKoji Sato bdescs[i].bd_offset); 5187942b919SKoji Sato if (ret < 0) { 5191f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 5207942b919SKoji Sato return ret; 5217942b919SKoji Sato } 5227942b919SKoji Sato } else { 5237942b919SKoji Sato ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, 5247942b919SKoji Sato bdescs[i].bd_level); 5257942b919SKoji Sato if (ret < 0) { 5261f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 5277942b919SKoji Sato return ret; 5287942b919SKoji Sato } 5297942b919SKoji Sato } 5307942b919SKoji Sato } 5317942b919SKoji Sato return nmembs; 5327942b919SKoji Sato } 5337942b919SKoji Sato 5347942b919SKoji Sato int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, 5354f6b8288SRyusuke Konishi struct nilfs_argv *argv, void **kbufs) 5367942b919SKoji Sato { 5371f5abe7eSRyusuke Konishi const char *msg; 5384f6b8288SRyusuke Konishi int ret; 5397942b919SKoji Sato 5404f6b8288SRyusuke Konishi ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); 5411f5abe7eSRyusuke Konishi if (ret < 0) { 5421f5abe7eSRyusuke Konishi /* 5431f5abe7eSRyusuke Konishi * can safely abort because checkpoints can be removed 5441f5abe7eSRyusuke Konishi * independently. 5451f5abe7eSRyusuke Konishi */ 5461f5abe7eSRyusuke Konishi msg = "cannot delete checkpoints"; 5471f5abe7eSRyusuke Konishi goto failed; 5481f5abe7eSRyusuke Konishi } 5494f6b8288SRyusuke Konishi ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); 5501f5abe7eSRyusuke Konishi if (ret < 0) { 5511f5abe7eSRyusuke Konishi /* 5521f5abe7eSRyusuke Konishi * can safely abort because DAT file is updated atomically 5531f5abe7eSRyusuke Konishi * using a copy-on-write technique. 5541f5abe7eSRyusuke Konishi */ 5551f5abe7eSRyusuke Konishi msg = "cannot delete virtual blocks from DAT file"; 5561f5abe7eSRyusuke Konishi goto failed; 5571f5abe7eSRyusuke Konishi } 5584f6b8288SRyusuke Konishi ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); 5591f5abe7eSRyusuke Konishi if (ret < 0) { 5601f5abe7eSRyusuke Konishi /* 5611f5abe7eSRyusuke Konishi * can safely abort because the operation is nondestructive. 5621f5abe7eSRyusuke Konishi */ 5631f5abe7eSRyusuke Konishi msg = "cannot mark copying blocks dirty"; 5641f5abe7eSRyusuke Konishi goto failed; 5651f5abe7eSRyusuke Konishi } 5667942b919SKoji Sato return 0; 5677942b919SKoji Sato 5681f5abe7eSRyusuke Konishi failed: 5691f5abe7eSRyusuke Konishi printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", 5701f5abe7eSRyusuke Konishi msg, ret); 5717942b919SKoji Sato return ret; 5727942b919SKoji Sato } 5737942b919SKoji Sato 5747942b919SKoji Sato static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, 5757942b919SKoji Sato unsigned int cmd, void __user *argp) 5767942b919SKoji Sato { 5774f6b8288SRyusuke Konishi struct nilfs_argv argv[5]; 57833e189bdSTobias Klauser static const size_t argsz[5] = { 5794f6b8288SRyusuke Konishi sizeof(struct nilfs_vdesc), 5804f6b8288SRyusuke Konishi sizeof(struct nilfs_period), 5814f6b8288SRyusuke Konishi sizeof(__u64), 5824f6b8288SRyusuke Konishi sizeof(struct nilfs_bdesc), 5834f6b8288SRyusuke Konishi sizeof(__u64), 5844f6b8288SRyusuke Konishi }; 5854f6b8288SRyusuke Konishi void __user *base; 5864f6b8288SRyusuke Konishi void *kbufs[5]; 5874f6b8288SRyusuke Konishi struct the_nilfs *nilfs; 5884f6b8288SRyusuke Konishi size_t len, nsegs; 5894f6b8288SRyusuke Konishi int n, ret; 5904f6b8288SRyusuke Konishi 5917942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 5927942b919SKoji Sato return -EPERM; 5934f6b8288SRyusuke Konishi 594a561be71SAl Viro ret = mnt_want_write_file(filp); 5957512487eSRyusuke Konishi if (ret) 5967512487eSRyusuke Konishi return ret; 5974f6b8288SRyusuke Konishi 5987512487eSRyusuke Konishi ret = -EFAULT; 5997512487eSRyusuke Konishi if (copy_from_user(argv, argp, sizeof(argv))) 6007512487eSRyusuke Konishi goto out; 6017512487eSRyusuke Konishi 6027512487eSRyusuke Konishi ret = -EINVAL; 6034f6b8288SRyusuke Konishi nsegs = argv[4].v_nmembs; 6044f6b8288SRyusuke Konishi if (argv[4].v_size != argsz[4]) 6057512487eSRyusuke Konishi goto out; 6067512487eSRyusuke Konishi 6074f6b8288SRyusuke Konishi /* 6084f6b8288SRyusuke Konishi * argv[4] points to segment numbers this ioctl cleans. We 6094f6b8288SRyusuke Konishi * use kmalloc() for its buffer because memory used for the 6104f6b8288SRyusuke Konishi * segment numbers is enough small. 6114f6b8288SRyusuke Konishi */ 6124f6b8288SRyusuke Konishi kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, 6134f6b8288SRyusuke Konishi nsegs * sizeof(__u64)); 6147512487eSRyusuke Konishi if (IS_ERR(kbufs[4])) { 6157512487eSRyusuke Konishi ret = PTR_ERR(kbufs[4]); 6167512487eSRyusuke Konishi goto out; 6177512487eSRyusuke Konishi } 618e3154e97SRyusuke Konishi nilfs = inode->i_sb->s_fs_info; 6194f6b8288SRyusuke Konishi 6204f6b8288SRyusuke Konishi for (n = 0; n < 4; n++) { 6214f6b8288SRyusuke Konishi ret = -EINVAL; 6224f6b8288SRyusuke Konishi if (argv[n].v_size != argsz[n]) 6234f6b8288SRyusuke Konishi goto out_free; 6244f6b8288SRyusuke Konishi 6254f6b8288SRyusuke Konishi if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) 6264f6b8288SRyusuke Konishi goto out_free; 6274f6b8288SRyusuke Konishi 628481fe17eSHaogang Chen if (argv[n].v_nmembs >= UINT_MAX / argv[n].v_size) 629481fe17eSHaogang Chen goto out_free; 630481fe17eSHaogang Chen 6314f6b8288SRyusuke Konishi len = argv[n].v_size * argv[n].v_nmembs; 6324f6b8288SRyusuke Konishi base = (void __user *)(unsigned long)argv[n].v_base; 6334f6b8288SRyusuke Konishi if (len == 0) { 6344f6b8288SRyusuke Konishi kbufs[n] = NULL; 6354f6b8288SRyusuke Konishi continue; 6364f6b8288SRyusuke Konishi } 6374f6b8288SRyusuke Konishi 6384f6b8288SRyusuke Konishi kbufs[n] = vmalloc(len); 6394f6b8288SRyusuke Konishi if (!kbufs[n]) { 6404f6b8288SRyusuke Konishi ret = -ENOMEM; 6414f6b8288SRyusuke Konishi goto out_free; 6424f6b8288SRyusuke Konishi } 6434f6b8288SRyusuke Konishi if (copy_from_user(kbufs[n], base, len)) { 6444f6b8288SRyusuke Konishi ret = -EFAULT; 6454f6b8288SRyusuke Konishi vfree(kbufs[n]); 6464f6b8288SRyusuke Konishi goto out_free; 6474f6b8288SRyusuke Konishi } 6484f6b8288SRyusuke Konishi } 6494f6b8288SRyusuke Konishi 6501cf58fa8SJiro SEKIBA /* 651263d90ceSRyusuke Konishi * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(), 6521cf58fa8SJiro SEKIBA * which will operates an inode list without blocking. 6531cf58fa8SJiro SEKIBA * To protect the list from concurrent operations, 6541cf58fa8SJiro SEKIBA * nilfs_ioctl_move_blocks should be atomic operation. 6551cf58fa8SJiro SEKIBA */ 6561cf58fa8SJiro SEKIBA if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { 6571cf58fa8SJiro SEKIBA ret = -EBUSY; 6581cf58fa8SJiro SEKIBA goto out_free; 6591cf58fa8SJiro SEKIBA } 6601cf58fa8SJiro SEKIBA 6615beb6e0bSRyusuke Konishi vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); 6625beb6e0bSRyusuke Konishi 663263d90ceSRyusuke Konishi ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); 6641cf58fa8SJiro SEKIBA if (ret < 0) 6651cf58fa8SJiro SEKIBA printk(KERN_ERR "NILFS: GC failed during preparation: " 6661cf58fa8SJiro SEKIBA "cannot read source blocks: err=%d\n", ret); 6671cf58fa8SJiro SEKIBA else 6684f6b8288SRyusuke Konishi ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); 6694f6b8288SRyusuke Konishi 670263d90ceSRyusuke Konishi nilfs_remove_all_gcinodes(nilfs); 6711cf58fa8SJiro SEKIBA clear_nilfs_gc_running(nilfs); 6721cf58fa8SJiro SEKIBA 6734f6b8288SRyusuke Konishi out_free: 674d5046853SRyusuke Konishi while (--n >= 0) 6754f6b8288SRyusuke Konishi vfree(kbufs[n]); 6764f6b8288SRyusuke Konishi kfree(kbufs[4]); 6777512487eSRyusuke Konishi out: 6782a79f17eSAl Viro mnt_drop_write_file(filp); 6794f6b8288SRyusuke Konishi return ret; 6807942b919SKoji Sato } 6817942b919SKoji Sato 6827942b919SKoji Sato static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, 6837942b919SKoji Sato unsigned int cmd, void __user *argp) 6847942b919SKoji Sato { 6857942b919SKoji Sato __u64 cno; 6867942b919SKoji Sato int ret; 6870d561f12SJiro SEKIBA struct the_nilfs *nilfs; 6887942b919SKoji Sato 6897942b919SKoji Sato ret = nilfs_construct_segment(inode->i_sb); 6907942b919SKoji Sato if (ret < 0) 6917942b919SKoji Sato return ret; 6927942b919SKoji Sato 6937942b919SKoji Sato if (argp != NULL) { 694e3154e97SRyusuke Konishi nilfs = inode->i_sb->s_fs_info; 6950d561f12SJiro SEKIBA down_read(&nilfs->ns_segctor_sem); 6960d561f12SJiro SEKIBA cno = nilfs->ns_cno - 1; 6970d561f12SJiro SEKIBA up_read(&nilfs->ns_segctor_sem); 6987942b919SKoji Sato if (copy_to_user(argp, &cno, sizeof(cno))) 6997942b919SKoji Sato return -EFAULT; 7007942b919SKoji Sato } 7017942b919SKoji Sato return 0; 7027942b919SKoji Sato } 7037942b919SKoji Sato 7044e33f9eaSRyusuke Konishi static int nilfs_ioctl_resize(struct inode *inode, struct file *filp, 7054e33f9eaSRyusuke Konishi void __user *argp) 7064e33f9eaSRyusuke Konishi { 7074e33f9eaSRyusuke Konishi __u64 newsize; 7084e33f9eaSRyusuke Konishi int ret = -EPERM; 7094e33f9eaSRyusuke Konishi 7104e33f9eaSRyusuke Konishi if (!capable(CAP_SYS_ADMIN)) 7114e33f9eaSRyusuke Konishi goto out; 7124e33f9eaSRyusuke Konishi 713a561be71SAl Viro ret = mnt_want_write_file(filp); 7144e33f9eaSRyusuke Konishi if (ret) 7154e33f9eaSRyusuke Konishi goto out; 7164e33f9eaSRyusuke Konishi 7174e33f9eaSRyusuke Konishi ret = -EFAULT; 7184e33f9eaSRyusuke Konishi if (copy_from_user(&newsize, argp, sizeof(newsize))) 7194e33f9eaSRyusuke Konishi goto out_drop_write; 7204e33f9eaSRyusuke Konishi 7214e33f9eaSRyusuke Konishi ret = nilfs_resize_fs(inode->i_sb, newsize); 7224e33f9eaSRyusuke Konishi 7234e33f9eaSRyusuke Konishi out_drop_write: 7242a79f17eSAl Viro mnt_drop_write_file(filp); 7254e33f9eaSRyusuke Konishi out: 7264e33f9eaSRyusuke Konishi return ret; 7274e33f9eaSRyusuke Konishi } 7284e33f9eaSRyusuke Konishi 729619205daSRyusuke Konishi static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp) 730619205daSRyusuke Konishi { 731619205daSRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 732619205daSRyusuke Konishi __u64 range[2]; 733619205daSRyusuke Konishi __u64 minseg, maxseg; 734619205daSRyusuke Konishi unsigned long segbytes; 735619205daSRyusuke Konishi int ret = -EPERM; 736619205daSRyusuke Konishi 737619205daSRyusuke Konishi if (!capable(CAP_SYS_ADMIN)) 738619205daSRyusuke Konishi goto out; 739619205daSRyusuke Konishi 740619205daSRyusuke Konishi ret = -EFAULT; 741619205daSRyusuke Konishi if (copy_from_user(range, argp, sizeof(__u64[2]))) 742619205daSRyusuke Konishi goto out; 743619205daSRyusuke Konishi 744619205daSRyusuke Konishi ret = -ERANGE; 745619205daSRyusuke Konishi if (range[1] > i_size_read(inode->i_sb->s_bdev->bd_inode)) 746619205daSRyusuke Konishi goto out; 747619205daSRyusuke Konishi 748619205daSRyusuke Konishi segbytes = nilfs->ns_blocks_per_segment * nilfs->ns_blocksize; 749619205daSRyusuke Konishi 750619205daSRyusuke Konishi minseg = range[0] + segbytes - 1; 751619205daSRyusuke Konishi do_div(minseg, segbytes); 752619205daSRyusuke Konishi maxseg = NILFS_SB2_OFFSET_BYTES(range[1]); 753619205daSRyusuke Konishi do_div(maxseg, segbytes); 754619205daSRyusuke Konishi maxseg--; 755619205daSRyusuke Konishi 756619205daSRyusuke Konishi ret = nilfs_sufile_set_alloc_range(nilfs->ns_sufile, minseg, maxseg); 757619205daSRyusuke Konishi out: 758619205daSRyusuke Konishi return ret; 759619205daSRyusuke Konishi } 760619205daSRyusuke Konishi 76147eb6b9cSRyusuke Konishi static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, 76247eb6b9cSRyusuke Konishi unsigned int cmd, void __user *argp, 76383aca8f4SRyusuke Konishi size_t membsz, 76447eb6b9cSRyusuke Konishi ssize_t (*dofunc)(struct the_nilfs *, 76547eb6b9cSRyusuke Konishi __u64 *, int, 76647eb6b9cSRyusuke Konishi void *, size_t, size_t)) 76747eb6b9cSRyusuke Konishi 76847eb6b9cSRyusuke Konishi { 769e3154e97SRyusuke Konishi struct the_nilfs *nilfs = inode->i_sb->s_fs_info; 77047eb6b9cSRyusuke Konishi struct nilfs_argv argv; 77147eb6b9cSRyusuke Konishi int ret; 77247eb6b9cSRyusuke Konishi 77347eb6b9cSRyusuke Konishi if (copy_from_user(&argv, argp, sizeof(argv))) 77447eb6b9cSRyusuke Konishi return -EFAULT; 77547eb6b9cSRyusuke Konishi 776003ff182SRyusuke Konishi if (argv.v_size < membsz) 77783aca8f4SRyusuke Konishi return -EINVAL; 77883aca8f4SRyusuke Konishi 77947eb6b9cSRyusuke Konishi ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); 78047eb6b9cSRyusuke Konishi if (ret < 0) 78147eb6b9cSRyusuke Konishi return ret; 78247eb6b9cSRyusuke Konishi 78347eb6b9cSRyusuke Konishi if (copy_to_user(argp, &argv, sizeof(argv))) 78447eb6b9cSRyusuke Konishi ret = -EFAULT; 78547eb6b9cSRyusuke Konishi return ret; 78647eb6b9cSRyusuke Konishi } 78747eb6b9cSRyusuke Konishi 7887a946193SRyusuke Konishi long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 7897942b919SKoji Sato { 7907a946193SRyusuke Konishi struct inode *inode = filp->f_dentry->d_inode; 79175323400SLi Hong void __user *argp = (void __user *)arg; 7927942b919SKoji Sato 7937942b919SKoji Sato switch (cmd) { 794cde98f0fSRyusuke Konishi case FS_IOC_GETFLAGS: 795cde98f0fSRyusuke Konishi return nilfs_ioctl_getflags(inode, argp); 796cde98f0fSRyusuke Konishi case FS_IOC_SETFLAGS: 797cde98f0fSRyusuke Konishi return nilfs_ioctl_setflags(inode, filp, argp); 798cde98f0fSRyusuke Konishi case FS_IOC_GETVERSION: 799cde98f0fSRyusuke Konishi return nilfs_ioctl_getversion(inode, argp); 8007942b919SKoji Sato case NILFS_IOCTL_CHANGE_CPMODE: 8017942b919SKoji Sato return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp); 8027942b919SKoji Sato case NILFS_IOCTL_DELETE_CHECKPOINT: 8037942b919SKoji Sato return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); 8047942b919SKoji Sato case NILFS_IOCTL_GET_CPINFO: 80547eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 80683aca8f4SRyusuke Konishi sizeof(struct nilfs_cpinfo), 80747eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_cpinfo); 8087942b919SKoji Sato case NILFS_IOCTL_GET_CPSTAT: 8097942b919SKoji Sato return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); 8107942b919SKoji Sato case NILFS_IOCTL_GET_SUINFO: 81147eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 81283aca8f4SRyusuke Konishi sizeof(struct nilfs_suinfo), 81347eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_suinfo); 8147942b919SKoji Sato case NILFS_IOCTL_GET_SUSTAT: 8157942b919SKoji Sato return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); 8167942b919SKoji Sato case NILFS_IOCTL_GET_VINFO: 81747eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 81883aca8f4SRyusuke Konishi sizeof(struct nilfs_vinfo), 81947eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_vinfo); 8207942b919SKoji Sato case NILFS_IOCTL_GET_BDESCS: 8217942b919SKoji Sato return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); 8227942b919SKoji Sato case NILFS_IOCTL_CLEAN_SEGMENTS: 8237942b919SKoji Sato return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); 8247942b919SKoji Sato case NILFS_IOCTL_SYNC: 8257942b919SKoji Sato return nilfs_ioctl_sync(inode, filp, cmd, argp); 8264e33f9eaSRyusuke Konishi case NILFS_IOCTL_RESIZE: 8274e33f9eaSRyusuke Konishi return nilfs_ioctl_resize(inode, filp, argp); 828619205daSRyusuke Konishi case NILFS_IOCTL_SET_ALLOC_RANGE: 829619205daSRyusuke Konishi return nilfs_ioctl_set_alloc_range(inode, argp); 8307942b919SKoji Sato default: 8317942b919SKoji Sato return -ENOTTY; 8327942b919SKoji Sato } 8337942b919SKoji Sato } 834828b1c50SRyusuke Konishi 835828b1c50SRyusuke Konishi #ifdef CONFIG_COMPAT 836828b1c50SRyusuke Konishi long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 837828b1c50SRyusuke Konishi { 838828b1c50SRyusuke Konishi switch (cmd) { 839828b1c50SRyusuke Konishi case FS_IOC32_GETFLAGS: 840828b1c50SRyusuke Konishi cmd = FS_IOC_GETFLAGS; 841828b1c50SRyusuke Konishi break; 842828b1c50SRyusuke Konishi case FS_IOC32_SETFLAGS: 843828b1c50SRyusuke Konishi cmd = FS_IOC_SETFLAGS; 844828b1c50SRyusuke Konishi break; 845828b1c50SRyusuke Konishi case FS_IOC32_GETVERSION: 846828b1c50SRyusuke Konishi cmd = FS_IOC_GETVERSION; 847828b1c50SRyusuke Konishi break; 848695c60f2SThomas Meyer case NILFS_IOCTL_CHANGE_CPMODE: 849695c60f2SThomas Meyer case NILFS_IOCTL_DELETE_CHECKPOINT: 850695c60f2SThomas Meyer case NILFS_IOCTL_GET_CPINFO: 851695c60f2SThomas Meyer case NILFS_IOCTL_GET_CPSTAT: 852695c60f2SThomas Meyer case NILFS_IOCTL_GET_SUINFO: 853695c60f2SThomas Meyer case NILFS_IOCTL_GET_SUSTAT: 854695c60f2SThomas Meyer case NILFS_IOCTL_GET_VINFO: 855695c60f2SThomas Meyer case NILFS_IOCTL_GET_BDESCS: 856695c60f2SThomas Meyer case NILFS_IOCTL_CLEAN_SEGMENTS: 857695c60f2SThomas Meyer case NILFS_IOCTL_SYNC: 858695c60f2SThomas Meyer case NILFS_IOCTL_RESIZE: 859695c60f2SThomas Meyer case NILFS_IOCTL_SET_ALLOC_RANGE: 860695c60f2SThomas Meyer break; 861828b1c50SRyusuke Konishi default: 862828b1c50SRyusuke Konishi return -ENOIOCTLCMD; 863828b1c50SRyusuke Konishi } 864828b1c50SRyusuke Konishi return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); 865828b1c50SRyusuke Konishi } 866828b1c50SRyusuke Konishi #endif 867