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_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_dirent_s uv_dirent_t;
typedef struct uv_passwd_s uv_passwd_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_utsname_s uv_utsname_t;
typedef struct uv_statfs_s uv_statfs_t; typedef struct uv_statfs_s uv_statfs_t;
@ -1139,6 +1140,12 @@ struct uv_passwd_s {
char* homedir; char* homedir;
}; };
struct uv_group_s {
char* groupname;
unsigned long gid;
char** members;
};
struct uv_utsname_s { struct uv_utsname_s {
char sysname[256]; char sysname[256];
char release[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_tmpdir(char* buffer, size_t* size);
UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
UV_EXTERN void uv_os_free_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_getpid(void);
UV_EXTERN uv_pid_t uv_os_getppid(void); UV_EXTERN uv_pid_t uv_os_getppid(void);

View File

@ -41,12 +41,12 @@
#include <sys/uio.h> /* writev */ #include <sys/uio.h> /* writev */
#include <sys/resource.h> /* getrusage */ #include <sys/resource.h> /* getrusage */
#include <pwd.h> #include <pwd.h>
#include <grp.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/time.h> #include <sys/time.h>
#ifdef __sun #ifdef __sun
# include <sys/filio.h> # include <sys/filio.h>
# include <sys/types.h>
# include <sys/wait.h> # include <sys/wait.h>
#endif #endif
@ -1097,8 +1097,8 @@ int uv_os_homedir(char* buffer, size_t* size) {
if (r != UV_ENOENT) if (r != UV_ENOENT)
return r; return r;
/* HOME is not set, so call uv__getpwuid_r() */ /* HOME is not set, so call uv_os_get_passwd() */
r = uv__getpwuid_r(&pwd); r = uv_os_get_passwd(&pwd);
if (r != 0) { if (r != 0) {
return r; 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 pw;
struct passwd* result; struct passwd* result;
char* buf; char* buf;
uid_t uid;
size_t bufsize; size_t bufsize;
size_t name_size; size_t name_size;
size_t homedir_size; size_t homedir_size;
@ -1185,8 +1184,6 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
if (pwd == NULL) if (pwd == NULL)
return UV_EINVAL; return UV_EINVAL;
uid = geteuid();
/* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it /* 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 * is frequently 1024 or 4096, so we can just use that directly. The pwent
* will not usually be large. */ * 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) { int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
if (pwd == NULL) struct group gp;
return; struct group* result;
char* buf;
char* gr_mem;
size_t bufsize;
size_t name_size;
long members;
size_t mem_size;
int r;
/* if (grp == NULL)
The memory for name, shell, and homedir are allocated in a single return UV_EINVAL;
uv__malloc() call. The base of the pointer is stored in pwd->username, so
that is the field that needs to be freed. /* 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
uv__free(pwd->username); * will not usually be large. */
pwd->username = NULL; for (bufsize = 2000;; bufsize *= 2) {
pwd->shell = NULL; buf = uv__malloc(bufsize);
pwd->homedir = NULL;
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) { 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_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle);
FILE* uv__open_file(const char* path); 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); int uv__search_path(const char* prog, char* buf, size_t* buflen);
void uv__wait_children(uv_loop_t* loop); 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; 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); #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
size_t uv_handle_size(uv_handle_type type) { 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); uint64_t uv__hrtime(unsigned int scale);
__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); __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_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); 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) if (r != UV_ENOENT)
return r; return r;
/* USERPROFILE is not set, so call uv__getpwuid_r() */ /* USERPROFILE is not set, so call uv_os_get_passwd() */
r = uv__getpwuid_r(&pwd); r = uv_os_get_passwd(&pwd);
if (r != 0) { if (r != 0) {
return r; 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 * Converts a UTF-16 string into a UTF-8 one. The resulting string is
* null-terminated. * 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; HANDLE token;
wchar_t username[UNLEN + 1]; wchar_t username[UNLEN + 1];
wchar_t *path; 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) { int uv_os_environ(uv_env_item_t** envitems, int* count) {
wchar_t* env; wchar_t* env;
wchar_t* penv; wchar_t* penv;

View File

@ -39,32 +39,32 @@ TEST_IMPL(get_passwd) {
/* Test the normal case */ /* Test the normal case */
r = uv_os_get_passwd(&pwd); r = uv_os_get_passwd(&pwd);
ASSERT(r == 0); ASSERT_EQ(r, 0);
len = strlen(pwd.username); len = strlen(pwd.username);
ASSERT(len > 0); ASSERT_GT(len, 0);
#ifdef _WIN32 #ifdef _WIN32
ASSERT_NULL(pwd.shell); ASSERT_NULL(pwd.shell);
#else #else
len = strlen(pwd.shell); len = strlen(pwd.shell);
# ifndef __PASE__ # ifndef __PASE__
ASSERT(len > 0); ASSERT_GT(len, 0);
# endif # endif
#endif #endif
len = strlen(pwd.homedir); len = strlen(pwd.homedir);
ASSERT(len > 0); ASSERT_GT(len, 0);
#ifdef _WIN32 #ifdef _WIN32
if (len == 3 && pwd.homedir[1] == ':') if (len == 3 && pwd.homedir[1] == ':')
ASSERT(pwd.homedir[2] == '\\'); ASSERT_EQ(pwd.homedir[2], '\\');
else else
ASSERT(pwd.homedir[len - 1] != '\\'); ASSERT_NE(pwd.homedir[len - 1], '\\');
#else #else
if (len == 1) if (len == 1)
ASSERT(pwd.homedir[0] == '/'); ASSERT_EQ(pwd.homedir[0], '/');
else else
ASSERT(pwd.homedir[len - 1] != '/'); ASSERT_NE(pwd.homedir[len - 1], '/');
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
@ -95,7 +95,110 @@ TEST_IMPL(get_passwd) {
/* Test invalid input */ /* Test invalid input */
r = uv_os_get_passwd(NULL); 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; return 0;
} }

View File

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

View File

@ -35,8 +35,10 @@ TEST_IMPL(platform_output) {
uv_cpu_info_t* cpus; uv_cpu_info_t* cpus;
uv_interface_address_t* interfaces; uv_interface_address_t* interfaces;
uv_passwd_t pwd; uv_passwd_t pwd;
uv_group_t grp;
uv_utsname_t uname; uv_utsname_t uname;
unsigned par; unsigned par;
char* const* member;
int count; int count;
int i; int i;
int err; int err;
@ -147,15 +149,38 @@ TEST_IMPL(platform_output) {
uv_free_interface_addresses(interfaces, count); uv_free_interface_addresses(interfaces, count);
err = uv_os_get_passwd(&pwd); 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("uv_os_get_passwd:\n");
printf(" euid: %ld\n", pwd.uid); 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(" username: %s\n", pwd.username);
if (pwd.shell != NULL) /* Not set on Windows */
printf(" shell: %s\n", pwd.shell); printf(" shell: %s\n", pwd.shell);
printf(" home directory: %s\n", pwd.homedir); printf(" home directory: %s\n", pwd.homedir);
uv_os_free_passwd(&pwd); uv_os_free_passwd(&pwd);
#if !defined(_WIN32)
uv_os_free_group(&grp);
#endif
pid = uv_os_getpid(); pid = uv_os_getpid();
ASSERT(pid > 0); ASSERT(pid > 0);