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