]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/makedb.c
Disable warnings due to deprecated libselinux symbols used by nss and nscd
[thirdparty/glibc.git] / nss / makedb.c
1 /* Create simple DB database from textual input.
2 Copyright (C) 1996-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library 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 GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20 #include <argp.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <error.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <libintl.h>
28 #include <locale.h>
29 #include <search.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <stdint.h>
36 #include <sys/mman.h>
37 #include <sys/param.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include "nss_db/nss_db.h"
41 #include <libc-diag.h>
42
43 /* Get libc version number. */
44 #include "../version.h"
45
46 /* The hashing function we use. */
47 #include "../intl/hash-string.h"
48
49 /* SELinux support. */
50 #ifdef HAVE_SELINUX
51 # include <selinux/selinux.h>
52 #endif
53
54 #ifndef MAP_POPULATE
55 # define MAP_POPULATE 0
56 #endif
57
58 #define PACKAGE _libc_intl_domainname
59
60 /* List of data bases. */
61 struct database
62 {
63 char dbid;
64 bool extra_string;
65 struct database *next;
66 void *entries;
67 size_t nentries;
68 size_t nhashentries;
69 stridx_t *hashtable;
70 size_t keystrlen;
71 stridx_t *keyidxtab;
72 char *keystrtab;
73 } *databases;
74 static size_t ndatabases;
75 static size_t nhashentries_total;
76 static size_t valstrlen;
77 static void *valstrtree;
78 static char *valstrtab;
79 static size_t extrastrlen;
80
81 /* Database entry. */
82 struct dbentry
83 {
84 stridx_t validx;
85 uint32_t hashval;
86 char str[0];
87 };
88
89 /* Stored string entry. */
90 struct valstrentry
91 {
92 stridx_t idx;
93 bool extra_string;
94 char str[0];
95 };
96
97
98 /* True if any entry has been added. */
99 static bool any_dbentry;
100
101 /* If non-zero convert key to lower case. */
102 static int to_lowercase;
103
104 /* If non-zero print content of input file, one entry per line. */
105 static int do_undo;
106
107 /* If non-zero do not print informational messages. */
108 static int be_quiet;
109
110 /* Name of output file. */
111 static const char *output_name;
112
113 /* Name and version of program. */
114 static void print_version (FILE *stream, struct argp_state *state);
115 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
116
117 /* Definitions of arguments for argp functions. */
118 static const struct argp_option options[] =
119 {
120 { "fold-case", 'f', NULL, 0, N_("Convert key to lower case") },
121 { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
122 { "quiet", 'q', NULL, 0,
123 N_("Do not print messages while building database") },
124 { "undo", 'u', NULL, 0,
125 N_("Print content of database file, one entry a line") },
126 { "generated", 'g', N_("CHAR"), 0,
127 N_("Generated line not part of iteration") },
128 { NULL, 0, NULL, 0, NULL }
129 };
130
131 /* Short description of program. */
132 static const char doc[] = N_("Create simple database from textual input.");
133
134 /* Strings for arguments in help texts. */
135 static const char args_doc[] = N_("\
136 INPUT-FILE OUTPUT-FILE\n-o OUTPUT-FILE INPUT-FILE\n-u INPUT-FILE");
137
138 /* Prototype for option handler. */
139 static error_t parse_opt (int key, char *arg, struct argp_state *state);
140
141 /* Function to print some extra text in the help message. */
142 static char *more_help (int key, const char *text, void *input);
143
144 /* Data structure to communicate with argp functions. */
145 static struct argp argp =
146 {
147 options, parse_opt, args_doc, doc, NULL, more_help
148 };
149
150
151 /* List of databases which are not part of the iteration table. */
152 static struct db_option
153 {
154 char dbid;
155 struct db_option *next;
156 } *db_options;
157
158
159 /* Prototypes for local functions. */
160 static int process_input (FILE *input, const char *inname,
161 int to_lowercase, int be_quiet);
162 static int print_database (int fd);
163 static void compute_tables (void);
164 static int write_output (int fd);
165
166 /* SELinux support. */
167 #ifdef HAVE_SELINUX
168 /* Set the SELinux file creation context for the given file. */
169 static void set_file_creation_context (const char *outname, mode_t mode);
170 static void reset_file_creation_context (void);
171 #else
172 # define set_file_creation_context(_outname,_mode)
173 # define reset_file_creation_context()
174 #endif
175
176
177 /* External functions. */
178 #include <programs/xmalloc.h>
179
180
181 int
182 main (int argc, char *argv[])
183 {
184 const char *input_name;
185 FILE *input_file;
186 int remaining;
187 int mode = 0644;
188
189 /* Set locale via LC_ALL. */
190 setlocale (LC_ALL, "");
191
192 /* Set the text message domain. */
193 textdomain (_libc_intl_domainname);
194
195 /* Initialize local variables. */
196 input_name = NULL;
197
198 /* Parse and process arguments. */
199 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
200
201 /* Determine file names. */
202 if (do_undo || output_name != NULL)
203 {
204 if (remaining + 1 != argc)
205 {
206 wrong_arguments:
207 error (0, 0, gettext ("wrong number of arguments"));
208 argp_help (&argp, stdout, ARGP_HELP_SEE,
209 program_invocation_short_name);
210 exit (1);
211 }
212 input_name = argv[remaining];
213 }
214 else
215 {
216 if (remaining + 2 != argc)
217 goto wrong_arguments;
218
219 input_name = argv[remaining++];
220 output_name = argv[remaining];
221 }
222
223 /* Special handling if we are asked to print the database. */
224 if (do_undo)
225 {
226 int fd = open (input_name, O_RDONLY);
227 if (fd == -1)
228 error (EXIT_FAILURE, errno, gettext ("cannot open database file `%s'"),
229 input_name);
230
231 int status = print_database (fd);
232
233 close (fd);
234
235 return status;
236 }
237
238 /* Open input file. */
239 if (strcmp (input_name, "-") == 0 || strcmp (input_name, "/dev/stdin") == 0)
240 input_file = stdin;
241 else
242 {
243 struct stat64 st;
244
245 input_file = fopen64 (input_name, "r");
246 if (input_file == NULL)
247 error (EXIT_FAILURE, errno, gettext ("cannot open input file `%s'"),
248 input_name);
249
250 /* Get the access rights from the source file. The output file should
251 have the same. */
252 if (fstat64 (fileno (input_file), &st) >= 0)
253 mode = st.st_mode & ACCESSPERMS;
254 }
255
256 /* Start the real work. */
257 int status = process_input (input_file, input_name, to_lowercase, be_quiet);
258
259 /* Close files. */
260 if (input_file != stdin)
261 fclose (input_file);
262
263 /* No need to continue when we did not read the file successfully. */
264 if (status != EXIT_SUCCESS)
265 return status;
266
267 /* Bail out if nothing is to be done. */
268 if (!any_dbentry)
269 {
270 if (be_quiet)
271 return EXIT_SUCCESS;
272 else
273 error (EXIT_SUCCESS, 0, gettext ("no entries to be processed"));
274 }
275
276 /* Compute hash and string tables. */
277 compute_tables ();
278
279 /* Open output file. This must not be standard output so we don't
280 handle "-" and "/dev/stdout" special. */
281 char *tmp_output_name;
282 if (asprintf (&tmp_output_name, "%s.XXXXXX", output_name) == -1)
283 error (EXIT_FAILURE, errno, gettext ("cannot create temporary file name"));
284
285 set_file_creation_context (output_name, mode);
286 int fd = mkstemp (tmp_output_name);
287 reset_file_creation_context ();
288 if (fd == -1)
289 error (EXIT_FAILURE, errno, gettext ("cannot create temporary file"));
290
291 status = write_output (fd);
292
293 if (status == EXIT_SUCCESS)
294 {
295 struct stat64 st;
296
297 if (fstat64 (fd, &st) == 0)
298 {
299 if ((st.st_mode & ACCESSPERMS) != mode)
300 /* We ignore problems with changing the mode. */
301 fchmod (fd, mode);
302 }
303 else
304 {
305 error (0, errno, gettext ("cannot stat newly created file"));
306 status = EXIT_FAILURE;
307 }
308 }
309
310 close (fd);
311
312 if (status == EXIT_SUCCESS)
313 {
314 if (rename (tmp_output_name, output_name) != 0)
315 {
316 error (0, errno, gettext ("cannot rename temporary file"));
317 status = EXIT_FAILURE;
318 goto do_unlink;
319 }
320 }
321 else
322 do_unlink:
323 unlink (tmp_output_name);
324
325 return status;
326 }
327
328
329 /* Handle program arguments. */
330 static error_t
331 parse_opt (int key, char *arg, struct argp_state *state)
332 {
333 struct db_option *newp;
334
335 switch (key)
336 {
337 case 'f':
338 to_lowercase = 1;
339 break;
340 case 'o':
341 output_name = arg;
342 break;
343 case 'q':
344 be_quiet = 1;
345 break;
346 case 'u':
347 do_undo = 1;
348 break;
349 case 'g':
350 newp = xmalloc (sizeof (*newp));
351 newp->dbid = arg[0];
352 newp->next = db_options;
353 db_options = newp;
354 break;
355 default:
356 return ARGP_ERR_UNKNOWN;
357 }
358 return 0;
359 }
360
361
362 static char *
363 more_help (int key, const char *text, void *input)
364 {
365 char *tp = NULL;
366 switch (key)
367 {
368 case ARGP_KEY_HELP_EXTRA:
369 /* We print some extra information. */
370 if (asprintf (&tp, gettext ("\
371 For bug reporting instructions, please see:\n\
372 %s.\n"), REPORT_BUGS_TO) < 0)
373 return NULL;
374 return tp;
375 default:
376 break;
377 }
378 return (char *) text;
379 }
380
381 /* Print the version information. */
382 static void
383 print_version (FILE *stream, struct argp_state *state)
384 {
385 fprintf (stream, "makedb %s%s\n", PKGVERSION, VERSION);
386 fprintf (stream, gettext ("\
387 Copyright (C) %s Free Software Foundation, Inc.\n\
388 This is free software; see the source for copying conditions. There is NO\n\
389 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
390 "), "2020");
391 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
392 }
393
394
395 static int
396 dbentry_compare (const void *p1, const void *p2)
397 {
398 const struct dbentry *d1 = (const struct dbentry *) p1;
399 const struct dbentry *d2 = (const struct dbentry *) p2;
400
401 if (d1->hashval != d2->hashval)
402 return d1->hashval < d2->hashval ? -1 : 1;
403
404 return strcmp (d1->str, d2->str);
405 }
406
407
408 static int
409 valstr_compare (const void *p1, const void *p2)
410 {
411 const struct valstrentry *d1 = (const struct valstrentry *) p1;
412 const struct valstrentry *d2 = (const struct valstrentry *) p2;
413
414 return strcmp (d1->str, d2->str);
415 }
416
417
418 static int
419 process_input (FILE *input, const char *inname, int to_lowercase, int be_quiet)
420 {
421 char *line;
422 size_t linelen;
423 int status;
424 size_t linenr;
425
426 line = NULL;
427 linelen = 0;
428 status = EXIT_SUCCESS;
429 linenr = 0;
430
431 struct database *last_database = NULL;
432
433 while (!feof_unlocked (input))
434 {
435 ssize_t n = getline (&line, &linelen, input);
436 if (n < 0)
437 /* This means end of file or some bug. */
438 break;
439 if (n == 0)
440 /* Short read. Probably interrupted system call. */
441 continue;
442
443 ++linenr;
444
445 if (line[n - 1] == '\n')
446 /* Remove trailing newline. */
447 line[--n] = '\0';
448
449 char *cp = line;
450 while (isspace (*cp))
451 ++cp;
452
453 if (*cp == '#' || *cp == '\0')
454 /* First non-space character in line '#': it's a comment.
455 Also go to the next line if it is empty except for whitespaces. */
456 continue;
457
458 /* Skip over the character indicating the database so that it is not
459 affected by TO_LOWERCASE. */
460 char *key = cp++;
461 while (*cp != '\0' && !isspace (*cp))
462 {
463 if (to_lowercase)
464 *cp = tolower (*cp);
465 ++cp;
466 }
467
468 if (*cp == '\0')
469 /* It's a line without a value field. */
470 continue;
471
472 *cp++ = '\0';
473 size_t keylen = cp - key;
474
475 while (isspace (*cp))
476 ++cp;
477
478 char *data = cp;
479 size_t datalen = (&line[n] - cp) + 1;
480
481 /* Find the database. */
482 if (last_database == NULL || last_database->dbid != key[0])
483 {
484 last_database = databases;
485 while (last_database != NULL && last_database->dbid != key[0])
486 last_database = last_database->next;
487
488 if (last_database == NULL)
489 {
490 last_database = xmalloc (sizeof (*last_database));
491 last_database->dbid = key[0];
492 last_database->extra_string = false;
493 last_database->next = databases;
494 last_database->entries = NULL;
495 last_database->nentries = 0;
496 last_database->keystrlen = 0;
497 databases = last_database;
498
499 struct db_option *runp = db_options;
500 while (runp != NULL)
501 if (runp->dbid == key[0])
502 {
503 last_database->extra_string = true;
504 break;
505 }
506 else
507 runp = runp->next;
508 }
509 }
510
511 /* Skip the database selector. */
512 ++key;
513 --keylen;
514
515 /* Store the data. */
516 struct valstrentry *nentry = xmalloc (sizeof (struct valstrentry)
517 + datalen);
518 if (last_database->extra_string)
519 nentry->idx = extrastrlen;
520 else
521 nentry->idx = valstrlen;
522 nentry->extra_string = last_database->extra_string;
523 memcpy (nentry->str, data, datalen);
524
525 struct valstrentry **fdata = tsearch (nentry, &valstrtree,
526 valstr_compare);
527 if (fdata == NULL)
528 error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
529
530 if (*fdata != nentry)
531 {
532 /* We can reuse a string. */
533 free (nentry);
534 nentry = *fdata;
535 }
536 else
537 if (last_database->extra_string)
538 extrastrlen += datalen;
539 else
540 valstrlen += datalen;
541
542 /* Store the key. */
543 struct dbentry *newp = xmalloc (sizeof (struct dbentry) + keylen);
544 newp->validx = nentry->idx;
545 newp->hashval = __hash_string (key);
546 memcpy (newp->str, key, keylen);
547
548 struct dbentry **found = tsearch (newp, &last_database->entries,
549 dbentry_compare);
550 if (found == NULL)
551 error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
552
553 if (*found != newp)
554 {
555 free (newp);
556 if (!be_quiet)
557 error_at_line (0, 0, inname, linenr, gettext ("duplicate key"));
558 continue;
559 }
560
561 ++last_database->nentries;
562 last_database->keystrlen += keylen;
563
564 any_dbentry = true;
565 }
566
567 if (ferror_unlocked (input))
568 {
569 error (0, 0, gettext ("problems while reading `%s'"), inname);
570 status = EXIT_FAILURE;
571 }
572
573 return status;
574 }
575
576
577 static void
578 copy_valstr (const void *nodep, const VISIT which, const int depth)
579 {
580 if (which != leaf && which != postorder)
581 return;
582
583 const struct valstrentry *p = *(const struct valstrentry **) nodep;
584
585 strcpy (valstrtab + (p->extra_string ? valstrlen : 0) + p->idx, p->str);
586 }
587
588
589 /* Determine if the candidate is prime by using a modified trial division
590 algorithm. The candidate must be both odd and greater than 4. */
591 static int
592 is_prime (size_t candidate)
593 {
594 size_t divn = 3;
595 size_t sq = divn * divn;
596
597 assert (candidate > 4 && candidate % 2 != 0);
598
599 while (sq < candidate && candidate % divn != 0)
600 {
601 ++divn;
602 sq += 4 * divn;
603 ++divn;
604 }
605
606 return candidate % divn != 0;
607 }
608
609
610 static size_t
611 next_prime (size_t seed)
612 {
613 /* Make sure that we're always greater than 4. */
614 seed = (seed + 4) | 1;
615
616 while (!is_prime (seed))
617 seed += 2;
618
619 return seed;
620 }
621
622
623 static void
624 compute_tables (void)
625 {
626 valstrtab = xmalloc (roundup (valstrlen + extrastrlen, sizeof (stridx_t)));
627 while ((valstrlen + extrastrlen) % sizeof (stridx_t) != 0)
628 valstrtab[valstrlen++] = '\0';
629 twalk (valstrtree, copy_valstr);
630
631 static struct database *db;
632 for (db = databases; db != NULL; db = db->next)
633 if (db->nentries != 0)
634 {
635 ++ndatabases;
636
637 /* We simply use an odd number large than twice the number of
638 elements to store in the hash table for the size. This gives
639 enough efficiency. */
640 #define TEST_RANGE 30
641 size_t nhashentries_min = next_prime (db->nentries < TEST_RANGE
642 ? db->nentries
643 : db->nentries * 2 - TEST_RANGE);
644 size_t nhashentries_max = MAX (nhashentries_min, db->nentries * 4);
645 size_t nhashentries_best = nhashentries_min;
646 size_t chainlength_best = db->nentries;
647
648 db->hashtable = xmalloc (2 * nhashentries_max * sizeof (stridx_t)
649 + db->keystrlen);
650 db->keyidxtab = db->hashtable + nhashentries_max;
651 db->keystrtab = (char *) (db->keyidxtab + nhashentries_max);
652
653 static size_t max_chainlength;
654 static char *wp;
655 static size_t nhashentries;
656 static bool copy_string;
657
658 void add_key(const void *nodep, const VISIT which, const int depth)
659 {
660 if (which != leaf && which != postorder)
661 return;
662
663 const struct dbentry *dbe = *(const struct dbentry **) nodep;
664
665 ptrdiff_t stridx;
666 if (copy_string)
667 {
668 stridx = wp - db->keystrtab;
669 wp = stpcpy (wp, dbe->str) + 1;
670 }
671 else
672 stridx = 0;
673
674 size_t hidx = dbe->hashval % nhashentries;
675 size_t hval2 = 1 + dbe->hashval % (nhashentries - 2);
676 size_t chainlength = 0;
677
678 while (db->hashtable[hidx] != ~((stridx_t) 0))
679 {
680 ++chainlength;
681 if ((hidx += hval2) >= nhashentries)
682 hidx -= nhashentries;
683 }
684
685 db->hashtable[hidx] = ((db->extra_string ? valstrlen : 0)
686 + dbe->validx);
687 db->keyidxtab[hidx] = stridx;
688
689 max_chainlength = MAX (max_chainlength, chainlength);
690 }
691
692 copy_string = false;
693 nhashentries = nhashentries_min;
694 for (size_t cnt = 0; cnt < TEST_RANGE; ++cnt)
695 {
696 memset (db->hashtable, '\xff', nhashentries * sizeof (stridx_t));
697
698 max_chainlength = 0;
699 wp = db->keystrtab;
700
701 twalk (db->entries, add_key);
702
703 if (max_chainlength == 0)
704 {
705 /* No need to look further, this is as good as it gets. */
706 nhashentries_best = nhashentries;
707 break;
708 }
709
710 if (max_chainlength < chainlength_best)
711 {
712 chainlength_best = max_chainlength;
713 nhashentries_best = nhashentries;
714 }
715
716 nhashentries = next_prime (nhashentries + 1);
717 if (nhashentries > nhashentries_max)
718 break;
719 }
720
721 /* Recompute the best table again, this time fill in the strings. */
722 nhashentries = nhashentries_best;
723 memset (db->hashtable, '\xff',
724 2 * nhashentries_max * sizeof (stridx_t));
725 copy_string = true;
726 wp = db->keystrtab;
727
728 twalk (db->entries, add_key);
729
730 db->nhashentries = nhashentries_best;
731 nhashentries_total += nhashentries_best;
732 }
733 }
734
735
736 static int
737 write_output (int fd)
738 {
739 struct nss_db_header *header;
740 uint64_t file_offset = (sizeof (struct nss_db_header)
741 + (ndatabases * sizeof (header->dbs[0])));
742 header = alloca (file_offset);
743
744 header->magic = NSS_DB_MAGIC;
745 header->ndbs = ndatabases;
746 header->valstroffset = file_offset;
747 header->valstrlen = valstrlen;
748
749 size_t filled_dbs = 0;
750 struct iovec iov[2 + ndatabases * 3];
751 iov[0].iov_base = header;
752 iov[0].iov_len = file_offset;
753
754 iov[1].iov_base = valstrtab;
755 iov[1].iov_len = valstrlen + extrastrlen;
756 file_offset += iov[1].iov_len;
757
758 size_t keydataoffset = file_offset + nhashentries_total * sizeof (stridx_t);
759 for (struct database *db = databases; db != NULL; db = db->next)
760 if (db->entries != NULL)
761 {
762 assert (file_offset % sizeof (stridx_t) == 0);
763 assert (filled_dbs < ndatabases);
764
765 header->dbs[filled_dbs].id = db->dbid;
766 memset (header->dbs[filled_dbs].pad, '\0',
767 sizeof (header->dbs[0].pad));
768 header->dbs[filled_dbs].hashsize = db->nhashentries;
769
770 iov[2 + filled_dbs].iov_base = db->hashtable;
771 iov[2 + filled_dbs].iov_len = db->nhashentries * sizeof (stridx_t);
772 header->dbs[filled_dbs].hashoffset = file_offset;
773 file_offset += iov[2 + filled_dbs].iov_len;
774
775 iov[2 + ndatabases + filled_dbs * 2].iov_base = db->keyidxtab;
776 iov[2 + ndatabases + filled_dbs * 2].iov_len
777 = db->nhashentries * sizeof (stridx_t);
778 header->dbs[filled_dbs].keyidxoffset = keydataoffset;
779 keydataoffset += iov[2 + ndatabases + filled_dbs * 2].iov_len;
780
781 iov[3 + ndatabases + filled_dbs * 2].iov_base = db->keystrtab;
782 iov[3 + ndatabases + filled_dbs * 2].iov_len = db->keystrlen;
783 header->dbs[filled_dbs].keystroffset = keydataoffset;
784 keydataoffset += iov[3 + ndatabases + filled_dbs * 2].iov_len;
785
786 ++filled_dbs;
787 }
788
789 assert (filled_dbs == ndatabases);
790 assert (file_offset == (iov[0].iov_len + iov[1].iov_len
791 + nhashentries_total * sizeof (stridx_t)));
792 header->allocate = file_offset;
793
794 if (writev (fd, iov, 2 + ndatabases * 3) != keydataoffset)
795 {
796 error (0, errno, gettext ("failed to write new database file"));
797 return EXIT_FAILURE;
798 }
799
800 return EXIT_SUCCESS;
801 }
802
803
804 static int
805 print_database (int fd)
806 {
807 struct stat64 st;
808 if (fstat64 (fd, &st) != 0)
809 error (EXIT_FAILURE, errno, gettext ("cannot stat database file"));
810
811 const struct nss_db_header *header = mmap (NULL, st.st_size, PROT_READ,
812 MAP_PRIVATE|MAP_POPULATE, fd, 0);
813 if (header == MAP_FAILED)
814 error (EXIT_FAILURE, errno, gettext ("cannot map database file"));
815
816 if (header->magic != NSS_DB_MAGIC)
817 error (EXIT_FAILURE, 0, gettext ("file not a database file"));
818
819 const char *valstrtab = (const char *) header + header->valstroffset;
820
821 for (unsigned int dbidx = 0; dbidx < header->ndbs; ++dbidx)
822 {
823 const stridx_t *stridxtab
824 = ((const stridx_t *) ((const char *) header
825 + header->dbs[dbidx].keyidxoffset));
826 const char *keystrtab
827 = (const char *) header + header->dbs[dbidx].keystroffset;
828 const stridx_t *hashtab
829 = (const stridx_t *) ((const char *) header
830 + header->dbs[dbidx].hashoffset);
831
832 for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx)
833 if (hashtab[hidx] != ~((stridx_t) 0))
834 printf ("%c%s %s\n",
835 header->dbs[dbidx].id,
836 keystrtab + stridxtab[hidx],
837 valstrtab + hashtab[hidx]);
838 }
839
840 return EXIT_SUCCESS;
841 }
842
843
844 #ifdef HAVE_SELINUX
845
846 /* security_context_t and matchpathcon (along with several other symbols) were
847 marked as deprecated by the SELinux API starting from version 3.1. We use
848 them here, but should eventually switch to the newer API. */
849 DIAG_PUSH_NEEDS_COMMENT
850 DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
851
852 static void
853 set_file_creation_context (const char *outname, mode_t mode)
854 {
855 static int enabled;
856 static int enforcing;
857 security_context_t ctx;
858
859 /* Check if SELinux is enabled, and remember. */
860 if (enabled == 0)
861 enabled = is_selinux_enabled () ? 1 : -1;
862 if (enabled < 0)
863 return;
864
865 /* Check if SELinux is enforcing, and remember. */
866 if (enforcing == 0)
867 enforcing = security_getenforce () ? 1 : -1;
868
869 /* Determine the context which the file should have. */
870 ctx = NULL;
871 if (matchpathcon (outname, S_IFREG | mode, &ctx) == 0 && ctx != NULL)
872 {
873 if (setfscreatecon (ctx) != 0)
874 error (enforcing > 0 ? EXIT_FAILURE : 0, 0,
875 gettext ("cannot set file creation context for `%s'"),
876 outname);
877
878 freecon (ctx);
879 }
880 }
881 DIAG_POP_NEEDS_COMMENT
882
883 static void
884 reset_file_creation_context (void)
885 {
886 setfscreatecon (NULL);
887 }
888 #endif