summaryrefslogtreecommitdiff
path: root/libgcompat/pwd.c
blob: 34c5d7b174a48926e639271ae16e75ca00f6bb47 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* some musl versions incorrectly mark fgetpwent() as a GNU extension */
#define _GNU_SOURCE
#include <assert.h>  /* assert */
#include <errno.h>   /* ENOENT, ERANGE */
#include <pthread.h> /* pthread_mutex_* */
#include <pwd.h>     /* fgetpwent, getpwent, struct passwd */
#include <stddef.h>  /* NULL, size_t */
#include <stdio.h>   /* FILE */
#include <string.h>  /* memcpy, stpcpy, strlcpy, strlen */

static pthread_mutex_t pwent_mutex = PTHREAD_MUTEX_INITIALIZER;

static int __fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf,
                         size_t len, struct passwd **result)
{
	struct passwd *pwtmp;
	char *cursor = buf, *end = buf + len;

	*result = NULL;
	pthread_mutex_lock(&pwent_mutex);
	pwtmp = stream != NULL ? fgetpwent(stream) : getpwent();
	if (pwtmp == NULL) {
		pthread_mutex_unlock(&pwent_mutex);
		return ENOENT;
	}
	memcpy(pwd, pwtmp, sizeof(*pwd));
	if (pwtmp->pw_name != NULL) {
		pwd->pw_name = cursor;
		cursor += strlcpy(cursor, pwtmp->pw_name, end - cursor) + 1;
		if (cursor > end) {
			goto err_unlock;
		}
	}
	if (pwtmp->pw_passwd != NULL) {
		pwd->pw_passwd = cursor;
		cursor += strlcpy(cursor, pwtmp->pw_passwd, end - cursor) + 1;
		if (cursor > end) {
			goto err_unlock;
		}
	}
	if (pwtmp->pw_gecos != NULL) {
		pwd->pw_gecos = cursor;
		cursor += strlcpy(cursor, pwtmp->pw_gecos, end - cursor) + 1;
		if (cursor > end) {
			goto err_unlock;
		}
	}
	if (pwtmp->pw_dir != NULL) {
		pwd->pw_dir = cursor;
		cursor += strlcpy(cursor, pwtmp->pw_dir, end - cursor) + 1;
		if (cursor > end) {
			goto err_unlock;
		}
	}
	if (pwtmp->pw_shell != NULL) {
		pwd->pw_shell = cursor;
		cursor += strlcpy(cursor, pwtmp->pw_shell, end - cursor) + 1;
		if (cursor > end) {
			goto err_unlock;
		}
	}
	pthread_mutex_unlock(&pwent_mutex);
	*result = pwd;

	return 0;

err_unlock:
	pthread_mutex_unlock(&pwent_mutex);
	return ERANGE;
}

/**
 * Get passwd file entry.
 */
int fgetpwent_r(FILE *stream, struct passwd *pwd, char *buf, size_t len,
                struct passwd **result)
{
	assert(stream != NULL);

	return fgetpwent_r(stream, pwd, buf, len, result);
}

/**
 * Get user database entry.
 *
 * LSB 5.0: LSB-Core-generic/baselib-getpwent-r-1.html
 */
int getpwent_r(struct passwd *pwd, char *buf, size_t len,
               struct passwd **result)
{
	return __fgetpwent_r(NULL, pwd, buf, len, result);
}