Skip to content

Commit 4108205

Browse files
committed
btrfs: use libbtrfsutil for listing subvolumes
Use libbtrfsutil's subvolume iterator to obtain a list of subvolumes in order. In the future this would allow exposing more information about a subvolume to list_subvolumes.
1 parent 2b47e34 commit 4108205

File tree

1 file changed

+27
-103
lines changed

1 file changed

+27
-103
lines changed

src/plugins/btrfs.c

Lines changed: 27 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include "check_deps.h"
2929

3030
#define BTRFS_MIN_VERSION "3.18.2"
31+
// HACK: btrfsutil issue?
32+
#define BTRFS_FSID_SIZE 16
3133

3234
/**
3335
* SECTION: btrfs
@@ -212,23 +214,6 @@ static BDBtrfsDeviceInfo* get_device_info_from_match (GMatchInfo *match_info) {
212214
return ret;
213215
}
214216

215-
static BDBtrfsSubvolumeInfo* get_subvolume_info_from_match (GMatchInfo *match_info) {
216-
BDBtrfsSubvolumeInfo *ret = g_new(BDBtrfsSubvolumeInfo, 1);
217-
gchar *item = NULL;
218-
219-
item = g_match_info_fetch_named (match_info, "id");
220-
ret->id = g_ascii_strtoull (item, NULL, 0);
221-
g_free (item);
222-
223-
item = g_match_info_fetch_named (match_info, "parent_id");
224-
ret->parent_id = g_ascii_strtoull (item, NULL, 0);
225-
g_free (item);
226-
227-
ret->path = g_match_info_fetch_named (match_info, "path");
228-
229-
return ret;
230-
}
231-
232217
static BDBtrfsFilesystemInfo* get_filesystem_info_from_match (GMatchInfo *match_info) {
233218
BDBtrfsFilesystemInfo *ret = g_new(BDBtrfsFilesystemInfo, 1);
234219
gchar *item = NULL;
@@ -608,112 +593,51 @@ BDBtrfsDeviceInfo** bd_btrfs_list_devices (const gchar *device, GError **error)
608593
* Tech category: %BD_BTRFS_TECH_SUBVOL-%BD_BTRFS_TECH_MODE_QUERY
609594
*/
610595
BDBtrfsSubvolumeInfo** bd_btrfs_list_subvolumes (const gchar *mountpoint, gboolean snapshots_only, GError **error) {
611-
const gchar *argv[7] = {"btrfs", "subvol", "list", "-p", NULL, NULL, NULL};
612-
gchar *output = NULL;
613-
gboolean success = FALSE;
614-
gchar **lines = NULL;
615-
gchar **line_p = NULL;
616-
gchar const * const pattern = "ID\\s+(?P<id>\\d+)\\s+gen\\s+\\d+\\s+(cgen\\s+\\d+\\s+)?" \
617-
"parent\\s+(?P<parent_id>\\d+)\\s+top\\s+level\\s+\\d+\\s+" \
618-
"(otime\\s+(\\d{4}-\\d{2}-\\d{2}\\s+\\d\\d:\\d\\d:\\d\\d|-)\\s+)?"\
619-
"path\\s+(?P<path>\\S+)";
620-
GRegex *regex = NULL;
621-
GMatchInfo *match_info = NULL;
622-
guint64 i = 0;
623-
guint64 y = 0;
624-
guint64 next_sorted_idx = 0;
625-
GPtrArray *subvol_infos;
626-
BDBtrfsSubvolumeInfo* item = NULL;
627-
BDBtrfsSubvolumeInfo* swap_item = NULL;
596+
struct btrfs_util_subvolume_iterator *iter;
597+
enum btrfs_util_error err;
598+
char *path;
599+
char empty_uuid[BTRFS_FSID_SIZE] = {0};
600+
struct btrfs_util_subvolume_info info;
628601
BDBtrfsSubvolumeInfo** ret = NULL;
629-
GError *l_error = NULL;
630-
631-
if (!check_deps (&avail_deps, DEPS_BTRFS_MASK, deps, DEPS_LAST, &deps_check_lock, error) ||
632-
!check_module_deps (&avail_module_deps, MODULE_DEPS_BTRFS_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error))
633-
return NULL;
634-
635-
if (snapshots_only) {
636-
argv[4] = "-s";
637-
argv[5] = mountpoint;
638-
} else
639-
argv[4] = mountpoint;
602+
GPtrArray *subvol_infos;
640603

641-
regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, error);
642-
if (!regex) {
643-
bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to create new GRegex");
644-
/* error is already populated */
604+
err = btrfs_util_create_subvolume_iterator (mountpoint, 5, 0, &iter);
605+
if (err) {
606+
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_NOT_FOUND, "%s: %m", btrfs_util_strerror (err));
645607
return NULL;
646608
}
647609

648-
success = bd_utils_exec_and_capture_output (argv, NULL, &output, &l_error);
649-
if (!success) {
650-
g_regex_unref (regex);
651-
if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
652-
/* no output -> no subvolumes */
653-
g_clear_error (&l_error);
654-
return g_new0 (BDBtrfsSubvolumeInfo*, 1);
655-
} else {
656-
g_propagate_error (error, l_error);
657-
return NULL;
658-
}
659-
}
660-
661-
lines = g_strsplit (output, "\n", 0);
662-
g_free (output);
663-
664610
subvol_infos = g_ptr_array_new ();
665-
for (line_p = lines; *line_p; line_p++) {
666-
success = g_regex_match (regex, *line_p, 0, &match_info);
667-
if (!success) {
668-
g_match_info_free (match_info);
611+
while (!(err = btrfs_util_subvolume_iterator_next_info (iter, &path, &info))) {
612+
/* See uapi/linux/btrfs.h */
613+
if (snapshots_only && memcmp (info.parent_uuid, empty_uuid, BTRFS_FSID_SIZE) == 0)
669614
continue;
670-
}
671615

672-
g_ptr_array_add (subvol_infos, get_subvolume_info_from_match (match_info));
673-
g_match_info_free (match_info);
616+
BDBtrfsSubvolumeInfo *subvol = g_new (BDBtrfsSubvolumeInfo, 1);
617+
subvol->id = info.id;
618+
subvol->parent_id = info.parent_id;
619+
subvol->path = g_strdup (path);
620+
g_ptr_array_add (subvol_infos, subvol);
621+
622+
free (path);
674623
}
675624

676-
g_strfreev (lines);
677-
g_regex_unref (regex);
625+
btrfs_util_destroy_subvolume_iterator (iter);
678626

679-
if (subvol_infos->len == 0) {
680-
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_PARSE, "Failed to parse information about subvolumes");
681-
g_ptr_array_free (subvol_infos, TRUE);
627+
if (err) {
628+
g_set_error (error, BD_BTRFS_ERROR, BD_BTRFS_ERROR_NOT_FOUND, "%s: %m", btrfs_util_strerror (err));
682629
return NULL;
683630
}
684631

685632
/* now we know how much space to allocate for the result (subvols + NULL) */
686633
ret = g_new0 (BDBtrfsSubvolumeInfo*, subvol_infos->len + 1);
687634

688-
/* we need to sort the subvolumes in a way that no child subvolume appears
689-
in the list before its parent (sub)volume */
690-
691-
/* let's start by moving all top-level (sub)volumes to the beginning */
692-
for (i=0; i < subvol_infos->len; i++) {
693-
item = (BDBtrfsSubvolumeInfo*) g_ptr_array_index (subvol_infos, i);
694-
if (item->parent_id == BD_BTRFS_MAIN_VOLUME_ID)
695-
/* top-level (sub)volume */
696-
ret[next_sorted_idx++] = item;
697-
}
698-
/* top-level (sub)volumes are now processed */
699-
for (i=0; i < next_sorted_idx; i++)
700-
g_ptr_array_remove_fast (subvol_infos, ret[i]);
701-
702-
/* now sort the rest in a way that we search for an already sorted parent or sibling */
635+
guint64 i = 0;
636+
BDBtrfsSubvolumeInfo* item = NULL;
703637
for (i=0; i < subvol_infos->len; i++) {
704638
item = (BDBtrfsSubvolumeInfo*) g_ptr_array_index (subvol_infos, i);
705-
ret[next_sorted_idx] = item;
706-
/* move the item towards beginning of the array checking if some parent
707-
or sibling has been already processed/sorted before or we reached the
708-
top-level (sub)volumes */
709-
for (y=next_sorted_idx; (y > 0 && (ret[y-1]->id != item->parent_id) && (ret[y-1]->parent_id != item->parent_id) && (ret[y-1]->parent_id != BD_BTRFS_MAIN_VOLUME_ID)); y--) {
710-
swap_item = ret[y-1];
711-
ret[y-1] = ret[y];
712-
ret[y] = swap_item;
713-
}
714-
next_sorted_idx++;
639+
ret[i] = item;
715640
}
716-
ret[next_sorted_idx] = NULL;
717641

718642
/* now just free the pointer array */
719643
g_ptr_array_free (subvol_infos, TRUE);

0 commit comments

Comments
 (0)