cosmopolitan/third_party/musl/grp.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;
}