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