251 lines
7.8 KiB
C
251 lines
7.8 KiB
C
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set et ft=c ts=2 tw=8 fenc=utf-8 :vi│
|
|
╚──────────────────────────────────────────────────────────────────────────────╝
|
|
│ │
|
|
│ Musl Libc │
|
|
│ Copyright © 2005-2014 Rich Felker, et al. │
|
|
│ │
|
|
│ Permission is hereby granted, free of charge, to any person obtaining │
|
|
│ a copy of this software and associated documentation files (the │
|
|
│ "Software"), to deal in the Software without restriction, including │
|
|
│ without limitation the rights to use, copy, modify, merge, publish, │
|
|
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
|
│ permit persons to whom the Software is furnished to do so, subject to │
|
|
│ the following conditions: │
|
|
│ │
|
|
│ The above copyright notice and this permission notice shall be │
|
|
│ included in all copies or substantial portions of the Software. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
|
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
|
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
|
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
|
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
|
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
|
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
│ │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/calls/weirdtypes.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "third_party/musl/passwd.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
Musl libc (MIT License)\\n\
|
|
Copyright 2005-2014 Rich Felker, et. al.\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
|
|
#define PTHREAD_CANCEL_DISABLE 0
|
|
#define pthread_setcancelstate(x, y) (void)y
|
|
|
|
static unsigned atou(char **s) {
|
|
unsigned x;
|
|
for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0');
|
|
return x;
|
|
}
|
|
|
|
static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size,
|
|
char ***mem, size_t *nmem, struct group **res) {
|
|
ssize_t l;
|
|
char *s, *mems;
|
|
size_t i;
|
|
int rv = 0;
|
|
int cs;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
for (;;) {
|
|
if ((l = getline(line, size, f)) < 0) {
|
|
rv = ferror(f) ? errno : 0;
|
|
free(*line);
|
|
*line = 0;
|
|
gr = 0;
|
|
goto end;
|
|
}
|
|
line[0][l - 1] = 0;
|
|
s = line[0];
|
|
gr->gr_name = s++;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
gr->gr_passwd = s;
|
|
if (!(s = strchr(s, ':'))) continue;
|
|
*s++ = 0;
|
|
gr->gr_gid = atou(&s);
|
|
if (*s != ':') continue;
|
|
*s++ = 0;
|
|
mems = s;
|
|
break;
|
|
}
|
|
for (*nmem = !!*s; *s; s++)
|
|
if (*s == ',') ++*nmem;
|
|
free(*mem);
|
|
*mem = calloc(sizeof(char *), *nmem + 1);
|
|
if (!*mem) {
|
|
rv = errno;
|
|
free(*line);
|
|
*line = 0;
|
|
gr = 0;
|
|
goto end;
|
|
}
|
|
if (*mems) {
|
|
mem[0][0] = mems;
|
|
for (s = mems, i = 0; *s; s++)
|
|
if (*s == ',') *s++ = 0, mem[0][++i] = s;
|
|
mem[0][++i] = 0;
|
|
} else {
|
|
mem[0][0] = 0;
|
|
}
|
|
gr->gr_mem = *mem;
|
|
end:
|
|
pthread_setcancelstate(cs, 0);
|
|
*res = gr;
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
}
|
|
|
|
static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf,
|
|
size_t *size, char ***mem, size_t *nmem,
|
|
struct group **res) {
|
|
FILE *f;
|
|
int rv = 0;
|
|
int cs;
|
|
*res = 0;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
f = fopen("/etc/group", "rbe");
|
|
if (!f) {
|
|
rv = errno;
|
|
goto done;
|
|
}
|
|
while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
|
|
if ((name && !strcmp(name, (*res)->gr_name)) ||
|
|
(!name && (*res)->gr_gid == gid)) {
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
pthread_setcancelstate(cs, 0);
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
}
|
|
|
|
static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf,
|
|
size_t size, struct group **res) {
|
|
#define FIX(x) (gr->gr_##x = gr->gr_##x - line + buf)
|
|
char *line = 0;
|
|
size_t len = 0;
|
|
char **mem = 0;
|
|
size_t nmem = 0;
|
|
int rv = 0;
|
|
size_t i;
|
|
int cs;
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
|
rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res);
|
|
if (*res && size < len + (nmem + 1) * sizeof(char *) + 32) {
|
|
*res = 0;
|
|
rv = ERANGE;
|
|
}
|
|
if (*res) {
|
|
buf += (16 - (uintptr_t)buf) % 16;
|
|
gr->gr_mem = (void *)buf;
|
|
buf += (nmem + 1) * sizeof(char *);
|
|
memcpy(buf, line, len);
|
|
FIX(name);
|
|
FIX(passwd);
|
|
for (i = 0; mem[i]; i++) gr->gr_mem[i] = mem[i] - line + buf;
|
|
gr->gr_mem[i] = 0;
|
|
}
|
|
free(mem);
|
|
free(line);
|
|
pthread_setcancelstate(cs, 0);
|
|
if (rv) errno = rv;
|
|
return rv;
|
|
#undef FIX
|
|
}
|
|
|
|
int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) {
|
|
int rv, nlim, ret = -1;
|
|
ssize_t i, n = 1;
|
|
struct group gr;
|
|
struct group *res;
|
|
FILE *f;
|
|
char *buf = 0;
|
|
char **mem = 0;
|
|
size_t nmem = 0;
|
|
size_t size;
|
|
nlim = *ngroups;
|
|
if (nlim >= 1) *groups++ = gid;
|
|
f = fopen("/etc/group", "rbe");
|
|
if (!f && errno != ENOENT && errno != ENOTDIR) goto cleanup;
|
|
if (f) {
|
|
while (!(rv = __getgrent_a(f, &gr, &buf, &size, &mem, &nmem, &res)) &&
|
|
res) {
|
|
for (i = 0; gr.gr_mem[i] && strcmp(user, gr.gr_mem[i]); i++)
|
|
;
|
|
if (!gr.gr_mem[i]) continue;
|
|
if (++n <= nlim) *groups++ = gr.gr_gid;
|
|
}
|
|
if (rv) {
|
|
errno = rv;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
ret = n > nlim ? -1 : n;
|
|
*ngroups = n;
|
|
cleanup:
|
|
if (f) fclose(f);
|
|
free(buf);
|
|
free(mem);
|
|
return ret;
|
|
}
|
|
|
|
int getgrnam_r(const char *name, struct group *gr, char *buf, size_t size,
|
|
struct group **res) {
|
|
return getgr_r(name, 0, gr, buf, size, res);
|
|
}
|
|
|
|
int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size,
|
|
struct group **res) {
|
|
return getgr_r(0, gid, gr, buf, size, res);
|
|
}
|
|
|
|
static struct GetgrentState {
|
|
FILE *f;
|
|
char *line;
|
|
char **mem;
|
|
struct group gr;
|
|
} g_getgrent[1];
|
|
|
|
void endgrent() {
|
|
setgrent();
|
|
}
|
|
void setgrent() {
|
|
if (g_getgrent->f) fclose(g_getgrent->f);
|
|
g_getgrent->f = 0;
|
|
}
|
|
|
|
struct group *getgrent() {
|
|
struct group *res;
|
|
size_t size = 0, nmem = 0;
|
|
if (!g_getgrent->f) g_getgrent->f = fopen("/etc/group", "rbe");
|
|
if (!g_getgrent->f) return 0;
|
|
__getgrent_a(g_getgrent->f, &g_getgrent->gr, &g_getgrent->line, &size,
|
|
&g_getgrent->mem, &nmem, &res);
|
|
return res;
|
|
}
|
|
|
|
struct group *getgrgid(gid_t gid) {
|
|
struct group *res;
|
|
size_t size = 0, nmem = 0;
|
|
__getgr_a(0, gid, &g_getgrent->gr, &g_getgrent->line, &size, &g_getgrent->mem,
|
|
&nmem, &res);
|
|
return res;
|
|
}
|
|
|
|
struct group *getgrnam(const char *name) {
|
|
struct group *res;
|
|
size_t size = 0, nmem = 0;
|
|
__getgr_a(name, 0, &g_getgrent->gr, &g_getgrent->line, &size,
|
|
&g_getgrent->mem, &nmem, &res);
|
|
return res;
|
|
}
|