From: Michal Bloch Date: Mon, 24 Feb 2025 12:49:41 +0000 (+0100) Subject: Add fixed-size backend. X-Git-Tag: accepted/tizen/unified/20250228.071927~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7220d1be684541cbb298f1e6eeabeaca7c20b6fb;p=platform%2Fcore%2Fsystem%2Fsessiond.git Add fixed-size backend. Change-Id: I398db9e586102c97b44724ae52c112b5f2c50638 --- diff --git a/src/service/CMakeLists.txt b/src/service/CMakeLists.txt index 3214eb9..d5dced8 100644 --- a/src/service/CMakeLists.txt +++ b/src/service/CMakeLists.txt @@ -11,6 +11,7 @@ set( src/fs_helpers.cpp src/os_ops.cpp src/dir_backend_regular_dir.cpp + src/dir_backend_fixed_size.cpp src/tuple_g_variant_helpers.hpp ) add_executable(sessiond ${sessiond_SRCS}) diff --git a/src/service/src/dir_backend_fixed_size.cpp b/src/service/src/dir_backend_fixed_size.cpp new file mode 100644 index 0000000..2e921b6 --- /dev/null +++ b/src/service/src/dir_backend_fixed_size.cpp @@ -0,0 +1,223 @@ +/* MIT License + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ + +#undef LOG_TAG +#define LOG_TAG "SESSIOND" +#include + +#include "dir_backend_fixed_size.hpp" + +#include +#include +#include + +#include + +namespace fs = std::filesystem; +using namespace std::literals; + +// Should be unique per-backend. +static const std::string TMP_NEW_PREFIX = ".tmpfixednew"; + +fs::path DirBackendFixedSize::GetImagePathFromSubsessionPath(fs::path subsession_path) +{ + subsession_path.replace_filename(subsession_path.filename().native() + ".img"); + return subsession_path; +} + +/* TODO: perhaps there should also be a "do_exec" wrapper which would + * accept a child lambda and do the throwing fork and the waitpid */ + +static int throwing_fork() +{ + const int child_pid = fork(); + if (child_pid == -1) { + const auto err = errno; + throw std::system_error (err, std::generic_category (), "fork() failed!"); + } + return child_pid; +} + +static void throw_if_child_failed(int child_pid, std::string_view message_on_fail) +{ + int status; + const int ret = waitpid(child_pid, &status, 0); + if (ret == -1) { + const auto err = errno; + throw std::system_error (err, std::generic_category (), "waitpid() failed!"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + throw std::runtime_error (std::string(message_on_fail)); +} + +static void do_mkfs(const fs::path& image_path, int uid, int gid, uint64_t size_kB) +{ + const auto child_pid = throwing_fork(); + if (child_pid == 0) { + const auto mkfs = "/usr/sbin/mkfs.ext2"sv; + + /* Would ideally be std::format instead of this ugly stack, + * but some important downstream forks use obsolete gcc. */ + std::string owner_arg = "root_owner="; + owner_arg += std::to_string(uid); + owner_arg += ":"; + owner_arg += std::to_string(gid); + + std::string size_arg = std::to_string(size_kB); + + execl + ( mkfs.data(), mkfs.data() /* argv[0] convention */ + , "-E", owner_arg.c_str() + , "-m", "0" + , image_path.c_str() + , size_arg.c_str() + , (char *) NULL + ); + + _exit(1); + } else { + throw_if_child_failed(child_pid, "mkfs.ext2 failed!"); + } +} + +static void do_mount(const fs::path& image_path, const fs::path& mount_path) +{ + /* Don't just call mount() since there's some extra steps involved + * in making it work properly, such as setting up a loop device, + * that the mount binary does for us. */ + + const auto child_pid = throwing_fork(); + if (child_pid == 0) { + const auto mount_exe = "/usr/bin/mount"sv; + + execl + ( mount_exe.data(), mount_exe.data() /* argv[0] convention */ + , "-o", "loop" + , image_path.c_str() + , mount_path.c_str() + , (char *) NULL + ); + + _exit(1); + } else { + throw_if_child_failed(child_pid, "mount failed!"); + } +} + +static void do_umount(const fs::path& path) +{ + const auto child_pid = throwing_fork(); + if (child_pid == 0) { + const auto umount_exe = "/usr/bin/umount"sv; + + execl + ( umount_exe.data(), umount_exe.data() /* argv[0] convention */ + , path.c_str() + , (char *) NULL + ); + + _exit(1); + } else { + throw_if_child_failed(child_pid, "umount failed!"); + } +} + +fs::path DirBackendAddFixedSize::AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const +{ + /* Work off a temp image first so that the "real" image + * can appear full-fledged atomically via rename. */ + auto image_path = DirBackendFixedSize::GetImagePathFromSubsessionPath(subsession_path); + image_path.replace_filename(TMP_NEW_PREFIX + image_path.filename().native()); + + do_mkfs(image_path, uid, gid, size_kB); + + auto tmp_subsession_path = subsession_path; + tmp_subsession_path.replace_filename(TMP_NEW_PREFIX + subsession_path.filename().native()); + + try { + fs::create_directory(tmp_subsession_path); + } catch (const std::exception& ex) { + fs::remove(image_path); + throw; + } + + try { + do_mount(image_path, tmp_subsession_path); + } catch (const std::exception& ex) { + fs::remove(tmp_subsession_path); + fs::remove(image_path); + throw; + } + + return tmp_subsession_path; +} + +void DirBackendAddFixedSize::AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const +{ + auto tmp_image_path = DirBackendFixedSize::GetImagePathFromSubsessionPath(subsession_path); + tmp_image_path.replace_filename(TMP_NEW_PREFIX + tmp_image_path.filename().native()); + + fs::remove(tmpdir_path); + fs::remove(tmp_image_path); +} + +void DirBackendAddFixedSize::AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path) const +{ + try { + do_umount(tmpdir_path); + fs::remove(tmpdir_path); + } catch (const std::exception& ex) { + LOGE("umount & rmdir (%s) failed!", tmpdir_path.c_str()); + throw; + } + + const auto image_path = DirBackendFixedSize::GetImagePathFromSubsessionPath(subsession_path); + auto temp_image_path = image_path; + temp_image_path.replace_filename(TMP_NEW_PREFIX + image_path.filename().native()); + fs::rename(temp_image_path, image_path); + + /* Order matters - handle .img first and the directory last, + * since all other logic assumes that the existence of the dir + * signifies that a valid subsession exists. */ + fs::create_directory(subsession_path); +} + +void DirBackendFixedSize::RemoveSubsession (const fs::path& subsession_path) const +{ + fs::remove(subsession_path); + + const auto image_path = GetImagePathFromSubsessionPath(subsession_path); + fs::remove(image_path); +} + +void DirBackendFixedSize::SwitchSubsessionAway (const std::filesystem::path& subsession_path) const +{ + /* Note, we cannot keep the subsession mounted permanently, + * from Add until Remove, because reboots happen. */ + do_umount(subsession_path); +} + +void DirBackendFixedSize::SwitchSubsessionInto (const std::filesystem::path& subsession_path) const +{ + const auto image_path = GetImagePathFromSubsessionPath(subsession_path); + do_mount(image_path, subsession_path); +} diff --git a/src/service/src/dir_backend_fixed_size.hpp b/src/service/src/dir_backend_fixed_size.hpp new file mode 100644 index 0000000..8cdca7f --- /dev/null +++ b/src/service/src/dir_backend_fixed_size.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "dir_backend.hpp" + +#include + +namespace fs = std::filesystem; + +struct DirBackendFixedSize : public DirBackend { + void RemoveSubsession (const fs::path& subsession_path) const override; + void SwitchSubsessionAway (const fs::path& subsession_path) const override; + void SwitchSubsessionInto (const fs::path& subsession_path) const override; + + static fs::path GetImagePathFromSubsessionPath(fs::path subsession_path); +}; + +struct DirBackendAddFixedSize : public DirBackendAdd { + uint32_t size_kB; + DirBackendAddFixedSize(uint32_t s) : size_kB(s) { } + + 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) const override; + void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const override; +}; diff --git a/src/service/src/fs_helpers.cpp b/src/service/src/fs_helpers.cpp index 1b1df6d..87c26a1 100644 --- a/src/service/src/fs_helpers.cpp +++ b/src/service/src/fs_helpers.cpp @@ -34,6 +34,7 @@ #include "fs_helpers.hpp" #include "dir_backend_regular_dir.hpp" +#include "dir_backend_fixed_size.hpp" namespace fs = std::filesystem; using namespace std::string_literals; @@ -251,6 +252,10 @@ void add_user_subsession(const int session_uid, const std::string_view subsessio static const DirBackend& GetBackendOfSubsession(const fs::path& path) { + static const DirBackendFixedSize dbfs; + if (fs::exists(DirBackendFixedSize::GetImagePathFromSubsessionPath(path))) + return dbfs; + static const DirBackendRegularDir dbrd; return dbrd; }