bcachefs: Snapshot deletion fix
authorKent Overstreet <kent.overstreet@gmail.com>
Fri, 1 Oct 2021 00:09:08 +0000 (20:09 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:13 +0000 (17:09 -0400)
When we delete a snapshot, we unlink the inode but we don't want to run
the inode_rm path - the unlink path deletes the subvolume directly,
which does everything we need. Also allowing the inode_rm path to run
was getting us "missing subvolume" errors.

There's still another bug with snapshot deletion: we need to make
snapshot deletion a multi stage process, where we unlink the root
dentry, then tear down the page cache, then delete the snapshot.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/fs-common.c

index 00c7ba17f6c8fe9a098afe8921b813e1ac5ff767..c49de741e1e3b156bf603b33a8d74eafdb879262 100644 (file)
@@ -267,18 +267,33 @@ int bch2_unlink_trans(struct btree_trans *trans,
        if (ret)
                goto err;
 
-       if (deleting_snapshot == 1 && !inode_u->bi_subvol) {
-               ret = -ENOENT;
-               goto err;
-       }
-
        if (deleting_snapshot <= 0 && S_ISDIR(inode_u->bi_mode)) {
                ret = bch2_empty_dir_trans(trans, inum);
                if (ret)
                        goto err;
        }
 
-       if (inode_u->bi_subvol) {
+       if (deleting_snapshot < 0 &&
+           inode_u->bi_subvol) {
+               struct bch_subvolume s;
+
+               ret = bch2_subvolume_get(trans, inode_u->bi_subvol, true,
+                                        BTREE_ITER_CACHED|
+                                        BTREE_ITER_WITH_UPDATES,
+                                        &s);
+               if (ret)
+                       goto err;
+
+               if (BCH_SUBVOLUME_SNAP(&s))
+                       deleting_snapshot = 1;
+       }
+
+       if (deleting_snapshot == 1) {
+               if (!inode_u->bi_subvol) {
+                       ret = -ENOENT;
+                       goto err;
+               }
+
                ret = bch2_subvolume_delete(trans, inode_u->bi_subvol,
                                            deleting_snapshot);
                if (ret)
@@ -297,6 +312,8 @@ int bch2_unlink_trans(struct btree_trans *trans,
                ret = bch2_btree_iter_traverse(&dirent_iter);
                if (ret)
                        goto err;
+       } else {
+               bch2_inode_nlink_dec(inode_u);
        }
 
        if (inode_u->bi_dir             == dirent_iter.pos.inode &&
@@ -307,7 +324,6 @@ int bch2_unlink_trans(struct btree_trans *trans,
 
        dir_u->bi_mtime = dir_u->bi_ctime = inode_u->bi_ctime = now;
        dir_u->bi_nlink -= is_subdir_for_nlink(inode_u);
-       bch2_inode_nlink_dec(inode_u);
 
        ret =   bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
                                    &dir_hash, &dirent_iter,