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