linux/fs/nilfs2/ioctl.c
<<
>>
Prefs
   1/*
   2 * ioctl.c - NILFS ioctl operations.
   3 *
   4 * Copyright (C) 2007, 2008 Nippon Telegraph and Telephone Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  19 *
  20 * Written by Koji Sato <koji@osrg.net>.
  21 */
  22
  23#include <linux/fs.h>
  24#include <linux/wait.h>
  25#include <linux/slab.h>
  26#include <linux/capability.h>   /* capable() */
  27#include <linux/uaccess.h>      /* copy_from_user(), copy_to_user() */
  28#include <linux/vmalloc.h>
  29#include <linux/mount.h>        /* mnt_want_write(), mnt_drop_write() */
  30#include <linux/nilfs2_fs.h>
  31#include "nilfs.h"
  32#include "segment.h"
  33#include "bmap.h"
  34#include "cpfile.h"
  35#include "sufile.h"
  36#include "dat.h"
  37
  38
  39static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
  40                                 struct nilfs_argv *argv, int dir,
  41                                 ssize_t (*dofunc)(struct the_nilfs *,
  42                                                   __u64 *, int,
  43                                                   void *, size_t, size_t))
  44{
  45        void *buf;
  46        void __user *base = (void __user *)(unsigned long)argv->v_base;
  47        size_t maxmembs, total, n;
  48        ssize_t nr;
  49        int ret, i;
  50        __u64 pos, ppos;
  51
  52        if (argv->v_nmembs == 0)
  53                return 0;
  54
  55        if (argv->v_size > PAGE_SIZE)
  56                return -EINVAL;
  57
  58        buf = (void *)__get_free_pages(GFP_NOFS, 0);
  59        if (unlikely(!buf))
  60                return -ENOMEM;
  61        maxmembs = PAGE_SIZE / argv->v_size;
  62
  63        ret = 0;
  64        total = 0;
  65        pos = argv->v_index;
  66        for (i = 0; i < argv->v_nmembs; i += n) {
  67                n = (argv->v_nmembs - i < maxmembs) ?
  68                        argv->v_nmembs - i : maxmembs;
  69                if ((dir & _IOC_WRITE) &&
  70                    copy_from_user(buf, base + argv->v_size * i,
  71                                   argv->v_size * n)) {
  72                        ret = -EFAULT;
  73                        break;
  74                }
  75                ppos = pos;
  76                nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size,
  77                               n);
  78                if (nr < 0) {
  79                        ret = nr;
  80                        break;
  81                }
  82                if ((dir & _IOC_READ) &&
  83                    copy_to_user(base + argv->v_size * i, buf,
  84                                 argv->v_size * nr)) {
  85                        ret = -EFAULT;
  86                        break;
  87                }
  88                total += nr;
  89                if ((size_t)nr < n)
  90                        break;
  91                if (pos == ppos)
  92                        pos += n;
  93        }
  94        argv->v_nmembs = total;
  95
  96        free_pages((unsigned long)buf, 0);
  97        return ret;
  98}
  99
 100static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
 101                                     unsigned int cmd, void __user *argp)
 102{
 103        struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 104        struct inode *cpfile = nilfs->ns_cpfile;
 105        struct nilfs_transaction_info ti;
 106        struct nilfs_cpmode cpmode;
 107        int ret;
 108
 109        if (!capable(CAP_SYS_ADMIN))
 110                return -EPERM;
 111
 112        ret = mnt_want_write(filp->f_path.mnt);
 113        if (ret)
 114                return ret;
 115
 116        ret = -EFAULT;
 117        if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
 118                goto out;
 119
 120        down_read(&inode->i_sb->s_umount);
 121
 122        nilfs_transaction_begin(inode->i_sb, &ti, 0);
 123        ret = nilfs_cpfile_change_cpmode(
 124                cpfile, cpmode.cm_cno, cpmode.cm_mode);
 125        if (unlikely(ret < 0))
 126                nilfs_transaction_abort(inode->i_sb);
 127        else
 128                nilfs_transaction_commit(inode->i_sb); /* never fails */
 129
 130        up_read(&inode->i_sb->s_umount);
 131out:
 132        mnt_drop_write(filp->f_path.mnt);
 133        return ret;
 134}
 135
 136static int
 137nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
 138                              unsigned int cmd, void __user *argp)
 139{
 140        struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
 141        struct nilfs_transaction_info ti;
 142        __u64 cno;
 143        int ret;
 144
 145        if (!capable(CAP_SYS_ADMIN))
 146                return -EPERM;
 147
 148        ret = mnt_want_write(filp->f_path.mnt);
 149        if (ret)
 150                return ret;
 151
 152        ret = -EFAULT;
 153        if (copy_from_user(&cno, argp, sizeof(cno)))
 154                goto out;
 155
 156        nilfs_transaction_begin(inode->i_sb, &ti, 0);
 157        ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
 158        if (unlikely(ret < 0))
 159                nilfs_transaction_abort(inode->i_sb);
 160        else
 161                nilfs_transaction_commit(inode->i_sb); /* never fails */
 162out:
 163        mnt_drop_write(filp->f_path.mnt);
 164        return ret;
 165}
 166
 167static ssize_t
 168nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
 169                          void *buf, size_t size, size_t nmembs)
 170{
 171        int ret;
 172
 173        down_read(&nilfs->ns_segctor_sem);
 174        ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
 175                                      size, nmembs);
 176        up_read(&nilfs->ns_segctor_sem);
 177        return ret;
 178}
 179
 180static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp,
 181                                  unsigned int cmd, void __user *argp)
 182{
 183        struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 184        struct nilfs_cpstat cpstat;
 185        int ret;
 186
 187        down_read(&nilfs->ns_segctor_sem);
 188        ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat);
 189        up_read(&nilfs->ns_segctor_sem);
 190        if (ret < 0)
 191                return ret;
 192
 193        if (copy_to_user(argp, &cpstat, sizeof(cpstat)))
 194                ret = -EFAULT;
 195        return ret;
 196}
 197
 198static ssize_t
 199nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
 200                          void *buf, size_t size, size_t nmembs)
 201{
 202        int ret;
 203
 204        down_read(&nilfs->ns_segctor_sem);
 205        ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
 206                                      nmembs);
 207        up_read(&nilfs->ns_segctor_sem);
 208        return ret;
 209}
 210
 211static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp,
 212                                  unsigned int cmd, void __user *argp)
 213{
 214        struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 215        struct nilfs_sustat sustat;
 216        int ret;
 217
 218        down_read(&nilfs->ns_segctor_sem);
 219        ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat);
 220        up_read(&nilfs->ns_segctor_sem);
 221        if (ret < 0)
 222                return ret;
 223
 224        if (copy_to_user(argp, &sustat, sizeof(sustat)))
 225                ret = -EFAULT;
 226        return ret;
 227}
 228
 229static ssize_t
 230nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
 231                         void *buf, size_t size, size_t nmembs)
 232{
 233        int ret;
 234
 235        down_read(&nilfs->ns_segctor_sem);
 236        ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs);
 237        up_read(&nilfs->ns_segctor_sem);
 238        return ret;
 239}
 240
 241static ssize_t
 242nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags,
 243                          void *buf, size_t size, size_t nmembs)
 244{
 245        struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
 246        struct nilfs_bdesc *bdescs = buf;
 247        int ret, i;
 248
 249        down_read(&nilfs->ns_segctor_sem);
 250        for (i = 0; i < nmembs; i++) {
 251                ret = nilfs_bmap_lookup_at_level(bmap,
 252                                                 bdescs[i].bd_offset,
 253                                                 bdescs[i].bd_level + 1,
 254                                                 &bdescs[i].bd_blocknr);
 255                if (ret < 0) {
 256                        if (ret != -ENOENT) {
 257                                up_read(&nilfs->ns_segctor_sem);
 258                                return ret;
 259                        }
 260                        bdescs[i].bd_blocknr = 0;
 261                }
 262        }
 263        up_read(&nilfs->ns_segctor_sem);
 264        return nmembs;
 265}
 266
 267static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,
 268                                  unsigned int cmd, void __user *argp)
 269{
 270        struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 271        struct nilfs_argv argv;
 272        int ret;
 273
 274        if (copy_from_user(&argv, argp, sizeof(argv)))
 275                return -EFAULT;
 276
 277        if (argv.v_size != sizeof(struct nilfs_bdesc))
 278                return -EINVAL;
 279
 280        ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
 281                                    nilfs_ioctl_do_get_bdescs);
 282        if (ret < 0)
 283                return ret;
 284
 285        if (copy_to_user(argp, &argv, sizeof(argv)))
 286                ret = -EFAULT;
 287        return ret;
 288}
 289
 290static int nilfs_ioctl_move_inode_block(struct inode *inode,
 291                                        struct nilfs_vdesc *vdesc,
 292                                        struct list_head *buffers)
 293{
 294        struct buffer_head *bh;
 295        int ret;
 296
 297        if (vdesc->vd_flags == 0)
 298                ret = nilfs_gccache_submit_read_data(
 299                        inode, vdesc->vd_offset, vdesc->vd_blocknr,
 300                        vdesc->vd_vblocknr, &bh);
 301        else
 302                ret = nilfs_gccache_submit_read_node(
 303                        inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
 304
 305        if (unlikely(ret < 0)) {
 306                if (ret == -ENOENT)
 307                        printk(KERN_CRIT
 308                               "%s: invalid virtual block address (%s): "
 309                               "ino=%llu, cno=%llu, offset=%llu, "
 310                               "blocknr=%llu, vblocknr=%llu\n",
 311                               __func__, vdesc->vd_flags ? "node" : "data",
 312                               (unsigned long long)vdesc->vd_ino,
 313                               (unsigned long long)vdesc->vd_cno,
 314                               (unsigned long long)vdesc->vd_offset,
 315                               (unsigned long long)vdesc->vd_blocknr,
 316                               (unsigned long long)vdesc->vd_vblocknr);
 317                return ret;
 318        }
 319        if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
 320                printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
 321                       "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
 322                       __func__, vdesc->vd_flags ? "node" : "data",
 323                       (unsigned long long)vdesc->vd_ino,
 324                       (unsigned long long)vdesc->vd_cno,
 325                       (unsigned long long)vdesc->vd_offset,
 326                       (unsigned long long)vdesc->vd_blocknr,
 327                       (unsigned long long)vdesc->vd_vblocknr);
 328                brelse(bh);
 329                return -EEXIST;
 330        }
 331        list_add_tail(&bh->b_assoc_buffers, buffers);
 332        return 0;
 333}
 334
 335static int nilfs_ioctl_move_blocks(struct super_block *sb,
 336                                   struct nilfs_argv *argv, void *buf)
 337{
 338        size_t nmembs = argv->v_nmembs;
 339        struct the_nilfs *nilfs = NILFS_SB(sb)->s_nilfs;
 340        struct inode *inode;
 341        struct nilfs_vdesc *vdesc;
 342        struct buffer_head *bh, *n;
 343        LIST_HEAD(buffers);
 344        ino_t ino;
 345        __u64 cno;
 346        int i, ret;
 347
 348        for (i = 0, vdesc = buf; i < nmembs; ) {
 349                ino = vdesc->vd_ino;
 350                cno = vdesc->vd_cno;
 351                inode = nilfs_iget_for_gc(sb, ino, cno);
 352                if (IS_ERR(inode)) {
 353                        ret = PTR_ERR(inode);
 354                        goto failed;
 355                }
 356                if (list_empty(&NILFS_I(inode)->i_dirty)) {
 357                        /*
 358                         * Add the inode to GC inode list. Garbage Collection
 359                         * is serialized and no two processes manipulate the
 360                         * list simultaneously.
 361                         */
 362                        igrab(inode);
 363                        list_add(&NILFS_I(inode)->i_dirty,
 364                                 &nilfs->ns_gc_inodes);
 365                }
 366
 367                do {
 368                        ret = nilfs_ioctl_move_inode_block(inode, vdesc,
 369                                                           &buffers);
 370                        if (unlikely(ret < 0)) {
 371                                iput(inode);
 372                                goto failed;
 373                        }
 374                        vdesc++;
 375                } while (++i < nmembs &&
 376                         vdesc->vd_ino == ino && vdesc->vd_cno == cno);
 377
 378                iput(inode); /* The inode still remains in GC inode list */
 379        }
 380
 381        list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
 382                ret = nilfs_gccache_wait_and_mark_dirty(bh);
 383                if (unlikely(ret < 0)) {
 384                        WARN_ON(ret == -EEXIST);
 385                        goto failed;
 386                }
 387                list_del_init(&bh->b_assoc_buffers);
 388                brelse(bh);
 389        }
 390        return nmembs;
 391
 392 failed:
 393        list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
 394                list_del_init(&bh->b_assoc_buffers);
 395                brelse(bh);
 396        }
 397        return ret;
 398}
 399
 400static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs,
 401                                          struct nilfs_argv *argv, void *buf)
 402{
 403        size_t nmembs = argv->v_nmembs;
 404        struct inode *cpfile = nilfs->ns_cpfile;
 405        struct nilfs_period *periods = buf;
 406        int ret, i;
 407
 408        for (i = 0; i < nmembs; i++) {
 409                ret = nilfs_cpfile_delete_checkpoints(
 410                        cpfile, periods[i].p_start, periods[i].p_end);
 411                if (ret < 0)
 412                        return ret;
 413        }
 414        return nmembs;
 415}
 416
 417static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs,
 418                                      struct nilfs_argv *argv, void *buf)
 419{
 420        size_t nmembs = argv->v_nmembs;
 421        int ret;
 422
 423        ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs);
 424
 425        return (ret < 0) ? ret : nmembs;
 426}
 427
 428static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
 429                                         struct nilfs_argv *argv, void *buf)
 430{
 431        size_t nmembs = argv->v_nmembs;
 432        struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
 433        struct nilfs_bdesc *bdescs = buf;
 434        int ret, i;
 435
 436        for (i = 0; i < nmembs; i++) {
 437                /* XXX: use macro or inline func to check liveness */
 438                ret = nilfs_bmap_lookup_at_level(bmap,
 439                                                 bdescs[i].bd_offset,
 440                                                 bdescs[i].bd_level + 1,
 441                                                 &bdescs[i].bd_blocknr);
 442                if (ret < 0) {
 443                        if (ret != -ENOENT)
 444                                return ret;
 445                        bdescs[i].bd_blocknr = 0;
 446                }
 447                if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr)
 448                        /* skip dead block */
 449                        continue;
 450                if (bdescs[i].bd_level == 0) {
 451                        ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat,
 452                                                         bdescs[i].bd_offset);
 453                        if (ret < 0) {
 454                                WARN_ON(ret == -ENOENT);
 455                                return ret;
 456                        }
 457                } else {
 458                        ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset,
 459                                              bdescs[i].bd_level);
 460                        if (ret < 0) {
 461                                WARN_ON(ret == -ENOENT);
 462                                return ret;
 463                        }
 464                }
 465        }
 466        return nmembs;
 467}
 468
 469int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
 470                                       struct nilfs_argv *argv, void **kbufs)
 471{
 472        const char *msg;
 473        int ret;
 474
 475        ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]);
 476        if (ret < 0) {
 477                /*
 478                 * can safely abort because checkpoints can be removed
 479                 * independently.
 480                 */
 481                msg = "cannot delete checkpoints";
 482                goto failed;
 483        }
 484        ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]);
 485        if (ret < 0) {
 486                /*
 487                 * can safely abort because DAT file is updated atomically
 488                 * using a copy-on-write technique.
 489                 */
 490                msg = "cannot delete virtual blocks from DAT file";
 491                goto failed;
 492        }
 493        ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]);
 494        if (ret < 0) {
 495                /*
 496                 * can safely abort because the operation is nondestructive.
 497                 */
 498                msg = "cannot mark copying blocks dirty";
 499                goto failed;
 500        }
 501        return 0;
 502
 503 failed:
 504        printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n",
 505               msg, ret);
 506        return ret;
 507}
 508
 509static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
 510                                      unsigned int cmd, void __user *argp)
 511{
 512        struct nilfs_argv argv[5];
 513        static const size_t argsz[5] = {
 514                sizeof(struct nilfs_vdesc),
 515                sizeof(struct nilfs_period),
 516                sizeof(__u64),
 517                sizeof(struct nilfs_bdesc),
 518                sizeof(__u64),
 519        };
 520        void __user *base;
 521        void *kbufs[5];
 522        struct the_nilfs *nilfs;
 523        size_t len, nsegs;
 524        int n, ret;
 525
 526        if (!capable(CAP_SYS_ADMIN))
 527                return -EPERM;
 528
 529        ret = mnt_want_write(filp->f_path.mnt);
 530        if (ret)
 531                return ret;
 532
 533        ret = -EFAULT;
 534        if (copy_from_user(argv, argp, sizeof(argv)))
 535                goto out;
 536
 537        ret = -EINVAL;
 538        nsegs = argv[4].v_nmembs;
 539        if (argv[4].v_size != argsz[4])
 540                goto out;
 541
 542        /*
 543         * argv[4] points to segment numbers this ioctl cleans.  We
 544         * use kmalloc() for its buffer because memory used for the
 545         * segment numbers is enough small.
 546         */
 547        kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base,
 548                               nsegs * sizeof(__u64));
 549        if (IS_ERR(kbufs[4])) {
 550                ret = PTR_ERR(kbufs[4]);
 551                goto out;
 552        }
 553        nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 554
 555        for (n = 0; n < 4; n++) {
 556                ret = -EINVAL;
 557                if (argv[n].v_size != argsz[n])
 558                        goto out_free;
 559
 560                if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment)
 561                        goto out_free;
 562
 563                len = argv[n].v_size * argv[n].v_nmembs;
 564                base = (void __user *)(unsigned long)argv[n].v_base;
 565                if (len == 0) {
 566                        kbufs[n] = NULL;
 567                        continue;
 568                }
 569
 570                kbufs[n] = vmalloc(len);
 571                if (!kbufs[n]) {
 572                        ret = -ENOMEM;
 573                        goto out_free;
 574                }
 575                if (copy_from_user(kbufs[n], base, len)) {
 576                        ret = -EFAULT;
 577                        vfree(kbufs[n]);
 578                        goto out_free;
 579                }
 580        }
 581
 582        /*
 583         * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(),
 584         * which will operates an inode list without blocking.
 585         * To protect the list from concurrent operations,
 586         * nilfs_ioctl_move_blocks should be atomic operation.
 587         */
 588        if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) {
 589                ret = -EBUSY;
 590                goto out_free;
 591        }
 592
 593        vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
 594
 595        ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]);
 596        if (ret < 0)
 597                printk(KERN_ERR "NILFS: GC failed during preparation: "
 598                        "cannot read source blocks: err=%d\n", ret);
 599        else
 600                ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
 601
 602        nilfs_remove_all_gcinodes(nilfs);
 603        clear_nilfs_gc_running(nilfs);
 604
 605out_free:
 606        while (--n >= 0)
 607                vfree(kbufs[n]);
 608        kfree(kbufs[4]);
 609out:
 610        mnt_drop_write(filp->f_path.mnt);
 611        return ret;
 612}
 613
 614static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
 615                            unsigned int cmd, void __user *argp)
 616{
 617        __u64 cno;
 618        int ret;
 619        struct the_nilfs *nilfs;
 620
 621        ret = nilfs_construct_segment(inode->i_sb);
 622        if (ret < 0)
 623                return ret;
 624
 625        if (argp != NULL) {
 626                nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 627                down_read(&nilfs->ns_segctor_sem);
 628                cno = nilfs->ns_cno - 1;
 629                up_read(&nilfs->ns_segctor_sem);
 630                if (copy_to_user(argp, &cno, sizeof(cno)))
 631                        return -EFAULT;
 632        }
 633        return 0;
 634}
 635
 636static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
 637                                unsigned int cmd, void __user *argp,
 638                                size_t membsz,
 639                                ssize_t (*dofunc)(struct the_nilfs *,
 640                                                  __u64 *, int,
 641                                                  void *, size_t, size_t))
 642
 643{
 644        struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 645        struct nilfs_argv argv;
 646        int ret;
 647
 648        if (copy_from_user(&argv, argp, sizeof(argv)))
 649                return -EFAULT;
 650
 651        if (argv.v_size < membsz)
 652                return -EINVAL;
 653
 654        ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);
 655        if (ret < 0)
 656                return ret;
 657
 658        if (copy_to_user(argp, &argv, sizeof(argv)))
 659                ret = -EFAULT;
 660        return ret;
 661}
 662
 663long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 664{
 665        struct inode *inode = filp->f_dentry->d_inode;
 666        void __user *argp = (void __user *)arg;
 667
 668        switch (cmd) {
 669        case NILFS_IOCTL_CHANGE_CPMODE:
 670                return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
 671        case NILFS_IOCTL_DELETE_CHECKPOINT:
 672                return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
 673        case NILFS_IOCTL_GET_CPINFO:
 674                return nilfs_ioctl_get_info(inode, filp, cmd, argp,
 675                                            sizeof(struct nilfs_cpinfo),
 676                                            nilfs_ioctl_do_get_cpinfo);
 677        case NILFS_IOCTL_GET_CPSTAT:
 678                return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
 679        case NILFS_IOCTL_GET_SUINFO:
 680                return nilfs_ioctl_get_info(inode, filp, cmd, argp,
 681                                            sizeof(struct nilfs_suinfo),
 682                                            nilfs_ioctl_do_get_suinfo);
 683        case NILFS_IOCTL_GET_SUSTAT:
 684                return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
 685        case NILFS_IOCTL_GET_VINFO:
 686                return nilfs_ioctl_get_info(inode, filp, cmd, argp,
 687                                            sizeof(struct nilfs_vinfo),
 688                                            nilfs_ioctl_do_get_vinfo);
 689        case NILFS_IOCTL_GET_BDESCS:
 690                return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
 691        case NILFS_IOCTL_CLEAN_SEGMENTS:
 692                return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
 693        case NILFS_IOCTL_SYNC:
 694                return nilfs_ioctl_sync(inode, filp, cmd, argp);
 695        default:
 696                return -ENOTTY;
 697        }
 698}
 699