1
0
mirror of https://github.com/libuv/libuv synced 2025-03-28 21:13:16 +00:00

misc: extend getpw to take uid as an argument (#3523)

File system operations may return uid and gid values, which we may want
to pretty-print. We already have the code for getting information for
the current user, so just need to add a parameter to make it exposed for
every user. We expose information about groups in a similar manner also.
This commit is contained in:
Jameson Nash 2023-01-17 21:51:28 -06:00 committed by GitHub
parent 7fd7e8264f
commit 2f110a50df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 286 additions and 48 deletions

View File

@ -245,6 +245,7 @@ typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
typedef struct uv_passwd_s uv_passwd_t;
typedef struct uv_group_s uv_group_t;
typedef struct uv_utsname_s uv_utsname_t;
typedef struct uv_statfs_s uv_statfs_t;
@ -1139,6 +1140,12 @@ struct uv_passwd_s {
char* homedir;
};
struct uv_group_s {
char* groupname;
unsigned long gid;
char** members;
};
struct uv_utsname_s {
char sysname[256];
char release[256];
@ -1219,6 +1226,9 @@ UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd);
UV_EXTERN int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid);
UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid);
UV_EXTERN void uv_os_free_group(uv_group_t* grp);
UV_EXTERN uv_pid_t uv_os_getpid(void);
UV_EXTERN uv_pid_t uv_os_getppid(void);

View File

@ -41,12 +41,12 @@
#include <sys/uio.h> /* writev */
#include <sys/resource.h> /* getrusage */
#include <pwd.h>
#include <grp.h>
#include <sys/utsname.h>
#include <sys/time.h>
#ifdef __sun
# include <sys/filio.h>
# include <sys/types.h>
# include <sys/wait.h>
#endif
@ -1097,8 +1097,8 @@ int uv_os_homedir(char* buffer, size_t* size) {
if (r != UV_ENOENT)
return r;
/* HOME is not set, so call uv__getpwuid_r() */
r = uv__getpwuid_r(&pwd);
/* HOME is not set, so call uv_os_get_passwd() */
r = uv_os_get_passwd(&pwd);
if (r != 0) {
return r;
@ -1171,11 +1171,10 @@ return_buffer:
}
int uv__getpwuid_r(uv_passwd_t* pwd) {
static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) {
struct passwd pw;
struct passwd* result;
char* buf;
uid_t uid;
size_t bufsize;
size_t name_size;
size_t homedir_size;
@ -1185,8 +1184,6 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
if (pwd == NULL)
return UV_EINVAL;
uid = geteuid();
/* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it
* is frequently 1024 or 4096, so we can just use that directly. The pwent
* will not usually be large. */
@ -1245,24 +1242,93 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
struct group gp;
struct group* result;
char* buf;
char* gr_mem;
size_t bufsize;
size_t name_size;
long members;
size_t mem_size;
int r;
/*
The memory for name, shell, and homedir are allocated in a single
uv__malloc() call. The base of the pointer is stored in pwd->username, so
that is the field that needs to be freed.
*/
uv__free(pwd->username);
pwd->username = NULL;
pwd->shell = NULL;
pwd->homedir = NULL;
if (grp == NULL)
return UV_EINVAL;
/* Calling sysconf(_SC_GETGR_R_SIZE_MAX) would get the suggested size, but it
* is frequently 1024 or 4096, so we can just use that directly. The pwent
* will not usually be large. */
for (bufsize = 2000;; bufsize *= 2) {
buf = uv__malloc(bufsize);
if (buf == NULL)
return UV_ENOMEM;
do
r = getgrgid_r(gid, &gp, buf, bufsize, &result);
while (r == EINTR);
if (r != 0 || result == NULL)
uv__free(buf);
if (r != ERANGE)
break;
}
if (r != 0)
return UV__ERR(r);
if (result == NULL)
return UV_ENOENT;
/* Allocate memory for the groupname and members. */
name_size = strlen(gp.gr_name) + 1;
members = 0;
mem_size = sizeof(char*);
for (r = 0; gp.gr_mem[r] != NULL; r++) {
mem_size += strlen(gp.gr_mem[r]) + 1 + sizeof(char*);
members++;
}
gr_mem = uv__malloc(name_size + mem_size);
if (gr_mem == NULL) {
uv__free(buf);
return UV_ENOMEM;
}
/* Copy the members */
grp->members = (char**) gr_mem;
grp->members[members] = NULL;
gr_mem = (char*) &grp->members[members + 1];
for (r = 0; r < members; r++) {
grp->members[r] = gr_mem;
strcpy(gr_mem, gp.gr_mem[r]);
gr_mem += strlen(gr_mem) + 1;
}
assert(gr_mem == (char*)grp->members + mem_size);
/* Copy the groupname */
grp->groupname = gr_mem;
memcpy(grp->groupname, gp.gr_name, name_size);
gr_mem += name_size;
/* Copy the gid */
grp->gid = gp.gr_gid;
uv__free(buf);
return 0;
}
int uv_os_get_passwd(uv_passwd_t* pwd) {
return uv__getpwuid_r(pwd);
return uv__getpwuid_r(pwd, geteuid());
}
int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
return uv__getpwuid_r(pwd, uid);
}

View File

@ -312,7 +312,6 @@ size_t uv__thread_stack_size(void);
void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd);
int uv__search_path(const char* prog, char* buf, size_t* buflen);
void uv__wait_children(uv_loop_t* loop);

View File

@ -128,6 +128,39 @@ int uv_replace_allocator(uv_malloc_func malloc_func,
return 0;
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
/* On unix, the memory for name, shell, and homedir are allocated in a single
* uv__malloc() call. The base of the pointer is stored in pwd->username, so
* that is the field that needs to be freed.
*/
uv__free(pwd->username);
#ifdef _WIN32
uv__free(pwd->homedir);
#endif
pwd->username = NULL;
pwd->shell = NULL;
pwd->homedir = NULL;
}
void uv_os_free_group(uv_group_t *grp) {
if (grp == NULL)
return;
/* The memory for is allocated in a single uv__malloc() call. The base of the
* pointer is stored in grp->members, so that is the only field that needs to
* be freed.
*/
uv__free(grp->members);
grp->members = NULL;
grp->groupname = NULL;
}
#define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
size_t uv_handle_size(uv_handle_type type) {

View File

@ -269,7 +269,6 @@ void uv__util_init(void);
uint64_t uv__hrtime(unsigned int scale);
__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
int uv__getpwuid_r(uv_passwd_t* pwd);
int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);

View File

@ -999,8 +999,8 @@ int uv_os_homedir(char* buffer, size_t* size) {
if (r != UV_ENOENT)
return r;
/* USERPROFILE is not set, so call uv__getpwuid_r() */
r = uv__getpwuid_r(&pwd);
/* USERPROFILE is not set, so call uv_os_get_passwd() */
r = uv_os_get_passwd(&pwd);
if (r != 0) {
return r;
@ -1087,17 +1087,6 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
uv__free(pwd->username);
uv__free(pwd->homedir);
pwd->username = NULL;
pwd->homedir = NULL;
}
/*
* Converts a UTF-16 string into a UTF-8 one. The resulting string is
* null-terminated.
@ -1194,7 +1183,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
}
int uv__getpwuid_r(uv_passwd_t* pwd) {
static int uv__getpwuid_r(uv_passwd_t* pwd) {
HANDLE token;
wchar_t username[UNLEN + 1];
wchar_t *path;
@ -1272,6 +1261,16 @@ int uv_os_get_passwd(uv_passwd_t* pwd) {
}
int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
return UV_ENOTSUP;
}
int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
return UV_ENOTSUP;
}
int uv_os_environ(uv_env_item_t** envitems, int* count) {
wchar_t* env;
wchar_t* penv;

View File

@ -39,32 +39,32 @@ TEST_IMPL(get_passwd) {
/* Test the normal case */
r = uv_os_get_passwd(&pwd);
ASSERT(r == 0);
ASSERT_EQ(r, 0);
len = strlen(pwd.username);
ASSERT(len > 0);
ASSERT_GT(len, 0);
#ifdef _WIN32
ASSERT_NULL(pwd.shell);
#else
len = strlen(pwd.shell);
# ifndef __PASE__
ASSERT(len > 0);
ASSERT_GT(len, 0);
# endif
#endif
len = strlen(pwd.homedir);
ASSERT(len > 0);
ASSERT_GT(len, 0);
#ifdef _WIN32
if (len == 3 && pwd.homedir[1] == ':')
ASSERT(pwd.homedir[2] == '\\');
ASSERT_EQ(pwd.homedir[2], '\\');
else
ASSERT(pwd.homedir[len - 1] != '\\');
ASSERT_NE(pwd.homedir[len - 1], '\\');
#else
if (len == 1)
ASSERT(pwd.homedir[0] == '/');
ASSERT_EQ(pwd.homedir[0], '/');
else
ASSERT(pwd.homedir[len - 1] != '/');
ASSERT_NE(pwd.homedir[len - 1], '/');
#endif
#ifdef _WIN32
@ -95,7 +95,110 @@ TEST_IMPL(get_passwd) {
/* Test invalid input */
r = uv_os_get_passwd(NULL);
ASSERT(r == UV_EINVAL);
ASSERT_EQ(r, UV_EINVAL);
return 0;
}
TEST_IMPL(get_passwd2) {
/* TODO(gengjiawen): Fix test on QEMU. */
#if defined(__QEMU__)
RETURN_SKIP("Test does not currently work in QEMU");
#endif
uv_passwd_t pwd;
uv_passwd_t pwd2;
size_t len;
int r;
/* Test the normal case */
r = uv_os_get_passwd(&pwd);
ASSERT_EQ(r, 0);
r = uv_os_get_passwd2(&pwd2, pwd.uid);
#ifdef _WIN32
ASSERT_EQ(r, UV_ENOTSUP);
#else
ASSERT_EQ(r, 0);
ASSERT_EQ(pwd.uid, pwd2.uid);
ASSERT_STR_EQ(pwd.username, pwd2.username);
ASSERT_STR_EQ(pwd.shell, pwd2.shell);
ASSERT_STR_EQ(pwd.homedir, pwd2.homedir);
uv_os_free_passwd(&pwd2);
r = uv_os_get_passwd2(&pwd2, 0);
ASSERT_EQ(r, 0);
len = strlen(pwd2.username);
ASSERT_GT(len, 0);
ASSERT_STR_EQ(pwd2.username, "root");
len = strlen(pwd2.homedir);
ASSERT_GT(len, 0);
len = strlen(pwd2.shell);
# ifndef __PASE__
ASSERT_GT(len, 0);
# endif
uv_os_free_passwd(&pwd2);
#endif
uv_os_free_passwd(&pwd);
/* Test invalid input */
r = uv_os_get_passwd2(NULL, pwd.uid);
#ifdef _WIN32
ASSERT_EQ(r, UV_ENOTSUP);
#else
ASSERT_EQ(r, UV_EINVAL);
#endif
return 0;
}
TEST_IMPL(get_group) {
/* TODO(gengjiawen): Fix test on QEMU. */
#if defined(__QEMU__)
RETURN_SKIP("Test does not currently work in QEMU");
#endif
uv_passwd_t pwd;
uv_group_t grp;
size_t len;
int r;
r = uv_os_get_passwd(&pwd);
ASSERT_EQ(r, 0);
r = uv_os_get_group(&grp, pwd.gid);
#ifdef _WIN32
ASSERT_EQ(r, UV_ENOTSUP);
#else
ASSERT_EQ(r, 0);
ASSERT_EQ(pwd.gid, grp.gid);
len = strlen(grp.groupname);
ASSERT_GT(len, 0);
uv_os_free_group(&grp);
#endif
uv_os_free_passwd(&pwd);
/* Test invalid input */
r = uv_os_get_group(NULL, pwd.gid);
#ifdef _WIN32
ASSERT_EQ(r, UV_ENOTSUP);
#else
ASSERT_EQ(r, UV_EINVAL);
#endif
return 0;
}

View File

@ -281,6 +281,8 @@ TEST_DECLARE (process_title_threadsafe)
TEST_DECLARE (cwd_and_chdir)
TEST_DECLARE (get_memory)
TEST_DECLARE (get_passwd)
TEST_DECLARE (get_passwd2)
TEST_DECLARE (get_group)
TEST_DECLARE (handle_fileno)
TEST_DECLARE (homedir)
TEST_DECLARE (tmpdir)
@ -899,6 +901,8 @@ TASK_LIST_START
TEST_ENTRY (get_memory)
TEST_ENTRY (get_passwd)
TEST_ENTRY (get_passwd2)
TEST_ENTRY (get_group)
TEST_ENTRY (get_loadavg)

View File

@ -35,8 +35,10 @@ TEST_IMPL(platform_output) {
uv_cpu_info_t* cpus;
uv_interface_address_t* interfaces;
uv_passwd_t pwd;
uv_group_t grp;
uv_utsname_t uname;
unsigned par;
char* const* member;
int count;
int i;
int err;
@ -147,15 +149,38 @@ TEST_IMPL(platform_output) {
uv_free_interface_addresses(interfaces, count);
err = uv_os_get_passwd(&pwd);
ASSERT(err == 0);
ASSERT_EQ(err, 0);
err = uv_os_get_group(&grp, pwd.gid);
#if defined(_WIN32)
ASSERT_EQ(err, UV_ENOTSUP);
ASSERT_EQ(pwd.uid, (unsigned long) -1);
ASSERT_EQ(pwd.gid, (unsigned long) -1);
(void) member;
grp.groupname = "ENOTSUP";
#else
ASSERT_EQ(err, 0);
ASSERT_EQ(pwd.gid, grp.gid);
#endif
printf("uv_os_get_passwd:\n");
printf(" euid: %ld\n", pwd.uid);
printf(" gid: %ld\n", pwd.gid);
printf(" gid: %ld (%s)\n", pwd.gid, grp.groupname);
#if !defined(_WIN32)
printf(" members: [");
for (member = grp.members; *member != NULL; member++) {
printf(" %s", *member);
}
printf(" ]\n");
#endif
printf(" username: %s\n", pwd.username);
printf(" shell: %s\n", pwd.shell);
if (pwd.shell != NULL) /* Not set on Windows */
printf(" shell: %s\n", pwd.shell);
printf(" home directory: %s\n", pwd.homedir);
uv_os_free_passwd(&pwd);
#if !defined(_WIN32)
uv_os_free_group(&grp);
#endif
pid = uv_os_getpid();
ASSERT(pid > 0);