From 5df0e25c1f4785e09ffe54182d4ba39987357466 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 28 Jun 2025 21:32:59 +0300 Subject: [PATCH] userspace: Implement chown utility --- userspace/programs/CMakeLists.txt | 1 + userspace/programs/chown/CMakeLists.txt | 9 ++ userspace/programs/chown/main.cpp | 109 ++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 userspace/programs/chown/CMakeLists.txt create mode 100644 userspace/programs/chown/main.cpp diff --git a/userspace/programs/CMakeLists.txt b/userspace/programs/CMakeLists.txt index 4a89a73d..01311621 100644 --- a/userspace/programs/CMakeLists.txt +++ b/userspace/programs/CMakeLists.txt @@ -4,6 +4,7 @@ set(USERSPACE_PROGRAMS cat cat-mmap chmod + chown cp dd dhcp-client diff --git a/userspace/programs/chown/CMakeLists.txt b/userspace/programs/chown/CMakeLists.txt new file mode 100644 index 00000000..fb907a68 --- /dev/null +++ b/userspace/programs/chown/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + main.cpp +) + +add_executable(chown ${SOURCES}) +banan_link_library(chown ban) +banan_link_library(chown libc) + +install(TARGETS chown OPTIONAL) diff --git a/userspace/programs/chown/main.cpp b/userspace/programs/chown/main.cpp new file mode 100644 index 00000000..04d2617a --- /dev/null +++ b/userspace/programs/chown/main.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(const char* argv0, int ret) +{ + FILE* out = (ret == 0) ? stdout : stderr; + fprintf(out, "usage: %s [OWNER][:[GROUP]] FILE...\n", argv0); + fprintf(out, " Change the owner and/or group of each FILE.\n"); + exit(ret); +} + +[[noreturn]] void print_error_and_exit(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(1); + __builtin_unreachable(); +} + +const passwd* get_user(const char* string) +{ + bool is_numeric = true; + for (size_t i = 0; string[i] && is_numeric; i++) + if (!isdigit(string[i])) + is_numeric = false; + if (is_numeric) + return getpwuid(atoll(string)); + return getpwnam(string); +} + +const group* get_group(const char* string) +{ + bool is_numeric = true; + for (size_t i = 0; string[i] && is_numeric; i++) + if (!isdigit(string[i])) + is_numeric = false; + if (is_numeric) + return getgrgid(atoll(string)); + return getgrnam(string); +} + +int main(int argc, char** argv) +{ + if (argc <= 2) + usage(argv[0], 1); + + uid_t uid = -1; + gid_t gid = -1; + + const char* owner_string = argv[1]; + + const char* colon = strchr(owner_string, ':'); + if (colon == owner_string) + { + const auto* group = get_group(owner_string + 1); + if (group == nullptr) + print_error_and_exit("could not find group %s\n", owner_string + 1); + gid = group->gr_gid; + } + else if (colon == nullptr) + { + const auto* user = get_user(owner_string); + if (user == nullptr) + print_error_and_exit("could not find user %s\n", owner_string); + uid = user->pw_uid; + } + else + { + char* user_name = strndup(owner_string, colon - owner_string); + if (user_name == nullptr) + print_error_and_exit("strndup: %s\n", strerror(errno)); + const auto* user = get_user(user_name); + if (user == nullptr) + print_error_and_exit("could not find user %s\n", user_name); + free(user_name); + uid = user->pw_uid; + if (colon[1] == '\0') + gid = user->pw_gid; + else + { + const auto* group = get_group(colon + 1); + if (group == nullptr) + print_error_and_exit("could not find group %s\n", colon + 1); + gid = group->gr_gid; + } + } + + int ret = 0; + for (int i = 2; i < argc; i++) + { + if (chown(argv[i], uid, gid) == -1) + { + perror("chown"); + ret = 1; + } + } + + return ret; +}