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