Skip to content

Commit 5bcd08d

Browse files
committed
btrfs: beef-up btrfs support with a limited understanding of quota
With this change we understand more than just leaf quota groups for btrfs file systems. Specifically: - When we create a subvolume we can now optionally add the new subvolume to all qgroups its parent subvolume was member of too. Alternatively it is also possible to insert an intermediary quota group between the parent's qgroups and the subvolume's leaf qgroup, which is useful for a concept of "subtree" qgroups, that contain a subvolume and all its children. - The remove logic for subvolumes has been updated to optionally remove any leaf qgroups or "subtree" qgroups, following the logic above. - The snapshot logic for subvolumes has been updated to replicate the original qgroup setup of the source, if it follows the "subtree" design described above. It will not cover qgroup setups that introduce arbitrary qgroups, especially those orthogonal to the subvolume hierarchy. This also tries to be more graceful when setting up /var/lib/machines as btrfs. For example, if mkfs.btrfs is missing we don't even try to set it up as loopback device. Fixes systemd#1559 Fixes systemd#1129
1 parent 16597ac commit 5bcd08d

File tree

14 files changed

+1077
-121
lines changed

14 files changed

+1077
-121
lines changed

src/basic/btrfs-util.c

Lines changed: 852 additions & 53 deletions
Large diffs are not rendered by default.

src/basic/btrfs-util.h

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,45 +47,82 @@ typedef enum BtrfsSnapshotFlags {
4747
BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
4848
BTRFS_SNAPSHOT_READ_ONLY = 2,
4949
BTRFS_SNAPSHOT_RECURSIVE = 4,
50+
BTRFS_SNAPSHOT_QUOTA = 8,
5051
} BtrfsSnapshotFlags;
5152

53+
typedef enum BtrfsRemoveFlags {
54+
BTRFS_REMOVE_RECURSIVE = 1,
55+
BTRFS_REMOVE_QUOTA = 2,
56+
} BtrfsRemoveFlags;
57+
5258
int btrfs_is_filesystem(int fd);
5359
int btrfs_is_subvol(int fd);
5460

61+
int btrfs_reflink(int infd, int outfd);
62+
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
63+
64+
int btrfs_get_block_device_fd(int fd, dev_t *dev);
65+
int btrfs_get_block_device(const char *path, dev_t *dev);
66+
67+
int btrfs_defrag_fd(int fd);
68+
int btrfs_defrag(const char *p);
69+
70+
int btrfs_quota_enable_fd(int fd, bool b);
71+
int btrfs_quota_enable(const char *path, bool b);
72+
73+
int btrfs_quota_scan_start(int fd);
74+
int btrfs_quota_scan_wait(int fd);
75+
int btrfs_quota_scan_ongoing(int fd);
76+
77+
int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
78+
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
79+
5580
int btrfs_subvol_make(const char *path);
5681
int btrfs_subvol_make_label(const char *path);
5782

5883
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
5984
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
6085

86+
int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags);
87+
int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags);
88+
6189
int btrfs_subvol_set_read_only_fd(int fd, bool b);
6290
int btrfs_subvol_set_read_only(const char *path, bool b);
6391
int btrfs_subvol_get_read_only_fd(int fd);
92+
6493
int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret);
6594
int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
66-
int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
67-
int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
95+
int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret);
6896

69-
int btrfs_reflink(int infd, int outfd);
70-
int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
97+
int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info);
7198

72-
int btrfs_get_block_device_fd(int fd, dev_t *dev);
73-
int btrfs_get_block_device(const char *path, dev_t *dev);
99+
int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret);
74100

75-
int btrfs_defrag_fd(int fd);
76-
int btrfs_defrag(const char *p);
101+
int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *quota);
102+
int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *quota);
77103

78-
int btrfs_quota_enable_fd(int fd, bool b);
79-
int btrfs_quota_enable(const char *path, bool b);
104+
int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max);
105+
int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max);
80106

81-
int btrfs_quota_limit_fd(int fd, uint64_t referenced_max);
82-
int btrfs_quota_limit(const char *path, uint64_t referenced_max);
107+
int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup);
108+
int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup);
83109

84-
int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
85-
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
110+
int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret);
111+
int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id);
112+
113+
int btrfs_qgroup_create(int fd, uint64_t qgroupid);
114+
int btrfs_qgroup_destroy(int fd, uint64_t qgroupid);
115+
int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid);
116+
117+
int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max);
118+
int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max);
119+
120+
int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid);
121+
122+
int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent);
123+
int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent);
86124

87-
int btrfs_subvol_remove(const char *path, bool recursive);
88-
int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive);
125+
int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret);
89126

90-
int btrfs_qgroup_create(int fd, uint64_t level, uint64_t id);
91-
int btrfs_qgroup_destroy(int fd, uint64_t level, uint64_t id);
127+
int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *quota);
128+
int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *quota);

src/basic/missing.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,10 @@ struct btrfs_ioctl_quota_ctl_args {
494494
#define BTRFS_QGROUP_LIMIT_KEY 244
495495
#endif
496496

497+
#ifndef BTRFS_QGROUP_RELATION_KEY
498+
#define BTRFS_QGROUP_RELATION_KEY 246
499+
#endif
500+
497501
#ifndef BTRFS_ROOT_BACKREF_KEY
498502
#define BTRFS_ROOT_BACKREF_KEY 144
499503
#endif

src/basic/path-util.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -796,14 +796,11 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
796796
return changed;
797797
}
798798

799-
int fsck_exists(const char *fstype) {
799+
static int binary_is_good(const char *binary) {
800800
_cleanup_free_ char *p = NULL, *d = NULL;
801-
const char *checker;
802801
int r;
803802

804-
checker = strjoina("fsck.", fstype);
805-
806-
r = find_binary(checker, true, &p);
803+
r = find_binary(binary, true, &p);
807804
if (r < 0)
808805
return r;
809806

@@ -820,6 +817,22 @@ int fsck_exists(const char *fstype) {
820817
return 0;
821818
}
822819

820+
int fsck_exists(const char *fstype) {
821+
const char *checker;
822+
823+
checker = strjoina("fsck.", fstype);
824+
825+
return binary_is_good(checker);
826+
}
827+
828+
int mkfs_exists(const char *fstype) {
829+
const char *mkfs;
830+
831+
mkfs = strjoina("mkfs.", fstype);
832+
833+
return binary_is_good(mkfs);
834+
}
835+
823836
char *prefix_root(const char *root, const char *path) {
824837
char *n, *p;
825838
size_t l;

src/basic/path-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ int find_binary(const char *name, bool local, char **filename);
6363
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
6464

6565
int fsck_exists(const char *fstype);
66+
int mkfs_exists(const char *fstype);
6667

6768
/* Iterates through the path prefixes of the specified path, going up
6869
* the tree, to root. Also returns "" (and not "/"!) for the root

src/basic/rm-rf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
120120

121121
/* This could be a subvolume, try to remove it */
122122

123-
r = btrfs_subvol_remove_fd(fd, de->d_name, true);
123+
r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
124124
if (r < 0) {
125125
if (r != -ENOTTY && r != -EINVAL) {
126126
if (ret == 0)
@@ -178,7 +178,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
178178

179179
if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
180180
/* Try to remove as subvolume first */
181-
r = btrfs_subvol_remove(path, true);
181+
r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
182182
if (r >= 0)
183183
return r;
184184

src/import/export-tar.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ TarExport *tar_export_unref(TarExport *e) {
7878
}
7979

8080
if (e->temp_path) {
81-
(void) btrfs_subvol_remove(e->temp_path, false);
81+
(void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA);
8282
free(e->temp_path);
8383
}
8484

@@ -283,7 +283,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType
283283
if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
284284
BtrfsQuotaInfo q;
285285

286-
r = btrfs_subvol_get_quota_fd(sfd, &q);
286+
r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q);
287287
if (r >= 0)
288288
e->quota_referenced = q.referenced;
289289

src/import/pull-common.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
138138
if (force_local)
139139
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
140140

141-
r = btrfs_subvol_snapshot(final, p, 0);
141+
r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA);
142142
if (r == -ENOTTY) {
143143
r = copy_tree(final, p, false);
144144
if (r < 0)

src/import/pull-dkr.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,16 @@ static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
490490
return r;
491491

492492
if (version == DKR_PULL_V2) {
493-
char **k = NULL;
493+
char **k;
494+
494495
STRV_FOREACH(k, i->ancestry) {
495-
_cleanup_free_ char *d = strjoin(i->image_root, "/.dkr-", *k, NULL);
496-
r = btrfs_subvol_remove(d, false);
496+
_cleanup_free_ char *d;
497+
498+
d = strjoin(i->image_root, "/.dkr-", *k, NULL);
499+
if (!d)
500+
return -ENOMEM;
501+
502+
r = btrfs_subvol_remove(d, BTRFS_REMOVE_QUOTA);
497503
if (r < 0)
498504
return r;
499505
}
@@ -531,7 +537,7 @@ static int dkr_pull_job_on_open_disk(PullJob *j) {
531537
const char *base_path;
532538

533539
base_path = strjoina(i->image_root, "/.dkr-", base);
534-
r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY);
540+
r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_QUOTA);
535541
} else
536542
r = btrfs_subvol_make(i->temp_path);
537543
if (r < 0)

src/machine/machined-dbus.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ static int property_get_pool_usage(
7979
if (fd >= 0) {
8080
BtrfsQuotaInfo q;
8181

82-
if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
82+
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
8383
usage = q.referenced;
8484
}
8585

@@ -115,7 +115,7 @@ static int property_get_pool_limit(
115115
if (fd >= 0) {
116116
BtrfsQuotaInfo q;
117117

118-
if (btrfs_subvol_get_quota_fd(fd, &q) >= 0)
118+
if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0)
119119
size = q.referenced_max;
120120
}
121121

@@ -831,7 +831,9 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
831831
if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */
832832
return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m");
833833

834-
r = btrfs_quota_limit("/var/lib/machines", limit);
834+
(void) btrfs_qgroup_set_limit("/var/lib/machines", 0, limit);
835+
836+
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, limit);
835837
if (r == -ENOTTY)
836838
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
837839
if (r < 0)

0 commit comments

Comments
 (0)