2 * Handle passwd entries via struct passwd
4 * Copyright (C) 2019 Simon Ruderich
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 // NOTE: This file is very similar to gr.c, keep in sync!
39 // off_name = 0, not stored on disk
46 * Data contains all strings (name, passwd, gecos, dir, shell)
47 * concatenated, with their trailing NUL. The off_* variables point to
48 * beginning of each string.
50 uint16_t data_size; // size of data in bytes
52 } __attribute__((packed));
54 static bool entry_to_passwd(const struct passwd_entry *e, struct passwd *p, char *tmp, size_t space) {
55 if (space < e->data_size) {
59 memcpy(tmp, e->data, e->data_size);
60 p->pw_uid = (uid_t)e->uid;
61 p->pw_gid = (gid_t)e->gid;
63 p->pw_passwd = tmp + e->off_passwd;
64 p->pw_gecos = tmp + e->off_gecos;
65 p->pw_dir = tmp + e->off_dir;
66 p->pw_shell = tmp + e->off_shell;
72 static struct file static_file = {
75 static pthread_mutex_t static_file_lock = PTHREAD_MUTEX_INITIALIZER;
77 enum nss_status _nss_cash_setpwent(int x) {
80 pthread_mutex_lock(&static_file_lock);
81 // Unmap is necessary to detect changes when the file was replaced on
83 unmap_file(&static_file);
84 // getpwent_r will open the file if necessary when called
85 pthread_mutex_unlock(&static_file_lock);
87 return NSS_STATUS_SUCCESS;
90 enum nss_status _nss_cash_endpwent(void) {
91 pthread_mutex_lock(&static_file_lock);
92 unmap_file(&static_file);
93 pthread_mutex_unlock(&static_file_lock);
95 return NSS_STATUS_SUCCESS;
98 static enum nss_status internal_getpwent_r(struct passwd *result, char *buffer, size_t buflen) {
99 // First call to getpwent_r, load file from disk
100 if (static_file.header == NULL) {
101 if (!map_file(NSSCASH_PASSWD_FILE, &static_file)) {
102 return NSS_STATUS_UNAVAIL;
106 const struct header *h = static_file.header;
107 // End of "file", stop
108 if (static_file.next_index >= h->count) {
110 return NSS_STATUS_NOTFOUND;
113 uint64_t *off_orig = (uint64_t *)(h->data + h->off_orig_index);
114 const char *e = h->data + h->off_data + off_orig[static_file.next_index];
115 if (!entry_to_passwd((struct passwd_entry *)e, result, buffer, buflen)) {
117 return NSS_STATUS_TRYAGAIN;
119 static_file.next_index++;
121 return NSS_STATUS_SUCCESS;
123 enum nss_status _nss_cash_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
124 pthread_mutex_lock(&static_file_lock);
125 enum nss_status s = internal_getpwent_r(result, buffer, buflen);
126 pthread_mutex_unlock(&static_file_lock);
127 if (s != NSS_STATUS_SUCCESS) {
134 static enum nss_status internal_getpw(struct search_key *key, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
136 if (!map_file(NSSCASH_PASSWD_FILE, &f)) {
138 return NSS_STATUS_UNAVAIL;
140 const struct header *h = f.header;
142 key->data = h->data + h->off_data;
143 uint64_t off_index = (key->name != NULL)
146 uint64_t *off = search(key, h->data + off_index, h->count);
151 return NSS_STATUS_NOTFOUND;
154 const char *e = key->data + *off;
155 if (!entry_to_passwd((struct passwd_entry *)e, result, buffer, buflen)) {
159 return NSS_STATUS_TRYAGAIN;
163 return NSS_STATUS_SUCCESS;
166 enum nss_status _nss_cash_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
167 uint64_t id = (uint64_t)uid;
168 struct search_key key = {
170 .offset = offsetof(struct passwd_entry, uid),
172 return internal_getpw(&key, result, buffer, buflen, errnop);
175 enum nss_status _nss_cash_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
176 struct search_key key = {
178 .offset = sizeof(struct passwd_entry), // name is first value in data[]
180 return internal_getpw(&key, result, buffer, buflen, errnop);