Skip to content

Commit

Permalink
Add APIs to get group details and to change ownership of file.
Browse files Browse the repository at this point in the history
  • Loading branch information
muthu90tech committed Sep 5, 2024
1 parent e42e382 commit 653962d
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 1 deletion.
2 changes: 2 additions & 0 deletions include/seastar/core/reactor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ public:
future<> touch_directory(std::string_view name, file_permissions permissions = file_permissions::default_dir_permissions) noexcept;
future<std::optional<directory_entry_type>> file_type(std::string_view name, follow_symlink = follow_symlink::yes) noexcept;
future<stat_data> file_stat(std::string_view pathname, follow_symlink) noexcept;
future<> chown(std::string_view filepath, uid_t owner, gid_t group);
future<std::optional<struct group>> getgrnam(std::string_view name, char *buf, size_t buflen);
future<uint64_t> file_size(std::string_view pathname) noexcept;
future<bool> file_accessible(std::string_view pathname, access_flags flags) noexcept;
future<bool> file_exists(std::string_view pathname) noexcept {
Expand Down
17 changes: 17 additions & 0 deletions include/seastar/core/seastar.hh
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,23 @@ using follow_symlink = bool_class<follow_symlink_tag>;
/// with follow_symlink::yes, or for the link itself, with follow_symlink::no.
future<stat_data> 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<std::optional<struct group>> 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
Expand Down
31 changes: 31 additions & 0 deletions src/core/reactor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ module;
#include <cassert>
#include <chrono>
#include <cmath>
#include <coroutine>
#include <exception>
#include <filesystem>
#include <fstream>
#include <regex>
#include <thread>

#include <grp.h>
#include <spawn.h>
#include <sys/syscall.h>
#include <sys/vfs.h>
Expand Down Expand Up @@ -2200,6 +2202,35 @@ void reactor::kill(pid_t pid, int sig) {
ret.throw_if_error();
}

future<std::optional<struct group>> reactor::getgrnam(std::string_view name, char *buf, size_t buflen) {
syscall_result_extra<std::optional<struct group>> sr = co_await _thread_pool->submit<syscall_result_extra<std::optional<struct group>>>(
[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<struct group>(*result) : std::nullopt);
});

if (sr.result != 0) {
throw std::system_error(sr.ec());
}

co_return std::move(sr.extra);
}

future<> reactor::chown(std::string_view filepath, uid_t owner, gid_t group) {
syscall_result<int> sr = co_await _thread_pool->submit<syscall_result<int>>(
[filepath = sstring(filepath), owner, group] {
int ret = ::chown(filepath.c_str(), owner, group);
return wrap_syscall(ret);
});

sr.throw_if_error();
co_return;
}

future<stat_data>
reactor::file_stat(std::string_view pathname, follow_symlink follow) noexcept {
// Allocating memory for a sstring can throw, hence the futurize_invoke
Expand Down
2 changes: 1 addition & 1 deletion src/core/syscall_result.hh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct syscall_result {
throw_fs_exception(reason, fs::path(path1), fs::path(path2));
}
}
protected:

std::error_code ec() const {
return std::error_code(error, std::system_category());
}
Expand Down
1 change: 1 addition & 0 deletions src/seastar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ module;
#include <sys/un.h>
#include <execinfo.h>
#include <fcntl.h>
#include <grp.h>
#include <malloc.h>
#include <pthread.h>
#include <setjmp.h>
Expand Down
9 changes: 9 additions & 0 deletions src/util/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module;
#include <iostream>
#include <list>
#include <vector>
#include <grp.h>
#include <sys/statvfs.h>

#ifdef SEASTAR_MODULE
Expand Down Expand Up @@ -132,6 +133,14 @@ future<stat_data> file_stat(std::string_view name, follow_symlink follow) noexce
return engine().file_stat(name, follow);
}

future<std::optional<struct group>> 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<uint64_t> file_size(std::string_view name) noexcept {
return engine().file_size(name);
}
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
52 changes: 52 additions & 0 deletions tests/unit/libc_wrapper_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 <coroutine>
#include <iostream>
#include <grp.h>

#include <seastar/core/coroutine.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/seastar.hh>

#include <seastar/testing/test_case.hh>

using namespace seastar;

SEASTAR_TEST_CASE(getgrnam_error_test) {
char buf[1];
BOOST_REQUIRE_EXCEPTION(co_await getgrnam("root", buf, sizeof(buf)), std::system_error, [] (const std::system_error& e) {
return e.code().value() == ERANGE;
});
}

SEASTAR_TEST_CASE(getgrnam_group_name_does_not_exist_test) {
char buf[1024];
std::optional<struct group> grp = co_await getgrnam("roo", buf, sizeof(buf));
BOOST_REQUIRE(!grp.has_value());
}

SEASTAR_TEST_CASE(getgrnam_group_name_exists_test) {
char buf[1024];
std::optional<struct group> grp = co_await getgrnam("root", buf, sizeof(buf));
BOOST_REQUIRE(grp.has_value());
BOOST_REQUIRE_EQUAL(grp.value().gr_name, "root");
}

0 comments on commit 653962d

Please sign in to comment.