nilfs2: fix gc failure on volumes keeping numerous snapshots
[linux-2.6.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         int ret;
582
583         if (!capable(CAP_SYS_ADMIN))
584                 return -EPERM;
585
586         ret = nilfs_clean_segments(inode->i_sb, argp);
587         clear_nilfs_cond_nongc_write(NILFS_SB(inode->i_sb)->s_nilfs);
588         return ret;
589 }
590
591 static int nilfs_ioctl_test_cond(struct the_nilfs *nilfs, int cond)
592 {
593         return (cond & NILFS_TIMEDWAIT_SEG_WRITE) &&
594                 nilfs_cond_nongc_write(nilfs);
595 }
596
597 static void nilfs_ioctl_clear_cond(struct the_nilfs *nilfs, int cond)
598 {
599         if (cond & NILFS_TIMEDWAIT_SEG_WRITE)
600                 clear_nilfs_cond_nongc_write(nilfs);
601 }
602
603 static int nilfs_ioctl_timedwait(struct inode *inode, struct file *filp,
604                                  unsigned int cmd, void __user *argp)
605 {
606         struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
607         struct nilfs_wait_cond wc;
608         long ret;
609
610         if (!capable(CAP_SYS_ADMIN))
611                 return -EPERM;
612         if (copy_from_user(&wc, argp, sizeof(wc)))
613                 return -EFAULT;
614
615         unlock_kernel();
616         ret = wc.wc_flags ?
617                 wait_event_interruptible_timeout(
618                         nilfs->ns_cleanerd_wq,
619                         nilfs_ioctl_test_cond(nilfs, wc.wc_cond),
620                         timespec_to_jiffies(&wc.wc_timeout)) :
621                 wait_event_interruptible(
622                         nilfs->ns_cleanerd_wq,
623                         nilfs_ioctl_test_cond(nilfs, wc.wc_cond));
624         lock_kernel();
625         nilfs_ioctl_clear_cond(nilfs, wc.wc_cond);
626
627         if (ret > 0) {
628                 jiffies_to_timespec(ret, &wc.wc_timeout);
629                 if (copy_to_user(argp, &wc, sizeof(wc)))
630                         return -EFAULT;
631                 return 0;
632         }
633         if (ret != 0)
634                 return -EINTR;
635
636         return wc.wc_flags ? -ETIME : 0;
637 }
638
639 static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
640                             unsigned int cmd, void __user *argp)
641 {
642         __u64 cno;
643         int ret;
644
645         ret = nilfs_construct_segment(inode->i_sb);
646         if (ret < 0)
647                 return ret;
648
649         if (argp != NULL) {
650                 cno = NILFS_SB(inode->i_sb)->s_nilfs->ns_cno - 1;
651                 if (copy_to_user(argp, &cno, sizeof(cno)))
652                         return -EFAULT;
653         }
654         return 0;
655 }
656
657 int nilfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
658                 unsigned long arg)
659 {
660         void __user *argp = (void * __user *)arg;
661
662         switch (cmd) {
663         case NILFS_IOCTL_CHANGE_CPMODE:
664                 return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
665         case NILFS_IOCTL_DELETE_CHECKPOINT:
666                 return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
667         case NILFS_IOCTL_GET_CPINFO:
668                 return nilfs_ioctl_get_cpinfo(inode, filp, cmd, argp);
669         case NILFS_IOCTL_GET_CPSTAT:
670                 return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
671         case NILFS_IOCTL_GET_SUINFO:
672                 return nilfs_ioctl_get_suinfo(inode, filp, cmd, argp);
673         case NILFS_IOCTL_GET_SUSTAT:
674                 return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
675         case NILFS_IOCTL_GET_VINFO:
676                 /* XXX: rename to ??? */
677                 return nilfs_ioctl_get_vinfo(inode, filp, cmd, argp);
678         case NILFS_IOCTL_GET_BDESCS:
679                 return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
680         case NILFS_IOCTL_CLEAN_SEGMENTS:
681                 return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
682         case NILFS_IOCTL_TIMEDWAIT:
683                 return nilfs_ioctl_timedwait(inode, filp, cmd, argp);
684         case NILFS_IOCTL_SYNC:
685                 return nilfs_ioctl_sync(inode, filp, cmd, argp);
686         default:
687                 return -ENOTTY;
688         }
689 }
690
691 /* compat_ioctl */
692 #ifdef CONFIG_COMPAT
693 #include <linux/compat.h>
694
695 static int nilfs_compat_locked_ioctl(struct inode *inode, struct file *filp,
696                                      unsigned int cmd, unsigned long arg)
697 {
698         int ret;
699
700         lock_kernel();
701         ret = nilfs_ioctl(inode, filp, cmd, arg);
702         unlock_kernel();
703         return ret;
704 }
705
706 static int
707 nilfs_compat_ioctl_uargv32_to_uargv(struct nilfs_argv32 __user *uargv32,
708                                     struct nilfs_argv __user *uargv)
709 {
710         compat_uptr_t base;
711         compat_size_t nmembs, size;
712         compat_int_t index, flags;
713
714         if (get_user(base, &uargv32->v_base) ||
715             put_user(compat_ptr(base), &uargv->v_base) ||
716             get_user(nmembs, &uargv32->v_nmembs) ||
717             put_user(nmembs, &uargv->v_nmembs) ||
718             get_user(size, &uargv32->v_size) ||
719             put_user(size, &uargv->v_size) ||
720             get_user(index, &uargv32->v_index) ||
721             put_user(index, &uargv->v_index) ||
722             get_user(flags, &uargv32->v_flags) ||
723             put_user(flags, &uargv->v_flags))
724                 return -EFAULT;
725         return 0;
726 }
727
728 static int
729 nilfs_compat_ioctl_uargv_to_uargv32(struct nilfs_argv __user *uargv,
730                                     struct nilfs_argv32 __user *uargv32)
731 {
732         size_t nmembs;
733
734         if (get_user(nmembs, &uargv->v_nmembs) ||
735             put_user(nmembs, &uargv32->v_nmembs))
736                 return -EFAULT;
737         return 0;
738 }
739
740 static int
741 nilfs_compat_ioctl_get_by_argv(struct inode *inode, struct file *filp,
742                                unsigned int cmd, unsigned long arg)
743 {
744         struct nilfs_argv __user *uargv;
745         struct nilfs_argv32 __user *uargv32;
746         int ret;
747
748         uargv = compat_alloc_user_space(sizeof(struct nilfs_argv));
749         uargv32 = compat_ptr(arg);
750         ret = nilfs_compat_ioctl_uargv32_to_uargv(uargv32, uargv);
751         if (ret < 0)
752                 return ret;
753
754         ret = nilfs_compat_locked_ioctl(inode, filp, cmd, (unsigned long)uargv);
755         if (ret < 0)
756                 return ret;
757
758         return nilfs_compat_ioctl_uargv_to_uargv32(uargv, uargv32);
759 }
760
761 static int
762 nilfs_compat_ioctl_change_cpmode(struct inode *inode, struct file *filp,
763                                  unsigned int cmd, unsigned long arg)
764 {
765         struct nilfs_cpmode __user *ucpmode;
766         struct nilfs_cpmode32 __user *ucpmode32;
767         int mode;
768
769         ucpmode = compat_alloc_user_space(sizeof(struct nilfs_cpmode));
770         ucpmode32 = compat_ptr(arg);
771         if (copy_in_user(&ucpmode->cm_cno, &ucpmode32->cm_cno,
772                          sizeof(__u64)) ||
773             get_user(mode, &ucpmode32->cm_mode) ||
774             put_user(mode, &ucpmode->cm_mode))
775                 return -EFAULT;
776
777         return nilfs_compat_locked_ioctl(
778                 inode, filp, cmd, (unsigned long)ucpmode);
779 }
780
781
782 static inline int
783 nilfs_compat_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
784                                      unsigned int cmd, unsigned long arg)
785 {
786         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
787 }
788
789 static inline int
790 nilfs_compat_ioctl_get_cpinfo(struct inode *inode, struct file *filp,
791                               unsigned int cmd, unsigned long arg)
792 {
793         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
794 }
795
796 static inline int
797 nilfs_compat_ioctl_get_cpstat(struct inode *inode, struct file *filp,
798                               unsigned int cmd, unsigned long arg)
799 {
800         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
801 }
802
803 static inline int
804 nilfs_compat_ioctl_get_suinfo(struct inode *inode, struct file *filp,
805                               unsigned int cmd, unsigned long arg)
806 {
807         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
808 }
809
810 static int
811 nilfs_compat_ioctl_get_sustat(struct inode *inode, struct file *filp,
812                               unsigned int cmd, unsigned long arg)
813 {
814         struct nilfs_sustat __user *usustat;
815         struct nilfs_sustat32 __user *usustat32;
816         time_t ctime, nongc_ctime;
817         int ret;
818
819         usustat = compat_alloc_user_space(sizeof(struct nilfs_sustat));
820         ret = nilfs_compat_locked_ioctl(inode, filp, cmd,
821                                         (unsigned long)usustat);
822         if (ret < 0)
823                 return ret;
824
825         usustat32 = compat_ptr(arg);
826         if (copy_in_user(&usustat32->ss_nsegs, &usustat->ss_nsegs,
827                          sizeof(__u64)) ||
828             copy_in_user(&usustat32->ss_ncleansegs, &usustat->ss_ncleansegs,
829                          sizeof(__u64)) ||
830             copy_in_user(&usustat32->ss_ndirtysegs, &usustat->ss_ndirtysegs,
831                          sizeof(__u64)) ||
832             get_user(ctime, &usustat->ss_ctime) ||
833             put_user(ctime, &usustat32->ss_ctime) ||
834             get_user(nongc_ctime, &usustat->ss_nongc_ctime) ||
835             put_user(nongc_ctime, &usustat32->ss_nongc_ctime))
836                 return -EFAULT;
837         return 0;
838 }
839
840 static inline int
841 nilfs_compat_ioctl_get_vinfo(struct inode *inode, struct file *filp,
842                               unsigned int cmd, unsigned long arg)
843 {
844         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
845 }
846
847 static inline int
848 nilfs_compat_ioctl_get_bdescs(struct inode *inode, struct file *filp,
849                              unsigned int cmd, unsigned long arg)
850 {
851         return nilfs_compat_ioctl_get_by_argv(inode, filp, cmd, arg);
852 }
853
854 static int
855 nilfs_compat_ioctl_clean_segments(struct inode *inode, struct file *filp,
856                                   unsigned int cmd, unsigned long arg)
857 {
858         struct nilfs_argv __user *uargv;
859         struct nilfs_argv32 __user *uargv32;
860         int i, ret;
861
862         uargv = compat_alloc_user_space(sizeof(struct nilfs_argv) * 5);
863         uargv32 = compat_ptr(arg);
864         for (i = 0; i < 5; i++) {
865                 ret = nilfs_compat_ioctl_uargv32_to_uargv(&uargv32[i],
866                                                           &uargv[i]);
867                 if (ret < 0)
868                         return ret;
869         }
870         return nilfs_compat_locked_ioctl(
871                 inode, filp, cmd, (unsigned long)uargv);
872 }
873
874 static int
875 nilfs_compat_ioctl_timedwait(struct inode *inode, struct file *filp,
876                              unsigned int cmd, unsigned long arg)
877 {
878         struct nilfs_wait_cond __user *uwcond;
879         struct nilfs_wait_cond32 __user *uwcond32;
880         struct timespec ts;
881         int cond, flags, ret;
882
883         uwcond = compat_alloc_user_space(sizeof(struct nilfs_wait_cond));
884         uwcond32 = compat_ptr(arg);
885         if (get_user(cond, &uwcond32->wc_cond) ||
886             put_user(cond, &uwcond->wc_cond) ||
887             get_user(flags, &uwcond32->wc_flags) ||
888             put_user(flags, &uwcond->wc_flags) ||
889             get_user(ts.tv_sec, &uwcond32->wc_timeout.tv_sec) ||
890             get_user(ts.tv_nsec, &uwcond32->wc_timeout.tv_nsec) ||
891             put_user(ts.tv_sec, &uwcond->wc_timeout.tv_sec) ||
892             put_user(ts.tv_nsec, &uwcond->wc_timeout.tv_nsec))
893                 return -EFAULT;
894
895         ret = nilfs_compat_locked_ioctl(inode, filp, cmd,
896                                         (unsigned long)uwcond);
897         if (ret < 0)
898                 return ret;
899
900         if (get_user(ts.tv_sec, &uwcond->wc_timeout.tv_sec) ||
901             get_user(ts.tv_nsec, &uwcond->wc_timeout.tv_nsec) ||
902             put_user(ts.tv_sec, &uwcond32->wc_timeout.tv_sec) ||
903             put_user(ts.tv_nsec, &uwcond32->wc_timeout.tv_nsec))
904                 return -EFAULT;
905
906         return 0;
907 }
908
909 static int nilfs_compat_ioctl_sync(struct inode *inode, struct file *filp,
910                                    unsigned int cmd, unsigned long arg)
911 {
912         return nilfs_compat_locked_ioctl(inode, filp, cmd, arg);
913 }
914
915 long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
916 {
917         struct inode *inode = filp->f_dentry->d_inode;
918
919         switch (cmd) {
920         case NILFS_IOCTL32_CHANGE_CPMODE:
921                 return nilfs_compat_ioctl_change_cpmode(
922                         inode, filp, NILFS_IOCTL_CHANGE_CPMODE, arg);
923         case NILFS_IOCTL_DELETE_CHECKPOINT:
924                 return nilfs_compat_ioctl_delete_checkpoint(
925                         inode, filp, cmd, arg);
926         case NILFS_IOCTL32_GET_CPINFO:
927                 return nilfs_compat_ioctl_get_cpinfo(
928                         inode, filp, NILFS_IOCTL_GET_CPINFO, arg);
929         case NILFS_IOCTL_GET_CPSTAT:
930                 return nilfs_compat_ioctl_get_cpstat(inode, filp, cmd, arg);
931         case NILFS_IOCTL32_GET_SUINFO:
932                 return nilfs_compat_ioctl_get_suinfo(
933                         inode, filp, NILFS_IOCTL_GET_SUINFO, arg);
934         case NILFS_IOCTL32_GET_SUSTAT:
935                 return nilfs_compat_ioctl_get_sustat(
936                         inode, filp, NILFS_IOCTL_GET_SUSTAT, arg);
937         case NILFS_IOCTL32_GET_VINFO:
938                 return nilfs_compat_ioctl_get_vinfo(
939                         inode, filp, NILFS_IOCTL_GET_VINFO, arg);
940         case NILFS_IOCTL32_GET_BDESCS:
941                 return nilfs_compat_ioctl_get_bdescs(
942                         inode, filp, NILFS_IOCTL_GET_BDESCS, arg);
943         case NILFS_IOCTL32_CLEAN_SEGMENTS:
944                 return nilfs_compat_ioctl_clean_segments(
945                         inode, filp, NILFS_IOCTL_CLEAN_SEGMENTS, arg);
946         case NILFS_IOCTL32_TIMEDWAIT:
947                 return nilfs_compat_ioctl_timedwait(
948                         inode, filp, NILFS_IOCTL_TIMEDWAIT, arg);
949         case NILFS_IOCTL_SYNC:
950                 return nilfs_compat_ioctl_sync(inode, filp, cmd, arg);
951         default:
952                 return -ENOIOCTLCMD;
953         }
954 }
955 #endif