diff --git a/apps/chown.cpp b/apps/chown.cpp index 16416cd3..b856c0dc 100644 --- a/apps/chown.cpp +++ b/apps/chown.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -31,8 +32,14 @@ Result luna_main(int argc, char** argv) if (rc.has_value()) { gid = (u32)rc.value(); } else { - fprintf(stderr, "FIXME: read entry from group file to determine GID for group %s\n", group.chars()); - return 1; + struct group* grp = getgrnam(group.chars()); + if (!grp) + { + fprintf(stderr, "%s: unknown group %s!\n", argv[0], group.chars()); + return 1; + } + + gid = grp->gr_gid; } } } diff --git a/initrd/etc/group b/initrd/etc/group new file mode 100644 index 00000000..59582096 --- /dev/null +++ b/initrd/etc/group @@ -0,0 +1,3 @@ +root:!:0: +users:!:1: +selene:!:1000: diff --git a/libc/include/grp.h b/libc/include/grp.h new file mode 100644 index 00000000..e00eca0e --- /dev/null +++ b/libc/include/grp.h @@ -0,0 +1,40 @@ +/* grp.h: Group file parsing. */ + +#ifndef _GRP_H +#define _GRP_H + +#include + +struct group +{ + char* gr_name; + char* gr_passwd; + gid_t gr_gid; + char** gr_mem; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Read the next entry from the group file. */ + struct group* getgrent(void); + + /* Find the entry with a matching group name in the group file. */ + struct group* getgrnam(const char* name); + + /* Find the entry with a matching group ID in the group file. */ + struct group* getgrgid(gid_t gid); + + /* Rewind the group file. */ + void setgrent(void); + + /* End group file parsing. */ + void endgrent(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/src/grp.cpp b/libc/src/grp.cpp new file mode 100644 index 00000000..37efaa3e --- /dev/null +++ b/libc/src/grp.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +static struct group grp; +static FILE* f { nullptr }; +static char buf[4096]; + +static Vector g_members; + +extern char* _strtok_once(char* str, const char* delim); + +extern "C" +{ + static Result try_getgrent() + { + if (!f) + { + f = fopen("/etc/group", "r"); + if (!f) return nullptr; + } + + while (true) + { + char* rc = fgets(buf, sizeof(buf), f); + if (!rc) return nullptr; + + char* name = _strtok_once(rc, ":\n"); + if (!name) continue; + + char* passwd = _strtok_once(nullptr, ":\n"); + if (!passwd) continue; + + char* gid = _strtok_once(nullptr, ":\n"); + if (!gid) continue; + + char* members = _strtok_once(nullptr, ":\n"); + if (!members) continue; + + g_members.clear(); + + char* member = strtok(members, ","); + TRY(g_members.try_append(member)); + + if (member) + { + while (true) + { + member = strtok(nullptr, ","); + TRY(g_members.try_append(member)); + if (!member) break; + } + } + + grp.gr_name = name; + grp.gr_passwd = passwd; + grp.gr_gid = (gid_t)strtoul(gid, NULL, 10); + grp.gr_mem = g_members.data(); + + return &grp; + } + } + + struct group* getgrent() + { + auto rc = try_getgrent(); + if (rc.has_error()) + { + errno = rc.error(); + return nullptr; + } + return rc.value(); + } + + struct group* getgrnam(const char* name) + { + setgrent(); + + struct group* entry; + + while ((entry = getgrent())) + { + if (!strcmp(entry->gr_name, name)) + { + endgrent(); + return entry; + } + } + + endgrent(); + return entry; + } + + struct group* getgrgid(gid_t gid) + { + setgrent(); + + struct group* entry; + + while ((entry = getgrent())) + { + if (entry->gr_gid == gid) + { + endgrent(); + return entry; + } + } + + endgrent(); + return entry; + } + + void setgrent() + { + if (f) rewind(f); + } + + void endgrent() + { + if (f) + { + fclose(f); + f = nullptr; + } + } +} diff --git a/libc/src/pwd.cpp b/libc/src/pwd.cpp index 33c1eaf5..80a0bae7 100644 --- a/libc/src/pwd.cpp +++ b/libc/src/pwd.cpp @@ -11,7 +11,7 @@ static char buf[4096]; // Example: // strtok("hello:world::!:", ":") -> "hello", "world", "!" // _strtok_once("hello:world::!:", ":") -> "hello", "world", "", "!" -static char* _strtok_once(char* str, const char* delim) +char* _strtok_once(char* str, const char* delim) { static char* s = nullptr; if (str) s = str;