Commit 003ff182 authored by Ryusuke Konishi's avatar Ryusuke Konishi

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: default avatarRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
parent 258ef67e
...@@ -384,9 +384,10 @@ static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile, ...@@ -384,9 +384,10 @@ static void nilfs_cpfile_checkpoint_to_cpinfo(struct inode *cpfile,
} }
static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop, static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
struct nilfs_cpinfo *ci, size_t nci) void *buf, unsigned cisz, size_t nci)
{ {
struct nilfs_checkpoint *cp; struct nilfs_checkpoint *cp;
struct nilfs_cpinfo *ci = buf;
struct buffer_head *bh; struct buffer_head *bh;
size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size; size_t cpsz = NILFS_MDT(cpfile)->mi_entry_size;
__u64 cur_cno = nilfs_mdt_cno(cpfile), cno = *cnop; __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,17 +411,22 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
kaddr = kmap_atomic(bh->b_page, KM_USER0); kaddr = kmap_atomic(bh->b_page, KM_USER0);
cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr); cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) { for (i = 0; i < ncps && n < nci; i++, cp = (void *)cp + cpsz) {
if (!nilfs_checkpoint_invalid(cp)) if (!nilfs_checkpoint_invalid(cp)) {
nilfs_cpfile_checkpoint_to_cpinfo( nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp,
cpfile, cp, &ci[n++]); ci);
ci = (void *)ci + cisz;
n++;
}
} }
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
brelse(bh); brelse(bh);
} }
ret = n; ret = n;
if (n > 0) if (n > 0) {
*cnop = ci[n - 1].ci_cno + 1; ci = (void *)ci - cisz;
*cnop = ci->ci_cno + 1;
}
out: out:
up_read(&NILFS_MDT(cpfile)->mi_sem); 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,11 +434,12 @@ static ssize_t nilfs_cpfile_do_get_cpinfo(struct inode *cpfile, __u64 *cnop,
} }
static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
struct nilfs_cpinfo *ci, size_t nci) void *buf, unsigned cisz, size_t nci)
{ {
struct buffer_head *bh; struct buffer_head *bh;
struct nilfs_cpfile_header *header; struct nilfs_cpfile_header *header;
struct nilfs_checkpoint *cp; struct nilfs_checkpoint *cp;
struct nilfs_cpinfo *ci = buf;
__u64 curr = *cnop, next; __u64 curr = *cnop, next;
unsigned long curr_blkoff, next_blkoff; unsigned long curr_blkoff, next_blkoff;
void *kaddr; void *kaddr;
...@@ -472,7 +479,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop, ...@@ -472,7 +479,9 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
if (unlikely(nilfs_checkpoint_invalid(cp) || if (unlikely(nilfs_checkpoint_invalid(cp) ||
!nilfs_checkpoint_snapshot(cp))) !nilfs_checkpoint_snapshot(cp)))
break; break;
nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, &ci[n++]); nilfs_cpfile_checkpoint_to_cpinfo(cpfile, cp, ci);
ci = (void *)ci + cisz;
n++;
next = le64_to_cpu(cp->cp_snapshot_list.ssl_next); next = le64_to_cpu(cp->cp_snapshot_list.ssl_next);
if (next == 0) if (next == 0)
break; /* reach end of the snapshot list */ 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,13 +520,13 @@ static ssize_t nilfs_cpfile_do_get_ssinfo(struct inode *cpfile, __u64 *cnop,
*/ */
ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode, ssize_t nilfs_cpfile_get_cpinfo(struct inode *cpfile, __u64 *cnop, int mode,
struct nilfs_cpinfo *ci, size_t nci) void *buf, unsigned cisz, size_t nci)
{ {
switch (mode) { switch (mode) {
case NILFS_CHECKPOINT: case NILFS_CHECKPOINT:
return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, ci, nci); return nilfs_cpfile_do_get_cpinfo(cpfile, cnop, buf, cisz, nci);
case NILFS_SNAPSHOT: case NILFS_SNAPSHOT:
return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, ci, nci); return nilfs_cpfile_do_get_ssinfo(cpfile, cnop, buf, cisz, nci);
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -535,7 +544,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno) ...@@ -535,7 +544,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *cpfile, __u64 cno)
ssize_t nci; ssize_t nci;
int ret; int ret;
nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, 1); nci = nilfs_cpfile_do_get_cpinfo(cpfile, &tcno, &ci, sizeof(ci), 1);
if (nci < 0) if (nci < 0)
return nci; return nci;
else if (nci == 0 || ci.ci_cno != cno) else if (nci == 0 || ci.ci_cno != cno)
......
...@@ -39,7 +39,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *, __u64); ...@@ -39,7 +39,7 @@ int nilfs_cpfile_delete_checkpoint(struct inode *, __u64);
int nilfs_cpfile_change_cpmode(struct inode *, __u64, int); int nilfs_cpfile_change_cpmode(struct inode *, __u64, int);
int nilfs_cpfile_is_snapshot(struct inode *, __u64); int nilfs_cpfile_is_snapshot(struct inode *, __u64);
int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *); int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *);
ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, void *, unsigned,
struct nilfs_cpinfo *, size_t); size_t);
#endif /* _NILFS_CPFILE_H */ #endif /* _NILFS_CPFILE_H */
...@@ -376,36 +376,37 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) ...@@ -376,36 +376,37 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp)
return ret; return ret;
} }
ssize_t nilfs_dat_get_vinfo(struct inode *dat, struct nilfs_vinfo *vinfo, ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned visz,
size_t nvi) size_t nvi)
{ {
struct buffer_head *entry_bh; struct buffer_head *entry_bh;
struct nilfs_dat_entry *entry; struct nilfs_dat_entry *entry;
struct nilfs_vinfo *vinfo = buf;
__u64 first, last; __u64 first, last;
void *kaddr; void *kaddr;
unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block; unsigned long entries_per_block = NILFS_MDT(dat)->mi_entries_per_block;
int i, j, n, ret; int i, j, n, ret;
for (i = 0; i < nvi; i += n) { for (i = 0; i < nvi; i += n) {
ret = nilfs_palloc_get_entry_block(dat, vinfo[i].vi_vblocknr, ret = nilfs_palloc_get_entry_block(dat, vinfo->vi_vblocknr,
0, &entry_bh); 0, &entry_bh);
if (ret < 0) if (ret < 0)
return ret; return ret;
kaddr = kmap_atomic(entry_bh->b_page, KM_USER0); kaddr = kmap_atomic(entry_bh->b_page, KM_USER0);
/* last virtual block number in this block */ /* last virtual block number in this block */
first = vinfo[i].vi_vblocknr; first = vinfo->vi_vblocknr;
do_div(first, entries_per_block); do_div(first, entries_per_block);
first *= entries_per_block; first *= entries_per_block;
last = first + entries_per_block - 1; last = first + entries_per_block - 1;
for (j = i, n = 0; for (j = i, n = 0;
j < nvi && vinfo[j].vi_vblocknr >= first && j < nvi && vinfo->vi_vblocknr >= first &&
vinfo[j].vi_vblocknr <= last; vinfo->vi_vblocknr <= last;
j++, n++) { j++, n++, vinfo = (void *)vinfo + visz) {
entry = nilfs_palloc_block_get_entry( entry = nilfs_palloc_block_get_entry(
dat, vinfo[j].vi_vblocknr, entry_bh, kaddr); dat, vinfo->vi_vblocknr, entry_bh, kaddr);
vinfo[j].vi_start = le64_to_cpu(entry->de_start); vinfo->vi_start = le64_to_cpu(entry->de_start);
vinfo[j].vi_end = le64_to_cpu(entry->de_end); vinfo->vi_end = le64_to_cpu(entry->de_end);
vinfo[j].vi_blocknr = le64_to_cpu(entry->de_blocknr); vinfo->vi_blocknr = le64_to_cpu(entry->de_blocknr);
} }
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
brelse(entry_bh); brelse(entry_bh);
......
...@@ -47,6 +47,6 @@ void nilfs_dat_abort_end(struct inode *, struct nilfs_palloc_req *); ...@@ -47,6 +47,6 @@ void nilfs_dat_abort_end(struct inode *, struct nilfs_palloc_req *);
int nilfs_dat_mark_dirty(struct inode *, __u64); int nilfs_dat_mark_dirty(struct inode *, __u64);
int nilfs_dat_freev(struct inode *, __u64 *, size_t); int nilfs_dat_freev(struct inode *, __u64 *, size_t);
int nilfs_dat_move(struct inode *, __u64, sector_t); int nilfs_dat_move(struct inode *, __u64, sector_t);
ssize_t nilfs_dat_get_vinfo(struct inode *, struct nilfs_vinfo *, size_t); ssize_t nilfs_dat_get_vinfo(struct inode *, void *, unsigned, size_t);
#endif /* _NILFS_DAT_H */ #endif /* _NILFS_DAT_H */
...@@ -152,7 +152,7 @@ nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, ...@@ -152,7 +152,7 @@ nilfs_ioctl_do_get_cpinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
down_read(&nilfs->ns_segctor_sem); down_read(&nilfs->ns_segctor_sem);
ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf, ret = nilfs_cpfile_get_cpinfo(nilfs->ns_cpfile, posp, flags, buf,
nmembs); size, nmembs);
up_read(&nilfs->ns_segctor_sem); up_read(&nilfs->ns_segctor_sem);
return ret; return ret;
} }
...@@ -182,7 +182,8 @@ nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, ...@@ -182,7 +182,8 @@ nilfs_ioctl_do_get_suinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
int ret; int ret;
down_read(&nilfs->ns_segctor_sem); down_read(&nilfs->ns_segctor_sem);
ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, nmembs); ret = nilfs_sufile_get_suinfo(nilfs->ns_sufile, *posp, buf, size,
nmembs);
up_read(&nilfs->ns_segctor_sem); up_read(&nilfs->ns_segctor_sem);
return ret; return ret;
} }
...@@ -212,7 +213,7 @@ nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags, ...@@ -212,7 +213,7 @@ nilfs_ioctl_do_get_vinfo(struct the_nilfs *nilfs, __u64 *posp, int flags,
int ret; int ret;
down_read(&nilfs->ns_segctor_sem); down_read(&nilfs->ns_segctor_sem);
ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, nmembs); ret = nilfs_dat_get_vinfo(nilfs_dat_inode(nilfs), buf, size, nmembs);
up_read(&nilfs->ns_segctor_sem); up_read(&nilfs->ns_segctor_sem);
return ret; return ret;
} }
...@@ -589,7 +590,7 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp, ...@@ -589,7 +590,7 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
if (copy_from_user(&argv, argp, sizeof(argv))) if (copy_from_user(&argv, argp, sizeof(argv)))
return -EFAULT; return -EFAULT;
if (argv.v_size != membsz) if (argv.v_size < membsz)
return -EINVAL; return -EINVAL;
ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc); ret = nilfs_ioctl_wrap_copy(nilfs, &argv, _IOC_DIR(cmd), dofunc);
......
...@@ -587,7 +587,8 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum, ...@@ -587,7 +587,8 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
* nilfs_sufile_get_suinfo - * nilfs_sufile_get_suinfo -
* @sufile: inode of segment usage file * @sufile: inode of segment usage file
* @segnum: segment number to start looking * @segnum: segment number to start looking
* @si: array of suinfo * @buf: array of suinfo
* @sisz: byte size of suinfo
* @nsi: size of suinfo array * @nsi: size of suinfo array
* *
* Description: * Description:
...@@ -599,11 +600,12 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum, ...@@ -599,11 +600,12 @@ void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
* *
* %-ENOMEM - Insufficient amount of memory available. * %-ENOMEM - Insufficient amount of memory available.
*/ */
ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
struct nilfs_suinfo *si, size_t nsi) unsigned sisz, size_t nsi)
{ {
struct buffer_head *su_bh; struct buffer_head *su_bh;
struct nilfs_segment_usage *su; struct nilfs_segment_usage *su;
struct nilfs_suinfo *si = buf;
size_t susz = NILFS_MDT(sufile)->mi_entry_size; size_t susz = NILFS_MDT(sufile)->mi_entry_size;
struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs; struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs;
void *kaddr; void *kaddr;
...@@ -628,20 +630,22 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, ...@@ -628,20 +630,22 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum,
if (ret != -ENOENT) if (ret != -ENOENT)
goto out; goto out;
/* hole */ /* hole */
memset(&si[i], 0, sizeof(struct nilfs_suinfo) * n); memset(si, 0, sisz * n);
si = (void *)si + sisz * n;
continue; continue;
} }
kaddr = kmap_atomic(su_bh->b_page, KM_USER0); kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
su = nilfs_sufile_block_get_segment_usage( su = nilfs_sufile_block_get_segment_usage(
sufile, segnum, su_bh, kaddr); sufile, segnum, su_bh, kaddr);
for (j = 0; j < n; j++, su = (void *)su + susz) { for (j = 0; j < n;
si[i + j].sui_lastmod = le64_to_cpu(su->su_lastmod); j++, su = (void *)su + susz, si = (void *)si + sisz) {
si[i + j].sui_nblocks = le32_to_cpu(su->su_nblocks); si->sui_lastmod = le64_to_cpu(su->su_lastmod);
si[i + j].sui_flags = le32_to_cpu(su->su_flags) & si->sui_nblocks = le32_to_cpu(su->su_nblocks);
si->sui_flags = le32_to_cpu(su->su_flags) &
~(1UL << NILFS_SEGMENT_USAGE_ACTIVE); ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
if (nilfs_segment_is_active(nilfs, segnum + j)) if (nilfs_segment_is_active(nilfs, segnum + j))
si[i + j].sui_flags |= si->sui_flags |=
(1UL << NILFS_SEGMENT_USAGE_ACTIVE); (1UL << NILFS_SEGMENT_USAGE_ACTIVE);
} }
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
......
...@@ -43,7 +43,7 @@ void nilfs_sufile_put_segment_usage(struct inode *, __u64, ...@@ -43,7 +43,7 @@ void nilfs_sufile_put_segment_usage(struct inode *, __u64,
struct buffer_head *); struct buffer_head *);
int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *); int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *); int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *);
ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, struct nilfs_suinfo *, ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
size_t); size_t);
int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *, int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment