Btrfs: rework qgroup accounting
[linux-2.6-block.git] / fs / btrfs / ioctl.c
index 242a37cd26b20b1e457d4cc58448db29f68e7b66..a21a4ac537b768842c3ce150428c23621e63c688 100644 (file)
@@ -58,6 +58,7 @@
 #include "dev-replace.h"
 #include "props.h"
 #include "sysfs.h"
+#include "qgroup.h"
 
 #ifdef CONFIG_64BIT
 /* If we have a 32-bit userspace and 64-bit kernel, then the UAPI
@@ -2941,6 +2942,41 @@ out:
        return ret;
 }
 
+/* Helper to check and see if this root currently has a ref on the given disk
+ * bytenr.  If it does then we need to update the quota for this root.  This
+ * doesn't do anything if quotas aren't enabled.
+ */
+static int check_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+                    u64 disko)
+{
+       struct seq_list tree_mod_seq_elem = {};
+       struct ulist *roots;
+       struct ulist_iterator uiter;
+       struct ulist_node *root_node = NULL;
+       int ret;
+
+       if (!root->fs_info->quota_enabled)
+               return 1;
+
+       btrfs_get_tree_mod_seq(root->fs_info, &tree_mod_seq_elem);
+       ret = btrfs_find_all_roots(trans, root->fs_info, disko,
+                                  tree_mod_seq_elem.seq, &roots);
+       if (ret < 0)
+               goto out;
+       ret = 0;
+       ULIST_ITER_INIT(&uiter);
+       while ((root_node = ulist_next(roots, &uiter))) {
+               if (root_node->val == root->objectid) {
+                       ret = 1;
+                       break;
+               }
+       }
+       ulist_free(roots);
+out:
+       btrfs_put_tree_mod_seq(root->fs_info, &tree_mod_seq_elem);
+       return ret;
+}
+
 /**
  * btrfs_clone() - clone a range from inode file to another
  *
@@ -2964,7 +3000,9 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
        u32 nritems;
        int slot;
        int ret;
+       int no_quota;
        u64 len = olen_aligned;
+       u64 last_disko = 0;
 
        ret = -ENOMEM;
        buf = vmalloc(btrfs_level_size(root, 0));
@@ -2996,6 +3034,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
 
                nritems = btrfs_header_nritems(path->nodes[0]);
 process_slot:
+               no_quota = 1;
                if (path->slots[0] >= nritems) {
                        ret = btrfs_next_leaf(BTRFS_I(src)->root, path);
                        if (ret < 0)
@@ -3128,6 +3167,28 @@ process_slot:
                                                             datao);
                                btrfs_set_file_extent_num_bytes(leaf, extent,
                                                                datal);
+
+                               /*
+                                * We need to look up the roots that point at
+                                * this bytenr and see if the new root does.  If
+                                * it does not we need to make sure we update
+                                * quotas appropriately.
+                                */
+                               if (disko && root != BTRFS_I(src)->root &&
+                                   disko != last_disko) {
+                                       no_quota = check_ref(trans, root,
+                                                            disko);
+                                       if (no_quota < 0) {
+                                               btrfs_abort_transaction(trans,
+                                                                       root,
+                                                                       ret);
+                                               btrfs_end_transaction(trans,
+                                                                     root);
+                                               ret = no_quota;
+                                               goto out;
+                                       }
+                               }
+
                                if (disko) {
                                        inode_add_bytes(inode, datal);
                                        ret = btrfs_inc_extent_ref(trans, root,
@@ -3135,7 +3196,7 @@ process_slot:
                                                        root->root_key.objectid,
                                                        btrfs_ino(inode),
                                                        new_key.offset - datao,
-                                                       0);
+                                                       no_quota);
                                        if (ret) {
                                                btrfs_abort_transaction(trans,
                                                                        root,