Create templates at sessiond start 79/324679/6
authorMichal Bloch <m.bloch@samsung.com>
Thu, 22 May 2025 15:16:17 +0000 (17:16 +0200)
committerMichal Bloch <m.bloch@samsung.com>
Fri, 30 May 2025 19:16:19 +0000 (21:16 +0200)
Will be used for a fast route to add subsessions,
since renaming a dir/image is much faster than
copying the whole skel folder hierarchy.

Change-Id: I0ff51c4126f44675445d3337a5ab51463c0aec03

17 files changed:
src/library/src/lib.c
src/service/src/dir_backend.hpp
src/service/src/dir_backend_fixed_size.cpp
src/service/src/dir_backend_fixed_size.hpp
src/service/src/dir_backend_regular_dir.cpp
src/service/src/dir_backend_regular_dir.hpp
src/service/src/fs_helpers.cpp
src/service/src/fs_helpers.hpp
src/service/src/globals.hpp
src/service/src/main.cpp
src/service/src/main_context.hpp
src/service/src/main_restore.cpp
src/service/src/main_restore.hpp
tests/api_tests/test_api_add_remove_user_err.cpp
tests/api_tests/test_api_get_user_list.cpp
tests/api_tests/test_api_switchuser_err.cpp
tests/api_tests/test_hlp.hpp

index dea0785c81b5ab04b7ff9ed95b20930738fbec0e..eb108ad4a59efb2a9d9bcbdcabe6da6d429628d7 100644 (file)
@@ -179,6 +179,12 @@ static int is_not_correct_name(const char *user_id) {
        return FALSE;
 }
 
+static bool is_reserved_name(const subsession_user_t subsession)
+{
+       return !strcmp(subsession, "__template_fixed__")
+           || !strcmp(subsession, "__template_reg__");
+}
+
 static gboolean error_on_bad_user_id(const char *user_id) {
        return (
                is_user_id_null(user_id)
@@ -637,6 +643,9 @@ EXPORT_API int subsession_add_user(int session_uid, const subsession_user_t user
                user_id_is_not_valid(user))
        )
 
+       if (is_reserved_name(user))
+               return SUBSESSION_ERROR_INVALID_PARAMETER;
+
        return_with_log_error_result_(method_call_async(dbus_method_call.AddUser, g_variant_new("(is)", session_uid, user), cb, user_data))
 }
 
@@ -648,6 +657,9 @@ EXPORT_API int subsession_add_user_fixed_size(int session_uid, const subsession_
                session_fixed_size_invalid_size(size_limit_kB)))
        )
 
+       if (is_reserved_name(user))
+               return SUBSESSION_ERROR_INVALID_PARAMETER;
+
        return_with_log_error_result_(method_call_async(dbus_method_call.AddUserFixedSize, g_variant_new("(isu)", session_uid, user, size_limit_kB), cb, user_data))
 }
 
@@ -658,6 +670,9 @@ EXPORT_API int subsession_remove_user(int session_uid, const subsession_user_t u
                user_id_is_not_valid(user))
        )
 
+       if (is_reserved_name(user))
+               return SUBSESSION_ERROR_INVALID_PARAMETER;
+
        return_with_log_error_result_(method_call_async(dbus_method_call.RemoveUser, g_variant_new("(is)", session_uid, user), cb, user_data))
 }
 
@@ -668,6 +683,9 @@ EXPORT_API int subsession_switch_user(int session_uid, const subsession_user_t n
                session_uid_is_not_valid(session_uid))
        )
 
+       if (is_reserved_name(next_user))
+               return SUBSESSION_ERROR_INVALID_PARAMETER;
+
        return_with_log_error_result_(method_call_async(dbus_method_call.SwitchUser, g_variant_new("(is)", session_uid, next_user), cb, user_data))
 }
 
index d1c66c5cc41935fe4f1842639fda842a14e188a2..b451c2c0ce527ac7f80b118bfc2ca289e4499ac5 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <filesystem>
+#include <string_view>
 
 namespace fs = std::filesystem;
 
@@ -14,4 +15,5 @@ struct DirBackendAdd {
        virtual fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const = 0;
        virtual void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const = 0;
        virtual void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const = 0;
+       virtual std::string_view TemplateName() const = 0;
 };
index bb7ae4083a53b3dfd76d1fbe1e36c1961c91d9c5..d4d1a529f7beb526dff2fa2af48217d18e375876 100644 (file)
@@ -38,6 +38,11 @@ using namespace std::literals;
 // Should be unique per-backend.
 static const std::string TMP_NEW_PREFIX = ".tmpfixednew";
 
+std::string_view DirBackendAddFixedSize::TemplateName() const
+{
+       return "__template_fixed__"sv;
+}
+
 fs::path DirBackendFixedSize::GetImagePathFromSubsessionPath(fs::path subsession_path)
 {
        subsession_path.replace_filename(".img."s + subsession_path.filename().native());
index cd752921d35bd17d9583a1d6e082bab01be7b9df..c1a94e4c4f60dd3dccad57ca112a4ca06b02b04d 100644 (file)
@@ -19,4 +19,5 @@ struct DirBackendAddFixedSize : public DirBackendAdd {
        fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const override;
        void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const override;
        void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const override;
+       std::string_view TemplateName() const override;
 };
index 5f46a7f946fff456721f845f340ab77e014a2edc..de5e971b939f21814057ff9b8ea24add85610009 100644 (file)
 #include "dir_backend_regular_dir.hpp"
 #include "os_ops.hpp"
 
-using namespace std::string_literals;
+using namespace std::literals;
+
+std::string_view DirBackendAddRegularDir::TemplateName() const
+{
+       /* Still has to be a valid subsession name (to be visible
+        * to external update scripts) so must fit in 20 chars. */
+       return "__template_reg__"sv;
+}
 
 fs::path DirBackendAddRegularDir::AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const
 {
index ed4a9f612077af4db094b6254a8ac5e0da5fb63d..d9b0dcaa99b8eb00c129c1d8f719727b20d6bf26 100644 (file)
@@ -12,4 +12,5 @@ struct DirBackendAddRegularDir : public DirBackendAdd {
        fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const override;
        void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const override;
        void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const override;
+       std::string_view TemplateName() const override;
 };
index c0d04d8737bb1e22a02b7ac464ca410a0c192a7b..13f2aa8424437271e197a527c829cbe1fcf0995b 100644 (file)
@@ -63,7 +63,7 @@ static inline bool should_copy_perms_from_skel(Directory_Class type) {
 }
 
 // Create `$HOME/subsession` directory if it doesn't exist
-static void create_main_subdirectory(const int session_uid, const fs::path& main_dir)
+void create_main_subdirectory(const int session_uid, const fs::path& main_dir)
 {
        if (fs::exists(main_dir))
                return;
index 65a51693f59a7ac8f4266f235b44d58437591849..366d97ac9e2440800ae06a32387e6626f3de2a9e 100644 (file)
@@ -10,6 +10,7 @@
 
 std::filesystem::path get_main_dir_by_user_id(const int uid);
 std::filesystem::path get_last_subsession_path_by_user_id(const int uid);
+void create_main_subdirectory(const int session_uid, const fs::path& main_dir);
 bool subsession_exists(const int session_uid, const std::string_view subsession_id);
 void add_user_subsession(const int session_uid, const std::string_view subsession_id, const DirBackendAdd& backend);
 void add_user_subsession_inner(const std::filesystem::path &main_path, const int session_uid, const std::string_view subsession_id, const DirBackendAdd& backend, bool force);
index 9f8f3769ff31c94e2b8653e1c1201a14c9aec519..bedc4583adb483ecbe43d9a2e6b94c38b5e014ca 100644 (file)
@@ -22,6 +22,9 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE. */
 
+#include "dir_backend_fixed_size.hpp"
+#include "dir_backend_regular_dir.hpp"
+
 #include <chrono>
 #include <exception>
 #include <stdexcept>
@@ -57,6 +60,12 @@ inline bool check_subsession_id_valid(const std::string_view subsession_id)
        return true;
 }
 
+inline bool check_subsession_id_restricted(const std::string_view subsession_id)
+{
+       return DirBackendAddFixedSize{0}.TemplateName() == subsession_id
+           || DirBackendAddRegularDir{}.TemplateName() == subsession_id;
+}
+
 inline bool check_uid_valid(int uid)
 {
        return uid > 0;
index 07a3ff92c571780a8f634511bdb05ca444f1154e..8eae03bf1d8bf6160fe72bbc80afbeb8ff60e280 100644 (file)
@@ -36,10 +36,10 @@ bool isRestoreOnly(int argc, char **argv)
 
 int main(int argc, char **argv) try {
        if (isRestoreOnly(argc, argv)) {
-               restore_all_user_sessions();
+               restore_all_user_sessions(true);
        } else {
                g_sessiond_context = std::make_unique <sessiond_context> ();
-               restore_all_user_sessions();
+               restore_all_user_sessions(false);
                g_sessiond_context->run();
        }
 } catch (const std::exception &ex) {
index 9fc05648f597b524d5646ed33fc0dd80d58719ae..2489d875a8eef15fd9126cf75bc079849914844c 100644 (file)
@@ -185,6 +185,12 @@ struct sessiond_context {
                        return true;
                }
 
+               if (check_subsession_id_restricted(subsession_id)) {
+                       g_dbus_method_invocation_return_dbus_error(invocation,
+                               get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Reserved subsession_id");
+                       return true;
+               }
+
                return false;
        }
 
@@ -344,7 +350,7 @@ struct sessiond_context {
                }
 
                // N.B. Switch to user "" (empty string) is possible and it means no subsession is currently active
-               if (next_subsession_id != SUBSESSION_INITIAL_SID && !check_subsession_id_valid(next_subsession_id)) {
+               if (next_subsession_id != SUBSESSION_INITIAL_SID && (!check_subsession_id_valid(next_subsession_id) || check_subsession_id_restricted(next_subsession_id))) {
                        g_dbus_method_invocation_return_dbus_error(invocation,
                                get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
                        return;
index 7b132513ecec5cc3f2f70b20c06ccf000dd9651b..ea94d5881e66bc9ad3f5e5cd87ea5b083470e666 100644 (file)
@@ -43,6 +43,8 @@
 
 using namespace std::string_literals;
 
+static constexpr auto TEMPLATE_IMAGE_SIZE_KB = 10240;
+
 std::string get_restoration_lock_filename(int uid)
 {
        return "/run/sessiond-restore-"s + std::to_string(uid) + ".lock";
@@ -138,7 +140,28 @@ static void remount_image_sessions(int uid) try
        throw;
 }
 
-void restore_all_user_sessions()
+static void regenerate_template_sessions(int uid) try
+{
+       const fs::path main_path = get_main_dir_by_user_id(uid);
+       create_main_subdirectory(uid, main_path);
+
+       const auto add_template_for_backend = [&] (const DirBackendAdd &backend) {
+               /* Don't regenerate templates and assume that whatever was left from
+                * a previous boot, if anything, is correct. Image-backed subsessions
+                * are remounted early on (including via an early `--restore-only`). */
+               if (subsession_exists(uid, backend.TemplateName()))
+                       return;
+               add_user_subsession_inner(main_path, uid, backend.TemplateName(), backend, false);
+       };
+
+       add_template_for_backend(DirBackendAddFixedSize {TEMPLATE_IMAGE_SIZE_KB});
+       add_template_for_backend(DirBackendAddRegularDir {});
+} catch (const std::exception &ex) {
+       LOGE("Exception while regenerating template subsessions for uid %d: %s", uid, ex.what());
+       throw;
+}
+
+void restore_all_user_sessions(bool restore_only)
 {
        /* In theory this should live among OS or FS helpers, but
         * this happens at early boot so we care about performance
@@ -170,6 +193,15 @@ void restore_all_user_sessions()
 
                LOGI("Restoring last session for user %s (uid %d)", username.c_str(), uid);
                restore_user_session(username, uid);
+
+               if (!restore_only) {
+                       /* Templates aren't supposed to be used directly,
+                        * and we always get a "fresh" copy when regenerated,
+                        * so we don't need to create them at this point and
+                        * can save up on some boot-time performance. */
+                       LOGI("Regenerating template sessions for user %s (uid %d)", username.c_str(), uid);
+                       regenerate_template_sessions(uid);
+               }
        }
 }
 
index 146b4106cbb57afcba1afc639c858db4c2fe90f7..d458ddaa6c0c98417fcb0a01be09e7db8e980249 100644 (file)
@@ -22,4 +22,4 @@
 
 #pragma once
 
-void restore_all_user_sessions();
+void restore_all_user_sessions(bool only_restore);
index dc2e744af44483fbb7b0dca50dde550c40727be4..aa86ed3a18a6abe64bb4bad5741bbdcdfa61026b 100644 (file)
@@ -9,7 +9,7 @@
 #include "test_hlp.hpp"
 
 
-const int  action_items = 44;
+const int  action_items = 50;
 
 TEST(subsession_add_remove_test, FailAtAddRemoveUser) {
        using ud_t = ud_data_t<std::array<api_call_res_t, action_items>>;
@@ -17,6 +17,46 @@ TEST(subsession_add_remove_test, FailAtAddRemoveUser) {
        ud_t ud_data = { .loop = g_main_loop_new(NULL, FALSE),
                .results = std::array<api_call_res_t, action_items>  {
 
+                       // Cannot add templates names (2 reserved names x 2 request backends, both names block both backends)
+                       api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::fixed_size_template)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if add  [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error (regular dir backend)",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error (regular dir backend)", },
+
+                       api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if add  [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error (regular dir backend)",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error (regular dir backend)", },
+
+
+                       api_call_res_t{ .call_result = std::move(subsession_add_user_fixed_size_l<subsession_5001>( TestBadUserStr::fixed_size_template, 10*1024)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if add  [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error (fixed size backend)",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error (fixed size backend)", },
+
+                       api_call_res_t{ .call_result = std::move(subsession_add_user_fixed_size_l<subsession_5001>( TestBadUserStr::regular_dir_template, 10*1024)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if add  [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error (fixed size backend)",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error (fixed size backend)", },
+
+                       // Cannot remove templates either
+                       api_call_res_t{ .call_result = std::move(subsession_remove_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if remove [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if remove [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error", },
+                       api_call_res_t{ .call_result = std::move(subsession_remove_user_l<subsession_5001>(TestBadUserStr::fixed_size_template)),
+                                                       .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .desc = "Check if remove [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error",
+                                                       .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                                                       .cb_desc = "Check if remove [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error", },
+
+
+                       // Misc
                        api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::bad_user_1)),
                                                        .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
                                                        .desc = "Check if add  [ " + std::string(TestBadUserStr::bad_user_1) + " ] returns error (regular dir backend)",
index 522b58572a298215e7486f2270255ff2a6c1a661..4533ab16172ce51af993d3b33a1c6c567b005d31 100644 (file)
@@ -154,15 +154,16 @@ TEST(subsession_get_user_list, GetUserListOk) {
 
        loop_run_for_test(callback_pending_ud<ud_get_user_t>,(gpointer*)&ud, ud.loop);
 
-       summarize_results_for_get_user_list(std::get<get_user::get_user_list_0>(ud.t), std::array<std::string, 1>({""}));
-       std::array<std::string, 3> arr{std::string(TestUserStr::user_0), std::string(TestUserStr::user_1), std::string(TestUserStr::user_2)};
-
-       summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_1_2>(ud.t), arr);
-       summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_mixed_1_2>(ud.t), arr);
-       summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_fixed_size_1_2>(ud.t), arr);
-       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_1_2_count_only>(ud.t), 3);
-       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_mixed_1_2_count_only>(ud.t), 3);
-       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_fixed_size_1_2_count_only>(ud.t), 3);
+       summarize_results_for_get_user_list(std::get<get_user::get_user_list_0>(ud.t), std::array<std::string, 3>({"", "__template_fixed__", "__template_reg__"}));
+       std::array<std::string, 5> arr{std::string(TestUserStr::user_0), std::string(TestUserStr::user_1), std::string(TestUserStr::user_2)
+               , "__template_fixed__", "__template_reg__"};
+
+       summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_1_2>(ud.t), arr);
+       summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_mixed_1_2>(ud.t), arr);
+       summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_fixed_size_1_2>(ud.t), arr);
+       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_1_2_count_only>(ud.t), 5);
+       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_mixed_1_2_count_only>(ud.t), 5);
+       summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_fixed_size_1_2_count_only>(ud.t), 5);
        summarize_results<get_user::action_items>(results);
 }
 
index 0ca6bd7f7b20689426a88ba4d468d8af02baecfe..24966532bc655931a410f3820858ea68245a52e3 100644 (file)
@@ -6,7 +6,7 @@
 #include "test_hlp.hpp"
 
 
-const int action_items = 12;
+const int action_items = 14;
 
 TEST(subsession_switch_user_test, FailAtSwitchUser) {
 
@@ -85,6 +85,19 @@ TEST(subsession_switch_user_test, FailAtSwitchUser) {
                                .desc = "Check if switch to   [ " + std::string(TestUserStr::user_2) + " ] returns no error",
                                .cb_expected = SUBSESSION_ERROR_NONE,
                                .cb_desc = "Check if switch to [" + std::string(TestUserStr::user_2) + " ] callback returns no error", },
+
+                       // Cannot switch into templates
+                       api_call_res_t{ .call_result = std::move(subsession_switch_user_l<subsession_5001>( TestBadUserStr::fixed_size_template)),
+                               .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                               .desc = "Check if switch to template [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error",
+                               .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                               .cb_desc = "Check if switch to template [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error", },
+
+                       api_call_res_t{ .call_result = std::move(subsession_switch_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+                               .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                               .desc = "Check if switch to template [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error",
+                               .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+                               .cb_desc = "Check if switch to template [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error", },
                }
        };
 
index bd714f372797aad31e751558061f0cd47d49d2f8..9c0ac667dd7229200a2bcb4c5dd0674bcf1a632e 100644 (file)
@@ -46,6 +46,9 @@ namespace TestBadUserStr {
        //Below filled  subsession_user_t array without '\0' (null) terminator, use only for testing.
        [[maybe_unused]]  static subsession_user_t bad_user_20 = {'1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6',
        '7','8','9','0'};
+
+       [[maybe_unused]]  static subsession_user_t  fixed_size_template = "__template_fixed__";
+       [[maybe_unused]]  static subsession_user_t regular_dir_template = "__template_reg__";
 };
 
 static constexpr int MAGIC_ADD     = 111;