From da2ee2ec117dfabe6220f14514e6c85193f422fa Mon Sep 17 00:00:00 2001 From: muthu90tech Date: Sun, 30 Jun 2024 16:08:18 -0700 Subject: [PATCH] Add APIs to get group details and to change ownership of file. --- include/seastar/core/reactor.hh | 2 ++ include/seastar/core/seastar.hh | 17 ++++++++++ src/core/reactor.cc | 31 +++++++++++++++++++ src/core/syscall_result.hh | 4 +++ src/seastar.cc | 1 + src/util/file.cc | 9 ++++++ tests/unit/CMakeLists.txt | 3 ++ tests/unit/libc_wrapper_test.cc | 55 +++++++++++++++++++++++++++++++++ 8 files changed, 122 insertions(+) create mode 100644 tests/unit/libc_wrapper_test.cc diff --git a/include/seastar/core/reactor.hh b/include/seastar/core/reactor.hh index 44a1c3fa93e..b9def3f31a9 100644 --- a/include/seastar/core/reactor.hh +++ b/include/seastar/core/reactor.hh @@ -493,6 +493,8 @@ public: future<> touch_directory(std::string_view name, file_permissions permissions = file_permissions::default_dir_permissions) noexcept; future> file_type(std::string_view name, follow_symlink = follow_symlink::yes) noexcept; future file_stat(std::string_view pathname, follow_symlink) noexcept; + future<> chown(std::string_view filepath, uid_t owner, gid_t group); + future> getgrnam(std::string_view name, char *buf, size_t buflen); future file_size(std::string_view pathname) noexcept; future file_accessible(std::string_view pathname, access_flags flags) noexcept; future file_exists(std::string_view pathname) noexcept { diff --git a/include/seastar/core/seastar.hh b/include/seastar/core/seastar.hh index 1ea67381a88..f3b94b820ea 100644 --- a/include/seastar/core/seastar.hh +++ b/include/seastar/core/seastar.hh @@ -359,6 +359,23 @@ using follow_symlink = bool_class; /// with follow_symlink::yes, or for the link itself, with follow_symlink::no. future file_stat(std::string_view name, follow_symlink fs = follow_symlink::yes) noexcept; +/// Wrapper around getgrnam_r. +/// If the provided group name does not exist in the group database, this call will return an empty optional. +/// If the provided group name exists in the group database, the optional returned will contain the struct group information. +/// When an unexpected error is thrown by the getgrnam_r libc call, this function throws std::system_error with std::error_code. +/// \param groupname name of the group +/// \param buf buffer to store the string fields pointed to by the members of group structure. +/// \param buflen size of the buffer +/// +/// \return optional struct group of the group identified by name. struct group has details of the group from the group database. +future> getgrnam(std::string_view name, char *buf, size_t buflen); + +/// Change the owner and group of file. This is a wrapper around chown syscall. +/// The function throws std::system_error, when the chown syscall fails. +/// \param filepath +/// \param owner +/// \param group +future<> chown(std::string_view filepath, uid_t owner, gid_t group); /// Return the size of a file. /// /// \param name name of the file to return the size diff --git a/src/core/reactor.cc b/src/core/reactor.cc index d3861c4b09f..8ac00a9a384 100644 --- a/src/core/reactor.cc +++ b/src/core/reactor.cc @@ -27,12 +27,14 @@ module; #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -2200,6 +2202,35 @@ void reactor::kill(pid_t pid, int sig) { ret.throw_if_error(); } +future> reactor::getgrnam(std::string_view name, char *buf, size_t buflen) { + syscall_result_extra> sr = co_await _thread_pool->submit>>( + [name = sstring(name), buf, buflen] { + struct group grp; + struct group *result; + memset(&grp, 0, sizeof(struct group)); + errno = 0; + int ret = ::getgrnam_r(name.c_str(), &grp, buf, buflen, &result); + return wrap_syscall(ret, result ? std::optional(*result) : std::nullopt); + }); + + if (sr.result != 0) { + throw std::system_error(sr.error_code()); + } + + co_return std::move(sr.extra); +} + +future<> reactor::chown(std::string_view filepath, uid_t owner, gid_t group) { + syscall_result sr = co_await _thread_pool->submit>( + [filepath = sstring(filepath), owner, group] { + int ret = ::chown(filepath.c_str(), owner, group); + return wrap_syscall(ret); + }); + + sr.throw_if_error(); + co_return; +} + future reactor::file_stat(std::string_view pathname, follow_symlink follow) noexcept { // Allocating memory for a sstring can throw, hence the futurize_invoke diff --git a/src/core/syscall_result.hh b/src/core/syscall_result.hh index 493eb5c261d..03a06763ea6 100644 --- a/src/core/syscall_result.hh +++ b/src/core/syscall_result.hh @@ -68,6 +68,10 @@ struct syscall_result_extra : public syscall_result { Extra extra; syscall_result_extra(int result, int error, Extra e) : syscall_result{result, error}, extra{std::move(e)} { } + + std::error_code error_code() const { + return this->ec(); + } }; template diff --git a/src/seastar.cc b/src/seastar.cc index ba3a1d4449d..6ffdb9b4dee 100644 --- a/src/seastar.cc +++ b/src/seastar.cc @@ -142,6 +142,7 @@ module; #include #include #include +#include #include #include #include diff --git a/src/util/file.cc b/src/util/file.cc index 99dacda6e84..2a09d3561b4 100644 --- a/src/util/file.cc +++ b/src/util/file.cc @@ -30,6 +30,7 @@ module; #include #include #include +#include #include #ifdef SEASTAR_MODULE @@ -132,6 +133,14 @@ future file_stat(std::string_view name, follow_symlink follow) noexce return engine().file_stat(name, follow); } +future> getgrnam(std::string_view name, char *buf, size_t buflen) { + return engine().getgrnam(name, buf, buflen); +} + +future<> chown(std::string_view filepath, uid_t owner, gid_t group) { + return engine().chown(filepath, owner, group); +} + future file_size(std::string_view name) noexcept { return engine().file_size(name); } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 03135ddc605..b807a86350b 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -354,6 +354,9 @@ seastar_add_test (network_interface seastar_add_test (json_formatter SOURCES json_formatter_test.cc) +seastar_add_test (libc_wrapper + SOURCES libc_wrapper_test.cc) + seastar_add_test (locking SOURCES locking_test.cc) diff --git a/tests/unit/libc_wrapper_test.cc b/tests/unit/libc_wrapper_test.cc new file mode 100644 index 00000000000..cdeb4db22ac --- /dev/null +++ b/tests/unit/libc_wrapper_test.cc @@ -0,0 +1,55 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. 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. + */ + +/* + * Copyright (C) 2024 ScyllaDB. + */ +#include +#include +#include + +#include +#include +#include + +#include + +using namespace seastar; + +SEASTAR_TEST_CASE(getgrnam_error_test) { + size_t buflen = 1; + char buf[1]; + memset(&buf, 0, buflen); + BOOST_REQUIRE_THROW(co_await getgrnam("root", buf, buflen), std::system_error); +} + +SEASTAR_TEST_CASE(getgrnam_group_name_does_not_exist_test) { + size_t buflen = 1024; + char buf[1024]; + memset(&buf, 0, buflen); + std::optional grp = co_await getgrnam("roo", buf, buflen); + BOOST_REQUIRE(!grp.has_value()); +} + +SEASTAR_TEST_CASE(getgrnam_group_name_exists_test) { + size_t buflen = 1024; + char buf[1024]; + memset(&buf, 0, buflen); + std::optional grp = co_await getgrnam("root", buf, buflen); + BOOST_REQUIRE(grp.has_value()); +} \ No newline at end of file