]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/home/user-record-util.c
homed: Create & advertise blob directory
[thirdparty/systemd.git] / src / home / user-record-util.c
index 464f1dbb7a096377f92d2a8e80ad075f8982f59f..b18a77d7407e98b1b44acc1224d06ef67c0eb644 100644 (file)
@@ -83,7 +83,7 @@ int user_record_synthesize(
                        JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
                                        JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
-                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("regular")),
                                        JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
                                                                        JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
                                                                                                        JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
@@ -150,7 +150,7 @@ int group_record_synthesize(GroupRecord *g, UserRecord *h) {
                                        JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
                                        JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
                                                                        JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
-                                                                                                       JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
+                                                                                                       JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.Home"))))))));
         if (r < 0)
                 return r;
 
@@ -282,7 +282,7 @@ int user_record_add_binding(
                 gid_t gid) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
-        _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
+        _cleanup_free_ char *blob = NULL, *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
         sd_id128_t mid;
         int r;
 
@@ -291,6 +291,10 @@ int user_record_add_binding(
         if (!h->json)
                 return -EUNATCH;
 
+        blob = path_join(home_system_blob_dir(), h->user_name);
+        if (!blob)
+                return -ENOMEM;
+
         r = sd_id128_get_machine(&mid);
         if (r < 0)
                 return r;
@@ -331,10 +335,11 @@ int user_record_add_binding(
 
         r = json_build(&new_binding_entry,
                        JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("blobDirectory", JSON_BUILD_STRING(blob)),
                                        JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(partition_uuid))),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(luks_uuid))),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(fs_uuid))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(partition_uuid))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(luks_uuid))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(fs_uuid))),
                                        JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
                                        JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
                                        JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
@@ -353,7 +358,7 @@ int user_record_add_binding(
                 /* Merge the new entry with an old one, if that exists */
                 be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
                 if (be) {
-                        r = json_variant_merge(&be, new_binding_entry);
+                        r = json_variant_merge_object(&be, new_binding_entry);
                         if (r < 0)
                                 return r;
 
@@ -370,6 +375,8 @@ int user_record_add_binding(
         if (r < 0)
                 return r;
 
+        free_and_replace(h->blob_directory, blob);
+
         if (storage >= 0)
                 h->storage = storage;
 
@@ -428,7 +435,7 @@ int user_record_test_home_directory(UserRecord *h) {
         if (r == 0)
                 return -ENOTDIR;
 
-        r = path_is_mount_point(hd, NULL, 0);
+        r = path_is_mount_point(hd);
         if (r < 0)
                 return r;
         if (r > 0)
@@ -445,7 +452,7 @@ int user_record_test_home_directory(UserRecord *h) {
         }
 
         /* Otherwise it's not OK */
-        r = dir_is_empty(hd);
+        r = dir_is_empty(hd, /* ignore_hidden_or_backup= */ false);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -501,7 +508,7 @@ int user_record_test_image_path(UserRecord *h) {
 
                         n = getxattr(ip, "user.home-dirty", x, sizeof(x));
                         if (n < 0) {
-                                if (errno != ENODATA)
+                                if (!ERRNO_IS_XATTR_ABSENT(errno))
                                         log_debug_errno(errno, "Unable to read dirty xattr off image file, ignoring: %m");
 
                         } else if (n == 1 && x[0] == '1')
@@ -563,7 +570,6 @@ int user_record_test_image_path_and_warn(UserRecord *h) {
 }
 
 int user_record_test_password(UserRecord *h, UserRecord *secret) {
-        char **i;
         int r;
 
         assert(h);
@@ -585,7 +591,6 @@ int user_record_test_password(UserRecord *h, UserRecord *secret) {
 }
 
 int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) {
-        char **i;
         int r;
 
         assert(h);
@@ -639,9 +644,6 @@ int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
         if (!h->json)
                 return -EUNATCH;
 
-        if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
-                return -ERANGE;
-
         r = sd_id128_get_machine(&mid);
         if (r < 0)
                 return r;
@@ -782,7 +784,6 @@ int user_record_update_last_changed(UserRecord *h, bool with_password) {
 int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
         _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
         _cleanup_strv_free_ char **np = NULL;
-        char **i;
         int r;
 
         assert(h);
@@ -877,7 +878,7 @@ int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
 
 int user_record_set_password(UserRecord *h, char **password, bool prepend) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
-        _cleanup_(strv_free_erasep) char **e = NULL;
+        _cleanup_strv_free_erase_ char **e = NULL;
         int r;
 
         assert(h);
@@ -939,7 +940,7 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) {
 
 int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
-        _cleanup_(strv_free_erasep) char **e = NULL;
+        _cleanup_strv_free_erase_ char **e = NULL;
         int r;
 
         assert(h);
@@ -1161,6 +1162,7 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
         int r;
 
         assert(h);
+        assert(secret);
 
         /* Merges the secrets from 'secret' into 'h'. */
 
@@ -1388,5 +1390,137 @@ int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
         if (hr->service && !streq(hr->service, "io.systemd.Home"))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
 
+        if (hr->blob_directory) {
+                /* This function is always called w/o binding section, so if hr->blob_dir is set then the caller set it themselves */
+                assert((hr->mask & USER_RECORD_BINDING) == 0);
+                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage custom blob directories.");
+        }
+
+        return 0;
+}
+
+bool user_record_shall_rebalance(UserRecord *h) {
+        assert(h);
+
+        if (user_record_rebalance_weight(h) == REBALANCE_WEIGHT_OFF)
+                return false;
+
+        if (user_record_storage(h) != USER_LUKS)
+                return false;
+
+        if (!path_startswith(user_record_image_path(h), get_home_root())) /* This is the only pool we rebalance in */
+                return false;
+
+        return true;
+}
+
+int user_record_set_rebalance_weight(UserRecord *h, uint64_t weight) {
+        _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine_array = NULL, *machine_id_variant = NULL,
+                *machine_id_array = NULL, *per_machine_entry = NULL;
+        _cleanup_free_ JsonVariant **array = NULL;
+        size_t idx = SIZE_MAX, n;
+        JsonVariant *per_machine;
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        if (!h->json)
+                return -EUNATCH;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        r = json_variant_new_id128(&machine_id_variant, mid);
+        if (r < 0)
+                return r;
+
+        r = json_variant_new_array(&machine_id_array, (JsonVariant*[]) { machine_id_variant }, 1);
+        if (r < 0)
+                return r;
+
+        per_machine = json_variant_by_key(h->json, "perMachine");
+        if (per_machine) {
+                if (!json_variant_is_array(per_machine))
+                        return -EINVAL;
+
+                n = json_variant_elements(per_machine);
+
+                array = new(JsonVariant*, n + 1);
+                if (!array)
+                        return -ENOMEM;
+
+                for (size_t i = 0; i < n; i++) {
+                        JsonVariant *m;
+
+                        array[i] = json_variant_by_index(per_machine, i);
+
+                        if (!json_variant_is_object(array[i]))
+                                return -EINVAL;
+
+                        m = json_variant_by_key(array[i], "matchMachineId");
+                        if (!m) {
+                                /* No machineId field? Let's ignore this, but invalidate what we found so far */
+                                idx = SIZE_MAX;
+                                continue;
+                        }
+
+                        if (json_variant_equal(m, machine_id_variant) ||
+                            json_variant_equal(m, machine_id_array)) {
+                                /* Matches exactly what we are looking for. Let's use this */
+                                idx = i;
+                                continue;
+                        }
+
+                        r = per_machine_id_match(m, JSON_PERMISSIVE);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                /* Also matches what we are looking for, but with a broader match. In this
+                                 * case let's ignore this entry, and add a new specific one to the end. */
+                                idx = SIZE_MAX;
+                }
+
+                if (idx == SIZE_MAX)
+                        idx = n++; /* Nothing suitable found, place new entry at end */
+                else
+                        per_machine_entry = json_variant_ref(array[idx]);
+
+        } else {
+                array = new(JsonVariant*, 1);
+                if (!array)
+                        return -ENOMEM;
+
+                idx = 0;
+                n = 1;
+        }
+
+        if (!per_machine_entry) {
+                r = json_variant_set_field(&per_machine_entry, "matchMachineId", machine_id_array);
+                if (r < 0)
+                        return r;
+        }
+
+        if (weight == REBALANCE_WEIGHT_UNSET)
+                r = json_variant_set_field(&per_machine_entry, "rebalanceWeight", NULL); /* set explicitly to NULL (so that the perMachine setting we are setting here can override the global setting) */
+        else
+                r = json_variant_set_field_unsigned(&per_machine_entry, "rebalanceWeight", weight);
+        if (r < 0)
+                return r;
+
+        assert(idx < n);
+        array[idx] = per_machine_entry;
+
+        r = json_variant_new_array(&new_per_machine_array, array, n);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "perMachine", new_per_machine_array);
+        if (r < 0)
+                return r;
+
+        h->rebalance_weight = weight;
+        h->mask |= USER_RECORD_PER_MACHINE;
         return 0;
 }