Skip to content

Commit

Permalink
nilfs2: fix timing issue between rmcp and chcp ioctls
Browse files Browse the repository at this point in the history
The checkpoint deletion ioctl (rmcp ioctl) has potential for breaking
snapshot because it is not fully exclusive with checkpoint mode change
ioctl (chcp ioctl).

The rmcp ioctl first tests if the specified checkpoint is a snapshot or
not within nilfs_cpfile_delete_checkpoint function, and then calls
nilfs_cpfile_delete_checkpoints function to actually invalidate the
checkpoint only if it's not a snapshot.  However, the checkpoint can be
changed into a snapshot by the chcp ioctl between these two operations.
In that case, calling nilfs_cpfile_delete_checkpoints() wrongly
invalidates the snapshot, which leads to snapshot list corruption and
snapshot count mismatch.

This fixes the issue by changing nilfs_cpfile_delete_checkpoints() so
that it reconfirms the target checkpoints are snapshot or not.

This second check is exclusive with the chcp operation since it is
protected by an existing semaphore.

Signed-off-by: Ryusuke Konishi <[email protected]>
Cc: Fernando Luis Vazquez Cao <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
konis authored and torvalds committed Jul 31, 2012
1 parent 278038a commit fe0627e
Showing 1 changed file with 7 additions and 3 deletions.
10 changes: 7 additions & 3 deletions fs/nilfs2/cpfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
__u64 cno;
void *kaddr;
unsigned long tnicps;
int ret, ncps, nicps, count, i;
int ret, ncps, nicps, nss, count, i;

if (unlikely(start == 0 || start > end)) {
printk(KERN_ERR "%s: invalid range of checkpoint numbers: "
Expand All @@ -301,6 +301,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
if (ret < 0)
goto out_sem;
tnicps = 0;
nss = 0;

for (cno = start; cno < end; cno += ncps) {
ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
Expand All @@ -318,8 +319,9 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
cpfile, cno, cp_bh, kaddr);
nicps = 0;
for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
WARN_ON(nilfs_checkpoint_snapshot(cp));
if (!nilfs_checkpoint_invalid(cp)) {
if (nilfs_checkpoint_snapshot(cp)) {
nss++;
} else if (!nilfs_checkpoint_invalid(cp)) {
nilfs_checkpoint_set_invalid(cp);
nicps++;
}
Expand Down Expand Up @@ -364,6 +366,8 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
}

brelse(header_bh);
if (nss > 0)
ret = -EBUSY;

out_sem:
up_write(&NILFS_MDT(cpfile)->mi_sem);
Expand Down

0 comments on commit fe0627e

Please sign in to comment.