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