From: Junghyun Yeon Date: Fri, 3 Sep 2021 00:48:51 +0000 (+0900) Subject: Implement res-copy and its unittests X-Git-Tag: submit/tizen_6.0/20210910.052738~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=520b8186e92ee203341939ec5a8637e4153f232e;p=platform%2Fcore%2Fappfw%2Fpkgmgr-tool.git Implement res-copy and its unittests Change-Id: Ib77c27297854f8e8b15195459aab69cd15668e0b Signed-off-by: Junghyun Yeon --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b4891e..0079093 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ SET(TARGET_PKG_GETSIZE "pkg_getsize") SET(TARGET_PKG_CLEARDATA "pkg_cleardata") SET(TARGET_INSTALL_PRELOAD_PKG "install_preload_pkg") SET(TARGET_PKG_UPGRADE "pkg_upgrade") +SET(TARGET_RES_COPY "res-copy") SET(TARGET_RSC_SLICE "rsc-slice") SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") @@ -36,15 +37,27 @@ PKG_CHECK_MODULES(STORAGE_DEPS REQUIRED storage) PKG_CHECK_MODULES(SQLITE_DEPS REQUIRED sqlite3) PKG_CHECK_MODULES(SMACK_DEPS REQUIRED libsmack) +FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem program_options iostreams) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wl,-zdefs -pie" ) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden") SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -Wall -Werror") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Werror -ffunction-sections -fdata-sections -fmerge-all-constants -fPIE") SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -fPIE") SET(CMAKE_C_FLAGS_RELEASE "-O2 -fPIE") +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed,--gc-sections -pie") + ADD_SUBDIRECTORY(src) +ADD_SUBDIRECTORY(tests) + +IF(NOT DEFINED MINIMUM_BUILD) +ENABLE_TESTING() +SET(PKGMGR_TOOL_UNIT_TESTS pkgmgr-tool_unittests) +ADD_TEST(NAME ${PKGMGR_TOOL_UNIT_TESTS} COMMAND ${PKGMGR_TOOL_UNIT_TESTS}) +ENDIF(NOT DEFINED MINIMUM_BUILD) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/mime.wac.xml DESTINATION /usr/share/mime/packages/) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/mime.tpk.xml DESTINATION /usr/share/mime/packages/) diff --git a/packaging/pkgmgr-tool.spec b/packaging/pkgmgr-tool.spec index 22e27d3..73c4fad 100644 --- a/packaging/pkgmgr-tool.spec +++ b/packaging/pkgmgr-tool.spec @@ -10,6 +10,7 @@ Source1001: %{name}.manifest Requires: unzip Requires: smack +BuildRequires: boost-devel BuildRequires: cmake BuildRequires: gettext-tools BuildRequires: pkgconfig(glib-2.0) @@ -26,31 +27,78 @@ BuildRequires: pkgconfig(pkgmgr-installer) BuildRequires: pkgconfig(aul) BuildRequires: pkgconfig(storage) BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(gmock) BuildRequires: pkgmgr-info-parser-devel BuildRequires: pkgmgr-info-parser BuildRequires: fdupes +%if 0%{?gcov:1} +BuildRequires: lcov +BuildRequires: zip +%endif Requires(posttrans): /usr/bin/pkg_initdb %description Packager Manager Tool for packaging +%package -n pkgmgr-tool_unittests +Summary: GTest for pkgmgr-tool +Group: Development/Libraries +Requires: %{name} + +%description -n pkgmgr-tool_unittests +GTest for pkgmgr-tool + +%if 0%{?gcov:1} +%package gcov +Summary: pkgmgr-tool API(gcov) +Group: System/API + +%description gcov +gcov objects of an pkgmgr-tool +%endif + %prep %setup -q cp %{SOURCE1001} . %build -%cmake . +%if 0%{?gcov:1} +export CFLAGS+=" -fprofile-arcs -ftest-coverage" +export CXXFLAGS+=" -fprofile-arcs -ftest-coverage" +export FFLAGS+=" -fprofile-arcs -ftest-coverage" +export LDFLAGS+=" -lgcov" +%endif +%cmake . %__make %{?_smp_mflags} +%if 0%{?gcov:1} +mkdir -p gcov-obj +find . -name '*.gcno' -exec cp '{}' gcov-obj ';' +%endif + +%check +ctest -V +%if 0%{?gcov:1} +lcov -c --ignore-errors graph --no-external -q -d . -o pkgmgr-tool.info +genhtml pkgmgr-tool.info -o pkgmgr-tool.out +zip -r pkgmgr-tool.zip pkgmgr-tool.out +install -m 0644 pkgmgr-tool.zip %{buildroot}%{_datadir}/gcov/pkgmgr-tool.zip +%endif %install %make_install mkdir -p %{buildroot}%{_sysconfdir}/opt/upgrade +%if 0%{?gcov:1} +mkdir -p %{buildroot}%{_datadir}/gcov/obj +install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj +%endif %fdupes %{buildroot} %post /sbin/ldconfig +mkdir -p %{_sysconfdir}/skel/priv_shared_res +chsmack -t -a User::Home %{_sysconfdir}/skel/priv_shared_res # Update mime database to support package mime types update-mime-database %{_datadir}/mime @@ -64,6 +112,7 @@ update-mime-database %{_datadir}/mime %{_bindir}/pkg_cleardata %{_bindir}/pkginfo %{_bindir}/rsc-slice +%{_bindir}/res-copy %{_bindir}/pkg_upgrade %attr(0755,root,root) %{_bindir}/install_preload_pkg %{_datadir}/mime/packages/mime.wac.xml @@ -82,3 +131,10 @@ update-mime-database %{_datadir}/mime %attr(0700,root,root) %{_sysconfdir}/opt/upgrade/pkgmgr.patch.sh %attr(0700,root,root) /usr/share/fixed_multiuser/scripts/pkgmgr-clear-skel.sh %attr(0700,root,root) %{_sysconfdir}/package-manager/pkgmgr-label-initial-image.sh + +%files -n pkgmgr-tool_unittests +%{_bindir}/pkgmgr-tool_unittests +%if 0%{?gcov:1} +%files gcov +%{_datadir}/gcov/* +%endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18fdd25..26e73c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,4 +4,5 @@ ADD_SUBDIRECTORY(pkg_getsize) ADD_SUBDIRECTORY(pkg_upgrade) ADD_SUBDIRECTORY(pkgcmd) ADD_SUBDIRECTORY(pkginfo) +ADD_SUBDIRECTORY(res-copy) ADD_SUBDIRECTORY(rsc-slice) diff --git a/src/res-copy/CMakeLists.txt b/src/res-copy/CMakeLists.txt new file mode 100644 index 0000000..172cabd --- /dev/null +++ b/src/res-copy/CMakeLists.txt @@ -0,0 +1,21 @@ +# Target - sources +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/src SRCS) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) + +# Target - definition +ADD_EXECUTABLE(${TARGET_RES_COPY} ${SRCS}) + +# Dependency +APPLY_PKG_CONFIG(${TARGET_RES_COPY} PUBLIC + AUL_DEPS + GLIB_DEPS + BUNDLE_DEPS + Boost + PKGMGR_DEPS + PKGMGR_INFO_DEPS + PKGMGR_INSTALLER_DEPS + TZPLATFORM_DEPS +) + +# Install +INSTALL(TARGETS ${TARGET_RES_COPY} DESTINATION bin) \ No newline at end of file diff --git a/src/res-copy/include/abstract_request_handler.hh b/src/res-copy/include/abstract_request_handler.hh new file mode 100644 index 0000000..26c98d8 --- /dev/null +++ b/src/res-copy/include/abstract_request_handler.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ABSTRACT_REQUEST_HANDLER_HH_ +#define ABSTRACT_REQUEST_HANDLER_HH_ + +#include +#include + +#include "include/error_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class AbstractRequestHandler { + public: + AbstractRequestHandler( + std::string pkgid, uid_t uid, + std::string root_path, std::list path_list) + : pkgid_(pkgid), uid_(uid), root_path_(root_path), + path_list_(path_list) {}; + + virtual ErrorType Execute() = 0; + virtual const std::string GetRequestHandlerType() const = 0; + virtual std::list GetResultPathList(); + + protected: + std::string GetRootPath(); + std::string GetSrcRootPath(); + std::string GetDstRootPath(); + const std::string GetPkgID() const; + std::list& GetPathList(); + uid_t GetUID() const; + + private: + std::string pkgid_; + uid_t uid_; + std::string root_path_; + std::list path_list_; + std::string result_path_; +}; + +} // namespace res_handler + +#endif // ABSTRACT_REQUEST_HANDLER_HH_ diff --git a/src/res-copy/include/condition_validator.hh b/src/res-copy/include/condition_validator.hh new file mode 100644 index 0000000..ca10a5a --- /dev/null +++ b/src/res-copy/include/condition_validator.hh @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONDITION_VALIDATOR_HH_ +#define CONDITION_VALIDATOR_HH_ + +#include +#include + +#include "include/error_type.hh" +#include "include/res_path_info.hh" +#include "include/request_type.hh" + +namespace res_handler { + +class ConditionValidator { + public: + ConditionValidator(std::string pkgid, uid_t uid); + ErrorType ValidateCondition(ReqType req_type, + std::list path_list); + + private: + std::string pkgid_; + std::string root_path_; + uid_t uid_; + + ErrorType CheckCopyRequest(std::list path_list); + ErrorType CheckRemoveRequest(std::list path_list); +}; + +} // namespace res_handler + +#endif // CONDITION_VALIDATOR_HH_ diff --git a/src/res-copy/include/copy_request_handler.hh b/src/res-copy/include/copy_request_handler.hh new file mode 100644 index 0000000..37971f1 --- /dev/null +++ b/src/res-copy/include/copy_request_handler.hh @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COPY_REQUEST_HANDLER_HH_ +#define COPY_REQUEST_HANDLER_HH_ + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class CopyRequestHandler : public AbstractRequestHandler { + public: + CopyRequestHandler(std::string pkgid, uid_t uid, std::string root_path, + std::list path_list); + + ErrorType Execute() override; + const std::string GetRequestHandlerType() const override; + std::list GetResultPathList() override; +}; + +} // namespace res_handler + +#endif // COPY_REQUEST_HANDLER_HH_ diff --git a/src/res-copy/include/createdir_request_handler.hh b/src/res-copy/include/createdir_request_handler.hh new file mode 100644 index 0000000..63eccdf --- /dev/null +++ b/src/res-copy/include/createdir_request_handler.hh @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CREATEDIR_REQUEST_HANDLER_HH_ +#define CREATEDIR_REQUEST_HANDLER_HH_ + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class CreateDirRequestHandler : public AbstractRequestHandler { + public: + CreateDirRequestHandler(std::string pkgid, uid_t uid, std::string root_path, + std::list path_list); + + ErrorType Execute() override; + const std::string GetRequestHandlerType() const override; +}; + +} // namespace res_handler + +#endif // CREATEDIR_REQUEST_HANDLER_HH_ diff --git a/src/res-copy/include/error_type.hh b/src/res-copy/include/error_type.hh new file mode 100644 index 0000000..c2309bd --- /dev/null +++ b/src/res-copy/include/error_type.hh @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ERROR_TYPE_HH_ +#define ERROR_TYPE_HH_ + +#include + +namespace res_handler { + +enum ErrorType { + ERROR_NONE = PKGMGR_INSTALLER_ERRCODE_OK, + ERROR_INVALID_PARAMETER = PKGMGR_INSTALLER_ERRCODE_INVALID_VALUE, + ERROR_PKG_NOT_FOUND = PKGMGR_INSTALLER_ERRCODE_PACKAGE_NOT_FOUND, + ERROR_PERMISSION_DENIED = PKGMGR_INSTALLER_ERRCODE_SECURITY_ERROR, + ERROR_SYSTEM_ERROR = PKGMGR_INSTALLER_ERRCODE_ERROR, + ERROR_RES_NOT_FOUND = PKGMGR_INSTALLER_ERRCODE_PACKAGE_NOT_FOUND, + ERROR_OUT_OF_SPACE = PKGMGR_INSTALLER_ERRCODE_OUT_OF_SPACE, + ERROR_OUT_OF_MEMORY = PKGMGR_INSTALLER_ERRCODE_ERROR +}; + +} // namespace res_handler + +#endif // ERROR_TYPE_HH_ diff --git a/src/res-copy/include/event_signal_sender.hh b/src/res-copy/include/event_signal_sender.hh new file mode 100644 index 0000000..f702208 --- /dev/null +++ b/src/res-copy/include/event_signal_sender.hh @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_SIGNAL_SENDER_HH_ +#define EVENT_SIGNAL_SENDER_HH_ + +#include +#include +#include + +#include + +#include "include/error_type.hh" +#include "include/res_path_info.hh" +#include "include/request_type.hh" + +namespace res_handler { + +class EventSignalSender { + public: + EventSignalSender(pkgmgr_installer* installer); + bool SendStart(const std::list& res_path_info); + bool SendOK(const std::list& res_path_info); + bool SendFail(ErrorType error, const std::list& res_path_info); + void SetPkgID(std::string pkgid); + void SetReqType(ReqType req_type); + void SetUID(uid_t uid); + void SetSessionID(std::string session_id); + + private: + bool SendSignal(const char* status, ErrorType error, + const std::list& res_path_info); + + std::string pkgid_; + ReqType req_type_; + uid_t uid_; + std::string session_id_; + std::unique_ptr installer_; +}; + +} // res_handler + +#endif // EVENT_SIGNAL_SENDER_HH_ diff --git a/src/res-copy/include/file_util.hh b/src/res-copy/include/file_util.hh new file mode 100644 index 0000000..238516a --- /dev/null +++ b/src/res-copy/include/file_util.hh @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILE_UTIL_HH_ +#define FILE_UTIL_HH_ + +#include +#include +#include + +namespace bf = boost::filesystem; + +namespace res_handler { + +enum FSFlag : int { + FS_NONE = 0, + FS_MERGE_SKIP = (1 << 0), + FS_MERGE_OVERWRITE = (1 << 1), + FS_COMMIT_COPY_FILE = (1 << 2), +}; + +FSFlag operator|(FSFlag a, FSFlag b); + +bool CreateDir(const boost::filesystem::path& path); +bool CreateDirs(const std::string& pkgid, const boost::filesystem::path& path); + +bool CopyDir(const boost::filesystem::path& src, + const boost::filesystem::path& dst, + FSFlag flags = FS_NONE, bool skip_symlink = false); + +bool CopyFile(const boost::filesystem::path& src, + const boost::filesystem::path& dst); + +bool RemoveAll(const boost::filesystem::path& path); + +bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path, + mode_t mode, uid_t uid, gid_t gid); + +bool SetDirPermissions(const boost::filesystem::path& path, + mode_t mode); + +bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid); + +} // namespace res_handler + +#endif // FILE_UTIL_HH_ diff --git a/src/res-copy/include/logging.hh b/src/res-copy/include/logging.hh new file mode 100644 index 0000000..814e875 --- /dev/null +++ b/src/res-copy/include/logging.hh @@ -0,0 +1,180 @@ +// Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#ifndef LOGGING_HH_ +#define LOGGING_HH_ + +#include + +#ifndef PROJECT_TAG +#define PROJECT_TAG "PKGMGR_TOOL" +#endif + +#ifdef LOG +#undef LOG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __FILENAME__ +#define __FILENAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +namespace utils { + +enum class LogLevel { + LOG_ERROR, + LOG_WARNING, + LOG_INFO, + LOG_DEBUG, +}; + +log_priority LogLevelToPriority(LogLevel level); + +template struct LogTag; +template<> struct LogTag { + static constexpr const char* value = "\033[1;31m| ERROR |\033[0m"; +}; +template<> struct LogTag { + static constexpr const char* value = "\033[1;33m| WARNING |\033[0m"; +}; +template<> struct LogTag { + static constexpr const char* value = "\033[1;32m| INFO |\033[0m"; +}; +template<> struct LogTag { + static constexpr const char* value = "\033[0m| DEBUG |\033[0m"; +}; + +template > +class StringStream : private std::basic_ostringstream { + public: + using std::basic_ostringstream::str; + + template + StringStream& operator<<(const T& value) { + static_cast &>(*this) << value; + return *this; + } +}; + +// Interface class for logging backends. The custom LogBackend which wants +// log using LOG() macro should be implement following interface. +class ILogBackend { + public: + virtual void WriteLog(LogLevel level, const std::string& tag, + const std::string& logstr) = 0; +}; + +class DLogBackend : public ILogBackend { + public: + void WriteLog(LogLevel level, const std::string& tag, + const std::string& logstr) override { + dlog_print(LogLevelToPriority(level), tag.c_str(), "%s", + Escape(logstr).c_str()); + } + + private: + // Since LogCatcher passes input to dlog_print(), the input which contains + // format string(such as %d, %n) can cause unexpected result. + // This is simple function to escape '%'. + // NOTE: Is there any gorgeous way instead of this? + std::string Escape(const std::string& str) const { + std::string escaped = std::string(str); + size_t start_pos = 0; + std::string from = "%"; + std::string to = "%%"; + while ((start_pos = escaped.find(from, start_pos)) != std::string::npos) { + escaped.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + return escaped; + } +}; + +class LogCore { + public: + // Do not call this function at destructor of global object + static LogCore& GetCore() { + static LogCore core; + return core; + } + + void AddLogBackend(std::shared_ptr backend) { + backend_list_.emplace_back(backend); + } + + void Log(LogLevel level, const std::string& tag, const std::string& log) { + for (auto& backend : backend_list_) + backend->WriteLog(level, tag, log); + } + + private: + LogCore() { + // add default dlog backend + AddLogBackend(std::shared_ptr(new DLogBackend())); + } + ~LogCore() = default; + LogCore(const LogCore&) = delete; + LogCore& operator=(const LogCore&) = delete; + + std::vector> backend_list_; +}; + +class LogCatcher { + public: + LogCatcher(LogLevel level, const char* tag) + : level_(level), tag_(tag) { } + + void operator&(const StringStream& str) const { + LogCore::GetCore().Log(level_, tag_, str.str()); + } + + private: + LogLevel level_; + std::string tag_; +}; + +} // namespace utils + + +inline static const constexpr char* __tag_for_logging() { + return ""; +} + +inline static const constexpr char* __tag_for_project() { + return PROJECT_TAG; +} + +// To be defined in class namespace if user want different log tag for given +// scope +#define SCOPE_LOG_TAG(TAG) \ + inline static const constexpr char* __tag_for_logging() { \ + return #TAG; \ + } \ + +// Simple logging macro of following usage: +// LOG(LEVEL) << object_1 << object_2 << object_n; +// where: +// LEVEL = ERROR | WARNING | INFO | DEBUG +#define LOG(LEVEL) \ + ::utils::LogCatcher( \ + ::utils::LogLevel::LOG_ ## LEVEL, __tag_for_project()) \ + & ::utils::StringStream() \ + << std::string(::utils::LogTag<::utils::LogLevel::LOG_ ## LEVEL>::value) \ + << " " << std::setw(25) << std::left << __tag_for_logging() \ + << " : " << std::setw(36) \ + << (std::string(__FILENAME__) + ":" + std::to_string(__LINE__)).c_str() \ + << std::setw(0) << " : " \ + +#endif // LOGGING_HH_ diff --git a/src/res-copy/include/param_checker.hh b/src/res-copy/include/param_checker.hh new file mode 100644 index 0000000..8fa10fb --- /dev/null +++ b/src/res-copy/include/param_checker.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARAM_CHECKER_HH_ +#define PARAM_CHECKER_HH_ + +#include +#include + +#include "include/request_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class ParamChecker { + public: + ParamChecker(int argc, char* argv[]); + ReqType GetRequestType(); + std::string GetPkgID() const; + std::string GetSessionID() const; + uid_t GetUID() const; + const std::list& GetPathList() const; + bool Validate(); + + private: + std::list path_info_list_; + std::string pkgid_; + std::string session_id_; + uid_t uid_ = 0; + ReqType req_type_ = ReqType::REQ_TYPE_UNKNOWN; + + void SetRequestType(std::string key); + bool ValidatePkgID(); + bool ValidatePathList(); +}; + +} // res_handler + +#endif // PARAM_CHECKER_HH_ diff --git a/src/res-copy/include/remove_request_handler.hh b/src/res-copy/include/remove_request_handler.hh new file mode 100644 index 0000000..0c3d3eb --- /dev/null +++ b/src/res-copy/include/remove_request_handler.hh @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REMOVE_REQUEST_HANDLER_HH_ +#define REMOVE_REQUEST_HANDLER_HH_ + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class RemoveRequestHandler : public AbstractRequestHandler { + public: + RemoveRequestHandler( + std::string pkgid, uid_t uid, std::string root_path, + std::list path_list); + + ErrorType Execute() override; + const std::string GetRequestHandlerType() const override; +}; + +} // namespace res_handler + +#endif // REMOVE_REQUEST_HANDLER_HH_ diff --git a/src/res-copy/include/request_handler_invoker.hh b/src/res-copy/include/request_handler_invoker.hh new file mode 100644 index 0000000..be52769 --- /dev/null +++ b/src/res-copy/include/request_handler_invoker.hh @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REQUEST_HANDLER_INVOKER_HH_ +#define REQUEST_HANDLER_INVOKER_HH_ + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/event_signal_sender.hh" +#include "include/param_checker.hh" + +namespace res_handler { + +class RequestHandlerInvoker { + public: + RequestHandlerInvoker(ParamChecker option, + std::shared_ptr signal); + + bool Validate(); + bool Execute(); + const std::string GetHandlerType() const; + + private: + ParamChecker option_; + std::unique_ptr handler_; + std::shared_ptr signal_; + + void SetReqHandler(); +}; + +} // namespace res_handler + +#endif // REQUEST_HANDLER_INVOKER_HH_ diff --git a/src/res-copy/include/request_type.hh b/src/res-copy/include/request_type.hh new file mode 100644 index 0000000..71be941 --- /dev/null +++ b/src/res-copy/include/request_type.hh @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REQUEST_TYPE_HH_ +#define REQUEST_TYPE_HH_ + +namespace res_handler { + +enum ReqType { + REQ_TYPE_NEW = 0, + REQ_TYPE_REMOVE = 1, + REQ_TYPE_UNINSTALL, + REQ_TYPE_CREATEDIR, + REQ_TYPE_UNKNOWN +}; + +} // namespace res_handler + +#endif // REQUEST_TYPE_HH_ diff --git a/src/res-copy/include/res_handler.hh b/src/res-copy/include/res_handler.hh new file mode 100644 index 0000000..c0bff45 --- /dev/null +++ b/src/res-copy/include/res_handler.hh @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RES_HANDLER_HH_ +#define RES_HANDLER_HH_ + +#include + +#include "include/request_handler_invoker.hh" + +namespace res_handler { + +class ResHandler { + public: + ResHandler() {}; + bool Init(int argc, char* argv[]); + bool Run(); + + private: + std::unique_ptr handler_; +}; + +} // namespace res_handler + +#endif // RES_HANDLER_HH_ diff --git a/src/res-copy/include/res_path_info.hh b/src/res-copy/include/res_path_info.hh new file mode 100644 index 0000000..54cc0f4 --- /dev/null +++ b/src/res-copy/include/res_path_info.hh @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RES_PATH_INFO_HH_ +#define RES_PATH_INFO_HH_ + +#include + +namespace res_handler { + +class ResPathInfo { + public: + enum class State { + NONE, + OK, + FAILED + }; + + ResPathInfo(std::string src, std::string dst, State state = State::NONE); + + const std::string& GetSrcPath() const; + const std::string& GetDstPath() const; + const State GetState() const; + void SetState(State state); + + private: + std::string src_; + std::string dst_; + State state_; +}; + +} // namespace res_handler + +#endif // RES_PATH_INFO_HH_ diff --git a/src/res-copy/include/uninstall_request_handler.hh b/src/res-copy/include/uninstall_request_handler.hh new file mode 100644 index 0000000..8844c94 --- /dev/null +++ b/src/res-copy/include/uninstall_request_handler.hh @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UNINSTALL_REQUEST_HANDLER_HH_ +#define UNINSTALL_REQUEST_HANDLER_HH_ + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/res_path_info.hh" + +namespace res_handler { + +class UninstallRequestHandler : public AbstractRequestHandler { + public: + UninstallRequestHandler( + std::string pkgid, uid_t uid, + std::string root_path, std::list path_list); + + ErrorType Execute() override; + const std::string GetRequestHandlerType() const override; +}; + +} // namespace res_handler + +#endif // UNINSTALL_REQUEST_HANDLER_HH_ diff --git a/src/res-copy/src/abstract_request_handler.cc b/src/res-copy/src/abstract_request_handler.cc new file mode 100644 index 0000000..b432e47 --- /dev/null +++ b/src/res-copy/src/abstract_request_handler.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/abstract_request_handler.hh" + +#include + +#include "include/request_type.hh" +#include "include/res_path_info.hh" + +namespace { + +constexpr char kPrivSharedRes[] = "/priv_shared_res/"; +constexpr char kAppsRw[] = "/apps_rw/"; + +} // namespace + +namespace res_handler { + +std::string AbstractRequestHandler::GetRootPath() { + return root_path_; +} + +std::string AbstractRequestHandler::GetSrcRootPath() { + return root_path_ + kAppsRw + pkgid_; +} + +std::string AbstractRequestHandler::GetDstRootPath() { + return root_path_ + kPrivSharedRes + pkgid_; +} + +const std::string AbstractRequestHandler::GetPkgID() const { + return pkgid_; +} + +std::list& AbstractRequestHandler::GetPathList() { + return path_list_; +} + +uid_t AbstractRequestHandler::GetUID() const { + return uid_; +} + +std::list AbstractRequestHandler::GetResultPathList() { + return path_list_; +} + +} // namespace res_handler diff --git a/src/res-copy/src/condition_validator.cc b/src/res-copy/src/condition_validator.cc new file mode 100644 index 0000000..b089129 --- /dev/null +++ b/src/res-copy/src/condition_validator.cc @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/condition_validator.hh" + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "include/logging.hh" +#include "include/request_type.hh" +#include "include/res_path_info.hh" + +namespace bf = boost::filesystem; + +namespace { + +std::string GetRootPath(uid_t uid) { + tzplatform_set_user(uid); + const char* rootpath = tzplatform_getenv(TZ_USER_HOME); + tzplatform_reset_user(); + return rootpath; +} + +bool IsPackageExists(std::string pkgid, uid_t uid) { + pkgmgrinfo_pkginfo_h handle; + if (pkgmgrinfo_pkginfo_get_usr_pkginfo( + pkgid.c_str(), uid, &handle) != PMINFO_R_OK) { + LOG(ERROR) << "package not exists : " << pkgid; + return false; + } + pkgmgrinfo_pkginfo_destroy_pkginfo(handle); + + return true; +} + +bool CheckFreeSpaceAtPath(int64_t required_size, + const bf::path& target_location) { + boost::system::error_code error; + bf::path root = target_location; + + while (!bf::exists(root) && root != root.root_path()) + root = root.parent_path(); + + if (!bf::exists(root)) { + LOG(ERROR) << "No mount point for path: " << target_location; + return false; + } + + bf::space_info space_info = bf::space(root, error); + if (error) { + LOG(ERROR) << "Failed to get space_info: " << error.message(); + return false; + } + + return (space_info.free >= static_cast(required_size)); +} + +int64_t GetBlockSizeForPath(const bf::path& path_in_partition) { + struct stat stats; + if (stat(path_in_partition.string().c_str(), &stats)) { + LOG(ERROR) << "stat(" << path_in_partition.string() + << ") failed - error code: " << errno; + return -1; + } + return stats.st_blksize; +} + +int64_t RoundUpToBlockSizeOf(int64_t size, int64_t block_size) { + return ((size + block_size - 1) / block_size) * block_size; +} + +int64_t GetDirectorySize(const bf::path& path) { + int64_t block_size = GetBlockSizeForPath(path); + + if (block_size == -1) + return -1; + + int64_t size = 0; + for (bf::recursive_directory_iterator iter(path); + iter != bf::recursive_directory_iterator(); ++iter) { + struct stat buf; + if (lstat(iter->path().c_str(), &buf) == -1) { + LOG(ERROR) << "lstat() failed for: " << iter->path(); + return -1; + } + size += RoundUpToBlockSizeOf(buf.st_size, block_size); + } + + return size; +} + +} // namespace + +namespace res_handler { + +ConditionValidator::ConditionValidator(std::string pkgid, uid_t uid) : + pkgid_(pkgid), uid_(uid) { + root_path_ = GetRootPath(uid_); +} + +ErrorType ConditionValidator::ValidateCondition(ReqType req_type, + std::list path_list) { + if (!IsPackageExists(pkgid_, uid_) && + req_type != ReqType::REQ_TYPE_UNINSTALL) + return ErrorType::ERROR_PKG_NOT_FOUND; + + if (req_type == ReqType::REQ_TYPE_NEW) + return CheckCopyRequest(path_list); + else if (req_type == ReqType::REQ_TYPE_REMOVE) + return CheckRemoveRequest(path_list); + + return ErrorType::ERROR_NONE; +} + +ErrorType ConditionValidator::CheckCopyRequest( + std::list path_list) { + boost::filesystem::path src_root_path(root_path_); + uintmax_t res_size = 0; + + src_root_path = src_root_path / "apps_rw" / pkgid_; + for (auto& path_info : path_list) { + boost::filesystem::path res_path(src_root_path); + res_path = res_path / path_info.GetSrcPath(); + if (!boost::filesystem::exists(res_path)) { + LOG(ERROR) << "Resource not exists : " << res_path; + return ErrorType::ERROR_RES_NOT_FOUND; + } + + if (boost::filesystem::is_directory(res_path)) + res_size += GetDirectorySize(res_path); + else + res_size += boost::filesystem::file_size(res_path); + } + LOG(INFO) << "Required size for resource: " << res_size; + + if (!CheckFreeSpaceAtPath(static_cast(res_size), root_path_)) { + LOG(ERROR) << "Not enough space for resource"; + return ErrorType::ERROR_OUT_OF_SPACE; + } + + return ErrorType::ERROR_NONE; +} + +ErrorType ConditionValidator::CheckRemoveRequest( + std::list path_list) { + for (auto& path_info : path_list) { + boost::filesystem::path dst_path(root_path_); + dst_path = dst_path / "priv_shared_res"/ pkgid_ / path_info.GetDstPath(); + if (!boost::filesystem::exists(dst_path)) { + LOG(ERROR) << "Resource not exists : " << dst_path; + return ErrorType::ERROR_RES_NOT_FOUND; + } + } + + return ErrorType::ERROR_NONE; +} + +} // namespace res_handler diff --git a/src/res-copy/src/copy_request_handler.cc b/src/res-copy/src/copy_request_handler.cc new file mode 100644 index 0000000..a1bc08d --- /dev/null +++ b/src/res-copy/src/copy_request_handler.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/copy_request_handler.hh" + +#include +#include + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/file_util.hh" +#include "include/logging.hh" +#include "include/res_path_info.hh" + +namespace bf = boost::filesystem; + +namespace { + +constexpr char kCopyReqHandlerType[] = "copy"; + +} // namespace + +namespace res_handler { + +CopyRequestHandler::CopyRequestHandler( + std::string pkgid, uid_t uid, std::string root_path, + std::list path_list) : + AbstractRequestHandler(pkgid, uid, root_path, path_list) {} + +ErrorType CopyRequestHandler::Execute() { + bf::path src_root_path(GetSrcRootPath()); + bf::path dst_root_path(GetDstRootPath()); + + if (!CreateDir(dst_root_path)) + return ErrorType::ERROR_SYSTEM_ERROR; + + for (auto& path_info : GetPathList()) { + bf::path src_path = src_root_path / path_info.GetSrcPath(); + if (!bf::exists(src_path)) { + LOG(ERROR) << "Path not exists :" << src_path; + path_info.SetState(ResPathInfo::State::FAILED); + return ErrorType::ERROR_RES_NOT_FOUND; + } + + bf::path dst_path = dst_root_path / path_info.GetDstPath(); + + if (bf::is_directory(src_path)) { + if (!CopyDir(src_path, dst_path, FS_MERGE_OVERWRITE, true)) { + LOG(ERROR) << "Failed to copy directory " << src_path; + path_info.SetState(ResPathInfo::State::FAILED); + return ErrorType::ERROR_SYSTEM_ERROR; + } + } else { + if (bf::is_directory(dst_path)) + dst_path /= src_path.filename(); + + if (!CopyFile(src_path, dst_path)) { + LOG(ERROR) << "Failed to copy directory " << src_path; + path_info.SetState(ResPathInfo::State::FAILED); + return ErrorType::ERROR_SYSTEM_ERROR; + } + } + path_info.SetState(ResPathInfo::State::OK); + } + + return ErrorType::ERROR_NONE; +} + +const std::string CopyRequestHandler::GetRequestHandlerType() const { + return kCopyReqHandlerType; +} + +std::list CopyRequestHandler::GetResultPathList() { + bf::path src_root_path(GetSrcRootPath()); + bf::path dst_root_path(GetDstRootPath()); + std::list result; + + for (const auto& path_info : GetPathList()) { + bf::path src_path = src_root_path / path_info.GetSrcPath(); + bf::path dst_path = dst_root_path / path_info.GetDstPath(); + bf::path result_path = path_info.GetDstPath(); + + if (bf::is_directory(dst_path) && !bf::is_directory(src_path)) + result_path /= src_path.filename(); + + result.emplace_back(result_path.string(), "", path_info.GetState()); + } + + return result; +} + +} // namespace res_handler diff --git a/src/res-copy/src/createdir_request_handler.cc b/src/res-copy/src/createdir_request_handler.cc new file mode 100644 index 0000000..eaacee6 --- /dev/null +++ b/src/res-copy/src/createdir_request_handler.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/createdir_request_handler.hh" + +#include +#include + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/file_util.hh" +#include "include/logging.hh" +#include "include/res_path_info.hh" + +namespace bf = boost::filesystem; + +namespace { + +constexpr char kCreateDirReqHandlerType[] = "createdir"; + +} // namespace + +namespace res_handler { + +CreateDirRequestHandler::CreateDirRequestHandler( + std::string pkgid, uid_t uid, std::string root_path, + std::list path_list) : + AbstractRequestHandler(pkgid, uid, root_path, path_list) {} + +ErrorType CreateDirRequestHandler::Execute() { + bf::path dst_root_path(GetDstRootPath()); + + if (!CreateDir(dst_root_path)) + return ErrorType::ERROR_SYSTEM_ERROR; + + for (auto& path_info : GetPathList()) { + bf::path dst_path = dst_root_path / path_info.GetSrcPath(); + if (!CreateDirs(GetPkgID(), dst_path)) { + path_info.SetState(ResPathInfo::State::FAILED); + return ErrorType::ERROR_SYSTEM_ERROR; + } + path_info.SetState(ResPathInfo::State::OK); + } + + return ErrorType::ERROR_NONE; +} + +const std::string CreateDirRequestHandler::GetRequestHandlerType() const { + return kCreateDirReqHandlerType; +} + +} // namespace res_handler diff --git a/src/res-copy/src/event_signal_sender.cc b/src/res-copy/src/event_signal_sender.cc new file mode 100644 index 0000000..467f389 --- /dev/null +++ b/src/res-copy/src/event_signal_sender.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/event_signal_sender.hh" + +#include + +#include +#include + +#include "include/error_type.hh" +#include "include/request_type.hh" +#include "include/logging.hh" + +namespace res_handler { + +EventSignalSender::EventSignalSender(pkgmgr_installer* installer) + : pkgid_(""), req_type_(ReqType::REQ_TYPE_UNKNOWN), + uid_(0), session_id_(""), installer_(installer, pkgmgr_installer_free) {} + +bool EventSignalSender::SendSignal(const char* status, ErrorType error, + const std::list& res_path_info) { + if (!installer_) + return false; + + std::unique_ptr + event_info(pkgmgr_res_event_info_new(), pkgmgr_res_event_info_free); + if (!event_info) { + LOG(ERROR) << "Fail to create event info"; + return false; + } + + if (pkgmgr_res_event_info_set_error_code(event_info.get(), + static_cast(error)) != PKGMGR_R_OK) { + LOG(ERROR) << "Fail to set error code"; + return false; + } + + for (const ResPathInfo& info : res_path_info) { + pkgmgr_res_event_path_state state = PM_RES_EVENT_PATH_STATE_NONE; + if (info.GetState() == ResPathInfo::State::NONE) + state = PM_RES_EVENT_PATH_STATE_NONE; + else if (info.GetState() == ResPathInfo::State::OK) + state = PM_RES_EVENT_PATH_STATE_OK; + else if (info.GetState() == ResPathInfo::State::FAILED) + state = PM_RES_EVENT_PATH_STATE_FAILED; + + if (pkgmgr_res_event_info_add_path_state(event_info.get(), + info.GetSrcPath().c_str(), state) != PKGMGR_R_OK) { + LOG(ERROR) << "Fail to add path state"; + return false; + } + } + + if (pkgmgr_installer_send_res_signal(installer_.get(), pkgid_.c_str(), + status, event_info.get()) != 0) { + LOG(ERROR) << "Fail to send resource copy signal"; + return false; + } + + if (pkgmgr_installer_send_res_signal_for_uid(installer_.get(), uid_, + pkgid_.c_str(), status, event_info.get()) != 0) { + LOG(ERROR) << "Fail to send resource copy signal for uid " << uid_; + return false; + } + + return true; +} + +bool EventSignalSender::SendStart(const std::list& res_path_info) { + if (!SendSignal(PKGMGR_INSTALLER_START_KEY_STR, + ErrorType::ERROR_NONE, res_path_info)) { + LOG(ERROR) << "Fail to send start signal"; + return false; + } + + return true; +} + +bool EventSignalSender::SendOK(const std::list& res_path_info) { + if (!SendSignal(PKGMGR_INSTALLER_OK_EVENT_STR, + ErrorType::ERROR_NONE, res_path_info)) { + LOG(ERROR) << "Fail to send ok signal"; + return false; + } + + return true; +} + +bool EventSignalSender::SendFail(ErrorType error, + const std::list& res_path_info) { + if (!SendSignal(PKGMGR_INSTALLER_FAIL_EVENT_STR, error, res_path_info)) { + LOG(ERROR) << "Fail to send fail signal"; + return false; + } + + return true; +} + +void EventSignalSender::SetPkgID(std::string pkgid) { + pkgid_ = pkgid; +} + +void EventSignalSender::SetReqType(ReqType req_type) { + if (req_type == ReqType::REQ_TYPE_NEW) + pkgmgr_installer_set_request_type(installer_.get(), PKGMGR_REQ_RES_COPY); + else if (req_type == ReqType::REQ_TYPE_CREATEDIR) + pkgmgr_installer_set_request_type(installer_.get(), + PKGMGR_REQ_RES_CREATE_DIR); + else if (req_type == ReqType::REQ_TYPE_REMOVE) + pkgmgr_installer_set_request_type(installer_.get(), PKGMGR_REQ_RES_REMOVE); + else if (req_type == ReqType::REQ_TYPE_UNINSTALL) + pkgmgr_installer_set_request_type(installer_.get(), + PKGMGR_REQ_RES_UNINSTALL); + req_type_ = req_type; +} + +void EventSignalSender::SetUID(uid_t uid) { + pkgmgr_installer_set_uid(installer_.get(), uid); + uid_ = uid; +} + +void EventSignalSender::SetSessionID(std::string session_id) { + pkgmgr_installer_set_session_id(installer_.get(), session_id.c_str()); + session_id_ = session_id; +} + +} // namespace res_handler diff --git a/src/res-copy/src/file_util.cc b/src/res-copy/src/file_util.cc new file mode 100644 index 0000000..1a8eb2e --- /dev/null +++ b/src/res-copy/src/file_util.cc @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/file_util.hh" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "include/logging.hh" + +namespace bs = boost::system; +namespace bf = boost::filesystem; + +namespace { + +constexpr uid_t kRootUID = 0; +constexpr gid_t kPrivPlatformGid = 10212; +constexpr mode_t kDefaultMode640 = S_IRUSR | S_IWUSR | S_IRGRP; +constexpr mode_t kDirectoryMode650 = S_IRUSR | S_IWUSR | S_IRGRP | S_IXGRP; + +} // namespace + +namespace res_handler { + +FSFlag operator|(FSFlag a, FSFlag b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +bool SetDirPermissions(const boost::filesystem::path& path, + mode_t mode) { + if (chmod(path.string().c_str(), mode) != 0) { + LOG(ERROR) << "Failed to set permissions for path: " << path + << ", errno : " << errno; + return false; + } + + return true; +} + +bool RemoveAll(const bf::path& path) { + if (!exists(path) && !bf::is_symlink(bf::symlink_status(path))) + return true; + + bs::error_code error; + bf::remove_all(path, error); + + if (error) { + LOG(ERROR) << "Cannot remove: " << path << ", " << error.message(); + return false; + } + + return true; +} + +bool CopyDir(const boost::filesystem::path& src, + const boost::filesystem::path& dst, + FSFlag flags, bool skip_symlink) { + try { + // Check whether the function call is valid + if (!bf::exists(src) || !bf::is_directory(src)) { + LOG(ERROR) << "Source directory " << src + << " does not exist or is not a directory."; + return false; + } + if (!bf::exists(dst)) { + // Create the destination directory + if (!CreateDir(dst)) { + LOG(ERROR) << "Unable to create destination directory" << dst; + return false; + } + } else { + if (!(flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE))) { + LOG(ERROR) << "Destination directory " << dst.string() + << " already exists."; + return false; + } + } + } catch (const bf::filesystem_error& error) { + LOG(ERROR) << "Failed to copy directory: " << error.what(); + return false; + } + + // Iterate through the source directory + for (bf::directory_iterator file(src); + file != bf::directory_iterator(); + ++file) { + try { + bf::path current(file->path()); + bf::path target = dst / current.filename(); + + if (bf::is_symlink(symlink_status(current))) { + if (skip_symlink) + continue; + if ((flags & (FS_MERGE_SKIP | FS_MERGE_OVERWRITE)) && + bf::exists(target)) + continue; + bs::error_code error; + bf::copy_symlink(current, target, error); + if (error) { + LOG(ERROR) << "Failed to copy symlink: " << current << ", " + << error.message(); + return false; + } + } else if (bf::is_directory(current)) { + // Found directory: Recursion + if (!CopyDir(current, target, flags, skip_symlink)) { + return false; + } + } else { + if ((flags & FS_MERGE_SKIP) && bf::exists(target)) + continue; + bf::path destination = target; + + if (flags & FS_COMMIT_COPY_FILE) + destination = + bf::unique_path(target.parent_path() / "%%%%-%%%%-%%%%-%%%%"); + + if (flags & FS_MERGE_OVERWRITE) + bf::copy_file(current, destination, + bf::copy_option::overwrite_if_exists); + else + bf::copy_file(current, destination); + + if (flags & FS_COMMIT_COPY_FILE) { + if (flags & FS_MERGE_OVERWRITE) + bf::remove(target); + + bf::rename(destination, target); + } + } + } catch (const bf::filesystem_error& error) { + LOG(ERROR) << "Failed to copy directory: " << error.what(); + return false; + } + } + return true; +} + +bool CreateDir(const bf::path& path) { + if (bf::exists(path)) + return true; + + boost::system::error_code error; + bf::create_directory(path, error); + + if (error) { + LOG(ERROR) << "Failed to create directory: " + << boost::system::system_error(error).what(); + return false; + } + + if (!SetDirOwnershipAndPermissions( + path, kDirectoryMode650, kRootUID, kPrivPlatformGid)) + return false; + + return true; +} + +bool CopyFile(const bf::path& src, const bf::path& dst) { + if (bf::is_symlink(bf::symlink_status(src))) + return true; + + bs::error_code error; + bf::copy_file(src, dst, bf::copy_option::overwrite_if_exists, error); + if (error) { + LOG(WARNING) << "copy file " << src << " due to error [" + << error.message() << "]"; + return false; + } + + if (!SetDirOwnershipAndPermissions( + dst, kDefaultMode640, kRootUID, kPrivPlatformGid)) { + LOG(ERROR) << "Failed set permission for file " << dst; + return false; + } + + return true; +} + +bool SetOwnership(const bf::path& path, uid_t uid, gid_t gid) { + int ret = lchown(path.c_str(), uid, gid); + if (ret != 0) { + LOG(ERROR) << "Failed to change owner of: " << path; + std::cerr << "jungh failed to set lchown : " << ret << std::endl; + return false; + } + return true; +} + +bool SetDirOwnershipAndPermissions(const boost::filesystem::path& path, + mode_t mode, uid_t uid, gid_t gid) { + if (!SetOwnership(path, uid, gid)) { + LOG(ERROR) << "Failed to change owner: " << path + << "(" << uid << ", " << gid << ")"; + return false; + } + if (!SetDirPermissions(path, mode)) { + LOG(ERROR) << "Failed to change permission: " << path; + return false; + } + + return true; +} + +bool CreateDirs( + const std::string& pkgid, const boost::filesystem::path& path) { + boost::system::error_code error; + bf::create_directories(path, error); + + if (error) { + LOG(ERROR) << "Failed to create directory: " + << boost::system::system_error(error).what(); + return false; + } + + bf::path target_path = path; + while(target_path.filename() != pkgid) { + + if (!SetDirOwnershipAndPermissions( + target_path, kDirectoryMode650, kRootUID, kPrivPlatformGid)) + return false; + + target_path = target_path.parent_path(); + } + + if (!SetDirOwnershipAndPermissions( + target_path, kDirectoryMode650, kRootUID, kPrivPlatformGid)) + return false; + + return true; +} + +} // namespace res_handler diff --git a/src/res-copy/src/logging.cc b/src/res-copy/src/logging.cc new file mode 100644 index 0000000..39bb6bc --- /dev/null +++ b/src/res-copy/src/logging.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#include "include/logging.hh" + +namespace utils { + +log_priority LogLevelToPriority(LogLevel level) { + switch (level) { + case LogLevel::LOG_ERROR: + return log_priority::DLOG_ERROR; + case LogLevel::LOG_WARNING: + return log_priority::DLOG_WARN; + case LogLevel::LOG_INFO: + return log_priority::DLOG_INFO; + case LogLevel::LOG_DEBUG: + return log_priority::DLOG_DEBUG; + default: + return log_priority::DLOG_UNKNOWN; + } +} + +} // namespace utils diff --git a/src/res-copy/src/param_checker.cc b/src/res-copy/src/param_checker.cc new file mode 100644 index 0000000..e583ca3 --- /dev/null +++ b/src/res-copy/src/param_checker.cc @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/param_checker.hh" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "include/logging.hh" +#include "include/request_type.hh" +#include "include/res_path_info.hh" + +namespace bs = boost::system; +namespace bpo = boost::program_options; + +namespace { + +bool IsPathTraversal(const std::string& path) { + if (path.size() == 0) + return true; + + if (path.find("..", 0) != std::string::npos) { + LOG(ERROR) << "Invalid path : " << path; + return false; + } + + return true; +} + +} // namespace + +namespace res_handler { + +ParamChecker::ParamChecker(int argc, char* argv[]) { + bpo::options_description options; + + options.add_options() + ("uid,u", bpo::value()->default_value(0), "user id") + ("session-id,k", bpo::value()->default_value(""), + "session id") + ("path,p", bpo::value>()->multitoken(), + "source-destination path") + ("remove,r", bpo::value(), "remove shared resource") + ("delete,d", bpo::value(), + "delete shared resource for package") + ("copy,c", bpo::value(), "copy resource") + ("createdir,D", bpo::value(), "create directories") + ("help,h", "Show this message"); + + bpo::parsed_options parsed_options = bpo::command_line_parser(argc, argv) + .options(options) + .run(); + + for (const bpo::option& o : parsed_options.options) { + if (o.string_key == "uid") { + uid_ = static_cast(std::stoi(o.value.front())); + } else if (o.string_key == "session-id") { + session_id_ = o.value.front(); + } else if (o.string_key == "path") { + if (o.value.front() == o.value.back()) + path_info_list_.emplace_back(o.value.front(), ""); + else + path_info_list_.emplace_back(o.value.front(), o.value.back()); + } else if (o.string_key == "copy" || + o.string_key == "delete" || + o.string_key == "remove" || + o.string_key == "createdir") { + pkgid_ = o.value.front(); + SetRequestType(o.string_key); + } else if (o.string_key == "help") { + std::cout << options; + } else { + std::cout << "Invalid option : " << o.string_key << std::endl; + } + } +} + +std::string ParamChecker::GetPkgID() const { + return pkgid_; +} + +std::string ParamChecker::GetSessionID() const { + return session_id_; +} + +uid_t ParamChecker::GetUID() const { + return uid_; +} + +ReqType ParamChecker::GetRequestType() { + return req_type_; +} + +const std::list& ParamChecker::GetPathList() const { + return path_info_list_; +} + +bool ParamChecker::Validate() { + if (uid_ == 0) { + LOG(ERROR) << "Invalid uid: " << uid_; + return false; + } + + if (req_type_ == ReqType::REQ_TYPE_UNKNOWN) { + LOG(ERROR) << "Invalid request type"; + return false; + } + + if (!ValidatePkgID()) + return false; + + if (!ValidatePathList()) + return false; + + return true; +} + +void ParamChecker::SetRequestType(std::string key) { + if (key == "copy") + req_type_ = ReqType::REQ_TYPE_NEW; + else if (key == "remove") + req_type_ = ReqType::REQ_TYPE_REMOVE; + else if (key == "delete") + req_type_ = ReqType::REQ_TYPE_UNINSTALL; + else if (key == "createdir") + req_type_ = ReqType::REQ_TYPE_CREATEDIR; +} + +bool ParamChecker::ValidatePkgID() { + if (pkgid_.size() == 0) { + LOG(ERROR) << "pkgid is empty"; + return false; + } + + return true; +} + +bool ParamChecker::ValidatePathList() { + if (req_type_ != ReqType::REQ_TYPE_UNINSTALL) { + if (path_info_list_.size() == 0) { + LOG(ERROR) << "Path is not given"; + return false; + } + } + + for (auto& path_info : path_info_list_) { + if (!IsPathTraversal(path_info.GetSrcPath()) || + !IsPathTraversal(path_info.GetDstPath())) + return false; + } + + return true; +} + +} // namespace res_handler diff --git a/src/res-copy/src/remove_request_handler.cc b/src/res-copy/src/remove_request_handler.cc new file mode 100644 index 0000000..ce97335 --- /dev/null +++ b/src/res-copy/src/remove_request_handler.cc @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/remove_request_handler.hh" + +#include +#include + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/file_util.hh" +#include "include/logging.hh" +#include "include/res_path_info.hh" + +namespace bf = boost::filesystem; + +namespace { + +constexpr char kRemoveReqHandlerType[] = "remove"; + +} // namespace + +namespace res_handler { + +RemoveRequestHandler::RemoveRequestHandler( + std::string pkgid, uid_t uid, std::string root_path, + std::list path_list) : + AbstractRequestHandler(pkgid, uid, root_path, path_list) {} + +ErrorType RemoveRequestHandler::Execute() { + bf::path dst_root_path(GetDstRootPath()); + + if (!bf::exists(dst_root_path)) { + LOG(ERROR) << "root path not exists"; + return ErrorType::ERROR_RES_NOT_FOUND; + } + + for (auto& path_info : GetPathList()) { + bf::path dst_path = dst_root_path / path_info.GetSrcPath(); + if (!bf::exists(dst_path)) { + LOG(ERROR) << "Path not exists: " << dst_path; + continue; + } + + if (!RemoveAll(dst_path)) { + path_info.SetState(ResPathInfo::State::FAILED); + return ErrorType::ERROR_SYSTEM_ERROR; + } + path_info.SetState(ResPathInfo::State::OK); + } + + return ErrorType::ERROR_NONE; +} + +const std::string RemoveRequestHandler::GetRequestHandlerType() const { + return kRemoveReqHandlerType; +} + +} // namespace res_handler diff --git a/src/res-copy/src/request_handler_invoker.cc b/src/res-copy/src/request_handler_invoker.cc new file mode 100644 index 0000000..c6d3b9c --- /dev/null +++ b/src/res-copy/src/request_handler_invoker.cc @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/request_handler_invoker.hh" + +#include + +#include + +#include "include/abstract_request_handler.hh" +#include "include/condition_validator.hh" +#include "include/copy_request_handler.hh" +#include "include/createdir_request_handler.hh" +#include "include/event_signal_sender.hh" +#include "include/logging.hh" +#include "include/param_checker.hh" +#include "include/remove_request_handler.hh" +#include "include/request_type.hh" +#include "include/uninstall_request_handler.hh" + +namespace { + +std::string GetRootPathForUid(uid_t uid) { + tzplatform_set_user(uid); + const char* rootpath = tzplatform_getenv(TZ_USER_HOME); + tzplatform_reset_user(); + return rootpath; +} + +} // namespace + +namespace res_handler { + +RequestHandlerInvoker::RequestHandlerInvoker( + ParamChecker option, std::shared_ptr signal) : + option_(option), signal_(signal) { + SetReqHandler(); +} + +bool RequestHandlerInvoker::Validate() { + if (handler_ == nullptr) { + LOG(ERROR) << "Failed to initialize handler"; + signal_->SendFail(ErrorType::ERROR_SYSTEM_ERROR, {}); + return false; + } + + ConditionValidator validator(option_.GetPkgID(), option_.GetUID()); + if (validator.ValidateCondition( + option_.GetRequestType(), option_.GetPathList()) + != ErrorType::ERROR_NONE) { + LOG(ERROR) << "Validation failed"; + signal_->SendFail(ErrorType::ERROR_SYSTEM_ERROR, + handler_->GetResultPathList()); + return false; + } + + return true; +} + +bool RequestHandlerInvoker::Execute() { + if (handler_ == nullptr) { + signal_->SendFail(ErrorType::ERROR_SYSTEM_ERROR, {}); + return false; + } + + signal_->SendStart(handler_->GetResultPathList()); + ErrorType ret = handler_->Execute(); + if (ret != ErrorType::ERROR_NONE) { + signal_->SendFail(ret, handler_->GetResultPathList()); + return false; + } + signal_->SendOK(handler_->GetResultPathList()); + + return true; +} + +const std::string RequestHandlerInvoker::GetHandlerType() const { + if (handler_ == nullptr) { + LOG(ERROR) << "handler has not initialized"; + return {}; + } + + return handler_->GetRequestHandlerType(); +} + +void RequestHandlerInvoker::SetReqHandler() { + switch (option_.GetRequestType()) { + case ReqType::REQ_TYPE_NEW: + handler_.reset( + new CopyRequestHandler( + option_.GetPkgID(), + option_.GetUID(), + GetRootPathForUid(option_.GetUID()), + option_.GetPathList())); + break; + case ReqType::REQ_TYPE_REMOVE: + + handler_.reset( + new RemoveRequestHandler( + option_.GetPkgID(), + option_.GetUID(), + GetRootPathForUid(option_.GetUID()), + option_.GetPathList())); + break; + case ReqType::REQ_TYPE_UNINSTALL: + handler_.reset( + new UninstallRequestHandler( + option_.GetPkgID(), + option_.GetUID(), + GetRootPathForUid(option_.GetUID()), + option_.GetPathList())); + break; + case ReqType::REQ_TYPE_CREATEDIR: + handler_.reset( + new CreateDirRequestHandler( + option_.GetPkgID(), + option_.GetUID(), + GetRootPathForUid(option_.GetUID()), + option_.GetPathList())); + break; + default: + LOG(ERROR) << "Invalid request type"; + break; + } +} + +} // namespace res_handler diff --git a/src/res-copy/src/res_copy_main.cc b/src/res-copy/src/res_copy_main.cc new file mode 100644 index 0000000..e2f084d --- /dev/null +++ b/src/res-copy/src/res_copy_main.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "include/res_handler.hh" +#include "include/logging.hh" + +int main(int argc, char *argv[]) { + try { + res_handler::ResHandler handler; + if (!handler.Init(argc, argv)) { + LOG(ERROR) << "Failed to initliaze handler"; + return -1; + } + + if (!handler.Run()) { + LOG(ERROR) << "Failed to handle request"; + return -1; + } + } catch (...) { + LOG(ERROR) << "Exception occured"; + return -1; + } + + return 0; +} diff --git a/src/res-copy/src/res_handler.cc b/src/res-copy/src/res_handler.cc new file mode 100644 index 0000000..a0012e1 --- /dev/null +++ b/src/res-copy/src/res_handler.cc @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/res_handler.hh" + +#include + +#include + +#include "include/event_signal_sender.hh" +#include "include/logging.hh" +#include "include/param_checker.hh" +#include "include/request_handler_invoker.hh" + +namespace res_handler { + +bool ResHandler::Init(int argc, char* argv[]) { + pkgmgr_installer *installer = pkgmgr_installer_new(); + if (!installer) { + LOG(ERROR) << "Fail to get pkgmgr installer"; + return false; + } + std::shared_ptr signal = + std::make_shared(installer); + + res_handler::ParamChecker option(argc, argv); + if (!option.Validate()) { + LOG(ERROR) << "Invalid argument has given"; + signal->SendFail(ErrorType::ERROR_INVALID_PARAMETER, {}); + return false; + } + signal->SetPkgID(option.GetPkgID()); + signal->SetReqType(option.GetRequestType()); + signal->SetUID(option.GetUID()); + signal->SetSessionID(option.GetSessionID()); + + handler_.reset(new RequestHandlerInvoker(option, signal)); + if (!handler_->Validate()) { + LOG(ERROR) << "Failed to initialize request handler"; + return false; + } + + return true; +} + +bool ResHandler::Run() { + return handler_->Execute(); +} + +} // namespace res_handler diff --git a/src/res-copy/src/res_path_info.cc b/src/res-copy/src/res_path_info.cc new file mode 100644 index 0000000..4c2fb76 --- /dev/null +++ b/src/res-copy/src/res_path_info.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/res_path_info.hh" + +#include +#include + +namespace res_handler { + +ResPathInfo::ResPathInfo(std::string src, std::string dst, State state) + : src_(src), dst_(dst), state_(state) {} + +const std::string& ResPathInfo::GetSrcPath() const { + return src_; +} + +const std::string& ResPathInfo::GetDstPath() const { + return dst_; +} + +const ResPathInfo::State ResPathInfo::GetState() const { + return state_; +} + +void ResPathInfo::SetState(ResPathInfo::State state) { + state_ = state; +} + +} // namespace res_handler diff --git a/src/res-copy/src/uninstall_request_handler.cc b/src/res-copy/src/uninstall_request_handler.cc new file mode 100644 index 0000000..c5912b0 --- /dev/null +++ b/src/res-copy/src/uninstall_request_handler.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/uninstall_request_handler.hh" + +#include +#include + +#include "include/abstract_request_handler.hh" +#include "include/error_type.hh" +#include "include/file_util.hh" +#include "include/logging.hh" +#include "include/res_path_info.hh" + +namespace bf = boost::filesystem; + +namespace { + +constexpr char kUninstallReqHandlerType[] = "delete"; + +} // namespace + +namespace res_handler { + +UninstallRequestHandler::UninstallRequestHandler( + std::string pkgid, uid_t uid, std::string root_path, + std::list path_list) : + AbstractRequestHandler(pkgid, uid, root_path, path_list) {} + +ErrorType UninstallRequestHandler::Execute() { + if (GetPkgID().length() == 0) { + LOG(ERROR) << "Invalid argument"; + return ErrorType::ERROR_INVALID_PARAMETER; + } + + bf::path root_path(GetDstRootPath()); + if (!bf::exists(root_path)) { + LOG(WARNING) << "path not exists : " << root_path; + return ErrorType::ERROR_NONE; + } + + if (!RemoveAll(root_path)) + return ErrorType::ERROR_SYSTEM_ERROR; + + return ErrorType::ERROR_NONE; +} + +const std::string UninstallRequestHandler::GetRequestHandlerType() const { + return kUninstallReqHandlerType; +} + +} // namespace res_handler diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..c90fac8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +ADD_SUBDIRECTORY(unit_tests) diff --git a/tests/mock/mock_hook.h b/tests/mock/mock_hook.h new file mode 100644 index 0000000..843f77d --- /dev/null +++ b/tests/mock/mock_hook.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_MOCK_HOOK_H_ +#define TESTS_MOCK_MOCK_HOOK_H_ + +#define MOCK_HOOK_P0(MOCK_CLASS, f) \ + TestFixture::GetMock().f() +#define MOCK_HOOK_P1(MOCK_CLASS, f, p1) \ + TestFixture::GetMock().f(p1) +#define MOCK_HOOK_P2(MOCK_CLASS, f, p1, p2) \ + TestFixture::GetMock().f(p1, p2) +#define MOCK_HOOK_P3(MOCK_CLASS, f, p1, p2, p3) \ + TestFixture::GetMock().f(p1, p2, p3) +#define MOCK_HOOK_P4(MOCK_CLASS, f, p1, p2, p3, p4) \ + TestFixture::GetMock().f(p1, p2, p3, p4) +#define MOCK_HOOK_P5(MOCK_CLASS, f, p1, p2, p3, p4, p5) \ + TestFixture::GetMock().f(p1, p2, p3, p4, p5) +#define MOCK_HOOK_P6(MOCK_CLASS, f, p1, p2, p3, p4, p5, p6) \ + TestFixture::GetMock().f(p1, p2, p3, p4, p5, p6) +#define MOCK_HOOK_P7(MOCK_CLASS, f, p1, p2, p3, p4, p5, p6, p7) \ + TestFixture::GetMock().f(p1, p2, p3, p4, p5, p6, p7) +#define MOCK_HOOK_P8(MOCK_CLASS, f, p1, p2, p3, p4, p5, p6, p7, p8) \ + TestFixture::GetMock().f(p1, p2, p3, p4, p5, p6, p7, p8) +#define MOCK_HOOK_P10(MOCK_CLASS, f, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) \ + TestFixture::GetMock().f( \ + p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) + +#endif // TESTS_MOCK_MOCK_HOOK_H_ diff --git a/tests/mock/module_mock.h b/tests/mock/module_mock.h new file mode 100644 index 0000000..ba72d76 --- /dev/null +++ b/tests/mock/module_mock.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_MODULE_MOCK_H_ +#define TESTS_MOCK_MODULE_MOCK_H_ + +class ModuleMock { + public: + virtual ~ModuleMock() {} +}; + +#endif // TESTS_MOCK_MODULE_MOCK_H_ diff --git a/tests/mock/os_mock.cc b/tests/mock/os_mock.cc new file mode 100644 index 0000000..6353862 --- /dev/null +++ b/tests/mock/os_mock.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/mock_hook.h" +#include "mock/os_mock.h" +#include "mock/test_fixture.h" + +extern "C" pid_t fork(void) { + return MOCK_HOOK_P0(OsMock, fork); +} + +extern "C" pid_t waitpid(pid_t pid, int *status, int options) { + return MOCK_HOOK_P3(OsMock, waitpid, pid, status, options); +} + +extern "C" int fchown(int fd, uid_t owner, gid_t group) { + return MOCK_HOOK_P3(OsMock, fchown, fd, owner, group); +} + +extern "C" int fchmod(int fd, mode_t mode) { + return MOCK_HOOK_P2(OsMock, fchmod, fd, mode); +} + +extern "C" int chmod(const char* pathanme, mode_t mode) { + return MOCK_HOOK_P2(OsMock, chmod, pathanme, mode); +} + +extern "C" int lchown(const char* pathname, uid_t uid, gid_t gid) { + return MOCK_HOOK_P3(OsMock, lchown, pathname, uid, gid); +} + +extern "C" int smack_setlabel(const char *path, const char* label, + enum smack_label_type type) { + return MOCK_HOOK_P3(OsMock, smack_setlabel, path, label, type); +} + +extern "C" int execvp(const char* file, char* const argv[]) { + return MOCK_HOOK_P2(OsMock, execvp, file, argv); +} diff --git a/tests/mock/os_mock.h b/tests/mock/os_mock.h new file mode 100644 index 0000000..93b2b68 --- /dev/null +++ b/tests/mock/os_mock.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_OS_MOCK_H_ +#define TESTS_MOCK_OS_MOCK_H_ + +#include +#include +#include +#include + +#include "mock/module_mock.h" + +class OsMock : public virtual ModuleMock { + public: + virtual ~OsMock() {} + + MOCK_METHOD0(fork, pid_t()); + MOCK_METHOD3(waitpid, pid_t(pid_t, int*, int)); + MOCK_METHOD3(fchown, int(int, uid_t, gid_t)); + MOCK_METHOD2(fchmod, int(int, mode_t)); + MOCK_METHOD2(chmod, int(const char*, mode_t)); + MOCK_METHOD3(lchown, int(const char*, uid_t, gid_t)); + MOCK_METHOD3(smack_setlabel, int(const char*, const char*, smack_label_type)); + MOCK_METHOD2(execvp, int(const char*, char* const[])); +}; + +#endif // TESTS_MOCK_OS_MOCK_H_ diff --git a/tests/mock/pkgmgr_info_mock.cc b/tests/mock/pkgmgr_info_mock.cc new file mode 100644 index 0000000..f73cb5b --- /dev/null +++ b/tests/mock/pkgmgr_info_mock.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/mock_hook.h" +#include "mock/pkgmgr_info_mock.h" +#include "mock/test_fixture.h" + +extern "C" int pkgmgrinfo_pkginfo_filter_create( + pkgmgrinfo_pkginfo_filter_h *handle) { + return MOCK_HOOK_P1(PkgMgrInfoMock, pkgmgrinfo_pkginfo_filter_create, handle); +} + +extern "C" int pkgmgrinfo_pkginfo_filter_destroy( + pkgmgrinfo_pkginfo_filter_h handle) { + return MOCK_HOOK_P1(PkgMgrInfoMock, pkgmgrinfo_pkginfo_filter_destroy, + handle); +} + +extern "C" int pkgmgrinfo_pkginfo_filter_add_bool( + pkgmgrinfo_pkginfo_filter_h handle, + const char *property, const bool value) { + return MOCK_HOOK_P3(PkgMgrInfoMock, pkgmgrinfo_pkginfo_filter_add_bool, + handle, property, value); +} + +extern "C" int pkgmgrinfo_pkginfo_filter_foreach_pkginfo( + pkgmgrinfo_pkginfo_filter_h handle, + pkgmgrinfo_pkg_list_cb pkg_cb, void *user_data) { + return MOCK_HOOK_P3(PkgMgrInfoMock, pkgmgrinfo_pkginfo_filter_foreach_pkginfo, + handle, pkg_cb, user_data); +} + +extern "C" int pkgmgrinfo_pkginfo_get_pkgid(pkgmgrinfo_pkginfo_h handle, + char **pkgid) { + return MOCK_HOOK_P2(PkgMgrInfoMock, pkgmgrinfo_pkginfo_get_pkgid, + handle, pkgid); +} + +extern "C" int pkgmgrinfo_pkginfo_get_type(pkgmgrinfo_pkginfo_h handle, + char **type) { + return MOCK_HOOK_P2(PkgMgrInfoMock, pkgmgrinfo_pkginfo_get_type, + handle, type); +} + +extern "C" int pkgmgrinfo_pkginfo_get_version(pkgmgrinfo_pkginfo_h handle, + char **version) { + return MOCK_HOOK_P2(PkgMgrInfoMock, pkgmgrinfo_pkginfo_get_version, + handle, version); +} + +extern "C" int pkgmgrinfo_pkginfo_get_usr_pkginfo( + const char* pkgid, uid_t uid, pkgmgrinfo_pkginfo_h* handle) { + return MOCK_HOOK_P3( + PkgMgrInfoMock, pkgmgrinfo_pkginfo_get_usr_pkginfo, pkgid, uid, handle); +} + +extern "C" int pkgmgrinfo_pkginfo_destroy_pkginfo( + pkgmgrinfo_pkginfo_h handle) { + return MOCK_HOOK_P1( + PkgMgrInfoMock, pkgmgrinfo_pkginfo_destroy_pkginfo, handle); +} diff --git a/tests/mock/pkgmgr_info_mock.h b/tests/mock/pkgmgr_info_mock.h new file mode 100644 index 0000000..bbc2a67 --- /dev/null +++ b/tests/mock/pkgmgr_info_mock.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_PKGMGR_INFO_MOCK_H_ +#define TESTS_MOCK_PKGMGR_INFO_MOCK_H_ + +#include +#include +#include + +#include "mock/module_mock.h" + +class PkgMgrInfoMock : public virtual ModuleMock { + public: + virtual ~PkgMgrInfoMock() {} + + MOCK_METHOD1(pkgmgrinfo_pkginfo_filter_create, + int(pkgmgrinfo_pkginfo_filter_h*)); + MOCK_METHOD1(pkgmgrinfo_pkginfo_filter_destroy, + int(pkgmgrinfo_pkginfo_filter_h)); + MOCK_METHOD3(pkgmgrinfo_pkginfo_filter_add_bool, + int(pkgmgrinfo_pkginfo_filter_h, const char*, const bool)); + MOCK_METHOD3(pkgmgrinfo_pkginfo_filter_foreach_pkginfo, + int(pkgmgrinfo_pkginfo_filter_h, pkgmgrinfo_pkg_list_cb, void*)); + MOCK_METHOD2(pkgmgrinfo_pkginfo_get_pkgid, + int(pkgmgrinfo_pkginfo_h, char**)); + MOCK_METHOD2(pkgmgrinfo_pkginfo_get_type, int(pkgmgrinfo_pkginfo_h, char**)); + MOCK_METHOD2(pkgmgrinfo_pkginfo_get_version, + int(pkgmgrinfo_pkginfo_h, char**)); + MOCK_METHOD3(pkgmgrinfo_pkginfo_get_usr_pkginfo, + int(const char*, uid_t, pkgmgrinfo_pkginfo_h*)); + MOCK_METHOD1(pkgmgrinfo_pkginfo_destroy_pkginfo, int(pkgmgrinfo_pkginfo_h)); +}; + +#endif // TESTS_MOCK_PKGMGR_INFO_MOCK_H_ diff --git a/tests/mock/pkgmgr_installer_mock.cc b/tests/mock/pkgmgr_installer_mock.cc new file mode 100644 index 0000000..1c697c4 --- /dev/null +++ b/tests/mock/pkgmgr_installer_mock.cc @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/mock_hook.h" +#include "mock/pkgmgr_installer_mock.h" +#include "mock/test_fixture.h" + +extern "C" int pkgmgr_installer_send_res_signal(pkgmgr_installer *pi, + const char *pkgid, const char *status, pkgmgr_res_event_info *event_info) { + return MOCK_HOOK_P4(PkgMgrInstallerMock, + pkgmgr_installer_send_res_signal, pi, pkgid, status, event_info); +} + +extern "C" int pkgmgr_installer_send_res_signal_for_uid( + pkgmgr_installer *pi, uid_t uid, const char *pkgid, + const char *status, pkgmgr_res_event_info *event_info) { + return MOCK_HOOK_P5(PkgMgrInstallerMock, + pkgmgr_installer_send_res_signal_for_uid, + pi, uid, pkgid, status, event_info); +} diff --git a/tests/mock/pkgmgr_installer_mock.h b/tests/mock/pkgmgr_installer_mock.h new file mode 100644 index 0000000..617d18e --- /dev/null +++ b/tests/mock/pkgmgr_installer_mock.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_PKGMGR_INSTALLER_MOCK_H_ +#define TESTS_MOCK_PKGMGR_INSTALLER_MOCK_H_ + +#include +#include +#include + +#include "mock/module_mock.h" + +class PkgMgrInstallerMock : public virtual ModuleMock { + public: + virtual ~PkgMgrInstallerMock() {} + + MOCK_METHOD4(pkgmgr_installer_send_res_signal, + int(pkgmgr_installer*, const char*, const char*, pkgmgr_res_event_info*)); + MOCK_METHOD5(pkgmgr_installer_send_res_signal_for_uid, + int(pkgmgr_installer*, uid_t, const char*, const char*, + pkgmgr_res_event_info*)); +}; + +#endif // TESTS_MOCK_PKGMGR_INSTALLER_MOCK_H_ diff --git a/tests/mock/test_fixture.cc b/tests/mock/test_fixture.cc new file mode 100644 index 0000000..4eceef4 --- /dev/null +++ b/tests/mock/test_fixture.cc @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/test_fixture.h" + +#include + +std::unique_ptr TestFixture::mock_; diff --git a/tests/mock/test_fixture.h b/tests/mock/test_fixture.h new file mode 100644 index 0000000..33643ce --- /dev/null +++ b/tests/mock/test_fixture.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_MOCK_TEST_FIXTURE_H_ +#define TESTS_MOCK_TEST_FIXTURE_H_ + +#include + +#include +#include +#include +#include + +#include "mock/module_mock.h" + +class TestFixture : public ::testing::Test { + public: + explicit TestFixture(std::unique_ptr&& mock) { + mock_ = std::move(mock); + } + virtual ~TestFixture() { + mock_.reset(); + } + + virtual void SetUp() {} + virtual void TearDown() {} + + template + static T& GetMock() { + auto ptr = dynamic_cast(mock_.get()); + if (!ptr) + throw std::invalid_argument("The test does not provide mock of \"" + + std::string(typeid(T).name()) + "\""); + return *ptr; + } + + static std::unique_ptr mock_; +}; + +#endif // TESTS_MOCK_TEST_FIXTURE_H_ diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt new file mode 100644 index 0000000..50b89c2 --- /dev/null +++ b/tests/unit_tests/CMakeLists.txt @@ -0,0 +1,53 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(pkgmgr-tool_unittests C CXX) + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(pkgmgr-tool_unittests REQUIRED + dlog + gmock + pkgmgr + pkgmgr-parser + pkgmgr-info + pkgmgr-installer + libtzplatform-config + libsmack + sqlite3 + boost +) + +FOREACH(flag ${pkgmgr-tool_unittests_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror -fPIE") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++14") +SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") +SET(CMAKE_CXX_FLAGS_RELEASE "-O2") + +ADD_DEFINITIONS("-DDB_PATH=\"${DB_PATH}\"") +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../../src/res-copy/include) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../../src/res-copy/) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../) + +#res-copy +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/res-copy/src RES_COPY_SRCS) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../../src/res-copy/src RES_COPY_LIB_SOURCES) +LIST(REMOVE_ITEM RES_COPY_LIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../src/res-copy/src/res_copy_main.cc) +#LIST(REMOVE_ITEM RES_COPY_LIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../src/res-copy/src/logging.cc) + +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../mock MOCK_SOURCES) + +ADD_EXECUTABLE(${PROJECT_NAME} + ${RES_COPY_SRCS} + ${MOCK_SOURCES} + ${RES_COPY_LIB_SOURCES} + test_main.cc +) + +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "${EXTRA_CFLAGS}") +TARGET_LINK_LIBRARIES(${PROJECT_NAME} + ${pkgmgr-tool_unittests_LDFLAGS} +) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin/) + diff --git a/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_dir2/resource_file2.txt b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_dir2/resource_file2.txt new file mode 100644 index 0000000..9de57b8 --- /dev/null +++ b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_dir2/resource_file2.txt @@ -0,0 +1 @@ +this is second resource file \ No newline at end of file diff --git a/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_file3.txt b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_file3.txt new file mode 100644 index 0000000..00a06c3 --- /dev/null +++ b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_dir1/resource_file3.txt @@ -0,0 +1 @@ +this is 3rd resource file \ No newline at end of file diff --git a/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_file.txt b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_file.txt new file mode 100644 index 0000000..aa260be --- /dev/null +++ b/tests/unit_tests/res-copy/data/apps_rw/test_pkg/data/resource_file.txt @@ -0,0 +1 @@ +this is resource file to being copied \ No newline at end of file diff --git a/tests/unit_tests/res-copy/src/test_condition_validator.cc b/tests/unit_tests/res-copy/src/test_condition_validator.cc new file mode 100644 index 0000000..c2fe687 --- /dev/null +++ b/tests/unit_tests/res-copy/src/test_condition_validator.cc @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include "mock/os_mock.h" +#include "mock/pkgmgr_info_mock.h" +#include "mock/test_fixture.h" + +#include "include/condition_validator.hh" +#include "include/param_checker.hh" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::InvokeArgument; +using ::testing::SaveArg; + +using res_handler::ConditionValidator; +using res_handler::ParamChecker; + +class Mocks : public ::testing::NiceMock, + public ::testing::NiceMock {}; + +class ConditionValidatorTest : public TestFixture { + public: + ConditionValidatorTest() : TestFixture(std::make_unique()) {} + virtual ~ConditionValidatorTest() {} + + virtual void SetUp() { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_destroy_pkginfo(_)) + .WillRepeatedly(Return(0)); + } + virtual void TearDown() {} +}; + +TEST_F(ConditionValidatorTest, PkgNotExist) { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(-1)); + + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--copy", + "org.test.targetpkgid", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_PKG_NOT_FOUND); +} + +TEST_F(ConditionValidatorTest, CopyPkgNotExist) { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(-1)); + + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--copy", + "org.test.targetpkgid", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_PKG_NOT_FOUND); +} + +TEST_F(ConditionValidatorTest, CopySrcNotExist) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--copy", + "org.test.targetpkgid", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_RES_NOT_FOUND); +} + +TEST_F(ConditionValidatorTest, RemovePkgNotExist) { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(-1)); + + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--remove", + "org.test.targetpkgid", "-p", "", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_PKG_NOT_FOUND); +} + +TEST_F(ConditionValidatorTest, RemoveSrcNotExist) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--remove", + "org.test.targetpkgid", "-p", "", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_RES_NOT_FOUND); +} + +TEST_F(ConditionValidatorTest, DeletePkgNotExist) { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(-1)); + + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--delete", + "org.test.targetpkgid", nullptr}; + ParamChecker checker(5, const_cast(argv)); + ConditionValidator validator(checker.GetPkgID(), checker.GetUID()); + res_handler::ErrorType ret = validator.ValidateCondition( + checker.GetRequestType(), checker.GetPathList()); + + EXPECT_EQ(ret, res_handler::ErrorType::ERROR_NONE); +} + diff --git a/tests/unit_tests/res-copy/src/test_event_signal_sender.cc b/tests/unit_tests/res-copy/src/test_event_signal_sender.cc new file mode 100644 index 0000000..50996c2 --- /dev/null +++ b/tests/unit_tests/res-copy/src/test_event_signal_sender.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include "mock/pkgmgr_installer_mock.h" +#include "mock/test_fixture.h" +#include "include/event_signal_sender.hh" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::InvokeArgument; +using ::testing::SaveArg; + +using res_handler::EventSignalSender; + +class InstallerMocks : public ::testing::NiceMock {}; + +class EventSignalSenderTest : public TestFixture { + public: + EventSignalSenderTest() : TestFixture(std::make_unique()) {} + virtual ~EventSignalSenderTest() {} + + virtual void SetUp() {} + virtual void TearDown() {} +}; + +TEST_F(EventSignalSenderTest, SendSignals) { + EXPECT_CALL(GetMock(), + pkgmgr_installer_send_res_signal(_, _, _, _)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(GetMock(), + pkgmgr_installer_send_res_signal_for_uid(_, _, _, _, _)) + .WillRepeatedly(Return(0)); + + pkgmgr_installer* pi = pkgmgr_installer_offline_new(); + EventSignalSender signal(pi); + signal.SetPkgID("org.test.targetpkgid"); + signal.SetReqType(res_handler::ReqType::REQ_TYPE_NEW); + signal.SetUID(0); + signal.SetSessionID("session_id"); + + EXPECT_TRUE(signal.SendStart({})); + EXPECT_TRUE(signal.SendOK({})); + EXPECT_TRUE(signal.SendFail(res_handler::ErrorType::ERROR_NONE, {})); +} + +TEST_F(EventSignalSenderTest, SendSignalWithNullPkgMgrInstaller) { + EXPECT_CALL(GetMock(), + pkgmgr_installer_send_res_signal(_, _, _, _)) + .Times(0); + EXPECT_CALL(GetMock(), + pkgmgr_installer_send_res_signal_for_uid(_, _, _, _, _)) + .Times(0); + + EventSignalSender signal(nullptr); + EXPECT_FALSE(signal.SendStart({})); + EXPECT_FALSE(signal.SendOK({})); + EXPECT_FALSE(signal.SendFail(res_handler::ErrorType::ERROR_NONE, {})); +} diff --git a/tests/unit_tests/res-copy/src/test_param_checker.cc b/tests/unit_tests/res-copy/src/test_param_checker.cc new file mode 100644 index 0000000..ffbe9b9 --- /dev/null +++ b/tests/unit_tests/res-copy/src/test_param_checker.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include "mock/os_mock.h" +#include "mock/pkgmgr_info_mock.h" +#include "mock/test_fixture.h" +#include "include/param_checker.hh" +#include "include/request_type.hh" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::InvokeArgument; +using ::testing::SaveArg; + +using res_handler::ParamChecker; + +class Mocks : public ::testing::NiceMock, + public ::testing::NiceMock {}; + +class ParamCheckerTest : public TestFixture { + public: + ParamCheckerTest() : TestFixture(std::make_unique()) {} + virtual ~ParamCheckerTest() {} + + virtual void SetUp() { + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_get_usr_pkginfo(_, _, _)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(GetMock(), + pkgmgrinfo_pkginfo_destroy_pkginfo(_)) + .WillRepeatedly(Return(0)); + } + virtual void TearDown() {} +}; + +TEST_F(ParamCheckerTest, InvalidUIDTest) { + const char *argv[] = { "/bin/res-copy", "--uid", "0", "--copy", + "org.test.targetpkgid", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), false); +} + +TEST_F(ParamCheckerTest, PkgIDNotGiven) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", + "-p", "srcpath", "dstpath", "--copy", "", nullptr}; + ParamChecker checker(8, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), false); +} + +TEST_F(ParamCheckerTest, CopyRes) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", + "-p", "srcpath", "dstpath", "--copy", "org.test.targetpkgid", nullptr}; + + ParamChecker checker(8, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), true); + EXPECT_EQ(checker.GetRequestType(), res_handler::ReqType::REQ_TYPE_NEW); + EXPECT_EQ(checker.GetPkgID(), "org.test.targetpkgid"); + EXPECT_EQ(checker.GetPathList().size(), 1); + EXPECT_EQ(checker.GetUID(), 5001); +} + +TEST_F(ParamCheckerTest, RemoveRes) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--remove", + "org.test.targetpkgid", "-p", "dstpath", nullptr}; + + ParamChecker checker(7, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), true); + EXPECT_EQ(checker.GetRequestType(), res_handler::ReqType::REQ_TYPE_REMOVE); + EXPECT_EQ(checker.GetPkgID(), "org.test.targetpkgid"); + EXPECT_EQ(checker.GetPathList().size(), 1); + EXPECT_EQ(checker.GetUID(), 5001); +} + +TEST_F(ParamCheckerTest, DeleteRes) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--delete", + "org.test.targetpkgid", nullptr}; + + ParamChecker checker(5, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), true); + EXPECT_EQ(checker.GetRequestType(), + res_handler::ReqType::REQ_TYPE_UNINSTALL); + EXPECT_EQ(checker.GetPkgID(), "org.test.targetpkgid"); + EXPECT_EQ(checker.GetPathList().size(), 0); + EXPECT_EQ(checker.GetUID(), 5001); +} + +TEST_F(ParamCheckerTest, EmptyPkgID) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--copy", + "", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), false); +} + +TEST_F(ParamCheckerTest, UnknownReqType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "-p", + "srcpath", "dstpath", nullptr}; + ParamChecker checker(6, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), false); +} + +TEST_F(ParamCheckerTest, PathNotGiven) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", + "-c", "org.tizen.pathnotgiven", nullptr}; + ParamChecker checker(5, const_cast(argv)); + + EXPECT_EQ(checker.Validate(), false); +} + +TEST_F(ParamCheckerTest, CopyRes_SrcPathTraverseAttack) { + const char *argv[] = { "/bin/res-copy", "--uid", + "5001", "-p", "data/../../attackpath", "dstpath", + "--copy", "org.test.targetpkgid", nullptr}; + + ParamChecker checker(8, const_cast(argv)); + + EXPECT_FALSE(checker.Validate()); +} + +TEST_F(ParamCheckerTest, CopyRes_DstPathTraverseAttack) { + const char *argv[] = { "/bin/res-copy", "--uid", + "5001", "-p", "data/normal_path", "../../attackpath", + "--copy", "org.test.targetpkgid", nullptr}; + + ParamChecker checker(8, const_cast(argv)); + + EXPECT_FALSE(checker.Validate()); +} + +TEST_F(ParamCheckerTest, RemoveRes_PathTraverseAttack) { + const char *argv[] = { "/bin/res-copy", "--uid", + "5001", "-p", "../../../attackpath", "", + "--remove", "org.test.targetpkgid", nullptr}; + + ParamChecker checker(8, const_cast(argv)); + + EXPECT_FALSE(checker.Validate()); +} diff --git a/tests/unit_tests/res-copy/src/test_request_handler.cc b/tests/unit_tests/res-copy/src/test_request_handler.cc new file mode 100644 index 0000000..33d06bd --- /dev/null +++ b/tests/unit_tests/res-copy/src/test_request_handler.cc @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "mock/os_mock.h" +#include "mock/pkgmgr_info_mock.h" +#include "mock/test_fixture.h" + +#include "include/abstract_request_handler.hh" +#include "include/copy_request_handler.hh" +#include "include/createdir_request_handler.hh" +#include "include/error_type.hh" +#include "include/param_checker.hh" +#include "include/remove_request_handler.hh" +#include "include/request_type.hh" +#include "include/uninstall_request_handler.hh" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::InvokeArgument; +using ::testing::SaveArg; + +using res_handler::CopyRequestHandler; +using res_handler::CreateDirRequestHandler; +using res_handler::RemoveRequestHandler; +using res_handler::ResPathInfo; +using res_handler::UninstallRequestHandler; + +namespace bf = boost::filesystem; +namespace bs = boost::system; + +namespace { + +constexpr char kTestPkgID[] = "test_pkg"; + +std::string GetTestPkgID() { + return kTestPkgID; +} + +std::string GetRootPath() { + return "./tests/unit_tests/res-copy/data"; +} + +bf::path GetSrcRootPath() { + return bf::path(GetRootPath()) / "apps_rw" / kTestPkgID; +} + +bf::path GetDstRootPath() { + return bf::path(GetRootPath()) / "priv_shared_res" / kTestPkgID; +} + +} // namespace + +class Mocks : public ::testing::NiceMock, + public ::testing::NiceMock {}; + +class CopyRequestHandlerTest : public TestFixture { + public: + CopyRequestHandlerTest() : TestFixture(std::make_unique()) {} + virtual ~CopyRequestHandlerTest() {} + + virtual void SetUp() { + bf::create_directories(GetDstRootPath() / "dest_dir"); + + EXPECT_CALL(GetMock(), lchown(_, _, _)).WillRepeatedly(Return(0)); + EXPECT_CALL(GetMock(), chmod(_, _)).WillRepeatedly(Return(0)); + } + virtual void TearDown() { + bf::remove_all(GetDstRootPath()); + } +}; + +class RemoveRequestHandlerTest : public TestFixture { + public: + RemoveRequestHandlerTest() : TestFixture(std::make_unique()) {} + virtual ~RemoveRequestHandlerTest() {} + + virtual void SetUp() { + bf::create_directories(GetDstRootPath() / "new_dir/new_dir2"); + bf::copy_file(GetSrcRootPath() / "data/resource_file.txt", + GetDstRootPath() / "resource_file.txt"); + bf::copy_file(GetSrcRootPath() / "data/resource_file.txt", + GetDstRootPath() / "new_dir/resource_file.txt"); + bf::copy_file(GetSrcRootPath() / "data/resource_file.txt", + GetDstRootPath() / "new_dir/new_dir2/resource_file.txt"); + } + virtual void TearDown() { + bf::remove_all(GetDstRootPath()); + } +}; + +class UninstallRequestHandlerTest : public TestFixture { + public: + UninstallRequestHandlerTest() : TestFixture(std::make_unique()) {} + virtual ~UninstallRequestHandlerTest() {} + + virtual void SetUp() { + bf::create_directories(GetDstRootPath()); + } + + virtual void TearDown() { + bf::remove_all(GetDstRootPath()); + } +}; + +class CreateDirRequestHandlerTest : public TestFixture { + public: + CreateDirRequestHandlerTest() : TestFixture(std::make_unique()) {} + virtual ~CreateDirRequestHandlerTest() {} + + virtual void SetUp() { + bf::create_directories(GetDstRootPath() / "existed_dir"); + } + + virtual void TearDown() { + bf::remove_all(GetDstRootPath()); + } +}; + +TEST_F(CopyRequestHandlerTest, CopyFileAtRootToRoot) { + std::list path_list; + path_list.emplace_back("data/resource_file.txt", ""); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtRootToRoot_ChangeFileName) { + std::list path_list; + path_list.emplace_back("data/resource_file.txt", "another_name.txt"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "another_name.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtRootToDirectory) { + std::list path_list; + path_list.emplace_back("data/resource_file.txt", "dest_dir"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtRootToDirectory_ChangeFileName) { + std::list path_list; + path_list.emplace_back("data/resource_file.txt", "dest_dir/newname.txt"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "newname.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtDirectoryToRoot) { + std::list path_list; + path_list.emplace_back("data/resource_dir1/resource_file3.txt", ""); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file3.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtDirectoryToRoot_ChangeFileName) { + std::list path_list; + path_list.emplace_back( + "data/resource_dir1/resource_file3.txt", "newname.txt"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "newname.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyFileAtDirectoryToDirectory) { + std::list path_list; + path_list.emplace_back("data/resource_dir1/resource_file3.txt", "dest_dir"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file3.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtRootToRoot_ChangeFileName) { + std::list path_list; + path_list.emplace_back( + "data/resource_dir1/resource_file3.txt", "dest_dir/newname.txt"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "newname.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtRootToRoot) { + std::list path_list; + path_list.emplace_back("data/resource_dir1", ""); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file3.txt"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path = check_path.parent_path(); + check_path /= "resource_dir2"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file2.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtRootToDirectory) { + std::list path_list; + path_list.emplace_back("data/resource_dir1", "dest_dir"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file3.txt"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path = check_path.parent_path(); + check_path /= "resource_dir2"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file2.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtRootToDirectory2) { + std::list path_list; + path_list.emplace_back("data/resource_dir1", "resource_dir1"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_dir1"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file3.txt"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path = check_path.parent_path(); + check_path /= "resource_dir2"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file2.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtDirectoryToRoot) { + std::list path_list; + path_list.emplace_back("data/resource_dir1/resource_dir2", ""); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file2.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, CopyDirectoryAtDirectoryToDirectory) { + std::list path_list; + path_list.emplace_back("data/resource_dir1/resource_dir2", "dest_dir"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path = GetDstRootPath(); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "dest_dir"; + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "resource_file2.txt"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CopyRequestHandlerTest, ResNotexists) { + std::list path_list; + path_list.emplace_back("data/not_existed_res", "new_dir"); + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_RES_NOT_FOUND); +} + +TEST_F(CopyRequestHandlerTest, ReplaceRes) { + std::list path_list; + path_list.emplace_back("data/resource_file.txt", "new_dir"); + + CopyRequestHandler handler(GetTestPkgID(), 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + CopyRequestHandler replace_handler( + GetTestPkgID(), 0, GetRootPath(), path_list); + EXPECT_EQ(replace_handler.Execute(), res_handler::ErrorType::ERROR_NONE); +} + +TEST_F(RemoveRequestHandlerTest, RemoveFileAtRoot) { + std::list path_list; + path_list.emplace_back("resource_file.txt", ""); + + RemoveRequestHandler handler("test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "resource_file.txt"); + EXPECT_FALSE(bf::exists(check_path)); +} + +TEST_F(RemoveRequestHandlerTest, RemoveFileAtDirectory) { + std::list path_list; + path_list.emplace_back("new_dir/resource_file.txt", ""); + + RemoveRequestHandler handler("test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "new_dir/resource_file.txt"); + EXPECT_FALSE(bf::exists(check_path)); +} + +TEST_F(RemoveRequestHandlerTest, RemoveDirectoryAtRoot) { + std::list path_list; + path_list.emplace_back("new_dir", ""); + + RemoveRequestHandler handler("test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "new_dir"); + EXPECT_FALSE(bf::exists(check_path)); +} + +TEST_F(RemoveRequestHandlerTest, RemoveDirectoryAtDirectory) { + std::list path_list; + path_list.emplace_back("new_dir/new_dir2", ""); + + RemoveRequestHandler handler("test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "new_dir/new_dir2"); + EXPECT_FALSE(bf::exists(check_path)); +} + +TEST_F(UninstallRequestHandlerTest, RemoveRootPath) { + std::list path_list; + + UninstallRequestHandler handler( + "test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); + EXPECT_FALSE(bf::exists(GetDstRootPath())); +} + +TEST_F(UninstallRequestHandlerTest, RootNotExists) { + std::list path_list; + bf::remove_all(GetDstRootPath()); + + UninstallRequestHandler handler( + "test_pkg", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), res_handler::ErrorType::ERROR_NONE); +} + +TEST_F(UninstallRequestHandlerTest, EmptyPkgID) { + std::list path_list; + + UninstallRequestHandler handler("", 0, GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), + res_handler::ErrorType::ERROR_INVALID_PARAMETER); +} + +TEST_F(CreateDirRequestHandlerTest, CreateOneAtRoot) { + std::list path_list; + path_list.emplace_back("new_dir", ""); + + CreateDirRequestHandler handler( + "test_pkg", getuid(), GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), + res_handler::ErrorType::ERROR_NONE); + + EXPECT_TRUE(bf::exists(GetDstRootPath() / "new_dir")); +} + +TEST_F(CreateDirRequestHandlerTest, CreateOneAtExistedDirectory) { + std::list path_list; + path_list.emplace_back("existed_dir/new_dir", ""); + + CreateDirRequestHandler handler( + "test_pkg", getuid(), GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), + res_handler::ErrorType::ERROR_NONE); + + EXPECT_TRUE(bf::exists(GetDstRootPath() / "existed_dir/new_dir")); +} + +TEST_F(CreateDirRequestHandlerTest, CreateHierachyAtRoot) { + std::list path_list; + path_list.emplace_back("new_dir/another_new_dir", ""); + + CreateDirRequestHandler handler( + "test_pkg", getuid(), GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), + res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "new_dir"); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "another_new_dir"; + EXPECT_TRUE(bf::exists(check_path)); +} + +TEST_F(CreateDirRequestHandlerTest, CreateHierachyAtExistedDirectory) { + std::list path_list; + path_list.emplace_back("existed_dir/new_dir/another_new_dir", ""); + + CreateDirRequestHandler handler( + "test_pkg", getuid(), GetRootPath(), path_list); + EXPECT_EQ(handler.Execute(), + res_handler::ErrorType::ERROR_NONE); + + bf::path check_path(GetDstRootPath() / "existed_dir/new_dir"); + EXPECT_TRUE(bf::exists(check_path)); + + check_path /= "another_new_dir"; + EXPECT_TRUE(bf::exists(check_path)); +} diff --git a/tests/unit_tests/res-copy/src/test_request_handler_invoker.cc b/tests/unit_tests/res-copy/src/test_request_handler_invoker.cc new file mode 100644 index 0000000..deb1b58 --- /dev/null +++ b/tests/unit_tests/res-copy/src/test_request_handler_invoker.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include "mock/os_mock.h" +#include "mock/pkgmgr_info_mock.h" +#include "mock/test_fixture.h" +#include "include/event_signal_sender.hh" +#include "include/param_checker.hh" +#include "include/request_handler_invoker.hh" +#include "include/request_type.hh" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::InvokeArgument; +using ::testing::SaveArg; + +using res_handler::ParamChecker; +using res_handler::RequestHandlerInvoker; + +class Mocks : public ::testing::NiceMock, + public ::testing::NiceMock {}; + +class RequestHandlerInvokerTest : public TestFixture { + public: + RequestHandlerInvokerTest() : TestFixture(std::make_unique()) { + signal_sender_ = std::make_shared(nullptr); + signal_sender_->SetPkgID("org.tizen.targepkgid"); + signal_sender_->SetReqType(res_handler::ReqType::REQ_TYPE_UNKNOWN); + } + virtual ~RequestHandlerInvokerTest() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + std::shared_ptr signal_sender_; +}; + +TEST_F(RequestHandlerInvokerTest, CopyType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--copy", + "org.test.targetpkgid", "-p", "srcpath", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + + RequestHandlerInvoker request_handler_invoker(checker, signal_sender_); + EXPECT_EQ(request_handler_invoker.GetHandlerType(), "copy"); +} + +TEST_F(RequestHandlerInvokerTest, RemoveType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--remove", + "org.test.targetpkgid", "-p", "", "dstpath", nullptr}; + ParamChecker checker(8, const_cast(argv)); + + RequestHandlerInvoker request_handler_invoker(checker, signal_sender_); + EXPECT_EQ(request_handler_invoker.GetHandlerType(), "remove"); +} + +TEST_F(RequestHandlerInvokerTest, UninstallType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "--delete", + "org.test.targetpkgid", nullptr}; + ParamChecker checker(5, const_cast(argv)); + + RequestHandlerInvoker request_handler_invoker(checker, signal_sender_); + EXPECT_EQ(request_handler_invoker.GetHandlerType(), "delete"); +} + +TEST_F(RequestHandlerInvokerTest, CreateDirType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", "-D", + "org.test.targetpkgid", "-p", "newdir", nullptr}; + ParamChecker checker(7, const_cast(argv)); + + RequestHandlerInvoker request_handler_invoker(checker, signal_sender_); + EXPECT_EQ(request_handler_invoker.GetHandlerType(), "createdir"); +} + +TEST_F(RequestHandlerInvokerTest, InvalidType) { + const char *argv[] = { "/bin/res-copy", "--uid", "5001", nullptr}; + ParamChecker checker(3, const_cast(argv)); + + RequestHandlerInvoker request_handler_invoker(checker, signal_sender_); + EXPECT_EQ(request_handler_invoker.GetHandlerType(), ""); + EXPECT_EQ(request_handler_invoker.Execute(), false); +} diff --git a/tests/unit_tests/test_main.cc b/tests/unit_tests/test_main.cc new file mode 100644 index 0000000..5a9f91d --- /dev/null +++ b/tests/unit_tests/test_main.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int main(int argc, char** argv) { + int ret = -1; + + try { + testing::InitGoogleTest(&argc, argv); + } catch(...) { + std::cout << "Exception occurred" << std::endl; + } + + try { + ret = RUN_ALL_TESTS(); + } catch (const ::testing::internal::GoogleTestFailureException& e) { + ret = -1; + std::cout << "GoogleTestFailureException was thrown:" + << e.what() << std::endl; + } + + return ret; +}