]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - fs/nilfs2/ioctl.c
nilfs2: remove timedwait ioctl command
[linux-2.6-omap-h63xx.git] / fs / nilfs2 / ioctl.c
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/smp_lock.h>     /* lock_kernel(), unlock_kernel() */
26 #include <linux/capability.h>   /* capable() */
27 #include <linux/uaccess.h>      /* copy_from_user(), copy_to_user() */
28 #include <linux/nilfs2_fs.h>
29 #include "nilfs.h"
30 #include "segment.h"
31 #include "bmap.h"
32 #include "cpfile.h"
33 #include "sufile.h"
34 #include "dat.h"
35
36
37 static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
38                                  struct nilfs_argv *argv, int dir,
39                                  ssize_t (*dofunc)(struct the_nilfs *,
40                                                    __u64 *, int,
41                                                    void *, size_t, size_t))
42 {
43         void *buf;
44         size_t maxmembs, total, n;
45         ssize_t nr;
46         int ret, i;
47         __u64 pos, ppos;
48
49         if (argv->v_nmembs == 0)
50                 return 0;
51
52         if (argv->v_size > PAGE_SIZE)
53                 return -EINVAL;
54
55         buf = (void *)__get_free_pages(GFP_NOFS, 0);
56         if (unlikely(!buf))
57                 return -ENOMEM;
58         maxmembs = PAGE_SIZE / argv->v_size;
59
60         ret = 0;
61         total = 0;
62         pos = argv->v_index;
63         for (i = 0; i < argv->v_nmembs; i += n) {
64                 n = (argv->v_nmembs - i < maxmembs) ?
65                         argv->v_nmembs - i : maxmembs;
66                 if ((dir & _IOC_WRITE) &&
67                     copy_from_user(buf,
68                             (void __user *)argv->v_base + argv->v_size * i,
69                             argv->v_size * n)) {
70                         ret = -EFAULT;
71                         break;
72                 }
73                 ppos = pos;
74                 nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size,
75                                n);
76                 if (nr < 0) {
77                         ret = nr;
78                         break;
79                 }
80                 if ((dir & _IOC_READ) &&
81                     copy_to_user(
82                             (void __user *)argv->v_base + argv->v_size * i,
83                             buf, argv->v_size * nr)) {
84                         ret = -EFAULT;
85                         break;
86                 }
87                 total += nr;
88                 if ((size_t)nr < n)
89                         break;
90                 if (pos == ppos)
91                         pos += n;
92         }
93         argv->v_nmembs = total;
94
95         free_pages((unsigned long)buf, 0);
96         return ret;
97 }
98
99 static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
100                                      unsigned int cmd, void __user *argp)
101 {
102         struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
103         struct nilfs_transaction_info ti;
104         struct nilfs_cpmode cpmode;
105         int ret;
106
107         if (!capable(CAP_SYS_ADMIN))
108                 return -EPERM;
109         if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
110                 return -EFAULT;
111
112         nilfs_transaction_begin(inode->i_sb, &ti, 0);
113         ret = nilfs_cpfile_change_cpmode(
114                 cpfile, cpmode.cm_cno, cpmode.cm_mode);
115         if (unlikely(ret < 0)) {
116                 nilfs_transaction_abort(inode->i_sb);
117                 return ret;
118         }
119         nilfs_transaction_commit(inode->i_sb); /* never fails */
120         return ret;
121 }
122
123 static int
124 nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
125                               unsigned int cmd, void __user *argp)
126 {
127         struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
128         struct nilfs_transaction_info ti;
129         __u64 cno;
130         int ret;
131
132         if (!capable(CAP_SYS_ADMIN))
133                 return -EPERM;
134         if (copy_from_user(&cno, argp, sizeof(cno)))
135                 return -EFAULT;
136
137         nilfs_transaction_begin(inode->i_sb, &ti, 0);
138         ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
139         if (unlikely(ret < 0)) {
140                 nilfs_transaction_abort(inode->i_sb);
141                 return ret;
142         }
143         nilfs_transaction_commit(inode->i_sb); /* never fails */
144         return ret;
145 }
146
147 static ssize_t
148 nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
149                           void *buf, size_t size, size_t nmembs)
150 {
151         return nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
152                                        nmembs);
153 }
154
155 static int nilfs_ioctl_get_cpinfo(struct inode *inode, struct file *filp,
156                                   unsigned int cmd, void __user *argp)
157 {
158         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
159         struct nilfs_argv argv;
160         int ret;
161
162         if (copy_from_user(&argv, argp, sizeof(argv)))
163                 return -EFAULT;
164
165         down_read(&nilfs->ns_segctor_sem);
166         ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
167                                     nilfs_ioctl_do_get_cpinfo);
168         up_read(&nilfs->ns_segctor_sem);
169         if (ret < 0)
170                 return ret;
171
172         if (copy_to_user(argp, &argv, sizeof(argv)))
173                 ret = -EFAULT;
174         return ret;
175 }
176
177 static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp,
178                                   unsigned int cmd, void __user *argp)
179 {
180         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
181         struct nilfs_cpstat cpstat;
182         int ret;
183
184         down_read(&nilfs->ns_segctor_sem);
185         ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat);
186         up_read(&nilfs->ns_segctor_sem);
187         if (ret < 0)
188                 return ret;
189
190         if (copy_to_user(argp, &cpstat, sizeof(cpstat)))
191                 ret = -EFAULT;
192         return ret;
193 }
194
195 static ssize_t
196 nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
197                           void *buf, size_t size, size_t nmembs)
198 {
199         return nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs);
200 }
201
202 static int nilfs_ioctl_get_suinfo(struct inode *inode, struct file *filp,
203                                   unsigned int cmd, void __user *argp)
204 {
205         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
206         struct nilfs_argv argv;
207         int ret;
208
209         if (copy_from_user(&argv, argp, sizeof(argv)))
210                 return -EFAULT;
211
212         down_read(&nilfs->ns_segctor_sem);
213         ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
214                                     nilfs_ioctl_do_get_suinfo);
215         up_read(&nilfs->ns_segctor_sem);
216         if (ret < 0)
217                 return ret;
218
219         if (copy_to_user(argp, &argv, sizeof(argv)))
220                 ret = -EFAULT;
221         return ret;
222 }
223
224 static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp,
225                                   unsigned int cmd, void __user *argp)
226 {
227         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
228         struct nilfs_sustat sustat;
229         int ret;
230
231         down_read(&nilfs->ns_segctor_sem);
232         ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat);
233         up_read(&nilfs->ns_segctor_sem);
234         if (ret < 0)
235                 return ret;
236
237         if (copy_to_user(argp, &sustat, sizeof(sustat)))
238                 ret = -EFAULT;
239         return ret;
240 }
241
242 static ssize_t
243 nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
244                          void *buf, size_t size, size_t nmembs)
245 {
246         return nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs);
247 }
248
249 static int nilfs_ioctl_get_vinfo(struct inode *inode, struct file *filp,
250                                  unsigned int cmd, void __user *argp)
251 {
252         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
253         struct nilfs_argv argv;
254         int ret;
255
256         if (copy_from_user(&argv, argp, sizeof(argv)))
257                 return -EFAULT;
258
259         down_read(&nilfs->ns_segctor_sem);
260         ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
261                                     nilfs_ioctl_do_get_vinfo);
262         up_read(&nilfs->ns_segctor_sem);
263         if (ret < 0)
264                 return ret;
265
266         if (copy_to_user(argp, &argv, sizeof(argv)))
267                 ret = -EFAULT;
268         return ret;
269 }
270
271 static ssize_t
272 nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags,
273                           void *buf, size_t size, size_t nmembs)
274 {
275         struct inode *dat = nilfs_dat_inode(nilfs);
276         struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap;
277         struct nilfs_bdesc *bdescs = buf;
278         int ret, i;
279
280         for (i = 0; i < nmembs; i++) {
281                 ret = nilfs_bmap_lookup_at_level(bmap,
282                                                  bdescs[i].bd_offset,
283                                                  bdescs[i].bd_level + 1,
284                                                  &bdescs[i].bd_blocknr);
285                 if (ret < 0) {
286                         if (ret != -ENOENT)
287                                 return ret;
288                         bdescs[i].bd_blocknr = 0;
289                 }
290         }
291         return nmembs;
292 }
293
294 static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,
295                                   unsigned int cmd, void __user *argp)
296 {
297         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
298         struct nilfs_argv argv;
299         int ret;
300
301         if (copy_from_user(&argv, argp, sizeof(argv)))
302                 return -EFAULT;
303
304         down_read(&nilfs->ns_segctor_sem);
305         ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
306                                     nilfs_ioctl_do_get_bdescs);
307         up_read(&nilfs->ns_segctor_sem);
308         if (ret < 0)
309                 return ret;
310
311         if (copy_to_user(argp, &argv, sizeof(argv)))
312                 ret = -EFAULT;
313         return ret;
314 }
315
316 static int nilfs_ioctl_move_inode_block(struct inode *inode,
317                                         struct nilfs_vdesc *vdesc,
318                                         struct list_head *buffers)
319 {
320         struct buffer_head *bh;
321         int ret;
322
323         if (vdesc->vd_flags == 0)
324                 ret = nilfs_gccache_submit_read_data(
325                         inode, vdesc->vd_offset, vdesc->vd_blocknr,
326                         vdesc->vd_vblocknr, &bh);
327         else
328                 ret = nilfs_gccache_submit_read_node(
329                         inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
330
331         if (unlikely(ret < 0)) {
332                 if (ret == -ENOENT)
333                         printk(KERN_CRIT
334                                "%s: invalid virtual block address (%s): "
335                                "ino=%llu, cno=%llu, offset=%llu, "
336                                "blocknr=%llu, vblocknr=%llu\n",
337                                __func__, vdesc->vd_flags ? "node" : "data",
338                                (unsigned long long)vdesc->vd_ino,
339                                (unsigned long long)vdesc->vd_cno,
340                                (unsigned long long)vdesc->vd_offset,
341                                (unsigned long long)vdesc->vd_blocknr,
342                                (unsigned long long)vdesc->vd_vblocknr);
343                 return ret;
344         }
345         bh->b_private = vdesc;
346         list_add_tail(&bh->b_assoc_buffers, buffers);
347         return 0;
348 }
349
350 static ssize_t
351 nilfs_ioctl_do_move_blocks(struct the_nilfs *nilfs, __u64 *posp, int flags,
352                            void *buf, size_t size, size_t nmembs)
353 {
354         struct inode *inode;
355         struct nilfs_vdesc *vdesc;
356         struct buffer_head *bh, *n;
357         LIST_HEAD(buffers);
358         ino_t ino;
359         __u64 cno;
360         int i, ret;
361
362         for (i = 0, vdesc = buf; i < nmembs; ) {
363                 ino = vdesc->vd_ino;
364                 cno = vdesc->vd_cno;
365                 inode = nilfs_gc_iget(nilfs, ino, cno);
366                 if (unlikely(inode == NULL)) {
367                         ret = -ENOMEM;
368                         goto failed;
369                 }
370                 do {
371                         ret = nilfs_ioctl_move_inode_block(inode, vdesc,
372                                                            &buffers);
373                         if (unlikely(ret < 0))
374                                 goto failed;
375                         vdesc++;
376                 } while (++i < nmembs &&
377                          vdesc->vd_ino == ino && vdesc->vd_cno == cno);
378         }
379
380         list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
381                 ret = nilfs_gccache_wait_and_mark_dirty(bh);
382                 if (unlikely(ret < 0)) {
383                         if (ret == -EEXIST) {
384                                 vdesc = bh->b_private;
385                                 printk(KERN_CRIT
386                                        "%s: conflicting %s buffer: "
387                                        "ino=%llu, cno=%llu, offset=%llu, "
388                                        "blocknr=%llu, vblocknr=%llu\n",
389                                        __func__,
390                                        vdesc->vd_flags ? "node" : "data",
391                                        (unsigned long long)vdesc->vd_ino,
392                                        (unsigned long long)vdesc->vd_cno,
393                                        (unsigned long long)vdesc->vd_offset,
394                                        (unsigned long long)vdesc->vd_blocknr,
395                                        (unsigned long long)vdesc->vd_vblocknr);
396                         }
397                         goto failed;
398                 }
399                 list_del_init(&bh->b_assoc_buffers);
400                 bh->b_private = NULL;
401                 brelse(bh);
402         }
403         return nmembs;
404
405  failed:
406         list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
407                 list_del_init(&bh->b_assoc_buffers);
408                 bh->b_private = NULL;
409                 brelse(bh);
410         }
411         return ret;
412 }
413
414 static inline int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs,
415                                           struct nilfs_argv *argv,
416                                           int dir)
417 {
418         return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
419                                      nilfs_ioctl_do_move_blocks);
420 }
421
422 static ssize_t
423 nilfs_ioctl_do_delete_checkpoints(struct the_nilfs *nilfs, __u64 *posp,
424                                   int flags, void *buf, size_t size,
425                                   size_t nmembs)
426 {
427         struct inode *cpfile = nilfs->ns_cpfile;
428         struct nilfs_period *periods = buf;
429         int ret, i;
430
431         for (i = 0; i < nmembs; i++) {
432                 ret = nilfs_cpfile_delete_checkpoints(
433                         cpfile, periods[i].p_start, periods[i].p_end);
434                 if (ret < 0)
435                         return ret;
436         }
437         return nmembs;
438 }
439
440 static inline int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs,
441                                                  struct nilfs_argv *argv,
442                                                  int dir)
443 {
444         return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
445                                      nilfs_ioctl_do_delete_checkpoints);
446 }
447
448 static ssize_t
449 nilfs_ioctl_do_free_vblocknrs(struct the_nilfs *nilfs, __u64 *posp, int flags,
450                               void *buf, size_t size, size_t nmembs)
451 {
452         int ret = nilfs_dat_freev(nilfs_dat_inode(nilfs), buf, nmembs);
453
454         return (ret < 0) ? ret : nmembs;
455 }
456
457 static inline int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs,
458                                              struct nilfs_argv *argv,
459                                              int dir)
460 {
461         return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
462                                      nilfs_ioctl_do_free_vblocknrs);
463 }
464
465 static ssize_t
466 nilfs_ioctl_do_mark_blocks_dirty(struct the_nilfs *nilfs, __u64 *posp,
467                                  int flags, void *buf, size_t size,
468                                  size_t nmembs)
469 {
470         struct inode *dat = nilfs_dat_inode(nilfs);
471         struct nilfs_bmap *bmap = NILFS_I(dat)->i_bmap;
472         struct nilfs_bdesc *bdescs = buf;
473         int ret, i;
474
475         for (i = 0; i < nmembs; i++) {
476                 /* XXX: use macro or inline func to check liveness */
477                 ret = nilfs_bmap_lookup_at_level(bmap,
478                                                  bdescs[i].bd_offset,
479                                                  bdescs[i].bd_level + 1,
480                                                  &bdescs[i].bd_blocknr);
481                 if (ret < 0) {
482                         if (ret != -ENOENT)
483                                 return ret;
484                         bdescs[i].bd_blocknr = 0;
485                 }
486                 if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr)
487                         /* skip dead block */
488                         continue;
489                 if (bdescs[i].bd_level == 0) {
490                         ret = nilfs_mdt_mark_block_dirty(dat,
491                                                          bdescs[i].bd_offset);
492                         if (ret < 0) {
493                                 BUG_ON(ret == -ENOENT);
494                                 return ret;
495                         }
496                 } else {
497                         ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset,
498                                               bdescs[i].bd_level);
499                         if (ret < 0) {
500                                 BUG_ON(ret == -ENOENT);
501                                 return ret;
502                         }
503                 }
504         }
505         return nmembs;
506 }
507
508 static inline int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
509                                                 struct nilfs_argv *argv,
510                                                 int dir)
511 {
512         return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
513                                      nilfs_ioctl_do_mark_blocks_dirty);
514 }
515
516 static ssize_t
517 nilfs_ioctl_do_free_segments(struct the_nilfs *nilfs, __u64 *posp, int flags,
518                              void *buf, size_t size, size_t nmembs)
519 {
520         struct nilfs_sb_info *sbi = nilfs_get_writer(nilfs);
521         int ret;
522
523         BUG_ON(!sbi);
524         ret = nilfs_segctor_add_segments_to_be_freed(
525                 NILFS_SC(sbi), buf, nmembs);
526         nilfs_put_writer(nilfs);
527
528         return (ret < 0) ? ret : nmembs;
529 }
530
531 static inline int nilfs_ioctl_free_segments(struct the_nilfs *nilfs,
532                                              struct nilfs_argv *argv,
533                                              int dir)
534 {
535         return nilfs_ioctl_wrap_copy(nilfs, argv, dir,
536                                      nilfs_ioctl_do_free_segments);
537 }
538
539 int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
540                                        void __user *argp)
541 {
542         struct nilfs_argv argv[5];
543         int dir, ret;
544
545         if (copy_from_user(argv, argp, sizeof(argv)))
546                 return -EFAULT;
547
548         dir = _IOC_WRITE;
549         ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], dir);
550         if (ret < 0)
551                 goto out_move_blks;
552         ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], dir);
553         if (ret < 0)
554                 goto out_del_cps;
555         ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], dir);
556         if (ret < 0)
557                 goto out_free_vbns;
558         ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], dir);
559         if (ret < 0)
560                 goto out_free_vbns;
561         ret = nilfs_ioctl_free_segments(nilfs, &argv[4], dir);
562         if (ret < 0)
563                 goto out_free_segs;
564
565         return 0;
566
567  out_free_segs:
568         BUG(); /* XXX: not implemented yet */
569  out_free_vbns:
570         BUG();/* XXX: not implemented yet */
571  out_del_cps:
572         BUG();/* XXX: not implemented yet */
573  out_move_blks:
574         nilfs_remove_all_gcinode(nilfs);
575         return ret;
576 }
577
578 static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
579                                       unsigned int cmd, void __user *argp)
580 {
581         if (!capable(CAP_SYS_ADMIN))
582                 return -EPERM;
583         return nilfs_clean_segments(inode->i_sb, argp);
584 }
585
586 static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
587                             unsigned int cmd, void __user *argp)
588 {
589         __u64 cno;
590         int ret;
591
592         ret = nilfs_construct_segment(inode->i_sb);
593         if (ret < 0)
594                 return ret;
595
596         if (argp != NULL) {
597                 cno = NILFS_SB(inode->i_sb)->s_nilfs->ns_cno - 1;
598                 if (copy_to_user(argp, &cno, sizeof(cno)))
599                         return -EFAULT;
600         }
601         return 0;
602 }
603
604 int nilfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
605                 unsigned long arg)
606 {
607         void __user *argp = (void * __user *)arg;
608
609         switch (cmd) {
610         case NILFS_IOCTL_CHANGE_CPMODE:
611                 return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
612         case NILFS_IOCTL_DELETE_CHECKPOINT:
613                 return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
614         case NILFS_IOCTL_GET_CPINFO:
615                 return nilfs_ioctl_get_cpinfo(inode, filp, cmd, argp);
616         case NILFS_IOCTL_GET_CPSTAT:
617                 return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
618         case NILFS_IOCTL_GET_SUINFO:
619                 return nilfs_ioctl_get_suinfo(inode, filp, cmd, argp);
620         case NILFS_IOCTL_GET_SUSTAT:
621                 return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
622         case NILFS_IOCTL_GET_VINFO:
623                 /* XXX: rename to ??? */
624                 return nilfs_ioctl_get_vinfo(inode, filp, cmd, argp);
625         case NILFS_IOCTL_GET_BDESCS:
626                 return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
627         case NILFS_IOCTL_CLEAN_SEGMENTS:
628                 return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
629         case NILFS_IOCTL_SYNC:
630                 return nilfs_ioctl_sync(inode, filp, cmd, argp);
631         default:
632                 return -ENOTTY;
633         }
634 }
635
636 /* compat_ioctl */
637 #ifdef CONFIG_COMPAT
638 #include <linux/compat.h>
639
640 static int nilfs_compat_locked_ioctl(struct inode *inode, struct file *filp,
641                                      unsigned int cmd, unsigned long arg)
642 {
643         int ret;
644
645         lock_kernel();
646         ret = nilfs_ioctl(inode, filp, cmd, arg);
647         unlock_kernel();
648         return ret;
649 }
650
651 static int
652 nilfs_compat_ioctl_uargv32_to_uargv(struct nilfs_argv32 __user *uargv32,
653                                     struct nilfs_argv __user *uargv)
654 {
655         compat_uptr_t base;
656         compat_size_t nmembs, size;
657         compat_int_t index, flags;
658
659         if (get_user(base, &uargv32->v_base) ||
660             put_user(compat_ptr(base), &uargv->v_base) ||
661             get_user(nmembs, &uargv32->v_nmembs) ||
662             put_user(nmembs, &uargv->v_nmembs) ||
663             get_user(size, &uargv32->v_size) ||
664             put_user(size, &uargv->v_size) ||
665             get_user(index, &uargv32->v_index) ||
666             put_user(index, &uargv->v_index) ||
667             get_user(flags, &uargv32->v_flags) ||
668             put_user(flags, &uargv->v_flags))
669                 return -EFAULT;
670         return 0;
671 }
672
673 static int
674 nilfs_compat_ioctl_uargv_to_uargv32(struct nilfs_argv __user *uargv,
675                                     struct nilfs_argv32 __user *uargv32)
676 {
677         size_t nmembs;
678
679         if (get_user(nmembs, &uargv->v_nmembs) ||
680             put_user(nmembs, &uargv32->v_nmembs))
681                 return -EFAULT;
682         return 0;
683 }
684
685 static int
686 nilfs_compat_ioctl_get_by_argv(struct inode *inode, struct file *filp,
687                                unsigned int cmd, unsigned long arg)
688 {
689         struct nilfs_argv __user *uargv;
690         struct nilfs_argv32 __user *uargv32;
691         int ret;
692
693         uargv = compat_alloc_user_space(sizeof(struct nilfs_argv));
694         uargv32 = compat_ptr(arg);
695         ret = nilfs_compat_ioctl_uargv32_to_uargv(uargv32, uargv);
696         if (ret < 0)
697                 return ret;
698
699         ret = nilfs_compat_locked_ioctl(inode, filp, cmd, (unsigned long)uargv);
700         if (ret < 0)
701                 return ret;
702
703         return nilfs_compat_ioctl_uargv_to_uargv32(uargv, uargv32);
704 }
705
706 static int
707 nilfs_compat_ioctl_change_cpmode(struct inode *inode, struct file *filp,
708                                  unsigned int cmd, unsigned long arg)
709 {
710         struct nilfs_cpmode __user *ucpmode;
711         struct nilfs_cpmode32 __user *ucpmode32;
712         int mode;
713
714         ucpmode = compat_alloc_user_space(sizeof(struct nilfs_cpmode));
715         ucpmode32 = compat_ptr(arg);
716         if (copy_in_user(&ucpmode->cm_cno, &ucpmode32->cm_cno,
717                          sizeof(__u64)) ||
718             get_user(mode, &ucpmode32->cm_mode) ||
719             put_user(mode, &ucpmode->cm_mode))
720                 return -EFAULT;
721
722         return nilfs_compat_locked_ioctl(
723                 inode, filp, cmd, (unsigned long)ucpmode);
724 }
725
726
727 static inline int
728 nilfs_compat_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
729                                      unsigned int cmd, unsigned long arg)
730 {
731         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
732 }
733
734 static inline int
735 nilfs_compat_ioctl_get_cpinfo(struct inode *inode, struct file *filp,
736                               unsigned int cmd, unsigned long arg)
737 {
738         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
739 }
740
741 static inline int
742 nilfs_compat_ioctl_get_cpstat(struct inode *inode, struct file *filp,
743                               unsigned int cmd, unsigned long arg)
744 {
745         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
746 }
747
748 static inline int
749 nilfs_compat_ioctl_get_suinfo(struct inode *inode, struct file *filp,
750                               unsigned int cmd, unsigned long arg)
751 {
752         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
753 }
754
755 static int
756 nilfs_compat_ioctl_get_sustat(struct inode *inode, struct file *filp,
757                               unsigned int cmd, unsigned long arg)
758 {
759         struct nilfs_sustat __user *usustat;
760         struct nilfs_sustat32 __user *usustat32;
761         time_t ctime, nongc_ctime;
762         int ret;
763
764         usustat = compat_alloc_user_space(sizeof(struct nilfs_sustat));
765         ret = nilfs_compat_locked_ioctl(inode, filp, cmd,
766                                         (unsigned long)usustat);
767         if (ret < 0)
768                 return ret;
769
770         usustat32 = compat_ptr(arg);
771         if (copy_in_user(&usustat32->ss_nsegs, &usustat->ss_nsegs,
772                          sizeof(__u64)) ||
773             copy_in_user(&usustat32->ss_ncleansegs, &usustat->ss_ncleansegs,
774                          sizeof(__u64)) ||
775             copy_in_user(&usustat32->ss_ndirtysegs, &usustat->ss_ndirtysegs,
776                          sizeof(__u64)) ||
777             get_user(ctime, &usustat->ss_ctime) ||
778             put_user(ctime, &usustat32->ss_ctime) ||
779             get_user(nongc_ctime, &usustat->ss_nongc_ctime) ||
780             put_user(nongc_ctime, &usustat32->ss_nongc_ctime))
781                 return -EFAULT;
782         return 0;
783 }
784
785 static inline int
786 nilfs_compat_ioctl_get_vinfo(struct inode *inode, struct file *filp,
787                               unsigned int cmd, unsigned long arg)
788 {
789         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
790 }
791
792 static inline int
793 nilfs_compat_ioctl_get_bdescs(struct inode *inode, struct file *filp,
794                              unsigned int cmd, unsigned long arg)
795 {
796         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
797 }
798
799 static int
800 nilfs_compat_ioctl_clean_segments(struct inode *inode, struct file *filp,
801                                   unsigned int cmd, unsigned long arg)
802 {
803         struct nilfs_argv __user *uargv;
804         struct nilfs_argv32 __user *uargv32;
805         int i, ret;
806
807         uargv = compat_alloc_user_space(sizeof(struct nilfs_argv) * 5);
808         uargv32 = compat_ptr(arg);
809         for (i = 0; i < 5; i++) {
810                 ret = nilfs_compat_ioctl_uargv32_to_uargv(&uargv32[i],
811                                                           &uargv[i]);
812                 if (ret < 0)
813                         return ret;
814         }
815         return nilfs_compat_locked_ioctl(
816                 inode, filp, cmd, (unsigned long)uargv);
817 }
818
819 static int nilfs_compat_ioctl_sync(struct inode *inode, struct file *filp,
820                                    unsigned int cmd, unsigned long arg)
821 {
822         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
823 }
824
825 long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
826 {
827         struct inode *inode = filp->f_dentry->d_inode;
828
829         switch (cmd) {
830         case NILFS_IOCTL32_CHANGE_CPMODE:
831                 return nilfs_compat_ioctl_change_cpmode(
832                         inode, filp, NILFS_IOCTL_CHANGE_CPMODE, arg);
833         case NILFS_IOCTL_DELETE_CHECKPOINT:
834                 return nilfs_compat_ioctl_delete_checkpoint(
835                         inode, filp, cmd, arg);
836         case NILFS_IOCTL32_GET_CPINFO:
837                 return nilfs_compat_ioctl_get_cpinfo(
838                         inode, filp, NILFS_IOCTL_GET_CPINFO, arg);
839         case NILFS_IOCTL_GET_CPSTAT:
840                 return nilfs_compat_ioctl_get_cpstat(inode, filp, cmd, arg);
841         case NILFS_IOCTL32_GET_SUINFO:
842                 return nilfs_compat_ioctl_get_suinfo(
843                         inode, filp, NILFS_IOCTL_GET_SUINFO, arg);
844         case NILFS_IOCTL32_GET_SUSTAT:
845                 return nilfs_compat_ioctl_get_sustat(
846                         inode, filp, NILFS_IOCTL_GET_SUSTAT, arg);
847         case NILFS_IOCTL32_GET_VINFO:
848                 return nilfs_compat_ioctl_get_vinfo(
849                         inode, filp, NILFS_IOCTL_GET_VINFO, arg);
850         case NILFS_IOCTL32_GET_BDESCS:
851                 return nilfs_compat_ioctl_get_bdescs(
852                         inode, filp, NILFS_IOCTL_GET_BDESCS, arg);
853         case NILFS_IOCTL32_CLEAN_SEGMENTS:
854                 return nilfs_compat_ioctl_clean_segments(
855                         inode, filp, NILFS_IOCTL_CLEAN_SEGMENTS, arg);
856         case NILFS_IOCTL_SYNC:
857                 return nilfs_compat_ioctl_sync(inode, filp, cmd, arg);
858         default:
859                 return -ENOIOCTLCMD;
860         }
861 }
862 #endif