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