aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2
diff options
context:
space:
mode:
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2012-07-30 17:42:05 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 20:25:19 -0400
commitfe0627e7b3d32a41c16fac6e0af091991545865e (patch)
treed2b358827dff033691a5ce145334f7e3f7b0c5bf /fs/nilfs2
parent278038ac53c6c4f53d1d34f978beb9aba1410b2c (diff)
nilfs2: fix timing issue between rmcp and chcp ioctls
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 <konishi.ryusuke@lab.ntt.co.jp> Cc: Fernando Luis Vazquez Cao <fernando@oss.ntt.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r--fs/nilfs2/cpfile.c10
1 files changed, 7 insertions, 3 deletions
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index dab5c4c6dfaf..deaa3d33a0aa 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -286,7 +286,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
286 __u64 cno; 286 __u64 cno;
287 void *kaddr; 287 void *kaddr;
288 unsigned long tnicps; 288 unsigned long tnicps;
289 int ret, ncps, nicps, count, i; 289 int ret, ncps, nicps, nss, count, i;
290 290
291 if (unlikely(start == 0 || start > end)) { 291 if (unlikely(start == 0 || start > end)) {
292 printk(KERN_ERR "%s: invalid range of checkpoint numbers: " 292 printk(KERN_ERR "%s: invalid range of checkpoint numbers: "
@@ -301,6 +301,7 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
301 if (ret < 0) 301 if (ret < 0)
302 goto out_sem; 302 goto out_sem;
303 tnicps = 0; 303 tnicps = 0;
304 nss = 0;
304 305
305 for (cno = start; cno < end; cno += ncps) { 306 for (cno = start; cno < end; cno += ncps) {
306 ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end); 307 ncps = nilfs_cpfile_checkpoints_in_block(cpfile, cno, end);
@@ -318,8 +319,9 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
318 cpfile, cno, cp_bh, kaddr); 319 cpfile, cno, cp_bh, kaddr);
319 nicps = 0; 320 nicps = 0;
320 for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) { 321 for (i = 0; i < ncps; i++, cp = (void *)cp + cpsz) {
321 WARN_ON(nilfs_checkpoint_snapshot(cp)); 322 if (nilfs_checkpoint_snapshot(cp)) {
322 if (!nilfs_checkpoint_invalid(cp)) { 323 nss++;
324 } else if (!nilfs_checkpoint_invalid(cp)) {
323 nilfs_checkpoint_set_invalid(cp); 325 nilfs_checkpoint_set_invalid(cp);
324 nicps++; 326 nicps++;
325 } 327 }
@@ -364,6 +366,8 @@ int nilfs_cpfile_delete_checkpoints(struct inode *cpfile,
364 } 366 }
365 367
366 brelse(header_bh); 368 brelse(header_bh);
369 if (nss > 0)
370 ret = -EBUSY;
367 371
368 out_sem: 372 out_sem:
369 up_write(&NILFS_MDT(cpfile)->mi_sem); 373 up_write(&NILFS_MDT(cpfile)->mi_sem);