1 | /* Copyright (C) 1996-2023 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <ctype.h> |
19 | #include <errno.h> |
20 | #include <fcntl.h> |
21 | #include <grp.h> |
22 | #include <nss.h> |
23 | #include <nsswitch.h> |
24 | #include <stdio_ext.h> |
25 | #include <string.h> |
26 | #include <libc-lock.h> |
27 | #include <kernel-features.h> |
28 | #include <nss_files.h> |
29 | |
30 | NSS_DECLARE_MODULE_FUNCTIONS (compat) |
31 | |
32 | static nss_action_list ni; |
33 | static enum nss_status (*setgrent_impl) (int stayopen); |
34 | static enum nss_status (*getgrnam_r_impl) (const char *name, |
35 | struct group * grp, char *buffer, |
36 | size_t buflen, int *errnop); |
37 | static enum nss_status (*getgrgid_r_impl) (gid_t gid, struct group * grp, |
38 | char *buffer, size_t buflen, |
39 | int *errnop); |
40 | static enum nss_status (*getgrent_r_impl) (struct group * grp, char *buffer, |
41 | size_t buflen, int *errnop); |
42 | static enum nss_status (*endgrent_impl) (void); |
43 | |
44 | /* Get the declaration of the parser function. */ |
45 | #define ENTNAME grent |
46 | #define STRUCTURE group |
47 | #define EXTERN_PARSER |
48 | #include <nss/nss_files/files-parse.c> |
49 | |
50 | /* Structure for remembering -group members ... */ |
51 | #define BLACKLIST_INITIAL_SIZE 512 |
52 | #define BLACKLIST_INCREMENT 256 |
53 | struct blacklist_t |
54 | { |
55 | char *data; |
56 | int current; |
57 | int size; |
58 | }; |
59 | |
60 | struct ent_t |
61 | { |
62 | bool files; |
63 | enum nss_status setent_status; |
64 | FILE *stream; |
65 | struct blacklist_t blacklist; |
66 | }; |
67 | typedef struct ent_t ent_t; |
68 | |
69 | static ent_t ext_ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
70 | |
71 | /* Protect global state against multiple changers. */ |
72 | __libc_lock_define_initialized (static, lock) |
73 | |
74 | /* Prototypes for local functions. */ |
75 | static void blacklist_store_name (const char *, ent_t *); |
76 | static bool in_blacklist (const char *, int, ent_t *); |
77 | |
78 | /* Initialize the NSS interface/functions. The calling function must |
79 | hold the lock. */ |
80 | static void |
81 | init_nss_interface (void) |
82 | { |
83 | if (__nss_database_get (nss_database_group_compat, &ni)) |
84 | { |
85 | setgrent_impl = __nss_lookup_function (ni, "setgrent" ); |
86 | getgrnam_r_impl = __nss_lookup_function (ni, "getgrnam_r" ); |
87 | getgrgid_r_impl = __nss_lookup_function (ni, "getgrgid_r" ); |
88 | getgrent_r_impl = __nss_lookup_function (ni, "getgrent_r" ); |
89 | endgrent_impl = __nss_lookup_function (ni, "endgrent" ); |
90 | } |
91 | } |
92 | |
93 | static enum nss_status |
94 | internal_setgrent (ent_t *ent, int stayopen, int needent) |
95 | { |
96 | enum nss_status status = NSS_STATUS_SUCCESS; |
97 | |
98 | ent->files = true; |
99 | |
100 | if (ent->blacklist.data != NULL) |
101 | { |
102 | ent->blacklist.current = 1; |
103 | ent->blacklist.data[0] = '|'; |
104 | ent->blacklist.data[1] = '\0'; |
105 | } |
106 | else |
107 | ent->blacklist.current = 0; |
108 | |
109 | if (ent->stream == NULL) |
110 | { |
111 | ent->stream = __nss_files_fopen ("/etc/group" ); |
112 | |
113 | if (ent->stream == NULL) |
114 | status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; |
115 | } |
116 | else |
117 | rewind (ent->stream); |
118 | |
119 | if (needent && status == NSS_STATUS_SUCCESS && setgrent_impl) |
120 | ent->setent_status = setgrent_impl (stayopen); |
121 | |
122 | return status; |
123 | } |
124 | |
125 | |
126 | enum nss_status |
127 | _nss_compat_setgrent (int stayopen) |
128 | { |
129 | enum nss_status result; |
130 | |
131 | __libc_lock_lock (lock); |
132 | |
133 | if (ni == NULL) |
134 | init_nss_interface (); |
135 | |
136 | result = internal_setgrent (&ext_ent, stayopen, 1); |
137 | |
138 | __libc_lock_unlock (lock); |
139 | |
140 | return result; |
141 | } |
142 | |
143 | |
144 | static enum nss_status __attribute_warn_unused_result__ |
145 | internal_endgrent (ent_t *ent) |
146 | { |
147 | if (ent->stream != NULL) |
148 | { |
149 | fclose (ent->stream); |
150 | ent->stream = NULL; |
151 | } |
152 | |
153 | if (ent->blacklist.data != NULL) |
154 | { |
155 | ent->blacklist.current = 1; |
156 | ent->blacklist.data[0] = '|'; |
157 | ent->blacklist.data[1] = '\0'; |
158 | } |
159 | else |
160 | ent->blacklist.current = 0; |
161 | |
162 | return NSS_STATUS_SUCCESS; |
163 | } |
164 | |
165 | /* Like internal_endgrent, but preserve errno in all cases. */ |
166 | static void |
167 | internal_endgrent_noerror (ent_t *ent) |
168 | { |
169 | int saved_errno = errno; |
170 | enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent); |
171 | __set_errno (saved_errno); |
172 | } |
173 | |
174 | enum nss_status |
175 | _nss_compat_endgrent (void) |
176 | { |
177 | enum nss_status result; |
178 | |
179 | __libc_lock_lock (lock); |
180 | |
181 | if (endgrent_impl) |
182 | endgrent_impl (); |
183 | |
184 | result = internal_endgrent (&ext_ent); |
185 | |
186 | __libc_lock_unlock (lock); |
187 | |
188 | return result; |
189 | } |
190 | |
191 | /* get the next group from NSS (+ entry) */ |
192 | static enum nss_status |
193 | getgrent_next_nss (struct group *result, ent_t *ent, char *buffer, |
194 | size_t buflen, int *errnop) |
195 | { |
196 | if (!getgrent_r_impl) |
197 | return NSS_STATUS_UNAVAIL; |
198 | |
199 | /* If the setgrent call failed, say so. */ |
200 | if (ent->setent_status != NSS_STATUS_SUCCESS) |
201 | return ent->setent_status; |
202 | |
203 | do |
204 | { |
205 | enum nss_status status; |
206 | |
207 | if ((status = getgrent_r_impl (result, buffer, buflen, errnop)) |
208 | != NSS_STATUS_SUCCESS) |
209 | return status; |
210 | } |
211 | while (in_blacklist (result->gr_name, strlen (result->gr_name), ent)); |
212 | |
213 | return NSS_STATUS_SUCCESS; |
214 | } |
215 | |
216 | /* This function handle the +group entries in /etc/group */ |
217 | static enum nss_status |
218 | getgrnam_plusgroup (const char *name, struct group *result, ent_t *ent, |
219 | char *buffer, size_t buflen, int *errnop) |
220 | { |
221 | if (!getgrnam_r_impl) |
222 | return NSS_STATUS_UNAVAIL; |
223 | |
224 | enum nss_status status = getgrnam_r_impl (name, result, buffer, buflen, |
225 | errnop); |
226 | if (status != NSS_STATUS_SUCCESS) |
227 | return status; |
228 | |
229 | if (in_blacklist (result->gr_name, strlen (result->gr_name), ent)) |
230 | return NSS_STATUS_NOTFOUND; |
231 | |
232 | /* We found the entry. */ |
233 | return NSS_STATUS_SUCCESS; |
234 | } |
235 | |
236 | static enum nss_status |
237 | getgrent_next_file (struct group *result, ent_t *ent, |
238 | char *buffer, size_t buflen, int *errnop) |
239 | { |
240 | struct parser_data *data = (void *) buffer; |
241 | while (1) |
242 | { |
243 | fpos_t pos; |
244 | int parse_res = 0; |
245 | char *p; |
246 | |
247 | do |
248 | { |
249 | /* We need at least 3 characters for one line. */ |
250 | if (__glibc_unlikely (buflen < 3)) |
251 | { |
252 | erange: |
253 | *errnop = ERANGE; |
254 | return NSS_STATUS_TRYAGAIN; |
255 | } |
256 | |
257 | fgetpos (ent->stream, &pos); |
258 | buffer[buflen - 1] = '\xff'; |
259 | p = fgets_unlocked (buffer, buflen, ent->stream); |
260 | if (p == NULL && feof_unlocked (ent->stream)) |
261 | return NSS_STATUS_NOTFOUND; |
262 | |
263 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
264 | { |
265 | erange_reset: |
266 | fsetpos (ent->stream, &pos); |
267 | goto erange; |
268 | } |
269 | |
270 | /* Terminate the line for any case. */ |
271 | buffer[buflen - 1] = '\0'; |
272 | |
273 | /* Skip leading blanks. */ |
274 | while (isspace (*p)) |
275 | ++p; |
276 | } |
277 | while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ |
278 | /* Parse the line. If it is invalid, loop to |
279 | get the next line of the file to parse. */ |
280 | || !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
281 | errnop))); |
282 | |
283 | if (__glibc_unlikely (parse_res == -1)) |
284 | /* The parser ran out of space. */ |
285 | goto erange_reset; |
286 | |
287 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
288 | /* This is a real entry. */ |
289 | break; |
290 | |
291 | /* -group */ |
292 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0' |
293 | && result->gr_name[1] != '@') |
294 | { |
295 | blacklist_store_name (&result->gr_name[1], ent); |
296 | continue; |
297 | } |
298 | |
299 | /* +group */ |
300 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0' |
301 | && result->gr_name[1] != '@') |
302 | { |
303 | size_t len = strlen (result->gr_name); |
304 | char buf[len]; |
305 | enum nss_status status; |
306 | |
307 | /* Store the group in the blacklist for the "+" at the end of |
308 | /etc/group */ |
309 | memcpy (buf, &result->gr_name[1], len); |
310 | status = getgrnam_plusgroup (&result->gr_name[1], result, ent, |
311 | buffer, buflen, errnop); |
312 | blacklist_store_name (buf, ent); |
313 | if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ |
314 | break; |
315 | else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry*/ |
316 | || status == NSS_STATUS_NOTFOUND) /* No group in NIS */ |
317 | continue; |
318 | else |
319 | { |
320 | if (status == NSS_STATUS_TRYAGAIN) |
321 | /* The parser ran out of space. */ |
322 | goto erange_reset; |
323 | |
324 | return status; |
325 | } |
326 | } |
327 | |
328 | /* +:... */ |
329 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
330 | { |
331 | ent->files = false; |
332 | |
333 | return getgrent_next_nss (result, ent, buffer, buflen, errnop); |
334 | } |
335 | } |
336 | |
337 | return NSS_STATUS_SUCCESS; |
338 | } |
339 | |
340 | |
341 | enum nss_status |
342 | _nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen, |
343 | int *errnop) |
344 | { |
345 | enum nss_status result = NSS_STATUS_SUCCESS; |
346 | |
347 | __libc_lock_lock (lock); |
348 | |
349 | /* Be prepared that the setgrent function was not called before. */ |
350 | if (ni == NULL) |
351 | init_nss_interface (); |
352 | |
353 | if (ext_ent.stream == NULL) |
354 | result = internal_setgrent (&ext_ent, 1, 1); |
355 | |
356 | if (result == NSS_STATUS_SUCCESS) |
357 | { |
358 | if (ext_ent.files) |
359 | result = getgrent_next_file (grp, &ext_ent, buffer, buflen, errnop); |
360 | else |
361 | result = getgrent_next_nss (grp, &ext_ent, buffer, buflen, errnop); |
362 | } |
363 | __libc_lock_unlock (lock); |
364 | |
365 | return result; |
366 | } |
367 | |
368 | /* Searches in /etc/group and the NIS/NIS+ map for a special group */ |
369 | static enum nss_status |
370 | internal_getgrnam_r (const char *name, struct group *result, ent_t *ent, |
371 | char *buffer, size_t buflen, int *errnop) |
372 | { |
373 | struct parser_data *data = (void *) buffer; |
374 | while (1) |
375 | { |
376 | fpos_t pos; |
377 | int parse_res = 0; |
378 | char *p; |
379 | |
380 | do |
381 | { |
382 | /* We need at least 3 characters for one line. */ |
383 | if (__glibc_unlikely (buflen < 3)) |
384 | { |
385 | erange: |
386 | *errnop = ERANGE; |
387 | return NSS_STATUS_TRYAGAIN; |
388 | } |
389 | |
390 | fgetpos (ent->stream, &pos); |
391 | buffer[buflen - 1] = '\xff'; |
392 | p = fgets_unlocked (buffer, buflen, ent->stream); |
393 | if (p == NULL && feof_unlocked (ent->stream)) |
394 | return NSS_STATUS_NOTFOUND; |
395 | |
396 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
397 | { |
398 | erange_reset: |
399 | fsetpos (ent->stream, &pos); |
400 | goto erange; |
401 | } |
402 | |
403 | /* Terminate the line for any case. */ |
404 | buffer[buflen - 1] = '\0'; |
405 | |
406 | /* Skip leading blanks. */ |
407 | while (isspace (*p)) |
408 | ++p; |
409 | } |
410 | while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ |
411 | /* Parse the line. If it is invalid, loop to |
412 | get the next line of the file to parse. */ |
413 | || !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
414 | errnop))); |
415 | |
416 | if (__glibc_unlikely (parse_res == -1)) |
417 | /* The parser ran out of space. */ |
418 | goto erange_reset; |
419 | |
420 | /* This is a real entry. */ |
421 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
422 | { |
423 | if (strcmp (result->gr_name, name) == 0) |
424 | return NSS_STATUS_SUCCESS; |
425 | else |
426 | continue; |
427 | } |
428 | |
429 | /* -group */ |
430 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') |
431 | { |
432 | if (strcmp (&result->gr_name[1], name) == 0) |
433 | return NSS_STATUS_NOTFOUND; |
434 | else |
435 | continue; |
436 | } |
437 | |
438 | /* +group */ |
439 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') |
440 | { |
441 | if (strcmp (name, &result->gr_name[1]) == 0) |
442 | { |
443 | enum nss_status status; |
444 | |
445 | status = getgrnam_plusgroup (name, result, ent, |
446 | buffer, buflen, errnop); |
447 | if (status == NSS_STATUS_RETURN) |
448 | /* We couldn't parse the entry */ |
449 | continue; |
450 | else |
451 | return status; |
452 | } |
453 | } |
454 | /* +:... */ |
455 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
456 | { |
457 | enum nss_status status; |
458 | |
459 | status = getgrnam_plusgroup (name, result, ent, |
460 | buffer, buflen, errnop); |
461 | if (status == NSS_STATUS_RETURN) |
462 | /* We couldn't parse the entry */ |
463 | continue; |
464 | else |
465 | return status; |
466 | } |
467 | } |
468 | |
469 | return NSS_STATUS_SUCCESS; |
470 | } |
471 | |
472 | enum nss_status |
473 | _nss_compat_getgrnam_r (const char *name, struct group *grp, |
474 | char *buffer, size_t buflen, int *errnop) |
475 | { |
476 | ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
477 | enum nss_status result; |
478 | |
479 | if (name[0] == '-' || name[0] == '+') |
480 | return NSS_STATUS_NOTFOUND; |
481 | |
482 | __libc_lock_lock (lock); |
483 | |
484 | if (ni == NULL) |
485 | init_nss_interface (); |
486 | |
487 | __libc_lock_unlock (lock); |
488 | |
489 | result = internal_setgrent (&ent, 0, 0); |
490 | |
491 | if (result == NSS_STATUS_SUCCESS) |
492 | result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop); |
493 | |
494 | internal_endgrent_noerror (&ent); |
495 | |
496 | return result; |
497 | } |
498 | |
499 | /* Searches in /etc/group and the NIS/NIS+ map for a special group id */ |
500 | static enum nss_status |
501 | internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent, |
502 | char *buffer, size_t buflen, int *errnop) |
503 | { |
504 | struct parser_data *data = (void *) buffer; |
505 | while (1) |
506 | { |
507 | fpos_t pos; |
508 | int parse_res = 0; |
509 | char *p; |
510 | |
511 | do |
512 | { |
513 | /* We need at least 3 characters for one line. */ |
514 | if (__glibc_unlikely (buflen < 3)) |
515 | { |
516 | erange: |
517 | *errnop = ERANGE; |
518 | return NSS_STATUS_TRYAGAIN; |
519 | } |
520 | |
521 | fgetpos (ent->stream, &pos); |
522 | buffer[buflen - 1] = '\xff'; |
523 | p = fgets_unlocked (buffer, buflen, ent->stream); |
524 | if (p == NULL && feof_unlocked (ent->stream)) |
525 | return NSS_STATUS_NOTFOUND; |
526 | |
527 | if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) |
528 | { |
529 | erange_reset: |
530 | fsetpos (ent->stream, &pos); |
531 | goto erange; |
532 | } |
533 | |
534 | /* Terminate the line for any case. */ |
535 | buffer[buflen - 1] = '\0'; |
536 | |
537 | /* Skip leading blanks. */ |
538 | while (isspace (*p)) |
539 | ++p; |
540 | } |
541 | while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ |
542 | /* Parse the line. If it is invalid, loop to |
543 | get the next line of the file to parse. */ |
544 | || !(parse_res = _nss_files_parse_grent (p, result, data, buflen, |
545 | errnop))); |
546 | |
547 | if (__glibc_unlikely (parse_res == -1)) |
548 | /* The parser ran out of space. */ |
549 | goto erange_reset; |
550 | |
551 | /* This is a real entry. */ |
552 | if (result->gr_name[0] != '+' && result->gr_name[0] != '-') |
553 | { |
554 | if (result->gr_gid == gid) |
555 | return NSS_STATUS_SUCCESS; |
556 | else |
557 | continue; |
558 | } |
559 | |
560 | /* -group */ |
561 | if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') |
562 | { |
563 | blacklist_store_name (&result->gr_name[1], ent); |
564 | continue; |
565 | } |
566 | |
567 | /* +group */ |
568 | if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') |
569 | { |
570 | /* Yes, no +1, see the memcpy call below. */ |
571 | size_t len = strlen (result->gr_name); |
572 | char buf[len]; |
573 | enum nss_status status; |
574 | |
575 | /* Store the group in the blacklist for the "+" at the end of |
576 | /etc/group */ |
577 | memcpy (buf, &result->gr_name[1], len); |
578 | status = getgrnam_plusgroup (&result->gr_name[1], result, ent, |
579 | buffer, buflen, errnop); |
580 | blacklist_store_name (buf, ent); |
581 | if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid) |
582 | break; |
583 | else |
584 | continue; |
585 | } |
586 | /* +:... */ |
587 | if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') |
588 | { |
589 | if (!getgrgid_r_impl) |
590 | return NSS_STATUS_UNAVAIL; |
591 | |
592 | enum nss_status status = getgrgid_r_impl (gid, result, |
593 | buffer, buflen, errnop); |
594 | if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ |
595 | return NSS_STATUS_NOTFOUND; |
596 | else |
597 | return status; |
598 | } |
599 | } |
600 | |
601 | return NSS_STATUS_SUCCESS; |
602 | } |
603 | |
604 | enum nss_status |
605 | _nss_compat_getgrgid_r (gid_t gid, struct group *grp, |
606 | char *buffer, size_t buflen, int *errnop) |
607 | { |
608 | ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; |
609 | enum nss_status result; |
610 | |
611 | __libc_lock_lock (lock); |
612 | |
613 | if (ni == NULL) |
614 | init_nss_interface (); |
615 | |
616 | __libc_lock_unlock (lock); |
617 | |
618 | result = internal_setgrent (&ent, 0, 0); |
619 | |
620 | if (result == NSS_STATUS_SUCCESS) |
621 | result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop); |
622 | |
623 | internal_endgrent_noerror (&ent); |
624 | |
625 | return result; |
626 | } |
627 | |
628 | |
629 | /* Support routines for remembering -@netgroup and -user entries. |
630 | The names are stored in a single string with `|' as separator. */ |
631 | static void |
632 | blacklist_store_name (const char *name, ent_t *ent) |
633 | { |
634 | int namelen = strlen (name); |
635 | char *tmp; |
636 | |
637 | /* first call, setup cache */ |
638 | if (ent->blacklist.size == 0) |
639 | { |
640 | ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); |
641 | ent->blacklist.data = malloc (ent->blacklist.size); |
642 | if (ent->blacklist.data == NULL) |
643 | return; |
644 | ent->blacklist.data[0] = '|'; |
645 | ent->blacklist.data[1] = '\0'; |
646 | ent->blacklist.current = 1; |
647 | } |
648 | else |
649 | { |
650 | if (in_blacklist (name, namelen, ent)) |
651 | return; /* no duplicates */ |
652 | |
653 | if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) |
654 | { |
655 | ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); |
656 | tmp = realloc (ent->blacklist.data, ent->blacklist.size); |
657 | if (tmp == NULL) |
658 | { |
659 | free (ent->blacklist.data); |
660 | ent->blacklist.size = 0; |
661 | return; |
662 | } |
663 | ent->blacklist.data = tmp; |
664 | } |
665 | } |
666 | |
667 | tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); |
668 | *tmp++ = '|'; |
669 | *tmp = '\0'; |
670 | ent->blacklist.current += namelen + 1; |
671 | |
672 | return; |
673 | } |
674 | |
675 | /* Return whether ent->blacklist contains name. */ |
676 | static bool |
677 | in_blacklist (const char *name, int namelen, ent_t *ent) |
678 | { |
679 | char buf[namelen + 3]; |
680 | char *cp; |
681 | |
682 | if (ent->blacklist.data == NULL) |
683 | return false; |
684 | |
685 | buf[0] = '|'; |
686 | cp = stpcpy (&buf[1], name); |
687 | *cp++ = '|'; |
688 | *cp = '\0'; |
689 | return strstr (ent->blacklist.data, buf) != NULL; |
690 | } |
691 | |