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() */ 284f6b8288SRyusuke Konishi #include <linux/vmalloc.h> 297942b919SKoji Sato #include <linux/nilfs2_fs.h> 307942b919SKoji Sato #include "nilfs.h" 317942b919SKoji Sato #include "segment.h" 327942b919SKoji Sato #include "bmap.h" 337942b919SKoji Sato #include "cpfile.h" 347942b919SKoji Sato #include "sufile.h" 357942b919SKoji Sato #include "dat.h" 367942b919SKoji Sato 377942b919SKoji Sato 387942b919SKoji Sato static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs, 397942b919SKoji Sato struct nilfs_argv *argv, int dir, 407942b919SKoji Sato ssize_t (*dofunc)(struct the_nilfs *, 41b028fcfcSRyusuke Konishi __u64 *, int, 427942b919SKoji Sato void *, size_t, size_t)) 437942b919SKoji Sato { 447942b919SKoji Sato void *buf; 45dc498d09SRyusuke Konishi void __user *base = (void __user *)(unsigned long)argv->v_base; 463358b4aaSRyusuke Konishi size_t maxmembs, total, n; 477942b919SKoji Sato ssize_t nr; 487942b919SKoji Sato int ret, i; 49b028fcfcSRyusuke Konishi __u64 pos, ppos; 507942b919SKoji Sato 517942b919SKoji Sato if (argv->v_nmembs == 0) 527942b919SKoji Sato return 0; 537942b919SKoji Sato 543358b4aaSRyusuke Konishi if (argv->v_size > PAGE_SIZE) 553358b4aaSRyusuke Konishi return -EINVAL; 563358b4aaSRyusuke Konishi 573358b4aaSRyusuke Konishi buf = (void *)__get_free_pages(GFP_NOFS, 0); 583358b4aaSRyusuke Konishi if (unlikely(!buf)) 597942b919SKoji Sato return -ENOMEM; 603358b4aaSRyusuke Konishi maxmembs = PAGE_SIZE / argv->v_size; 617942b919SKoji Sato 627942b919SKoji Sato ret = 0; 637942b919SKoji Sato total = 0; 64b028fcfcSRyusuke Konishi pos = argv->v_index; 657942b919SKoji Sato for (i = 0; i < argv->v_nmembs; i += n) { 667942b919SKoji Sato n = (argv->v_nmembs - i < maxmembs) ? 677942b919SKoji Sato argv->v_nmembs - i : maxmembs; 687942b919SKoji Sato if ((dir & _IOC_WRITE) && 69dc498d09SRyusuke Konishi copy_from_user(buf, base + argv->v_size * i, 707942b919SKoji Sato argv->v_size * n)) { 717942b919SKoji Sato ret = -EFAULT; 727942b919SKoji Sato break; 737942b919SKoji Sato } 74b028fcfcSRyusuke Konishi ppos = pos; 758acfbf09SPekka Enberg nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size, 76b028fcfcSRyusuke Konishi n); 777942b919SKoji Sato if (nr < 0) { 787942b919SKoji Sato ret = nr; 797942b919SKoji Sato break; 807942b919SKoji Sato } 817942b919SKoji Sato if ((dir & _IOC_READ) && 82dc498d09SRyusuke Konishi copy_to_user(base + argv->v_size * i, buf, 83dc498d09SRyusuke Konishi argv->v_size * nr)) { 847942b919SKoji Sato ret = -EFAULT; 857942b919SKoji Sato break; 867942b919SKoji Sato } 877942b919SKoji Sato total += nr; 88b028fcfcSRyusuke Konishi if ((size_t)nr < n) 89b028fcfcSRyusuke Konishi break; 90b028fcfcSRyusuke Konishi if (pos == ppos) 91b028fcfcSRyusuke Konishi pos += n; 927942b919SKoji Sato } 937942b919SKoji Sato argv->v_nmembs = total; 947942b919SKoji Sato 953358b4aaSRyusuke Konishi free_pages((unsigned long)buf, 0); 967942b919SKoji Sato return ret; 977942b919SKoji Sato } 987942b919SKoji Sato 997942b919SKoji Sato static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, 1007942b919SKoji Sato unsigned int cmd, void __user *argp) 1017942b919SKoji Sato { 1027942b919SKoji Sato struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile; 1037942b919SKoji Sato struct nilfs_transaction_info ti; 1047942b919SKoji Sato struct nilfs_cpmode cpmode; 1057942b919SKoji Sato int ret; 1067942b919SKoji Sato 1077942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 1087942b919SKoji Sato return -EPERM; 1097942b919SKoji Sato if (copy_from_user(&cpmode, argp, sizeof(cpmode))) 1107942b919SKoji Sato return -EFAULT; 1117942b919SKoji Sato 1127942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 1137942b919SKoji Sato ret = nilfs_cpfile_change_cpmode( 1147942b919SKoji Sato cpfile, cpmode.cm_cno, cpmode.cm_mode); 11547420c79SRyusuke Konishi if (unlikely(ret < 0)) { 11647420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 11747420c79SRyusuke Konishi return ret; 11847420c79SRyusuke Konishi } 11947420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 1207942b919SKoji Sato return ret; 1217942b919SKoji Sato } 1227942b919SKoji Sato 1237942b919SKoji Sato static int 1247942b919SKoji Sato nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, 1257942b919SKoji Sato unsigned int cmd, void __user *argp) 1267942b919SKoji Sato { 1277942b919SKoji Sato struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile; 1287942b919SKoji Sato struct nilfs_transaction_info ti; 1297942b919SKoji Sato __u64 cno; 1307942b919SKoji Sato int ret; 1317942b919SKoji Sato 1327942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 1337942b919SKoji Sato return -EPERM; 1347942b919SKoji Sato if (copy_from_user(&cno, argp, sizeof(cno))) 1357942b919SKoji Sato return -EFAULT; 1367942b919SKoji Sato 1377942b919SKoji Sato nilfs_transaction_begin(inode->i_sb, &ti, 0); 1387942b919SKoji Sato ret = nilfs_cpfile_delete_checkpoint(cpfile, cno); 13947420c79SRyusuke Konishi if (unlikely(ret < 0)) { 14047420c79SRyusuke Konishi nilfs_transaction_abort(inode->i_sb); 14147420c79SRyusuke Konishi return ret; 14247420c79SRyusuke Konishi } 14347420c79SRyusuke Konishi nilfs_transaction_commit(inode->i_sb); /* never fails */ 1447942b919SKoji Sato return ret; 1457942b919SKoji Sato } 1467942b919SKoji Sato 1477942b919SKoji Sato static ssize_t 148b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 1497942b919SKoji Sato void *buf, size_t size, size_t nmembs) 1507942b919SKoji Sato { 1517942b919SKoji Sato int ret; 1527942b919SKoji Sato 15347420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 15447eb6b9cSRyusuke Konishi ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, 155003ff182SRyusuke Konishi size, nmembs); 15647420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 1577942b919SKoji Sato return ret; 1587942b919SKoji Sato } 1597942b919SKoji Sato 1607942b919SKoji Sato static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp, 1617942b919SKoji Sato unsigned int cmd, void __user *argp) 1627942b919SKoji Sato { 16347420c79SRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 1647942b919SKoji Sato struct nilfs_cpstat cpstat; 1657942b919SKoji Sato int ret; 1667942b919SKoji Sato 16747420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 16847420c79SRyusuke Konishi ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat); 16947420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 1707942b919SKoji Sato if (ret < 0) 1717942b919SKoji Sato return ret; 1727942b919SKoji Sato 1737942b919SKoji Sato if (copy_to_user(argp, &cpstat, sizeof(cpstat))) 1747942b919SKoji Sato ret = -EFAULT; 1757942b919SKoji Sato return ret; 1767942b919SKoji Sato } 1777942b919SKoji Sato 1787942b919SKoji Sato static ssize_t 179b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 1807942b919SKoji Sato void *buf, size_t size, size_t nmembs) 1817942b919SKoji Sato { 1827942b919SKoji Sato int ret; 1837942b919SKoji Sato 18447420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 185003ff182SRyusuke Konishi ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size, 186003ff182SRyusuke Konishi nmembs); 18747420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 1887942b919SKoji Sato return ret; 1897942b919SKoji Sato } 1907942b919SKoji Sato 1917942b919SKoji Sato static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp, 1927942b919SKoji Sato unsigned int cmd, void __user *argp) 1937942b919SKoji Sato { 19447420c79SRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 1957942b919SKoji Sato struct nilfs_sustat sustat; 1967942b919SKoji Sato int ret; 1977942b919SKoji Sato 19847420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 19947420c79SRyusuke Konishi ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat); 20047420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2017942b919SKoji Sato if (ret < 0) 2027942b919SKoji Sato return ret; 2037942b919SKoji Sato 2047942b919SKoji Sato if (copy_to_user(argp, &sustat, sizeof(sustat))) 2057942b919SKoji Sato ret = -EFAULT; 2067942b919SKoji Sato return ret; 2077942b919SKoji Sato } 2087942b919SKoji Sato 2097942b919SKoji Sato static ssize_t 210b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, 2117942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2127942b919SKoji Sato { 2137942b919SKoji Sato int ret; 2147942b919SKoji Sato 21547420c79SRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 216003ff182SRyusuke Konishi ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, size, nmembs); 21747420c79SRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2187942b919SKoji Sato return ret; 2197942b919SKoji Sato } 2207942b919SKoji Sato 2217942b919SKoji Sato static ssize_t 222b028fcfcSRyusuke Konishi nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags, 2237942b919SKoji Sato void *buf, size_t size, size_t nmembs) 2247942b919SKoji Sato { 2257942b919SKoji Sato struct inode *dat = nilfs_dat_inode(nilfs); 2267942b919SKoji Sato struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; 2277942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 2287942b919SKoji Sato int ret, i; 2297942b919SKoji Sato 23047eb6b9cSRyusuke Konishi down_read(&nilfs->ns_segctor_sem); 2317942b919SKoji Sato for (i = 0; i < nmembs; i++) { 2327942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 2337942b919SKoji Sato bdescs[i].bd_offset, 2347942b919SKoji Sato bdescs[i].bd_level + 1, 2357942b919SKoji Sato &bdescs[i].bd_blocknr); 2367942b919SKoji Sato if (ret < 0) { 23747eb6b9cSRyusuke Konishi if (ret != -ENOENT) { 23847eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2397942b919SKoji Sato return ret; 24047eb6b9cSRyusuke Konishi } 2417942b919SKoji Sato bdescs[i].bd_blocknr = 0; 2427942b919SKoji Sato } 2437942b919SKoji Sato } 24447eb6b9cSRyusuke Konishi up_read(&nilfs->ns_segctor_sem); 2457942b919SKoji Sato return nmembs; 2467942b919SKoji Sato } 2477942b919SKoji Sato 2487942b919SKoji Sato static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp, 2497942b919SKoji Sato unsigned int cmd, void __user *argp) 2507942b919SKoji Sato { 2517942b919SKoji Sato struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 2527942b919SKoji Sato struct nilfs_argv argv; 2537942b919SKoji Sato int ret; 2547942b919SKoji Sato 2557942b919SKoji Sato if (copy_from_user(&argv, argp, sizeof(argv))) 2567942b919SKoji Sato return -EFAULT; 2577942b919SKoji Sato 25883aca8f4SRyusuke Konishi if (argv.v_size != sizeof(struct nilfs_bdesc)) 25983aca8f4SRyusuke Konishi return -EINVAL; 26083aca8f4SRyusuke Konishi 2617942b919SKoji Sato ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), 2627942b919SKoji Sato nilfs_ioctl_do_get_bdescs); 26347420c79SRyusuke Konishi if (ret < 0) 26447420c79SRyusuke Konishi return ret; 2657942b919SKoji Sato 2667942b919SKoji Sato if (copy_to_user(argp, &argv, sizeof(argv))) 2677942b919SKoji Sato ret = -EFAULT; 2687942b919SKoji Sato return ret; 2697942b919SKoji Sato } 2707942b919SKoji Sato 2717942b919SKoji Sato static int nilfs_ioctl_move_inode_block(struct inode *inode, 2727942b919SKoji Sato struct nilfs_vdesc *vdesc, 2737942b919SKoji Sato struct list_head *buffers) 2747942b919SKoji Sato { 2757942b919SKoji Sato struct buffer_head *bh; 2767942b919SKoji Sato int ret; 2777942b919SKoji Sato 2787942b919SKoji Sato if (vdesc->vd_flags == 0) 2797942b919SKoji Sato ret = nilfs_gccache_submit_read_data( 2807942b919SKoji Sato inode, vdesc->vd_offset, vdesc->vd_blocknr, 2817942b919SKoji Sato vdesc->vd_vblocknr, &bh); 2827942b919SKoji Sato else 2837942b919SKoji Sato ret = nilfs_gccache_submit_read_node( 2847942b919SKoji Sato inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh); 2857942b919SKoji Sato 2867942b919SKoji Sato if (unlikely(ret < 0)) { 2877942b919SKoji Sato if (ret == -ENOENT) 2887942b919SKoji Sato printk(KERN_CRIT 2897942b919SKoji Sato "%s: invalid virtual block address (%s): " 2907942b919SKoji Sato "ino=%llu, cno=%llu, offset=%llu, " 2917942b919SKoji Sato "blocknr=%llu, vblocknr=%llu\n", 2927942b919SKoji Sato __func__, vdesc->vd_flags ? "node" : "data", 2937942b919SKoji Sato (unsigned long long)vdesc->vd_ino, 2947942b919SKoji Sato (unsigned long long)vdesc->vd_cno, 2957942b919SKoji Sato (unsigned long long)vdesc->vd_offset, 2967942b919SKoji Sato (unsigned long long)vdesc->vd_blocknr, 2977942b919SKoji Sato (unsigned long long)vdesc->vd_vblocknr); 2987942b919SKoji Sato return ret; 2997942b919SKoji Sato } 3005399dd1fSRyusuke Konishi if (unlikely(!list_empty(&bh->b_assoc_buffers))) { 3015399dd1fSRyusuke Konishi printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, " 3025399dd1fSRyusuke Konishi "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n", 3035399dd1fSRyusuke Konishi __func__, vdesc->vd_flags ? "node" : "data", 3045399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_ino, 3055399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_cno, 3065399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_offset, 3075399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_blocknr, 3085399dd1fSRyusuke Konishi (unsigned long long)vdesc->vd_vblocknr); 3095399dd1fSRyusuke Konishi brelse(bh); 3105399dd1fSRyusuke Konishi return -EEXIST; 3115399dd1fSRyusuke Konishi } 3127942b919SKoji Sato list_add_tail(&bh->b_assoc_buffers, buffers); 3137942b919SKoji Sato return 0; 3147942b919SKoji Sato } 3157942b919SKoji Sato 3164f6b8288SRyusuke Konishi static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, 3174f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3187942b919SKoji Sato { 3194f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3207942b919SKoji Sato struct inode *inode; 3217942b919SKoji Sato struct nilfs_vdesc *vdesc; 3227942b919SKoji Sato struct buffer_head *bh, *n; 3237942b919SKoji Sato LIST_HEAD(buffers); 3247942b919SKoji Sato ino_t ino; 3257942b919SKoji Sato __u64 cno; 3267942b919SKoji Sato int i, ret; 3277942b919SKoji Sato 3287942b919SKoji Sato for (i = 0, vdesc = buf; i < nmembs; ) { 3297942b919SKoji Sato ino = vdesc->vd_ino; 3307942b919SKoji Sato cno = vdesc->vd_cno; 3317942b919SKoji Sato inode = nilfs_gc_iget(nilfs, ino, cno); 3327942b919SKoji Sato if (unlikely(inode == NULL)) { 3337942b919SKoji Sato ret = -ENOMEM; 3347942b919SKoji Sato goto failed; 3357942b919SKoji Sato } 3367942b919SKoji Sato do { 3377942b919SKoji Sato ret = nilfs_ioctl_move_inode_block(inode, vdesc, 3387942b919SKoji Sato &buffers); 3397942b919SKoji Sato if (unlikely(ret < 0)) 3407942b919SKoji Sato goto failed; 3417942b919SKoji Sato vdesc++; 3427942b919SKoji Sato } while (++i < nmembs && 3437942b919SKoji Sato vdesc->vd_ino == ino && vdesc->vd_cno == cno); 3447942b919SKoji Sato } 3457942b919SKoji Sato 3467942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 3477942b919SKoji Sato ret = nilfs_gccache_wait_and_mark_dirty(bh); 3487942b919SKoji Sato if (unlikely(ret < 0)) { 3495399dd1fSRyusuke Konishi WARN_ON(ret == -EEXIST); 3507942b919SKoji Sato goto failed; 3517942b919SKoji Sato } 3527942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 3537942b919SKoji Sato brelse(bh); 3547942b919SKoji Sato } 3557942b919SKoji Sato return nmembs; 3567942b919SKoji Sato 3577942b919SKoji Sato failed: 3587942b919SKoji Sato list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { 3597942b919SKoji Sato list_del_init(&bh->b_assoc_buffers); 3607942b919SKoji Sato brelse(bh); 3617942b919SKoji Sato } 3627942b919SKoji Sato return ret; 3637942b919SKoji Sato } 3647942b919SKoji Sato 3654f6b8288SRyusuke Konishi static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs, 3664f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3677942b919SKoji Sato { 3684f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3697942b919SKoji Sato struct inode *cpfile = nilfs->ns_cpfile; 3707942b919SKoji Sato struct nilfs_period *periods = buf; 3717942b919SKoji Sato int ret, i; 3727942b919SKoji Sato 3737942b919SKoji Sato for (i = 0; i < nmembs; i++) { 3747942b919SKoji Sato ret = nilfs_cpfile_delete_checkpoints( 3757942b919SKoji Sato cpfile, periods[i].p_start, periods[i].p_end); 3767942b919SKoji Sato if (ret < 0) 3777942b919SKoji Sato return ret; 3787942b919SKoji Sato } 3797942b919SKoji Sato return nmembs; 3807942b919SKoji Sato } 3817942b919SKoji Sato 3824f6b8288SRyusuke Konishi static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs, 3834f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3847942b919SKoji Sato { 3854f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3864f6b8288SRyusuke Konishi int ret; 3877942b919SKoji Sato 3884f6b8288SRyusuke Konishi ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs); 3897942b919SKoji Sato 3907942b919SKoji Sato return (ret < 0) ? ret : nmembs; 3917942b919SKoji Sato } 3927942b919SKoji Sato 3934f6b8288SRyusuke Konishi static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs, 3944f6b8288SRyusuke Konishi struct nilfs_argv *argv, void *buf) 3957942b919SKoji Sato { 3964f6b8288SRyusuke Konishi size_t nmembs = argv->v_nmembs; 3977942b919SKoji Sato struct inode *dat = nilfs_dat_inode(nilfs); 3987942b919SKoji Sato struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap; 3997942b919SKoji Sato struct nilfs_bdesc *bdescs = buf; 4007942b919SKoji Sato int ret, i; 4017942b919SKoji Sato 4027942b919SKoji Sato for (i = 0; i < nmembs; i++) { 4037942b919SKoji Sato /* XXX: use macro or inline func to check liveness */ 4047942b919SKoji Sato ret = nilfs_bmap_lookup_at_level(bmap, 4057942b919SKoji Sato bdescs[i].bd_offset, 4067942b919SKoji Sato bdescs[i].bd_level + 1, 4077942b919SKoji Sato &bdescs[i].bd_blocknr); 4087942b919SKoji Sato if (ret < 0) { 4097942b919SKoji Sato if (ret != -ENOENT) 4107942b919SKoji Sato return ret; 4117942b919SKoji Sato bdescs[i].bd_blocknr = 0; 4127942b919SKoji Sato } 4137942b919SKoji Sato if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr) 4147942b919SKoji Sato /* skip dead block */ 4157942b919SKoji Sato continue; 4167942b919SKoji Sato if (bdescs[i].bd_level == 0) { 4177942b919SKoji Sato ret = nilfs_mdt_mark_block_dirty(dat, 4187942b919SKoji Sato bdescs[i].bd_offset); 4197942b919SKoji Sato if (ret < 0) { 4201f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 4217942b919SKoji Sato return ret; 4227942b919SKoji Sato } 4237942b919SKoji Sato } else { 4247942b919SKoji Sato ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset, 4257942b919SKoji Sato bdescs[i].bd_level); 4267942b919SKoji Sato if (ret < 0) { 4271f5abe7eSRyusuke Konishi WARN_ON(ret == -ENOENT); 4287942b919SKoji Sato return ret; 4297942b919SKoji Sato } 4307942b919SKoji Sato } 4317942b919SKoji Sato } 4327942b919SKoji Sato return nmembs; 4337942b919SKoji Sato } 4347942b919SKoji Sato 4357942b919SKoji Sato int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs, 4364f6b8288SRyusuke Konishi struct nilfs_argv *argv, void **kbufs) 4377942b919SKoji Sato { 4381f5abe7eSRyusuke Konishi const char *msg; 4394f6b8288SRyusuke Konishi int ret; 4407942b919SKoji Sato 4414f6b8288SRyusuke Konishi ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]); 4421f5abe7eSRyusuke Konishi if (ret < 0) { 4431f5abe7eSRyusuke Konishi /* 4441f5abe7eSRyusuke Konishi * can safely abort because checkpoints can be removed 4451f5abe7eSRyusuke Konishi * independently. 4461f5abe7eSRyusuke Konishi */ 4471f5abe7eSRyusuke Konishi msg = "cannot delete checkpoints"; 4481f5abe7eSRyusuke Konishi goto failed; 4491f5abe7eSRyusuke Konishi } 4504f6b8288SRyusuke Konishi ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]); 4511f5abe7eSRyusuke Konishi if (ret < 0) { 4521f5abe7eSRyusuke Konishi /* 4531f5abe7eSRyusuke Konishi * can safely abort because DAT file is updated atomically 4541f5abe7eSRyusuke Konishi * using a copy-on-write technique. 4551f5abe7eSRyusuke Konishi */ 4561f5abe7eSRyusuke Konishi msg = "cannot delete virtual blocks from DAT file"; 4571f5abe7eSRyusuke Konishi goto failed; 4581f5abe7eSRyusuke Konishi } 4594f6b8288SRyusuke Konishi ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]); 4601f5abe7eSRyusuke Konishi if (ret < 0) { 4611f5abe7eSRyusuke Konishi /* 4621f5abe7eSRyusuke Konishi * can safely abort because the operation is nondestructive. 4631f5abe7eSRyusuke Konishi */ 4641f5abe7eSRyusuke Konishi msg = "cannot mark copying blocks dirty"; 4651f5abe7eSRyusuke Konishi goto failed; 4661f5abe7eSRyusuke Konishi } 4677942b919SKoji Sato return 0; 4687942b919SKoji Sato 4691f5abe7eSRyusuke Konishi failed: 4707942b919SKoji Sato nilfs_remove_all_gcinode(nilfs); 4711f5abe7eSRyusuke Konishi printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n", 4721f5abe7eSRyusuke Konishi msg, ret); 4737942b919SKoji Sato return ret; 4747942b919SKoji Sato } 4757942b919SKoji Sato 4767942b919SKoji Sato static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, 4777942b919SKoji Sato unsigned int cmd, void __user *argp) 4787942b919SKoji Sato { 4794f6b8288SRyusuke Konishi struct nilfs_argv argv[5]; 4804f6b8288SRyusuke Konishi const static size_t argsz[5] = { 4814f6b8288SRyusuke Konishi sizeof(struct nilfs_vdesc), 4824f6b8288SRyusuke Konishi sizeof(struct nilfs_period), 4834f6b8288SRyusuke Konishi sizeof(__u64), 4844f6b8288SRyusuke Konishi sizeof(struct nilfs_bdesc), 4854f6b8288SRyusuke Konishi sizeof(__u64), 4864f6b8288SRyusuke Konishi }; 4874f6b8288SRyusuke Konishi void __user *base; 4884f6b8288SRyusuke Konishi void *kbufs[5]; 4894f6b8288SRyusuke Konishi struct the_nilfs *nilfs; 4904f6b8288SRyusuke Konishi size_t len, nsegs; 4914f6b8288SRyusuke Konishi int n, ret; 4924f6b8288SRyusuke Konishi 4937942b919SKoji Sato if (!capable(CAP_SYS_ADMIN)) 4947942b919SKoji Sato return -EPERM; 4954f6b8288SRyusuke Konishi 4964f6b8288SRyusuke Konishi if (copy_from_user(argv, argp, sizeof(argv))) 4974f6b8288SRyusuke Konishi return -EFAULT; 4984f6b8288SRyusuke Konishi 4994f6b8288SRyusuke Konishi nsegs = argv[4].v_nmembs; 5004f6b8288SRyusuke Konishi if (argv[4].v_size != argsz[4]) 5014f6b8288SRyusuke Konishi return -EINVAL; 5024f6b8288SRyusuke Konishi /* 5034f6b8288SRyusuke Konishi * argv[4] points to segment numbers this ioctl cleans. We 5044f6b8288SRyusuke Konishi * use kmalloc() for its buffer because memory used for the 5054f6b8288SRyusuke Konishi * segment numbers is enough small. 5064f6b8288SRyusuke Konishi */ 5074f6b8288SRyusuke Konishi kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base, 5084f6b8288SRyusuke Konishi nsegs * sizeof(__u64)); 5094f6b8288SRyusuke Konishi if (IS_ERR(kbufs[4])) 5104f6b8288SRyusuke Konishi return PTR_ERR(kbufs[4]); 5114f6b8288SRyusuke Konishi 5124f6b8288SRyusuke Konishi nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 5134f6b8288SRyusuke Konishi 5144f6b8288SRyusuke Konishi for (n = 0; n < 4; n++) { 5154f6b8288SRyusuke Konishi ret = -EINVAL; 5164f6b8288SRyusuke Konishi if (argv[n].v_size != argsz[n]) 5174f6b8288SRyusuke Konishi goto out_free; 5184f6b8288SRyusuke Konishi 5194f6b8288SRyusuke Konishi if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment) 5204f6b8288SRyusuke Konishi goto out_free; 5214f6b8288SRyusuke Konishi 5224f6b8288SRyusuke Konishi len = argv[n].v_size * argv[n].v_nmembs; 5234f6b8288SRyusuke Konishi base = (void __user *)(unsigned long)argv[n].v_base; 5244f6b8288SRyusuke Konishi if (len == 0) { 5254f6b8288SRyusuke Konishi kbufs[n] = NULL; 5264f6b8288SRyusuke Konishi continue; 5274f6b8288SRyusuke Konishi } 5284f6b8288SRyusuke Konishi 5294f6b8288SRyusuke Konishi kbufs[n] = vmalloc(len); 5304f6b8288SRyusuke Konishi if (!kbufs[n]) { 5314f6b8288SRyusuke Konishi ret = -ENOMEM; 5324f6b8288SRyusuke Konishi goto out_free; 5334f6b8288SRyusuke Konishi } 5344f6b8288SRyusuke Konishi if (copy_from_user(kbufs[n], base, len)) { 5354f6b8288SRyusuke Konishi ret = -EFAULT; 5364f6b8288SRyusuke Konishi vfree(kbufs[n]); 5374f6b8288SRyusuke Konishi goto out_free; 5384f6b8288SRyusuke Konishi } 5394f6b8288SRyusuke Konishi } 5404f6b8288SRyusuke Konishi 5411cf58fa8SJiro SEKIBA /* 5421cf58fa8SJiro SEKIBA * nilfs_ioctl_move_blocks() will call nilfs_gc_iget(), 5431cf58fa8SJiro SEKIBA * which will operates an inode list without blocking. 5441cf58fa8SJiro SEKIBA * To protect the list from concurrent operations, 5451cf58fa8SJiro SEKIBA * nilfs_ioctl_move_blocks should be atomic operation. 5461cf58fa8SJiro SEKIBA */ 5471cf58fa8SJiro SEKIBA if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) { 5481cf58fa8SJiro SEKIBA ret = -EBUSY; 5491cf58fa8SJiro SEKIBA goto out_free; 5501cf58fa8SJiro SEKIBA } 5511cf58fa8SJiro SEKIBA 5521cf58fa8SJiro SEKIBA ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]); 5531cf58fa8SJiro SEKIBA if (ret < 0) 5541cf58fa8SJiro SEKIBA printk(KERN_ERR "NILFS: GC failed during preparation: " 5551cf58fa8SJiro SEKIBA "cannot read source blocks: err=%d\n", ret); 5561cf58fa8SJiro SEKIBA else 5574f6b8288SRyusuke Konishi ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); 5584f6b8288SRyusuke Konishi 5591cf58fa8SJiro SEKIBA clear_nilfs_gc_running(nilfs); 5601cf58fa8SJiro SEKIBA 5614f6b8288SRyusuke Konishi out_free: 562d5046853SRyusuke Konishi while (--n >= 0) 5634f6b8288SRyusuke Konishi vfree(kbufs[n]); 5644f6b8288SRyusuke Konishi kfree(kbufs[4]); 5654f6b8288SRyusuke Konishi return ret; 5667942b919SKoji Sato } 5677942b919SKoji Sato 5687942b919SKoji Sato static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, 5697942b919SKoji Sato unsigned int cmd, void __user *argp) 5707942b919SKoji Sato { 5717942b919SKoji Sato __u64 cno; 5727942b919SKoji Sato int ret; 5737942b919SKoji Sato 5747942b919SKoji Sato ret = nilfs_construct_segment(inode->i_sb); 5757942b919SKoji Sato if (ret < 0) 5767942b919SKoji Sato return ret; 5777942b919SKoji Sato 5787942b919SKoji Sato if (argp != NULL) { 5797942b919SKoji Sato cno = NILFS_SB(inode->i_sb)->s_nilfs->ns_cno - 1; 5807942b919SKoji Sato if (copy_to_user(argp, &cno, sizeof(cno))) 5817942b919SKoji Sato return -EFAULT; 5827942b919SKoji Sato } 5837942b919SKoji Sato return 0; 5847942b919SKoji Sato } 5857942b919SKoji Sato 58647eb6b9cSRyusuke Konishi static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, 58747eb6b9cSRyusuke Konishi unsigned int cmd, void __user *argp, 58883aca8f4SRyusuke Konishi size_t membsz, 58947eb6b9cSRyusuke Konishi ssize_t (*dofunc)(struct the_nilfs *, 59047eb6b9cSRyusuke Konishi __u64 *, int, 59147eb6b9cSRyusuke Konishi void *, size_t, size_t)) 59247eb6b9cSRyusuke Konishi 59347eb6b9cSRyusuke Konishi { 59447eb6b9cSRyusuke Konishi struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; 59547eb6b9cSRyusuke Konishi struct nilfs_argv argv; 59647eb6b9cSRyusuke Konishi int ret; 59747eb6b9cSRyusuke Konishi 59847eb6b9cSRyusuke Konishi if (copy_from_user(&argv, argp, sizeof(argv))) 59947eb6b9cSRyusuke Konishi return -EFAULT; 60047eb6b9cSRyusuke Konishi 601003ff182SRyusuke Konishi if (argv.v_size < membsz) 60283aca8f4SRyusuke Konishi return -EINVAL; 60383aca8f4SRyusuke Konishi 60447eb6b9cSRyusuke Konishi ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); 60547eb6b9cSRyusuke Konishi if (ret < 0) 60647eb6b9cSRyusuke Konishi return ret; 60747eb6b9cSRyusuke Konishi 60847eb6b9cSRyusuke Konishi if (copy_to_user(argp, &argv, sizeof(argv))) 60947eb6b9cSRyusuke Konishi ret = -EFAULT; 61047eb6b9cSRyusuke Konishi return ret; 61147eb6b9cSRyusuke Konishi } 61247eb6b9cSRyusuke Konishi 6137a946193SRyusuke Konishi long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 6147942b919SKoji Sato { 6157a946193SRyusuke Konishi struct inode *inode = filp->f_dentry->d_inode; 6167942b919SKoji Sato void __user *argp = (void * __user *)arg; 6177942b919SKoji Sato 6187942b919SKoji Sato switch (cmd) { 6197942b919SKoji Sato case NILFS_IOCTL_CHANGE_CPMODE: 6207942b919SKoji Sato return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp); 6217942b919SKoji Sato case NILFS_IOCTL_DELETE_CHECKPOINT: 6227942b919SKoji Sato return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp); 6237942b919SKoji Sato case NILFS_IOCTL_GET_CPINFO: 62447eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 62583aca8f4SRyusuke Konishi sizeof(struct nilfs_cpinfo), 62647eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_cpinfo); 6277942b919SKoji Sato case NILFS_IOCTL_GET_CPSTAT: 6287942b919SKoji Sato return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp); 6297942b919SKoji Sato case NILFS_IOCTL_GET_SUINFO: 63047eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 63183aca8f4SRyusuke Konishi sizeof(struct nilfs_suinfo), 63247eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_suinfo); 6337942b919SKoji Sato case NILFS_IOCTL_GET_SUSTAT: 6347942b919SKoji Sato return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); 6357942b919SKoji Sato case NILFS_IOCTL_GET_VINFO: 63647eb6b9cSRyusuke Konishi return nilfs_ioctl_get_info(inode, filp, cmd, argp, 63783aca8f4SRyusuke Konishi sizeof(struct nilfs_vinfo), 63847eb6b9cSRyusuke Konishi nilfs_ioctl_do_get_vinfo); 6397942b919SKoji Sato case NILFS_IOCTL_GET_BDESCS: 6407942b919SKoji Sato return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp); 6417942b919SKoji Sato case NILFS_IOCTL_CLEAN_SEGMENTS: 6427942b919SKoji Sato return nilfs_ioctl_clean_segments(inode, filp, cmd, argp); 6437942b919SKoji Sato case NILFS_IOCTL_SYNC: 6447942b919SKoji Sato return nilfs_ioctl_sync(inode, filp, cmd, argp); 6457942b919SKoji Sato default: 6467942b919SKoji Sato return -ENOTTY; 6477942b919SKoji Sato } 6487942b919SKoji Sato } 649