A function for parsing /etc/sub[ug]id
Signed-off-by: Ján Tomko <jtomko(a)redhat.com>
---
src/libvirt_private.syms | 2 ++
src/util/virutil.c | 70 ++++++++++++++++++++++++++++++++++++
src/util/virutil.h | 12 +++++++
tests/utiltest.c | 33 +++++++++++++++++
tests/virutiltestdata/subuid | 4 +++
5 files changed, 121 insertions(+)
create mode 100644 tests/virutiltestdata/subuid
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 553b01b8c0..20f34d72b5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3640,6 +3640,7 @@ virGetHostname;
virGetHostnameQuiet;
virGetPassword;
virGetSelfLastChanged;
+virGetSubIDs;
virGetSystemPageSize;
virGetSystemPageSizeKB;
virGetUserCacheDirectory;
@@ -3670,6 +3671,7 @@ virSetNonBlock;
virSetSockReuseAddr;
virSetUIDGID;
virSetUIDGIDWithCaps;
+virSubIDsFree;
virUpdateSelfLastChanged;
virValidateWWN;
virWaitForDevices;
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 17d65ad834..369e0bc4dc 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -738,6 +738,76 @@ virGetUserIDByName(const char *name, uid_t *uid, bool missing_ok)
return ret;
}
+void
+virSubIDsFree(virSubID **uids, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ if ((*uids)[i].idstr)
+ g_free((*uids)[i].idstr);
+ }
+ g_clear_pointer(uids, g_free);
+}
+
+int
+virGetSubIDs(virSubID **retval, const char *file)
+{
+ g_autofree char *buf = NULL;
+ g_auto(GStrv) lines = NULL;
+ virSubID *entries = NULL;
+ size_t i = 0;
+ size_t len;
+ int ret = -1;
+
+ *retval = NULL;
+
+ if (virFileReadAll(file, BUFSIZ, &buf) < 0)
+ return -1;
+
+ lines = g_strsplit(buf, "\n", 0);
+ if (!lines)
+ return -1;
+
+ len = g_strv_length(lines);
+ entries = g_new0(virSubID, len);
+
+ for (i = 0; i < len; i++) {
+ g_auto(GStrv) fields = NULL;
+ unsigned long ulong_id;
+
+ fields = g_strsplit(lines[i], ":", 0);
+ if (!fields)
+ goto cleanup;
+
+ if (g_strv_length(fields) != 3)
+ break;
+
+ if (g_ascii_isdigit(fields[0][0])) {
+ if (virStrToLong_ul(fields[0], NULL, 10, &ulong_id) < 0)
+ goto cleanup;
+ entries[i].id = ulong_id;
+ } else {
+ entries[i].idstr = g_strdup(fields[0]);
+ }
+
+ if (virStrToLong_ul(fields[1], NULL, 10, &ulong_id) < 0)
+ goto cleanup;
+ entries[i].start = ulong_id;
+
+ if (virStrToLong_ul(fields[2], NULL, 10, &ulong_id) < 0)
+ goto cleanup;
+ entries[i].range = ulong_id;
+ }
+
+ *retval = g_steal_pointer(&entries);
+ ret = i;
+ cleanup:
+ if (entries)
+ virSubIDsFree(&entries, len);
+ return ret;
+}
+
/* Try to match a user id based on `user`. The default behavior is to parse
* `user` first as a user name and then as a user id. However if `user`
* contains a leading '+', the rest of the string is always parsed as a uid.
diff --git a/src/util/virutil.h b/src/util/virutil.h
index ab8511bf8d..3bac15d02b 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -102,6 +102,18 @@ char *virGetUserName(uid_t uid) G_NO_INLINE;
char *virGetGroupName(gid_t gid) G_NO_INLINE;
int virGetGroupList(uid_t uid, gid_t group, gid_t **groups)
ATTRIBUTE_NONNULL(3);
+
+typedef struct _virSubID {
+ uid_t id;
+ char *idstr;
+ uid_t start;
+ uid_t range;
+} virSubID;
+
+int virGetSubIDs(virSubID **ret, const char *file);
+void virSubIDsFree(virSubID **uids, size_t n);
+
+
int virGetUserID(const char *name,
uid_t *uid) G_GNUC_WARN_UNUSED_RESULT;
int virGetGroupID(const char *name,
diff --git a/tests/utiltest.c b/tests/utiltest.c
index 5930557c08..b30bfbed70 100644
--- a/tests/utiltest.c
+++ b/tests/utiltest.c
@@ -377,6 +377,38 @@ testKernelCmdlineMatchParam(const void *data G_GNUC_UNUSED)
}
+static int
+testGetSubIDs(const void *data G_GNUC_UNUSED)
+{
+ g_autofree char *subuid_file = g_strdup_printf("%s/virutiltestdata/subuid",
abs_srcdir);
+ virSubID *subids = NULL;
+ int len = 0;
+ int ret = -1;
+
+ if ((len = virGetSubIDs(&subids, subuid_file)) < 0) {
+ VIR_TEST_DEBUG("virGetSubIDs failed");
+ goto cleanup;
+ }
+
+ if (len != 4) {
+ VIR_TEST_DEBUG("virGetSubIDs returned %d (expected 4)", len);
+ goto cleanup;
+ }
+
+ if (STRNEQ(subids[0].idstr, "joe")) {
+ VIR_TEST_DEBUG("virGetSubIDs returned wrong name for entry 0:
'%s'", NULLSTR(subids[0].idstr));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (len >= 0)
+ virSubIDsFree(&subids, len);
+ return ret;
+}
+
+
static int
mymain(void)
{
@@ -400,6 +432,7 @@ mymain(void)
DO_TEST(OverflowCheckMacro);
DO_TEST(KernelCmdlineNextParam);
DO_TEST(KernelCmdlineMatchParam);
+ DO_TEST(GetSubIDs);
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tests/virutiltestdata/subuid b/tests/virutiltestdata/subuid
new file mode 100644
index 0000000000..c873c0e3bf
--- /dev/null
+++ b/tests/virutiltestdata/subuid
@@ -0,0 +1,4 @@
+joe:100000:65535
+bob:300000:65535
+1024:400000:65535
+alice:200000:65535
--
2.42.0