2 * Handle group entries via struct group
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 pw.c, keep in sync!
35 // TODO: adapt offsets to 32 bit to fit more than 5000 users per group (for 9
40 // off_name = 0, not stored on disk
44 uint16_t mem_count; // group member count
47 * Data contains all strings (name, passwd) concatenated, with their
48 * trailing NUL. The off_* variables point to beginning of each string.
50 * After that the offsets of the members of the group are stored as
51 * mem_count uint16_t values, followed by the member names concatenated as
52 * with the strings above.
54 * All offsets are relative to the beginning of data.
56 uint16_t data_size; // size of data in bytes
58 } __attribute__((packed));
60 static bool entry_to_group(const struct group_entry *e, struct group *g, char *tmp, size_t space) {
61 // Space required for the gr_mem array
62 const size_t mem_size = (size_t)(e->mem_count + 1) * sizeof(char *);
64 if (space < e->data_size + mem_size) {
68 char **groups = (char **)tmp;
70 const uint16_t *offs_mem = (const uint16_t *)(e->data + e->off_mem_off);
71 for (uint16_t i = 0; i < e->mem_count; i++) {
72 groups[i] = tmp + mem_size + offs_mem[i];
74 groups[e->mem_count] = NULL;
76 // This unnecessarily copies offs_mem[] as well but keeps the code simpler
77 // and the meaning of variables consistent with pw.c
78 memcpy(tmp + mem_size, e->data, e->data_size);
80 g->gr_gid = (gid_t)e->gid;
81 g->gr_name = tmp + mem_size + 0;
82 g->gr_passwd = tmp + mem_size + e->off_passwd;
89 static struct file static_file = {
92 static pthread_mutex_t static_file_lock = PTHREAD_MUTEX_INITIALIZER;
94 static void internal_unmap_static_file(void) {
95 pthread_mutex_lock(&static_file_lock);
96 unmap_file(&static_file);
97 pthread_mutex_unlock(&static_file_lock);
100 enum nss_status _nss_cash_setgrent(int x) {
103 // Unmap is necessary to detect changes when the file was replaced on
104 // disk; getgrent_r will open the file if necessary when called
105 internal_unmap_static_file();
106 return NSS_STATUS_SUCCESS;
109 enum nss_status _nss_cash_endgrent(void) {
110 internal_unmap_static_file();
111 return NSS_STATUS_SUCCESS;
114 static enum nss_status internal_getgrent_r(struct group *result, char *buffer, size_t buflen) {
115 // First call to getgrent_r, load file from disk
116 if (static_file.header == NULL) {
117 if (!map_file(NSSCASH_GROUP_FILE, &static_file)) {
118 return NSS_STATUS_UNAVAIL;
122 const struct header *h = static_file.header;
123 // End of "file", stop
124 if (static_file.next_index >= h->count) {
126 return NSS_STATUS_NOTFOUND;
129 uint64_t *off_orig = (uint64_t *)(h->data + h->off_orig_index);
130 const char *e = h->data + h->off_data + off_orig[static_file.next_index];
131 if (!entry_to_group((struct group_entry *)e, result, buffer, buflen)) {
133 return NSS_STATUS_TRYAGAIN;
135 static_file.next_index++;
137 return NSS_STATUS_SUCCESS;
139 enum nss_status _nss_cash_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
140 pthread_mutex_lock(&static_file_lock);
141 enum nss_status s = internal_getgrent_r(result, buffer, buflen);
142 pthread_mutex_unlock(&static_file_lock);
143 if (s != NSS_STATUS_SUCCESS) {
150 static enum nss_status internal_getgr(struct search_key *key, struct group *result, char *buffer, size_t buflen, int *errnop) {
152 if (!map_file(NSSCASH_GROUP_FILE, &f)) {
154 return NSS_STATUS_UNAVAIL;
156 const struct header *h = f.header;
158 key->data = h->data + h->off_data;
159 uint64_t off_index = (key->name != NULL)
162 uint64_t *off = search(key, h->data + off_index, h->count);
167 return NSS_STATUS_NOTFOUND;
170 const char *e = key->data + *off;
171 if (!entry_to_group((struct group_entry *)e, result, buffer, buflen)) {
175 return NSS_STATUS_TRYAGAIN;
179 return NSS_STATUS_SUCCESS;
182 enum nss_status _nss_cash_getgrgid_r(gid_t gid, struct group *result, char *buffer, size_t buflen, int *errnop) {
183 struct search_key key = {
185 .offset = offsetof(struct group_entry, gid),
187 return internal_getgr(&key, result, buffer, buflen, errnop);
190 enum nss_status _nss_cash_getgrnam_r(const char *name, struct group *result, char *buffer, size_t buflen, int *errnop) {
191 struct search_key key = {
193 .offset = sizeof(struct group_entry), // name is first value in data[]
195 return internal_getgr(&key, result, buffer, buflen, errnop);