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