nilfs2: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION
[deliverable/linux.git] / fs / nilfs2 / ioctl.c
CommitLineData
7942b919
KS
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>
5a0e3ad6 25#include <linux/slab.h>
7942b919
KS
26#include <linux/capability.h> /* capable() */
27#include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */
4f6b8288 28#include <linux/vmalloc.h>
7512487e 29#include <linux/mount.h> /* mnt_want_write(), mnt_drop_write() */
7942b919
KS
30#include <linux/nilfs2_fs.h>
31#include "nilfs.h"
32#include "segment.h"
33#include "bmap.h"
34#include "cpfile.h"
35#include "sufile.h"
36#include "dat.h"
37
38
7942b919
KS
39static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
40 struct nilfs_argv *argv, int dir,
41 ssize_t (*dofunc)(struct the_nilfs *,
b028fcfc 42 __u64 *, int,
7942b919
KS
43 void *, size_t, size_t))
44{
45 void *buf;
dc498d09 46 void __user *base = (void __user *)(unsigned long)argv->v_base;
3358b4aa 47 size_t maxmembs, total, n;
7942b919
KS
48 ssize_t nr;
49 int ret, i;
b028fcfc 50 __u64 pos, ppos;
7942b919
KS
51
52 if (argv->v_nmembs == 0)
53 return 0;
54
3358b4aa
RK
55 if (argv->v_size > PAGE_SIZE)
56 return -EINVAL;
57
58 buf = (void *)__get_free_pages(GFP_NOFS, 0);
59 if (unlikely(!buf))
7942b919 60 return -ENOMEM;
3358b4aa 61 maxmembs = PAGE_SIZE / argv->v_size;
7942b919
KS
62
63 ret = 0;
64 total = 0;
b028fcfc 65 pos = argv->v_index;
7942b919
KS
66 for (i = 0; i < argv->v_nmembs; i += n) {
67 n = (argv->v_nmembs - i < maxmembs) ?
68 argv->v_nmembs - i : maxmembs;
69 if ((dir & _IOC_WRITE) &&
dc498d09
RK
70 copy_from_user(buf, base + argv->v_size * i,
71 argv->v_size * n)) {
7942b919
KS
72 ret = -EFAULT;
73 break;
74 }
b028fcfc 75 ppos = pos;
8acfbf09 76 nr = dofunc(nilfs, &pos, argv->v_flags, buf, argv->v_size,
b028fcfc 77 n);
7942b919
KS
78 if (nr < 0) {
79 ret = nr;
80 break;
81 }
82 if ((dir & _IOC_READ) &&
dc498d09
RK
83 copy_to_user(base + argv->v_size * i, buf,
84 argv->v_size * nr)) {
7942b919
KS
85 ret = -EFAULT;
86 break;
87 }
88 total += nr;
b028fcfc
RK
89 if ((size_t)nr < n)
90 break;
91 if (pos == ppos)
92 pos += n;
7942b919
KS
93 }
94 argv->v_nmembs = total;
95
3358b4aa 96 free_pages((unsigned long)buf, 0);
7942b919
KS
97 return ret;
98}
99
cde98f0f
RK
100static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp)
101{
102 unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE;
103
104 return put_user(flags, (int __user *)argp);
105}
106
107static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
108 void __user *argp)
109{
110 struct nilfs_transaction_info ti;
111 unsigned int flags, oldflags;
112 int ret;
113
114 if (!is_owner_or_cap(inode))
115 return -EACCES;
116
117 if (get_user(flags, (int __user *)argp))
118 return -EFAULT;
119
120 ret = mnt_want_write(filp->f_path.mnt);
121 if (ret)
122 return ret;
123
124 flags = nilfs_mask_flags(inode->i_mode, flags);
125
126 mutex_lock(&inode->i_mutex);
127
128 oldflags = NILFS_I(inode)->i_flags;
129
130 /*
131 * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
132 * relevant capability.
133 */
134 ret = -EPERM;
135 if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
136 !capable(CAP_LINUX_IMMUTABLE))
137 goto out;
138
139 ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
140 if (ret)
141 goto out;
142
143 NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) |
144 (flags & FS_FL_USER_MODIFIABLE);
145
146 nilfs_set_inode_flags(inode);
147 inode->i_ctime = CURRENT_TIME;
148 if (IS_SYNC(inode))
149 nilfs_set_transaction_flag(NILFS_TI_SYNC);
150
151 nilfs_mark_inode_dirty(inode);
152 ret = nilfs_transaction_commit(inode->i_sb);
153out:
154 mutex_unlock(&inode->i_mutex);
155 mnt_drop_write(filp->f_path.mnt);
156 return ret;
157}
158
159static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp)
160{
161 return put_user(inode->i_generation, (int __user *)argp);
162}
163
7942b919
KS
164static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
165 unsigned int cmd, void __user *argp)
166{
c1ea985c
RK
167 struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
168 struct inode *cpfile = nilfs->ns_cpfile;
7942b919
KS
169 struct nilfs_transaction_info ti;
170 struct nilfs_cpmode cpmode;
171 int ret;
172
173 if (!capable(CAP_SYS_ADMIN))
174 return -EPERM;
7512487e
RK
175
176 ret = mnt_want_write(filp->f_path.mnt);
177 if (ret)
178 return ret;
179
180 ret = -EFAULT;
7942b919 181 if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
7512487e 182 goto out;
7942b919 183
348fe8da 184 down_read(&inode->i_sb->s_umount);
7512487e 185
7942b919
KS
186 nilfs_transaction_begin(inode->i_sb, &ti, 0);
187 ret = nilfs_cpfile_change_cpmode(
188 cpfile, cpmode.cm_cno, cpmode.cm_mode);
7512487e 189 if (unlikely(ret < 0))
47420c79 190 nilfs_transaction_abort(inode->i_sb);
7512487e
RK
191 else
192 nilfs_transaction_commit(inode->i_sb); /* never fails */
193
348fe8da 194 up_read(&inode->i_sb->s_umount);
7512487e
RK
195out:
196 mnt_drop_write(filp->f_path.mnt);
7942b919
KS
197 return ret;
198}
199
200static int
201nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
202 unsigned int cmd, void __user *argp)
203{
204 struct inode *cpfile = NILFS_SB(inode->i_sb)->s_nilfs->ns_cpfile;
205 struct nilfs_transaction_info ti;
206 __u64 cno;
207 int ret;
208
209 if (!capable(CAP_SYS_ADMIN))
210 return -EPERM;
7512487e
RK
211
212 ret = mnt_want_write(filp->f_path.mnt);
213 if (ret)
214 return ret;
215
216 ret = -EFAULT;
7942b919 217 if (copy_from_user(&cno, argp, sizeof(cno)))
7512487e 218 goto out;
7942b919
KS
219
220 nilfs_transaction_begin(inode->i_sb, &ti, 0);
221 ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
7512487e 222 if (unlikely(ret < 0))
47420c79 223 nilfs_transaction_abort(inode->i_sb);
7512487e
RK
224 else
225 nilfs_transaction_commit(inode->i_sb); /* never fails */
226out:
227 mnt_drop_write(filp->f_path.mnt);
7942b919
KS
228 return ret;
229}
230
231static ssize_t
b028fcfc 232nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
7942b919
KS
233 void *buf, size_t size, size_t nmembs)
234{
7942b919
KS
235 int ret;
236
47420c79 237 down_read(&nilfs->ns_segctor_sem);
47eb6b9c 238 ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
003ff182 239 size, nmembs);
47420c79 240 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
241 return ret;
242}
243
244static int nilfs_ioctl_get_cpstat(struct inode *inode, struct file *filp,
245 unsigned int cmd, void __user *argp)
246{
47420c79 247 struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
7942b919 248 struct nilfs_cpstat cpstat;
7942b919
KS
249 int ret;
250
47420c79
RK
251 down_read(&nilfs->ns_segctor_sem);
252 ret = nilfs_cpfile_get_stat(nilfs->ns_cpfile, &cpstat);
253 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
254 if (ret < 0)
255 return ret;
256
257 if (copy_to_user(argp, &cpstat, sizeof(cpstat)))
258 ret = -EFAULT;
259 return ret;
260}
261
262static ssize_t
b028fcfc 263nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
7942b919
KS
264 void *buf, size_t size, size_t nmembs)
265{
7942b919
KS
266 int ret;
267
47420c79 268 down_read(&nilfs->ns_segctor_sem);
003ff182
RK
269 ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
270 nmembs);
47420c79 271 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
272 return ret;
273}
274
275static int nilfs_ioctl_get_sustat(struct inode *inode, struct file *filp,
276 unsigned int cmd, void __user *argp)
277{
47420c79 278 struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
7942b919 279 struct nilfs_sustat sustat;
7942b919
KS
280 int ret;
281
47420c79
RK
282 down_read(&nilfs->ns_segctor_sem);
283 ret = nilfs_sufile_get_stat(nilfs->ns_sufile, &sustat);
284 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
285 if (ret < 0)
286 return ret;
287
288 if (copy_to_user(argp, &sustat, sizeof(sustat)))
289 ret = -EFAULT;
290 return ret;
291}
292
293static ssize_t
b028fcfc 294nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
7942b919
KS
295 void *buf, size_t size, size_t nmembs)
296{
7942b919
KS
297 int ret;
298
47420c79 299 down_read(&nilfs->ns_segctor_sem);
365e215c 300 ret = nilfs_dat_get_vinfo(nilfs->ns_dat, buf, size, nmembs);
47420c79 301 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
302 return ret;
303}
304
305static ssize_t
b028fcfc 306nilfs_ioctl_do_get_bdescs(struct the_nilfs *nilfs, __u64 *posp, int flags,
7942b919
KS
307 void *buf, size_t size, size_t nmembs)
308{
365e215c 309 struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
7942b919
KS
310 struct nilfs_bdesc *bdescs = buf;
311 int ret, i;
312
47eb6b9c 313 down_read(&nilfs->ns_segctor_sem);
7942b919
KS
314 for (i = 0; i < nmembs; i++) {
315 ret = nilfs_bmap_lookup_at_level(bmap,
316 bdescs[i].bd_offset,
317 bdescs[i].bd_level + 1,
318 &bdescs[i].bd_blocknr);
319 if (ret < 0) {
47eb6b9c
RK
320 if (ret != -ENOENT) {
321 up_read(&nilfs->ns_segctor_sem);
7942b919 322 return ret;
47eb6b9c 323 }
7942b919
KS
324 bdescs[i].bd_blocknr = 0;
325 }
326 }
47eb6b9c 327 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
328 return nmembs;
329}
330
331static int nilfs_ioctl_get_bdescs(struct inode *inode, struct file *filp,
332 unsigned int cmd, void __user *argp)
333{
334 struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
335 struct nilfs_argv argv;
7942b919
KS
336 int ret;
337
338 if (copy_from_user(&argv, argp, sizeof(argv)))
339 return -EFAULT;
340
83aca8f4
RK
341 if (argv.v_size != sizeof(struct nilfs_bdesc))
342 return -EINVAL;
343
7942b919
KS
344 ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd),
345 nilfs_ioctl_do_get_bdescs);
47420c79
RK
346 if (ret < 0)
347 return ret;
7942b919
KS
348
349 if (copy_to_user(argp, &argv, sizeof(argv)))
350 ret = -EFAULT;
351 return ret;
352}
353
354static int nilfs_ioctl_move_inode_block(struct inode *inode,
355 struct nilfs_vdesc *vdesc,
356 struct list_head *buffers)
357{
358 struct buffer_head *bh;
359 int ret;
360
361 if (vdesc->vd_flags == 0)
362 ret = nilfs_gccache_submit_read_data(
363 inode, vdesc->vd_offset, vdesc->vd_blocknr,
364 vdesc->vd_vblocknr, &bh);
365 else
366 ret = nilfs_gccache_submit_read_node(
367 inode, vdesc->vd_blocknr, vdesc->vd_vblocknr, &bh);
368
369 if (unlikely(ret < 0)) {
370 if (ret == -ENOENT)
371 printk(KERN_CRIT
372 "%s: invalid virtual block address (%s): "
373 "ino=%llu, cno=%llu, offset=%llu, "
374 "blocknr=%llu, vblocknr=%llu\n",
375 __func__, vdesc->vd_flags ? "node" : "data",
376 (unsigned long long)vdesc->vd_ino,
377 (unsigned long long)vdesc->vd_cno,
378 (unsigned long long)vdesc->vd_offset,
379 (unsigned long long)vdesc->vd_blocknr,
380 (unsigned long long)vdesc->vd_vblocknr);
381 return ret;
382 }
5399dd1f
RK
383 if (unlikely(!list_empty(&bh->b_assoc_buffers))) {
384 printk(KERN_CRIT "%s: conflicting %s buffer: ino=%llu, "
385 "cno=%llu, offset=%llu, blocknr=%llu, vblocknr=%llu\n",
386 __func__, vdesc->vd_flags ? "node" : "data",
387 (unsigned long long)vdesc->vd_ino,
388 (unsigned long long)vdesc->vd_cno,
389 (unsigned long long)vdesc->vd_offset,
390 (unsigned long long)vdesc->vd_blocknr,
391 (unsigned long long)vdesc->vd_vblocknr);
392 brelse(bh);
393 return -EEXIST;
394 }
7942b919
KS
395 list_add_tail(&bh->b_assoc_buffers, buffers);
396 return 0;
397}
398
263d90ce 399static int nilfs_ioctl_move_blocks(struct super_block *sb,
4f6b8288 400 struct nilfs_argv *argv, void *buf)
7942b919 401{
4f6b8288 402 size_t nmembs = argv->v_nmembs;
947b10ae 403 struct the_nilfs *nilfs = NILFS_SB(sb)->s_nilfs;
7942b919
KS
404 struct inode *inode;
405 struct nilfs_vdesc *vdesc;
406 struct buffer_head *bh, *n;
407 LIST_HEAD(buffers);
408 ino_t ino;
409 __u64 cno;
410 int i, ret;
411
412 for (i = 0, vdesc = buf; i < nmembs; ) {
413 ino = vdesc->vd_ino;
414 cno = vdesc->vd_cno;
263d90ce 415 inode = nilfs_iget_for_gc(sb, ino, cno);
103cfcf5
DC
416 if (IS_ERR(inode)) {
417 ret = PTR_ERR(inode);
7942b919
KS
418 goto failed;
419 }
947b10ae
RK
420 if (list_empty(&NILFS_I(inode)->i_dirty)) {
421 /*
422 * Add the inode to GC inode list. Garbage Collection
423 * is serialized and no two processes manipulate the
424 * list simultaneously.
425 */
426 igrab(inode);
427 list_add(&NILFS_I(inode)->i_dirty,
428 &nilfs->ns_gc_inodes);
429 }
430
7942b919
KS
431 do {
432 ret = nilfs_ioctl_move_inode_block(inode, vdesc,
433 &buffers);
263d90ce
RK
434 if (unlikely(ret < 0)) {
435 iput(inode);
7942b919 436 goto failed;
263d90ce 437 }
7942b919
KS
438 vdesc++;
439 } while (++i < nmembs &&
440 vdesc->vd_ino == ino && vdesc->vd_cno == cno);
263d90ce
RK
441
442 iput(inode); /* The inode still remains in GC inode list */
7942b919
KS
443 }
444
445 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
446 ret = nilfs_gccache_wait_and_mark_dirty(bh);
447 if (unlikely(ret < 0)) {
5399dd1f 448 WARN_ON(ret == -EEXIST);
7942b919
KS
449 goto failed;
450 }
451 list_del_init(&bh->b_assoc_buffers);
7942b919
KS
452 brelse(bh);
453 }
454 return nmembs;
455
456 failed:
457 list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) {
458 list_del_init(&bh->b_assoc_buffers);
7942b919
KS
459 brelse(bh);
460 }
461 return ret;
462}
463
4f6b8288
RK
464static int nilfs_ioctl_delete_checkpoints(struct the_nilfs *nilfs,
465 struct nilfs_argv *argv, void *buf)
7942b919 466{
4f6b8288 467 size_t nmembs = argv->v_nmembs;
7942b919
KS
468 struct inode *cpfile = nilfs->ns_cpfile;
469 struct nilfs_period *periods = buf;
470 int ret, i;
471
472 for (i = 0; i < nmembs; i++) {
473 ret = nilfs_cpfile_delete_checkpoints(
474 cpfile, periods[i].p_start, periods[i].p_end);
475 if (ret < 0)
476 return ret;
477 }
478 return nmembs;
479}
480
4f6b8288
RK
481static int nilfs_ioctl_free_vblocknrs(struct the_nilfs *nilfs,
482 struct nilfs_argv *argv, void *buf)
7942b919 483{
4f6b8288
RK
484 size_t nmembs = argv->v_nmembs;
485 int ret;
7942b919 486
365e215c 487 ret = nilfs_dat_freev(nilfs->ns_dat, buf, nmembs);
7942b919
KS
488
489 return (ret < 0) ? ret : nmembs;
490}
491
4f6b8288
RK
492static int nilfs_ioctl_mark_blocks_dirty(struct the_nilfs *nilfs,
493 struct nilfs_argv *argv, void *buf)
7942b919 494{
4f6b8288 495 size_t nmembs = argv->v_nmembs;
365e215c 496 struct nilfs_bmap *bmap = NILFS_I(nilfs->ns_dat)->i_bmap;
7942b919
KS
497 struct nilfs_bdesc *bdescs = buf;
498 int ret, i;
499
500 for (i = 0; i < nmembs; i++) {
501 /* XXX: use macro or inline func to check liveness */
502 ret = nilfs_bmap_lookup_at_level(bmap,
503 bdescs[i].bd_offset,
504 bdescs[i].bd_level + 1,
505 &bdescs[i].bd_blocknr);
506 if (ret < 0) {
507 if (ret != -ENOENT)
508 return ret;
509 bdescs[i].bd_blocknr = 0;
510 }
511 if (bdescs[i].bd_blocknr != bdescs[i].bd_oblocknr)
512 /* skip dead block */
513 continue;
514 if (bdescs[i].bd_level == 0) {
365e215c 515 ret = nilfs_mdt_mark_block_dirty(nilfs->ns_dat,
7942b919
KS
516 bdescs[i].bd_offset);
517 if (ret < 0) {
1f5abe7e 518 WARN_ON(ret == -ENOENT);
7942b919
KS
519 return ret;
520 }
521 } else {
522 ret = nilfs_bmap_mark(bmap, bdescs[i].bd_offset,
523 bdescs[i].bd_level);
524 if (ret < 0) {
1f5abe7e 525 WARN_ON(ret == -ENOENT);
7942b919
KS
526 return ret;
527 }
528 }
529 }
530 return nmembs;
531}
532
7942b919 533int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
4f6b8288 534 struct nilfs_argv *argv, void **kbufs)
7942b919 535{
1f5abe7e 536 const char *msg;
4f6b8288 537 int ret;
7942b919 538
4f6b8288 539 ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]);
1f5abe7e
RK
540 if (ret < 0) {
541 /*
542 * can safely abort because checkpoints can be removed
543 * independently.
544 */
545 msg = "cannot delete checkpoints";
546 goto failed;
547 }
4f6b8288 548 ret = nilfs_ioctl_free_vblocknrs(nilfs, &argv[2], kbufs[2]);
1f5abe7e
RK
549 if (ret < 0) {
550 /*
551 * can safely abort because DAT file is updated atomically
552 * using a copy-on-write technique.
553 */
554 msg = "cannot delete virtual blocks from DAT file";
555 goto failed;
556 }
4f6b8288 557 ret = nilfs_ioctl_mark_blocks_dirty(nilfs, &argv[3], kbufs[3]);
1f5abe7e
RK
558 if (ret < 0) {
559 /*
560 * can safely abort because the operation is nondestructive.
561 */
562 msg = "cannot mark copying blocks dirty";
563 goto failed;
564 }
7942b919
KS
565 return 0;
566
1f5abe7e 567 failed:
1f5abe7e
RK
568 printk(KERN_ERR "NILFS: GC failed during preparation: %s: err=%d\n",
569 msg, ret);
7942b919
KS
570 return ret;
571}
572
573static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
574 unsigned int cmd, void __user *argp)
575{
4f6b8288 576 struct nilfs_argv argv[5];
33e189bd 577 static const size_t argsz[5] = {
4f6b8288
RK
578 sizeof(struct nilfs_vdesc),
579 sizeof(struct nilfs_period),
580 sizeof(__u64),
581 sizeof(struct nilfs_bdesc),
582 sizeof(__u64),
583 };
584 void __user *base;
585 void *kbufs[5];
586 struct the_nilfs *nilfs;
587 size_t len, nsegs;
588 int n, ret;
589
7942b919
KS
590 if (!capable(CAP_SYS_ADMIN))
591 return -EPERM;
4f6b8288 592
7512487e
RK
593 ret = mnt_want_write(filp->f_path.mnt);
594 if (ret)
595 return ret;
596
597 ret = -EFAULT;
4f6b8288 598 if (copy_from_user(argv, argp, sizeof(argv)))
7512487e 599 goto out;
4f6b8288 600
7512487e 601 ret = -EINVAL;
4f6b8288
RK
602 nsegs = argv[4].v_nmembs;
603 if (argv[4].v_size != argsz[4])
7512487e
RK
604 goto out;
605
4f6b8288
RK
606 /*
607 * argv[4] points to segment numbers this ioctl cleans. We
608 * use kmalloc() for its buffer because memory used for the
609 * segment numbers is enough small.
610 */
611 kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base,
612 nsegs * sizeof(__u64));
7512487e
RK
613 if (IS_ERR(kbufs[4])) {
614 ret = PTR_ERR(kbufs[4]);
615 goto out;
616 }
4f6b8288
RK
617 nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
618
619 for (n = 0; n < 4; n++) {
620 ret = -EINVAL;
621 if (argv[n].v_size != argsz[n])
622 goto out_free;
623
624 if (argv[n].v_nmembs > nsegs * nilfs->ns_blocks_per_segment)
625 goto out_free;
626
627 len = argv[n].v_size * argv[n].v_nmembs;
628 base = (void __user *)(unsigned long)argv[n].v_base;
629 if (len == 0) {
630 kbufs[n] = NULL;
631 continue;
632 }
633
634 kbufs[n] = vmalloc(len);
635 if (!kbufs[n]) {
636 ret = -ENOMEM;
637 goto out_free;
638 }
639 if (copy_from_user(kbufs[n], base, len)) {
640 ret = -EFAULT;
641 vfree(kbufs[n]);
642 goto out_free;
643 }
644 }
645
1cf58fa8 646 /*
263d90ce 647 * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(),
1cf58fa8
JS
648 * which will operates an inode list without blocking.
649 * To protect the list from concurrent operations,
650 * nilfs_ioctl_move_blocks should be atomic operation.
651 */
652 if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) {
653 ret = -EBUSY;
654 goto out_free;
655 }
656
5beb6e0b
RK
657 vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
658
263d90ce 659 ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]);
1cf58fa8
JS
660 if (ret < 0)
661 printk(KERN_ERR "NILFS: GC failed during preparation: "
662 "cannot read source blocks: err=%d\n", ret);
663 else
664 ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
665
263d90ce 666 nilfs_remove_all_gcinodes(nilfs);
1cf58fa8 667 clear_nilfs_gc_running(nilfs);
4f6b8288 668
7512487e 669out_free:
d5046853 670 while (--n >= 0)
4f6b8288
RK
671 vfree(kbufs[n]);
672 kfree(kbufs[4]);
7512487e
RK
673out:
674 mnt_drop_write(filp->f_path.mnt);
4f6b8288 675 return ret;
7942b919
KS
676}
677
678static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
679 unsigned int cmd, void __user *argp)
680{
681 __u64 cno;
682 int ret;
0d561f12 683 struct the_nilfs *nilfs;
7942b919
KS
684
685 ret = nilfs_construct_segment(inode->i_sb);
686 if (ret < 0)
687 return ret;
688
689 if (argp != NULL) {
0d561f12
JS
690 nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
691 down_read(&nilfs->ns_segctor_sem);
692 cno = nilfs->ns_cno - 1;
693 up_read(&nilfs->ns_segctor_sem);
7942b919
KS
694 if (copy_to_user(argp, &cno, sizeof(cno)))
695 return -EFAULT;
696 }
697 return 0;
698}
699
47eb6b9c
RK
700static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
701 unsigned int cmd, void __user *argp,
83aca8f4 702 size_t membsz,
47eb6b9c
RK
703 ssize_t (*dofunc)(struct the_nilfs *,
704 __u64 *, int,
705 void *, size_t, size_t))
706
707{
708 struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
709 struct nilfs_argv argv;
710 int ret;
711
712 if (copy_from_user(&argv, argp, sizeof(argv)))
713 return -EFAULT;
714
003ff182 715 if (argv.v_size < membsz)
83aca8f4
RK
716 return -EINVAL;
717
47eb6b9c
RK
718 ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);
719 if (ret < 0)
720 return ret;
721
722 if (copy_to_user(argp, &argv, sizeof(argv)))
723 ret = -EFAULT;
724 return ret;
725}
726
7a946193 727long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
7942b919 728{
7a946193 729 struct inode *inode = filp->f_dentry->d_inode;
75323400 730 void __user *argp = (void __user *)arg;
7942b919
KS
731
732 switch (cmd) {
cde98f0f
RK
733 case FS_IOC_GETFLAGS:
734 return nilfs_ioctl_getflags(inode, argp);
735 case FS_IOC_SETFLAGS:
736 return nilfs_ioctl_setflags(inode, filp, argp);
737 case FS_IOC_GETVERSION:
738 return nilfs_ioctl_getversion(inode, argp);
7942b919
KS
739 case NILFS_IOCTL_CHANGE_CPMODE:
740 return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
741 case NILFS_IOCTL_DELETE_CHECKPOINT:
742 return nilfs_ioctl_delete_checkpoint(inode, filp, cmd, argp);
743 case NILFS_IOCTL_GET_CPINFO:
47eb6b9c 744 return nilfs_ioctl_get_info(inode, filp, cmd, argp,
83aca8f4 745 sizeof(struct nilfs_cpinfo),
47eb6b9c 746 nilfs_ioctl_do_get_cpinfo);
7942b919
KS
747 case NILFS_IOCTL_GET_CPSTAT:
748 return nilfs_ioctl_get_cpstat(inode, filp, cmd, argp);
749 case NILFS_IOCTL_GET_SUINFO:
47eb6b9c 750 return nilfs_ioctl_get_info(inode, filp, cmd, argp,
83aca8f4 751 sizeof(struct nilfs_suinfo),
47eb6b9c 752 nilfs_ioctl_do_get_suinfo);
7942b919
KS
753 case NILFS_IOCTL_GET_SUSTAT:
754 return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
755 case NILFS_IOCTL_GET_VINFO:
47eb6b9c 756 return nilfs_ioctl_get_info(inode, filp, cmd, argp,
83aca8f4 757 sizeof(struct nilfs_vinfo),
47eb6b9c 758 nilfs_ioctl_do_get_vinfo);
7942b919
KS
759 case NILFS_IOCTL_GET_BDESCS:
760 return nilfs_ioctl_get_bdescs(inode, filp, cmd, argp);
761 case NILFS_IOCTL_CLEAN_SEGMENTS:
762 return nilfs_ioctl_clean_segments(inode, filp, cmd, argp);
7942b919
KS
763 case NILFS_IOCTL_SYNC:
764 return nilfs_ioctl_sync(inode, filp, cmd, argp);
765 default:
766 return -ENOTTY;
767 }
768}
This page took 0.20997 seconds and 5 git commands to generate.