@@ -257,7 +257,7 @@ convert_objects = convert/main.o convert/common.o convert/source-fs.o \
mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o
image_objects = image/main.o image/sanitize.o
tune_objects = tune/main.o tune/seeding.o tune/change-uuid.o tune/change-metadata-uuid.o \
- tune/convert-bgt.o tune/change-csum.o check/clear-cache.o
+ tune/convert-bgt.o tune/change-csum.o tune/quota.o check/clear-cache.o
all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
$(mkfs_objects) $(image_objects) $(tune_objects) $(libbtrfsutil_objects)
@@ -103,6 +103,7 @@ static const char * const tune_usage[] = {
OPTLINE("-x", "enable skinny metadata extent refs (mkfs: skinny-metadata)"),
OPTLINE("-n", "enable no-holes feature (mkfs: no-holes, more efficient sparse file representation)"),
OPTLINE("-S <0|1>", "set/unset seeding status of a device"),
+ OPTLINE("-q", "enable simple quotas on the file system. (mkfs: squota)"),
OPTLINE("--convert-to-block-group-tree", "convert filesystem to track block groups in "
"the separate block-group-tree instead of extent tree (sets the incompat bit)"),
OPTLINE("--convert-from-block-group-tree",
@@ -147,6 +148,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
char *new_fsid_str = NULL;
int ret;
u64 super_flags = 0;
+ int quota = 0;
int fd = -1;
btrfs_config_init();
@@ -169,7 +171,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
#endif
{ NULL, 0, NULL, 0 }
};
- int c = getopt_long(argc, argv, "S:rxfuU:nmM:", long_options, NULL);
+ int c = getopt_long(argc, argv, "S:rxqfuU:nmM:", long_options, NULL);
if (c < 0)
break;
@@ -184,6 +186,9 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
case 'x':
super_flags |= BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
break;
+ case 'q':
+ quota = 1;
+ break;
case 'n':
super_flags |= BTRFS_FEATURE_INCOMPAT_NO_HOLES;
break;
@@ -241,7 +246,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
}
if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str) &&
!change_metadata_uuid && csum_type == -1 && !to_bg_tree &&
- !to_extent_tree && !to_fst) {
+ !to_extent_tree && !to_fst && !quota) {
error("at least one option should be specified");
usage(&tune_cmd, 1);
return 1;
@@ -420,6 +425,10 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
total++;
}
+ if (quota) {
+ ret = enable_quota(root->fs_info, true);
+ }
+
if (success == total) {
ret = 0;
} else {
new file mode 100644
@@ -0,0 +1,172 @@
+#include <errno.h>
+
+#include "common/messages.h"
+#include "kernel-shared/ctree.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/transaction.h"
+#include "kernel-shared/uapi/btrfs_tree.h"
+#include "tune/tune.h"
+
+static int create_qgroup(struct btrfs_fs_info *fs_info,
+ struct btrfs_trans_handle *trans,
+ u64 qgroupid)
+{
+ struct btrfs_path path;
+ struct btrfs_root *quota_root = fs_info->quota_root;
+ struct btrfs_key key;
+ int ret;
+
+ if (qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT) {
+ error("qgroup level other than 0 is not supported yet");
+ return -ENOTTY;
+ }
+
+ key.objectid = 0;
+ key.type = BTRFS_QGROUP_INFO_KEY;
+ key.offset = qgroupid;
+
+ btrfs_init_path(&path);
+ ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+ sizeof(struct btrfs_qgroup_info_item));
+ btrfs_release_path(&path);
+ if (ret < 0)
+ return ret;
+
+ key.objectid = 0;
+ key.type = BTRFS_QGROUP_LIMIT_KEY;
+ key.offset = qgroupid;
+ ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+ sizeof(struct btrfs_qgroup_limit_item));
+ btrfs_release_path(&path);
+
+ printf("created qgroup for %llu\n", qgroupid);
+ return ret;
+}
+
+static int create_qgroups(struct btrfs_fs_info *fs_info,
+ struct btrfs_trans_handle *trans)
+{
+ struct btrfs_key key = {
+ .objectid = 0,
+ .type = BTRFS_ROOT_REF_KEY,
+ .offset = 0,
+ };
+ struct btrfs_path path;
+ struct extent_buffer *leaf;
+ int slot;
+ struct btrfs_root *tree_root = fs_info->tree_root;
+ int ret;
+
+
+ ret = create_qgroup(fs_info, trans, BTRFS_FS_TREE_OBJECTID);
+ if (ret)
+ goto out;
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot_for_read(tree_root, &key, &path, 1, 0);
+ if (ret)
+ goto out;
+
+ while (1) {
+ slot = path.slots[0];
+ leaf = path.nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.type == BTRFS_ROOT_REF_KEY) {
+ ret = create_qgroup(fs_info, trans, key.offset);
+ if (ret)
+ goto out;
+ }
+ ret = btrfs_next_item(tree_root, &path);
+ if (ret < 0) {
+ error("failed to advance to next item");
+ goto out;
+ }
+ if (ret)
+ break;
+ }
+
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+int enable_quota(struct btrfs_fs_info *fs_info, bool simple)
+{
+ struct btrfs_super_block *sb = fs_info->super_copy;
+ struct btrfs_trans_handle *trans;
+ int super_flags = btrfs_super_incompat_flags(sb);
+ struct btrfs_qgroup_status_item *qsi;
+ struct btrfs_root *quota_root;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int flags;
+ int ret;
+
+ trans = btrfs_start_transaction(fs_info->tree_root, 2);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ errno = -ret;
+ error_msg(ERROR_MSG_START_TRANS, "%m");
+ return ret;
+ }
+
+ ret = btrfs_create_root(trans, fs_info, BTRFS_QUOTA_TREE_OBJECTID);
+ if (ret < 0) {
+ error("failed to create quota root: %d (%m)", ret);
+ goto fail;
+ }
+ quota_root = fs_info->quota_root;
+
+ /* Create the qgroup status item */
+ key.objectid = 0;
+ key.type = BTRFS_QGROUP_STATUS_KEY;
+ key.offset = 0;
+
+ btrfs_init_path(&path);
+ ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+ sizeof(*qsi));
+ if (ret < 0) {
+ error("failed to insert qgroup status item: %d (%m)", ret);
+ goto fail;
+ }
+
+ qsi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_qgroup_status_item);
+ btrfs_set_qgroup_status_generation(path.nodes[0], qsi, trans->transid);
+ btrfs_set_qgroup_status_rescan(path.nodes[0], qsi, 0);
+ flags = BTRFS_QGROUP_STATUS_FLAG_ON;
+ if (simple) {
+ btrfs_set_qgroup_status_enable_gen(path.nodes[0], qsi, trans->transid);
+ flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE;
+ } else {
+ flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+ }
+
+ btrfs_set_qgroup_status_version(path.nodes[0], qsi, 1);
+ btrfs_set_qgroup_status_flags(path.nodes[0], qsi, flags);
+ btrfs_release_path(&path);
+
+ /* Create the qgroup items */
+ ret = create_qgroups(fs_info, trans);
+ if (ret < 0) {
+ error("failed to create qgroup items for subvols %d (%m)", ret);
+ goto fail;
+ }
+
+ /* Set squota incompat flag */
+ if (simple) {
+ super_flags |= BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA;
+ btrfs_set_super_incompat_flags(sb, super_flags);
+ }
+
+ ret = btrfs_commit_transaction(trans, fs_info->tree_root);
+ if (ret < 0) {
+ errno = -ret;
+ error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
+ return ret;
+ }
+ return ret;
+fail:
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+}
@@ -33,4 +33,7 @@ int convert_to_bg_tree(struct btrfs_fs_info *fs_info);
int convert_to_extent_tree(struct btrfs_fs_info *fs_info);
int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type);
+
+int enable_quota(struct btrfs_fs_info *fs_info, bool simple);
+
#endif