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() */ 265a0e3ad6STejun Heo #include <linux/slab.h> 277942b919SKoji Sato #include <linux/capability.h> /* capable() */ 287942b919SKoji Sato #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ 294f6b8288SRyusuke Konishi #include <linux/vmalloc.h> 307512487eSRyusuke Konishi #include <linux/mount.h> /* mnt_want_write(), mnt_drop_write() */ 317942b919SKoji Sato #include <linux/nilfs2_fs.h> 327942b919SKoji Sato #include "nilfs.h" 337942b919SKoji Sato #include "segment.h" 347942b919SKoji Sato #include "bmap.h" 357942b919SKoji Sato #include "cpfile.h" 367942b919SKoji Sato #include "sufile.h" 377942b919SKoji Sato #include "dat.h" 387942b919SKoji Sato 397942b919SKoji Sato 407942b919SKoji Sato static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, 417942b919SKoji Sato struct nilfs_argv *argv, int dir, 427942b919SKoji Sato ssize_t (*dofunc)(struct the_nilfs *, 43b028fcfcSRyusuke Konishi __u64 *, int, 447942b919SKoji Sato void *, size_t, size_t)) 457942b919SKoji Sato { 467942b919SKoji Sato void *buf; 47dc498d09SRyusuke Konishi void __user *base = (void __user *)(unsigned long)argv->v_base; 483358b4aaSRyusuke Konishi size_t maxmembs, total, n; 497942b919SKoji Sato ssize_t nr; 507942b919SKoji Sato int ret, i; 51b028fcfcSRyusuke Konishi __u64 pos, ppos; 527942b919SKoji Sato 537942b919SKoji Sato if (argv->v_nmembs == 0) 547942b919SKoji Sato return 0; 557942b919SKoji Sato 563358b4aaSRyusuke Konishi if (argv->v_size > PAGE_SIZE) 573358b4aaSRyusuke Konishi return -EINVAL; 583358b4aaSRyusuke Konishi 593358b4aaSRyusuke Konishi buf = (void *)__get_free_pages(GFP_NOFS, 0); 603358b4aaSRyusuke Konishi if (unlikely(!buf)) 617942b919SKoji Sato return -ENOMEM; 623358b4aaSRyusuke Konishi maxmembs = PAGE_SIZE / argv->v_size; 637942b919SKoji Sato 647942b919SKoji Sato ret = 0; 657942b919SKoji Sato total = 0; 66b028fcfcSRyusuke Konishi pos = argv->v_index; 677942b919SKoji Sato for (i = 0; i < argv->v_nmembs; i += n) { 687942b919SKoji Sato n = (argv->v_nmembs - i < maxmembs) ? 697942b919SKoji Sato argv->v_nmembs - i : maxmembs; 707942b919SKoji Sato if ((dir & _IOC_WRITE) && 71dc498d09SRyusuke Konishi copy_from_user(buf, base + argv->v_size * i, 727942b919SKoji Sato argv->v_size * n)) { 737942b919SKoji Sato ret = -EFAULT; 747942b919SKoji Sato break; 757942b919SKoji Sato } 76b028fcfcSRyusuke Konishi ppos = pos; 778acfbf09SPekka Enberg nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, 78b028fcfcSRyusuke Konishi n); 797942b919SKoji Sato if (nr < 0) { 807942b919SKoji Sato ret = nr; 817942b919SKoji Sato break; 827942b919SKoji Sato } 837942b919SKoji Sato if ((dir & _IOC_READ) && 84dc498d09SRyusuke Konishi copy_to_user(base + argv->v_size * i, buf, 85dc498d09SRyusuke Konishi argv->v_size * nr)) { 867942b919SKoji Sato ret = -EFAULT; 877942b919SKoji Sato break; 887942b919SKoji Sato } 897942b919SKoji Sato total += nr; 90b028fcfcSRyusuke Konishi if ((size_t)nr < n) 91b028fcfcSRyusuke Konishi break; 92b028fcfcSRyusuke Konishi if (pos == ppos) 93b028fcfcSRyusuke Konishi pos += n; 947942b919SKoji Sato } 957942b919SKoji Sato argv->v_nmembs = total; 967942b919SKoji Sato 973358b4aaSRyusuke Konishi free_pages((unsigned long)buf, 0); 987942b919SKoji Sato return ret; 997942b919SKoji Sato } 1007942b919SKoji Sato 1017942b919SKoji Sato static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, 1027942b919SKoji Sato unsigned int cmd, void __user *argp) 1037942b919SKoji Sato { 104c1ea985cSRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 105c1ea985cSRyusuke Konishi struct inode *cpfile = nilfs->ns_cpfile; 1067942b919SKoji Sato struct nilfs_transaction_info ti; 1077942b919SKoji Sato struct nilfs_cpmode cpmode; 1087942b919SKoji Sato int ret; 1097942b919SKoji Sato 1107942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 1117942b919SKoji Sato return -EPERM; 1127512487eSRyusuke Konishi 1137512487eSRyusuke Konishi ret = mnt_want_write(filp->f_path.mnt); 1147512487eSRyusuke Konishi if (ret) 1157512487eSRyusuke Konishi return ret; 1167512487eSRyusuke Konishi 1177512487eSRyusuke Konishi ret = -EFAULT; 1187942b919SKoji Sato if (copy_from_user(&cpmode, argp, sizeof(cpmode))) 1197512487eSRyusuke Konishi goto out; 1207942b919SKoji Sato 121c1ea985cSRyusuke Konishi mutex_lock(&nilfs->ns_mount_mutex); 1227512487eSRyusuke Konishi 1237942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 1247942b919SKoji Sato ret = nilfs_cpfile_change_cpmode( 1257942b919SKoji Sato cpfile, cpmode.cm_cno, cpmode.cm_mode); 1267512487eSRyusuke Konishi if (unlikely(ret < 0)) 12747420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 1287512487eSRyusuke Konishi else 12947420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 1307512487eSRyusuke Konishi 131c1ea985cSRyusuke Konishi mutex_unlock(&nilfs->ns_mount_mutex); 1327512487eSRyusuke Konishi out: 1337512487eSRyusuke Konishi mnt_drop_write(filp->f_path.mnt); 1347942b919SKoji Sato return ret; 1357942b919SKoji Sato } 1367942b919SKoji Sato 1377942b919SKoji Sato static int 1387942b919SKoji Sato nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, 1397942b919SKoji Sato unsigned int cmd, void __user *argp) 1407942b919SKoji Sato { 1417942b919SKoji Sato struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile; 1427942b919SKoji Sato struct nilfs_transaction_info ti; 1437942b919SKoji Sato __u64 cno; 1447942b919SKoji Sato int ret; 1457942b919SKoji Sato 1467942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 1477942b919SKoji Sato return -EPERM; 1487512487eSRyusuke Konishi 1497512487eSRyusuke Konishi ret = mnt_want_write(filp->f_path.mnt); 1507512487eSRyusuke Konishi if (ret) 1517512487eSRyusuke Konishi return ret; 1527512487eSRyusuke Konishi 1537512487eSRyusuke Konishi ret = -EFAULT; 1547942b919SKoji Sato if (copy_from_user(&cno, argp, sizeof(cno))) 1557512487eSRyusuke Konishi goto out; 1567942b919SKoji Sato 1577942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 1587942b919SKoji Sato ret = nilfs_cpfile_delete_checkpoint(cpfile, cno); 1597512487eSRyusuke Konishi if (unlikely(ret < 0)) 16047420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 1617512487eSRyusuke Konishi else 16247420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 1637512487eSRyusuke Konishi out: 1647512487eSRyusuke Konishi mnt_drop_write(filp->f_path.mnt); 1657942b919SKoji Sato return ret; 1667942b919SKoji Sato } 1677942b919SKoji Sato 1687942b919SKoji Sato static ssize_t 169b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 1707942b919SKoji Sato void *buf, size_t size, size_t nmembs) 1717942b919SKoji Sato { 1727942b919SKoji Sato int ret; 1737942b919SKoji Sato 17447420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 17547eb6b9cSRyusuke Konishi ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, 176003ff182SRyusuke Konishi size, nmembs); 17747420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 1787942b919SKoji Sato return ret; 1797942b919SKoji Sato } 1807942b919SKoji Sato 1817942b919SKoji Sato static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, 1827942b919SKoji Sato unsigned int cmd, void __user *argp) 1837942b919SKoji Sato { 18447420c79SRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 1857942b919SKoji Sato struct nilfs_cpstat cpstat; 1867942b919SKoji Sato int ret; 1877942b919SKoji Sato 18847420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 18947420c79SRyusuke Konishi ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); 19047420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 1917942b919SKoji Sato if (ret < 0) 1927942b919SKoji Sato return ret; 1937942b919SKoji Sato 1947942b919SKoji Sato if (copy_to_user(argp, &cpstat, sizeof(cpstat))) 1957942b919SKoji Sato ret = -EFAULT; 1967942b919SKoji Sato return ret; 1977942b919SKoji Sato } 1987942b919SKoji Sato 1997942b919SKoji Sato static ssize_t 200b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2017942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2027942b919SKoji Sato { 2037942b919SKoji Sato int ret; 2047942b919SKoji Sato 20547420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 206003ff182SRyusuke Konishi ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, 207003ff182SRyusuke Konishi nmembs); 20847420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2097942b919SKoji Sato return ret; 2107942b919SKoji Sato } 2117942b919SKoji Sato 2127942b919SKoji Sato static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, 2137942b919SKoji Sato unsigned int cmd, void __user *argp) 2147942b919SKoji Sato { 21547420c79SRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 2167942b919SKoji Sato struct nilfs_sustat sustat; 2177942b919SKoji Sato int ret; 2187942b919SKoji Sato 21947420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 22047420c79SRyusuke Konishi ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); 22147420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2227942b919SKoji Sato if (ret < 0) 2237942b919SKoji Sato return ret; 2247942b919SKoji Sato 2257942b919SKoji Sato if (copy_to_user(argp, &sustat, sizeof(sustat))) 2267942b919SKoji Sato ret = -EFAULT; 2277942b919SKoji Sato return ret; 2287942b919SKoji Sato } 2297942b919SKoji Sato 2307942b919SKoji Sato static ssize_t 231b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2327942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2337942b919SKoji Sato { 2347942b919SKoji Sato int ret; 2357942b919SKoji Sato 23647420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 237003ff182SRyusuke Konishi ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, size, nmembs); 23847420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2397942b919SKoji Sato return ret; 2407942b919SKoji Sato } 2417942b919SKoji Sato 2427942b919SKoji Sato static ssize_t 243b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, 2447942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2457942b919SKoji Sato { 2467942b919SKoji Sato struct inode *dat = nilfs_dat_inode(nilfs); 2477942b919SKoji Sato struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; 2487942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 2497942b919SKoji Sato int ret, i; 2507942b919SKoji Sato 25147eb6b9cSRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 2527942b919SKoji Sato for (i = 0; i < nmembs; i++) { 2537942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 2547942b919SKoji Sato bdescs[i].bd_offset, 2557942b919SKoji Sato bdescs[i].bd_level + 1, 2567942b919SKoji Sato &bdescs[i].bd_blocknr); 2577942b919SKoji Sato if (ret < 0) { 25847eb6b9cSRyusuke Konishi if (ret != -ENOENT) { 25947eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2607942b919SKoji Sato return ret; 26147eb6b9cSRyusuke Konishi } 2627942b919SKoji Sato bdescs[i].bd_blocknr = 0; 2637942b919SKoji Sato } 2647942b919SKoji Sato } 26547eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2667942b919SKoji Sato return nmembs; 2677942b919SKoji Sato } 2687942b919SKoji Sato 2697942b919SKoji Sato static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, 2707942b919SKoji Sato unsigned int cmd, void __user *argp) 2717942b919SKoji Sato { 2727942b919SKoji Sato struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 2737942b919SKoji Sato struct nilfs_argv argv; 2747942b919SKoji Sato int ret; 2757942b919SKoji Sato 2767942b919SKoji Sato if (copy_from_user(&argv, argp, sizeof(argv))) 2777942b919SKoji Sato return -EFAULT; 2787942b919SKoji Sato 27983aca8f4SRyusuke Konishi if (argv.v_size != sizeof(struct nilfs_bdesc)) 28083aca8f4SRyusuke Konishi return -EINVAL; 28183aca8f4SRyusuke Konishi 2827942b919SKoji Sato ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), 2837942b919SKoji Sato nilfs_ioctl_do_get_bdescs); 28447420c79SRyusuke Konishi if (ret < 0) 28547420c79SRyusuke Konishi return ret; 2867942b919SKoji Sato 2877942b919SKoji Sato if (copy_to_user(argp, &argv, sizeof(argv))) 2887942b919SKoji Sato ret = -EFAULT; 2897942b919SKoji Sato return ret; 2907942b919SKoji Sato } 2917942b919SKoji Sato 2927942b919SKoji Sato static int nilfs_ioctl_move_inode_block(struct inode *inode, 2937942b919SKoji Sato struct nilfs_vdesc *vdesc, 2947942b919SKoji Sato struct list_head *buffers) 2957942b919SKoji Sato { 2967942b919SKoji Sato struct buffer_head *bh; 2977942b919SKoji Sato int ret; 2987942b919SKoji Sato 2997942b919SKoji Sato if (vdesc->vd_flags == 0) 3007942b919SKoji Sato ret = nilfs_gccache_submit_read_data( 3017942b919SKoji Sato inode, vdesc->vd_offset, vdesc->vd_blocknr, 3027942b919SKoji Sato vdesc->vd_vblocknr, &bh); 3037942b919SKoji Sato else 3047942b919SKoji Sato ret = nilfs_gccache_submit_read_node( 3057942b919SKoji Sato inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); 3067942b919SKoji Sato 3077942b919SKoji Sato if (unlikely(ret < 0)) { 3087942b919SKoji Sato if (ret == -ENOENT) 3097942b919SKoji Sato printk(KERN_CRIT 3107942b919SKoji Sato "%s: invalid virtual block address (%s): " 3117942b919SKoji Sato "ino=%llu, cno=%llu, offset=%llu, " 3127942b919SKoji Sato "blocknr=%llu, vblocknr=%llu\n", 3137942b919SKoji Sato __func__, vdesc->vd_flags ? "node" : "data", 3147942b919SKoji Sato (unsigned long long)vdesc->vd_ino, 3157942b919SKoji Sato (unsigned long long)vdesc->vd_cno, 3167942b919SKoji Sato (unsigned long long)vdesc->vd_offset, 3177942b919SKoji Sato (unsigned long long)vdesc->vd_blocknr, 3187942b919SKoji Sato (unsigned long long)vdesc->vd_vblocknr); 3197942b919SKoji Sato return ret; 3207942b919SKoji Sato } 3215399dd1fSRyusuke Konishi if (unlikely(!list_empty(&bh->b_assoc_buffers))) { 3225399dd1fSRyusuke Konishi printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " 3235399dd1fSRyusuke Konishi "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", 3245399dd1fSRyusuke Konishi __func__, vdesc->vd_flags ? "node" : "data", 3255399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_ino, 3265399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_cno, 3275399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_offset, 3285399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_blocknr, 3295399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_vblocknr); 3305399dd1fSRyusuke Konishi brelse(bh); 3315399dd1fSRyusuke Konishi return -EEXIST; 3325399dd1fSRyusuke Konishi } 3337942b919SKoji Sato list_add_tail(&bh->b_assoc_buffers, buffers); 3347942b919SKoji Sato return 0; 3357942b919SKoji Sato } 3367942b919SKoji Sato 3374f6b8288SRyusuke Konishi static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, 3384f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3397942b919SKoji Sato { 3404f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3417942b919SKoji Sato struct inode *inode; 3427942b919SKoji Sato struct nilfs_vdesc *vdesc; 3437942b919SKoji Sato struct buffer_head *bh, *n; 3447942b919SKoji Sato LIST_HEAD(buffers); 3457942b919SKoji Sato ino_t ino; 3467942b919SKoji Sato __u64 cno; 3477942b919SKoji Sato int i, ret; 3487942b919SKoji Sato 3497942b919SKoji Sato for (i = 0, vdesc = buf; i < nmembs; ) { 3507942b919SKoji Sato ino = vdesc->vd_ino; 3517942b919SKoji Sato cno = vdesc->vd_cno; 3527942b919SKoji Sato inode = nilfs_gc_iget(nilfs, ino, cno); 3537942b919SKoji Sato if (unlikely(inode == NULL)) { 3547942b919SKoji Sato ret = -ENOMEM; 3557942b919SKoji Sato goto failed; 3567942b919SKoji Sato } 3577942b919SKoji Sato do { 3587942b919SKoji Sato ret = nilfs_ioctl_move_inode_block(inode, vdesc, 3597942b919SKoji Sato &buffers); 3607942b919SKoji Sato if (unlikely(ret < 0)) 3617942b919SKoji Sato goto failed; 3627942b919SKoji Sato vdesc++; 3637942b919SKoji Sato } while (++i < nmembs && 3647942b919SKoji Sato vdesc->vd_ino == ino && vdesc->vd_cno == cno); 3657942b919SKoji Sato } 3667942b919SKoji Sato 3677942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 3687942b919SKoji Sato ret = nilfs_gccache_wait_and_mark_dirty(bh); 3697942b919SKoji Sato if (unlikely(ret < 0)) { 3705399dd1fSRyusuke Konishi WARN_ON(ret == -EEXIST); 3717942b919SKoji Sato goto failed; 3727942b919SKoji Sato } 3737942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 3747942b919SKoji Sato brelse(bh); 3757942b919SKoji Sato } 3767942b919SKoji Sato return nmembs; 3777942b919SKoji Sato 3787942b919SKoji Sato failed: 3797942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 3807942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 3817942b919SKoji Sato brelse(bh); 3827942b919SKoji Sato } 3837942b919SKoji Sato return ret; 3847942b919SKoji Sato } 3857942b919SKoji Sato 3864f6b8288SRyusuke Konishi static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, 3874f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3887942b919SKoji Sato { 3894f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3907942b919SKoji Sato struct inode *cpfile = nilfs->ns_cpfile; 3917942b919SKoji Sato struct nilfs_period *periods = buf; 3927942b919SKoji Sato int ret, i; 3937942b919SKoji Sato 3947942b919SKoji Sato for (i = 0; i < nmembs; i++) { 3957942b919SKoji Sato ret = nilfs_cpfile_delete_checkpoints( 3967942b919SKoji Sato cpfile, periods[i].p_start, periods[i].p_end); 3977942b919SKoji Sato if (ret < 0) 3987942b919SKoji Sato return ret; 3997942b919SKoji Sato } 4007942b919SKoji Sato return nmembs; 4017942b919SKoji Sato } 4027942b919SKoji Sato 4034f6b8288SRyusuke Konishi static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, 4044f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4057942b919SKoji Sato { 4064f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 4074f6b8288SRyusuke Konishi int ret; 4087942b919SKoji Sato 4094f6b8288SRyusuke Konishi ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs); 4107942b919SKoji Sato 4117942b919SKoji Sato return (ret < 0) ? ret : nmembs; 4127942b919SKoji Sato } 4137942b919SKoji Sato 4144f6b8288SRyusuke Konishi static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, 4154f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 4167942b919SKoji Sato { 4174f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 4187942b919SKoji Sato struct inode *dat = nilfs_dat_inode(nilfs); 4197942b919SKoji Sato struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; 4207942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 4217942b919SKoji Sato int ret, i; 4227942b919SKoji Sato 4237942b919SKoji Sato for (i = 0; i < nmembs; i++) { 4247942b919SKoji Sato /* XXX: use macro or inline func to check liveness */ 4257942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 4267942b919SKoji Sato bdescs[i].bd_offset, 4277942b919SKoji Sato bdescs[i].bd_level + 1, 4287942b919SKoji Sato &bdescs[i].bd_blocknr); 4297942b919SKoji Sato if (ret < 0) { 4307942b919SKoji Sato if (ret != -ENOENT) 4317942b919SKoji Sato return ret; 4327942b919SKoji Sato bdescs[i].bd_blocknr = 0; 4337942b919SKoji Sato } 4347942b919SKoji Sato if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) 4357942b919SKoji Sato /* skip dead block */ 4367942b919SKoji Sato continue; 4377942b919SKoji Sato if (bdescs[i].bd_level == 0) { 4387942b919SKoji Sato ret = nilfs_mdt_mark_block_dirty(dat, 4397942b919SKoji Sato bdescs[i].bd_offset); 4407942b919SKoji Sato if (ret < 0) { 4411f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 4427942b919SKoji Sato return ret; 4437942b919SKoji Sato } 4447942b919SKoji Sato } else { 4457942b919SKoji Sato ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, 4467942b919SKoji Sato bdescs[i].bd_level); 4477942b919SKoji Sato if (ret < 0) { 4481f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 4497942b919SKoji Sato return ret; 4507942b919SKoji Sato } 4517942b919SKoji Sato } 4527942b919SKoji Sato } 4537942b919SKoji Sato return nmembs; 4547942b919SKoji Sato } 4557942b919SKoji Sato 4567942b919SKoji Sato int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, 4574f6b8288SRyusuke Konishi struct nilfs_argv *argv, void **kbufs) 4587942b919SKoji Sato { 4591f5abe7eSRyusuke Konishi const char *msg; 4604f6b8288SRyusuke Konishi int ret; 4617942b919SKoji Sato 4624f6b8288SRyusuke Konishi ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); 4631f5abe7eSRyusuke Konishi if (ret < 0) { 4641f5abe7eSRyusuke Konishi /* 4651f5abe7eSRyusuke Konishi * can safely abort because checkpoints can be removed 4661f5abe7eSRyusuke Konishi * independently. 4671f5abe7eSRyusuke Konishi */ 4681f5abe7eSRyusuke Konishi msg = "cannot delete checkpoints"; 4691f5abe7eSRyusuke Konishi goto failed; 4701f5abe7eSRyusuke Konishi } 4714f6b8288SRyusuke Konishi ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); 4721f5abe7eSRyusuke Konishi if (ret < 0) { 4731f5abe7eSRyusuke Konishi /* 4741f5abe7eSRyusuke Konishi * can safely abort because DAT file is updated atomically 4751f5abe7eSRyusuke Konishi * using a copy-on-write technique. 4761f5abe7eSRyusuke Konishi */ 4771f5abe7eSRyusuke Konishi msg = "cannot delete virtual blocks from DAT file"; 4781f5abe7eSRyusuke Konishi goto failed; 4791f5abe7eSRyusuke Konishi } 4804f6b8288SRyusuke Konishi ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); 4811f5abe7eSRyusuke Konishi if (ret < 0) { 4821f5abe7eSRyusuke Konishi /* 4831f5abe7eSRyusuke Konishi * can safely abort because the operation is nondestructive. 4841f5abe7eSRyusuke Konishi */ 4851f5abe7eSRyusuke Konishi msg = "cannot mark copying blocks dirty"; 4861f5abe7eSRyusuke Konishi goto failed; 4871f5abe7eSRyusuke Konishi } 4887942b919SKoji Sato return 0; 4897942b919SKoji Sato 4901f5abe7eSRyusuke Konishi failed: 4911f5abe7eSRyusuke Konishi printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", 4921f5abe7eSRyusuke Konishi msg, ret); 4937942b919SKoji Sato return ret; 4947942b919SKoji Sato } 4957942b919SKoji Sato 4967942b919SKoji Sato static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, 4977942b919SKoji Sato unsigned int cmd, void __user *argp) 4987942b919SKoji Sato { 4994f6b8288SRyusuke Konishi struct nilfs_argv argv[5]; 50033e189bdSTobias Klauser static const size_t argsz[5] = { 5014f6b8288SRyusuke Konishi sizeof(struct nilfs_vdesc), 5024f6b8288SRyusuke Konishi sizeof(struct nilfs_period), 5034f6b8288SRyusuke Konishi sizeof(__u64), 5044f6b8288SRyusuke Konishi sizeof(struct nilfs_bdesc), 5054f6b8288SRyusuke Konishi sizeof(__u64), 5064f6b8288SRyusuke Konishi }; 5074f6b8288SRyusuke Konishi void __user *base; 5084f6b8288SRyusuke Konishi void *kbufs[5]; 5094f6b8288SRyusuke Konishi struct the_nilfs *nilfs; 5104f6b8288SRyusuke Konishi size_t len, nsegs; 5114f6b8288SRyusuke Konishi int n, ret; 5124f6b8288SRyusuke Konishi 5137942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 5147942b919SKoji Sato return -EPERM; 5154f6b8288SRyusuke Konishi 5167512487eSRyusuke Konishi ret = mnt_want_write(filp->f_path.mnt); 5177512487eSRyusuke Konishi if (ret) 5187512487eSRyusuke Konishi return ret; 5194f6b8288SRyusuke Konishi 5207512487eSRyusuke Konishi ret = -EFAULT; 5217512487eSRyusuke Konishi if (copy_from_user(argv, argp, sizeof(argv))) 5227512487eSRyusuke Konishi goto out; 5237512487eSRyusuke Konishi 5247512487eSRyusuke Konishi ret = -EINVAL; 5254f6b8288SRyusuke Konishi nsegs = argv[4].v_nmembs; 5264f6b8288SRyusuke Konishi if (argv[4].v_size != argsz[4]) 5277512487eSRyusuke Konishi goto out; 5287512487eSRyusuke Konishi 5294f6b8288SRyusuke Konishi /* 5304f6b8288SRyusuke Konishi * argv[4] points to segment numbers this ioctl cleans. We 5314f6b8288SRyusuke Konishi * use kmalloc() for its buffer because memory used for the 5324f6b8288SRyusuke Konishi * segment numbers is enough small. 5334f6b8288SRyusuke Konishi */ 5344f6b8288SRyusuke Konishi kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, 5354f6b8288SRyusuke Konishi nsegs * sizeof(__u64)); 5367512487eSRyusuke Konishi if (IS_ERR(kbufs[4])) { 5377512487eSRyusuke Konishi ret = PTR_ERR(kbufs[4]); 5387512487eSRyusuke Konishi goto out; 5397512487eSRyusuke Konishi } 5404f6b8288SRyusuke Konishi nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 5414f6b8288SRyusuke Konishi 5424f6b8288SRyusuke Konishi for (n = 0; n < 4; n++) { 5434f6b8288SRyusuke Konishi ret = -EINVAL; 5444f6b8288SRyusuke Konishi if (argv[n].v_size != argsz[n]) 5454f6b8288SRyusuke Konishi goto out_free; 5464f6b8288SRyusuke Konishi 5474f6b8288SRyusuke Konishi if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) 5484f6b8288SRyusuke Konishi goto out_free; 5494f6b8288SRyusuke Konishi 5504f6b8288SRyusuke Konishi len = argv[n].v_size * argv[n].v_nmembs; 5514f6b8288SRyusuke Konishi base = (void __user *)(unsigned long)argv[n].v_base; 5524f6b8288SRyusuke Konishi if (len == 0) { 5534f6b8288SRyusuke Konishi kbufs[n] = NULL; 5544f6b8288SRyusuke Konishi continue; 5554f6b8288SRyusuke Konishi } 5564f6b8288SRyusuke Konishi 5574f6b8288SRyusuke Konishi kbufs[n] = vmalloc(len); 5584f6b8288SRyusuke Konishi if (!kbufs[n]) { 5594f6b8288SRyusuke Konishi ret = -ENOMEM; 5604f6b8288SRyusuke Konishi goto out_free; 5614f6b8288SRyusuke Konishi } 5624f6b8288SRyusuke Konishi if (copy_from_user(kbufs[n], base, len)) { 5634f6b8288SRyusuke Konishi ret = -EFAULT; 5644f6b8288SRyusuke Konishi vfree(kbufs[n]); 5654f6b8288SRyusuke Konishi goto out_free; 5664f6b8288SRyusuke Konishi } 5674f6b8288SRyusuke Konishi } 5684f6b8288SRyusuke Konishi 5691cf58fa8SJiro SEKIBA /* 5701cf58fa8SJiro SEKIBA * nilfs_ioctl_move_blocks() will call nilfs_gc_iget(), 5711cf58fa8SJiro SEKIBA * which will operates an inode list without blocking. 5721cf58fa8SJiro SEKIBA * To protect the list from concurrent operations, 5731cf58fa8SJiro SEKIBA * nilfs_ioctl_move_blocks should be atomic operation. 5741cf58fa8SJiro SEKIBA */ 5751cf58fa8SJiro SEKIBA if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { 5761cf58fa8SJiro SEKIBA ret = -EBUSY; 5771cf58fa8SJiro SEKIBA goto out_free; 5781cf58fa8SJiro SEKIBA } 5791cf58fa8SJiro SEKIBA 5801cf58fa8SJiro SEKIBA ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]); 5811cf58fa8SJiro SEKIBA if (ret < 0) 5821cf58fa8SJiro SEKIBA printk(KERN_ERR "NILFS: GC failed during preparation: " 5831cf58fa8SJiro SEKIBA "cannot read source blocks: err=%d\n", ret); 5841cf58fa8SJiro SEKIBA else 5854f6b8288SRyusuke Konishi ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); 5864f6b8288SRyusuke Konishi 587c083234fSRyusuke Konishi if (ret < 0) 588c083234fSRyusuke Konishi nilfs_remove_all_gcinode(nilfs); 5891cf58fa8SJiro SEKIBA clear_nilfs_gc_running(nilfs); 5901cf58fa8SJiro SEKIBA 5914f6b8288SRyusuke Konishi out_free: 592d5046853SRyusuke Konishi while (--n >= 0) 5934f6b8288SRyusuke Konishi vfree(kbufs[n]); 5944f6b8288SRyusuke Konishi kfree(kbufs[4]); 5957512487eSRyusuke Konishi out: 5967512487eSRyusuke Konishi mnt_drop_write(filp->f_path.mnt); 5974f6b8288SRyusuke Konishi return ret; 5987942b919SKoji Sato } 5997942b919SKoji Sato 6007942b919SKoji Sato static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, 6017942b919SKoji Sato unsigned int cmd, void __user *argp) 6027942b919SKoji Sato { 6037942b919SKoji Sato __u64 cno; 6047942b919SKoji Sato int ret; 6050d561f12SJiro SEKIBA struct the_nilfs *nilfs; 6067942b919SKoji Sato 6077942b919SKoji Sato ret = nilfs_construct_segment(inode->i_sb); 6087942b919SKoji Sato if (ret < 0) 6097942b919SKoji Sato return ret; 6107942b919SKoji Sato 6117942b919SKoji Sato if (argp != NULL) { 6120d561f12SJiro SEKIBA nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 6130d561f12SJiro SEKIBA down_read(&nilfs->ns_segctor_sem); 6140d561f12SJiro SEKIBA cno = nilfs->ns_cno - 1; 6150d561f12SJiro SEKIBA up_read(&nilfs->ns_segctor_sem); 6167942b919SKoji Sato if (copy_to_user(argp, &cno, sizeof(cno))) 6177942b919SKoji Sato return -EFAULT; 6187942b919SKoji Sato } 6197942b919SKoji Sato return 0; 6207942b919SKoji Sato } 6217942b919SKoji Sato 62247eb6b9cSRyusuke Konishi static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, 62347eb6b9cSRyusuke Konishi unsigned int cmd, void __user *argp, 62483aca8f4SRyusuke Konishi size_t membsz, 62547eb6b9cSRyusuke Konishi ssize_t (*dofunc)(struct the_nilfs *, 62647eb6b9cSRyusuke Konishi __u64 *, int, 62747eb6b9cSRyusuke Konishi void *, size_t, size_t)) 62847eb6b9cSRyusuke Konishi 62947eb6b9cSRyusuke Konishi { 63047eb6b9cSRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 63147eb6b9cSRyusuke Konishi struct nilfs_argv argv; 63247eb6b9cSRyusuke Konishi int ret; 63347eb6b9cSRyusuke Konishi 63447eb6b9cSRyusuke Konishi if (copy_from_user(&argv, argp, sizeof(argv))) 63547eb6b9cSRyusuke Konishi return -EFAULT; 63647eb6b9cSRyusuke Konishi 637003ff182SRyusuke Konishi if (argv.v_size < membsz) 63883aca8f4SRyusuke Konishi return -EINVAL; 63983aca8f4SRyusuke Konishi 64047eb6b9cSRyusuke Konishi ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); 64147eb6b9cSRyusuke Konishi if (ret < 0) 64247eb6b9cSRyusuke Konishi return ret; 64347eb6b9cSRyusuke Konishi 64447eb6b9cSRyusuke Konishi if (copy_to_user(argp, &argv, sizeof(argv))) 64547eb6b9cSRyusuke Konishi ret = -EFAULT; 64647eb6b9cSRyusuke Konishi return ret; 64747eb6b9cSRyusuke Konishi } 64847eb6b9cSRyusuke Konishi 6497a946193SRyusuke Konishi long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 6507942b919SKoji Sato { 6517a946193SRyusuke Konishi struct inode *inode = filp->f_dentry->d_inode; 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: 66047eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 66183aca8f4SRyusuke Konishi sizeof(struct nilfs_cpinfo), 66247eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_cpinfo); 6637942b919SKoji Sato case NILFS_IOCTL_GET_CPSTAT: 6647942b919SKoji Sato return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); 6657942b919SKoji Sato case NILFS_IOCTL_GET_SUINFO: 66647eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 66783aca8f4SRyusuke Konishi sizeof(struct nilfs_suinfo), 66847eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_suinfo); 6697942b919SKoji Sato case NILFS_IOCTL_GET_SUSTAT: 6707942b919SKoji Sato return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); 6717942b919SKoji Sato case NILFS_IOCTL_GET_VINFO: 67247eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 67383aca8f4SRyusuke Konishi sizeof(struct nilfs_vinfo), 67447eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_vinfo); 6757942b919SKoji Sato case NILFS_IOCTL_GET_BDESCS: 6767942b919SKoji Sato return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); 6777942b919SKoji Sato case NILFS_IOCTL_CLEAN_SEGMENTS: 6787942b919SKoji Sato return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); 6797942b919SKoji Sato case NILFS_IOCTL_SYNC: 6807942b919SKoji Sato return nilfs_ioctl_sync(inode, filp, cmd, argp); 6817942b919SKoji Sato default: 6827942b919SKoji Sato return -ENOTTY; 6837942b919SKoji Sato } 6847942b919SKoji Sato } 685