aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-05-11 14:58:47 -0400
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2009-06-10 10:41:11 -0400
commit003ff182fddde09ddfb8d079bbdb02f9d2122082 (patch)
treed949fa673353767d8854355987e49d82821935f6
parent258ef67e246fd548e7ad91c23004ad157c03cce5 (diff)
nilfs2: allow future expansion of metadata read out via get info ioctl
Nilfs has some ioctl commands to read out metadata from meta data files: - NILFS_IOCTL_GET_CPINFO for checkpoint file, - NILFS_IOCTL_GET_SUINFO for segment usage file, and - NILFS_IOCTL_GET_VINFO for Disk Address Transalation (DAT) file, respectively. Every routine on these metadata files is implemented so that it allows future expansion of on-disk format. But, the above ioctl commands do not support expansion even though nilfs_argv structure can handle arbitrary size for data exchanged via ioctl. This allows future expansion of the following structures which give basic format of the "get information" ioctls: - struct nilfs_cpinfo - struct nilfs_suinfo - struct nilfs_vinfo So, this introduces forward compatility of such ioctl commands. In this patch, a sanity check in nilfs_ioctl_get_info() function is changed to accept larger data structure [1], and metadata read routines are rewritten so that they become compatible for larger structures; the routines will just ignore the remaining fields which the current version of nilfs doesn't know. [1] The ioctl function already has another upper limit (PAGE_SIZE against a structure, which appears in nilfs_ioctl_wrap_copy function), and this will not cause security problem. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
-rw-r--r--fs/nilfs2/cpfile.c33
-rw-r--r--fs/nilfs2/cpfile.h4
-rw-r--r--fs/nilfs2/dat.c21
-rw-r--r--fs/nilfs2/dat.h2
-rw-r--r--fs/nilfs2/ioctl.c9
-rw-r--r--fs/nilfs2/sufile.c22
-rw-r--r--fs/nilfs2/sufile.h2
7 files changed, 54 insertions, 39 deletions
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index 300f1cdfa862..b5a8cd6b474f 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -384,9 +384,10 @@ static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
384} 384}
385 385
386static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop, 386static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
387 struct nilfs_cpinfo *ci, size_t nci) 387 void *buf, unsigned cisz, size_t nci)
388{ 388{
389 struct nilfs_checkpoint *cp; 389 struct nilfs_checkpoint *cp;
390 struct nilfs_cpinfo *ci = buf;
390 struct buffer_head *bh; 391 struct buffer_head *bh;
391 size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; 392 size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
392 __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop; 393 __u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop;
@@ -410,17 +411,22 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
410 kaddr = kmap_atomic(bh->b_page, KM_USER0); 411 kaddr = kmap_atomic(bh->b_page, KM_USER0);
411 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); 412 cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
412 for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) { 413 for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {
413 if (!nilfs_checkpoint_invalid(cp)) 414 if (!nilfs_checkpoint_invalid(cp)) {
414 nilfs_cpfile_checkpoint_to_cpinfo( 415 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,
415 cpfile, cp, &ci[n++]); 416 ci);
417 ci = (void *)ci + cisz;
418 n++;
419 }
416 } 420 }
417 kunmap_atomic(kaddr, KM_USER0); 421 kunmap_atomic(kaddr, KM_USER0);
418 brelse(bh); 422 brelse(bh);
419 } 423 }
420 424
421 ret = n; 425 ret = n;
422 if (n > 0) 426 if (n > 0) {
423 *cnop = ci[n - 1].ci_cno + 1; 427 ci = (void *)ci - cisz;
428 *cnop = ci->ci_cno + 1;
429 }
424 430
425 out: 431 out:
426 up_read(&NILFS_MDT(cpfile)->mi_sem); 432 up_read(&NILFS_MDT(cpfile)->mi_sem);
@@ -428,11 +434,12 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
428} 434}
429 435
430static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, 436static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
431 struct nilfs_cpinfo *ci, size_t nci) 437 void *buf, unsigned cisz, size_t nci)
432{ 438{
433 struct buffer_head *bh; 439 struct buffer_head *bh;
434 struct nilfs_cpfile_header *header; 440 struct nilfs_cpfile_header *header;
435 struct nilfs_checkpoint *cp; 441 struct nilfs_checkpoint *cp;
442 struct nilfs_cpinfo *ci = buf;
436 __u64 curr = *cnop, next; 443 __u64 curr = *cnop, next;
437 unsigned long curr_blkoff, next_blkoff; 444 unsigned long curr_blkoff, next_blkoff;
438 void *kaddr; 445 void *kaddr;
@@ -472,7 +479,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
472 if (unlikely(nilfs_checkpoint_invalid(cp) || 479 if (unlikely(nilfs_checkpoint_invalid(cp) ||
473 !nilfs_checkpoint_snapshot(cp))) 480 !nilfs_checkpoint_snapshot(cp)))
474 break; 481 break;
475 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, &ci[n++]); 482 nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);
483 ci = (void *)ci + cisz;
484 n++;
476 next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); 485 next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
477 if (next == 0) 486 if (next == 0)
478 break; /* reach end of the snapshot list */ 487 break; /* reach end of the snapshot list */
@@ -511,13 +520,13 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
511 */ 520 */
512 521
513ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode, 522ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
514 struct nilfs_cpinfo *ci, size_t nci) 523 void *buf, unsigned cisz, size_t nci)
515{ 524{
516 switch (mode) { 525 switch (mode) {
517 case NILFS_CHECKPOINT: 526 case NILFS_CHECKPOINT:
518 return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, ci, nci); 527 return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);
519 case NILFS_SNAPSHOT: 528 case NILFS_SNAPSHOT:
520 return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, ci, nci); 529 return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);
521 default: 530 default:
522 return -EINVAL; 531 return -EINVAL;
523 } 532 }
@@ -535,7 +544,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
535 ssize_t nci; 544 ssize_t nci;
536 int ret; 545 int ret;
537 546
538 nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, 1); 547 nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);
539 if (nci < 0) 548 if (nci < 0)
540 return nci; 549 return nci;
541 else if (nci == 0 || ci.ci_cno != cno) 550 else if (nci == 0 || ci.ci_cno != cno)
diff --git a/fs/nilfs2/cpfile.h b/fs/nilfs2/cpfile.h
index 1a8a1008c342..788a45950197 100644
--- a/fs/nilfs2/cpfile.h
+++ b/fs/nilfs2/cpfile.h
@@ -39,7 +39,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *, __u64);
39int nilfs_cpfile_change_cpmode(struct inode *, __u64, int); 39int nilfs_cpfile_change_cpmode(struct inode *, __u64, int);
40int nilfs_cpfile_is_snapshot(struct inode *, __u64); 40int nilfs_cpfile_is_snapshot(struct inode *, __u64);
41int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *); 41int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *);
42ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, 42ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, void *, unsigned,
43 struct nilfs_cpinfo *, size_t); 43 size_t);
44 44
45#endif /* _NILFS_CPFILE_H */ 45#endif /* _NILFS_CPFILE_H */
diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c
index 1827f62c772d..0b2710e2d565 100644
--- a/fs/nilfs2/dat.c
+++ b/fs/nilfs2/dat.c
@@ -376,36 +376,37 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp)
376 return ret; 376 return ret;
377} 377}
378 378
379ssize_t nilfs_dat_get_vinfo(struct inode *dat, struct nilfs_vinfo *vinfo, 379ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned visz,
380 size_t nvi) 380 size_t nvi)
381{ 381{
382 struct buffer_head *entry_bh; 382 struct buffer_head *entry_bh;
383 struct nilfs_dat_entry *entry; 383 struct nilfs_dat_entry *entry;
384 struct nilfs_vinfo *vinfo = buf;
384 __u64 first, last; 385 __u64 first, last;
385 void *kaddr; 386 void *kaddr;
386 unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block; 387 unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block;
387 int i, j, n, ret; 388 int i, j, n, ret;
388 389
389 for (i = 0; i < nvi; i += n) { 390 for (i = 0; i < nvi; i += n) {
390 ret = nilfs_palloc_get_entry_block(dat, vinfo[i].vi_vblocknr, 391 ret = nilfs_palloc_get_entry_block(dat, vinfo->vi_vblocknr,
391 0, &entry_bh); 392 0, &entry_bh);
392 if (ret < 0) 393 if (ret < 0)
393 return ret; 394 return ret;
394 kaddr = kmap_atomic(entry_bh->b_page, KM_USER0); 395 kaddr = kmap_atomic(entry_bh->b_page, KM_USER0);
395 /* last virtual block number in this block */ 396 /* last virtual block number in this block */
396 first = vinfo[i].vi_vblocknr; 397 first = vinfo->vi_vblocknr;
397 do_div(first, entries_per_block); 398 do_div(first, entries_per_block);
398 first *= entries_per_block; 399 first *= entries_per_block;
399 last = first + entries_per_block - 1; 400 last = first + entries_per_block - 1;
400 for (j = i, n = 0; 401 for (j = i, n = 0;
401 j < nvi && vinfo[j].vi_vblocknr >= first && 402 j < nvi && vinfo->vi_vblocknr >= first &&
402 vinfo[j].vi_vblocknr <= last; 403 vinfo->vi_vblocknr <= last;
403 j++, n++) { 404 j++, n++, vinfo = (void *)vinfo + visz) {
404 entry = nilfs_palloc_block_get_entry( 405 entry = nilfs_palloc_block_get_entry(
405 dat, vinfo[j].vi_vblocknr, entry_bh, kaddr); 406 dat, vinfo->vi_vblocknr, entry_bh, kaddr);
406 vinfo[j].vi_start = le64_to_cpu(entry->de_start); 407 vinfo->vi_start = le64_to_cpu(entry->de_start);
407 vinfo[j].vi_end = le64_to_cpu(entry->de_end); 408 vinfo->vi_end = le64_to_cpu(entry->de_end);
408 vinfo[j].vi_blocknr = le64_to_cpu(entry->de_blocknr); 409 vinfo->vi_blocknr = le64_to_cpu(entry->de_blocknr);
409 } 410 }
410 kunmap_atomic(kaddr, KM_USER0); 411 kunmap_atomic(kaddr, KM_USER0);
411 brelse(entry_bh); 412 brelse(entry_bh);
diff --git a/fs/nilfs2/dat.h b/fs/nilfs2/dat.h
index d9560654a4b7..d328b81eead4 100644
--- a/fs/nilfs2/dat.h
+++ b/fs/nilfs2/dat.h
@@ -47,6 +47,6 @@ void nilfs_dat_abort_end(struct inode *, struct nilfs_palloc_req *);
47int nilfs_dat_mark_dirty(struct inode *, __u64); 47int nilfs_dat_mark_dirty(struct inode *, __u64);
48int nilfs_dat_freev(struct inode *, __u64 *, size_t); 48int nilfs_dat_freev(struct inode *, __u64 *, size_t);
49int nilfs_dat_move(struct inode *, __u64, sector_t); 49int nilfs_dat_move(struct inode *, __u64, sector_t);
50ssize_t nilfs_dat_get_vinfo(struct inode *, struct nilfs_vinfo *, size_t); 50ssize_t nilfs_dat_get_vinfo(struct inode *, void *, unsigned, size_t);
51 51
52#endif /* _NILFS_DAT_H */ 52#endif /* _NILFS_DAT_H */
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index bdad7e4980b0..6ea5f872e2de 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -152,7 +152,7 @@ nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
152 152
153 down_read(&nilfs->ns_segctor_sem); 153 down_read(&nilfs->ns_segctor_sem);
154 ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, 154 ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
155 nmembs); 155 size, nmembs);
156 up_read(&nilfs->ns_segctor_sem); 156 up_read(&nilfs->ns_segctor_sem);
157 return ret; 157 return ret;
158} 158}
@@ -182,7 +182,8 @@ nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
182 int ret; 182 int ret;
183 183
184 down_read(&nilfs->ns_segctor_sem); 184 down_read(&nilfs->ns_segctor_sem);
185 ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); 185 ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
186 nmembs);
186 up_read(&nilfs->ns_segctor_sem); 187 up_read(&nilfs->ns_segctor_sem);
187 return ret; 188 return ret;
188} 189}
@@ -212,7 +213,7 @@ nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
212 int ret; 213 int ret;
213 214
214 down_read(&nilfs->ns_segctor_sem); 215 down_read(&nilfs->ns_segctor_sem);
215 ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); 216 ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, size, nmembs);
216 up_read(&nilfs->ns_segctor_sem); 217 up_read(&nilfs->ns_segctor_sem);
217 return ret; 218 return ret;
218} 219}
@@ -589,7 +590,7 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
589 if (copy_from_user(&argv, argp, sizeof(argv))) 590 if (copy_from_user(&argv, argp, sizeof(argv)))
590 return -EFAULT; 591 return -EFAULT;
591 592
592 if (argv.v_size != membsz) 593 if (argv.v_size < membsz)
593 return -EINVAL; 594 return -EINVAL;
594 595
595 ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); 596 ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
index da127325fdaa..37994d4a59cc 100644
--- a/fs/nilfs2/sufile.c
+++ b/fs/nilfs2/sufile.c
@@ -587,7 +587,8 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
587 * nilfs_sufile_get_suinfo - 587 * nilfs_sufile_get_suinfo -
588 * @sufile: inode of segment usage file 588 * @sufile: inode of segment usage file
589 * @segnum: segment number to start looking 589 * @segnum: segment number to start looking
590 * @si: array of suinfo 590 * @buf: array of suinfo
591 * @sisz: byte size of suinfo
591 * @nsi: size of suinfo array 592 * @nsi: size of suinfo array
592 * 593 *
593 * Description: 594 * Description:
@@ -599,11 +600,12 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
599 * 600 *
600 * %-ENOMEM - Insufficient amount of memory available. 601 * %-ENOMEM - Insufficient amount of memory available.
601 */ 602 */
602ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, 603ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
603 struct nilfs_suinfo *si, size_t nsi) 604 unsigned sisz, size_t nsi)
604{ 605{
605 struct buffer_head *su_bh; 606 struct buffer_head *su_bh;
606 struct nilfs_segment_usage *su; 607 struct nilfs_segment_usage *su;
608 struct nilfs_suinfo *si = buf;
607 size_t susz = NILFS_MDT(sufile)->mi_entry_size; 609 size_t susz = NILFS_MDT(sufile)->mi_entry_size;
608 struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs; 610 struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs;
609 void *kaddr; 611 void *kaddr;
@@ -628,20 +630,22 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum,
628 if (ret != -ENOENT) 630 if (ret != -ENOENT)
629 goto out; 631 goto out;
630 /* hole */ 632 /* hole */
631 memset(&si[i], 0, sizeof(struct nilfs_suinfo) * n); 633 memset(si, 0, sisz * n);
634 si = (void *)si + sisz * n;
632 continue; 635 continue;
633 } 636 }
634 637
635 kaddr = kmap_atomic(su_bh->b_page, KM_USER0); 638 kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
636 su = nilfs_sufile_block_get_segment_usage( 639 su = nilfs_sufile_block_get_segment_usage(
637 sufile, segnum, su_bh, kaddr); 640 sufile, segnum, su_bh, kaddr);
638 for (j = 0; j < n; j++, su = (void *)su + susz) { 641 for (j = 0; j < n;
639 si[i + j].sui_lastmod = le64_to_cpu(su->su_lastmod); 642 j++, su = (void *)su + susz, si = (void *)si + sisz) {
640 si[i + j].sui_nblocks = le32_to_cpu(su->su_nblocks); 643 si->sui_lastmod = le64_to_cpu(su->su_lastmod);
641 si[i + j].sui_flags = le32_to_cpu(su->su_flags) & 644 si->sui_nblocks = le32_to_cpu(su->su_nblocks);
645 si->sui_flags = le32_to_cpu(su->su_flags) &
642 ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE); 646 ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
643 if (nilfs_segment_is_active(nilfs, segnum + j)) 647 if (nilfs_segment_is_active(nilfs, segnum + j))
644 si[i + j].sui_flags |= 648 si->sui_flags |=
645 (1UL << NILFS_SEGMENT_USAGE_ACTIVE); 649 (1UL << NILFS_SEGMENT_USAGE_ACTIVE);
646 } 650 }
647 kunmap_atomic(kaddr, KM_USER0); 651 kunmap_atomic(kaddr, KM_USER0);
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index fd6232e365ca..a2c4d76c3366 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -43,7 +43,7 @@ void nilfs_sufile_put_segment_usage(struct inode *, __u64,
43 struct buffer_head *); 43 struct buffer_head *);
44int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *); 44int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
45int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *); 45int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *);
46ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, struct nilfs_suinfo *, 46ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
47 size_t); 47 size_t);
48 48
49int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *, 49int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *,