Account for the extra stack size when rlimit is small enough
[platform/upstream/glibc.git] / nss / makedb.c
1 /* Create simple DB database from textual input.
2    Copyright (C) 1996-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/param.h>
36 #include <sys/stat.h>
37 #include <sys/uio.h>
38 #include "nss_db/nss_db.h"
39
40 /* Get libc version number.  */
41 #include "../version.h"
42
43 /* The hashing function we use.  */
44 #include "../intl/hash-string.h"
45
46 /* SELinux support.  */
47 #ifdef HAVE_SELINUX
48 # include <selinux/selinux.h>
49 #endif
50
51 #ifndef MAP_POPULATE
52 # define MAP_POPULATE 0
53 #endif
54
55 #define PACKAGE _libc_intl_domainname
56
57 /* List of data bases.  */
58 struct database
59 {
60   char dbid;
61   bool extra_string;
62   struct database *next;
63   void *entries;
64   size_t nentries;
65   size_t nhashentries;
66   stridx_t *hashtable;
67   size_t keystrlen;
68   stridx_t *keyidxtab;
69   char *keystrtab;
70 } *databases;
71 static size_t ndatabases;
72 static size_t nhashentries_total;
73 static size_t valstrlen;
74 static void *valstrtree;
75 static char *valstrtab;
76 static size_t extrastrlen;
77
78 /* Database entry.  */
79 struct dbentry
80 {
81   stridx_t validx;
82   uint32_t hashval;
83   char str[0];
84 };
85
86 /* Stored string entry.  */
87 struct valstrentry
88 {
89   stridx_t idx;
90   bool extra_string;
91   char str[0];
92 };
93
94
95 /* True if any entry has been added.  */
96 static bool any_dbentry;
97
98 /* If non-zero convert key to lower case.  */
99 static int to_lowercase;
100
101 /* If non-zero print content of input file, one entry per line.  */
102 static int do_undo;
103
104 /* If non-zero do not print informational messages.  */
105 static int be_quiet;
106
107 /* Name of output file.  */
108 static const char *output_name;
109
110 /* Name and version of program.  */
111 static void print_version (FILE *stream, struct argp_state *state);
112 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
113
114 /* Definitions of arguments for argp functions.  */
115 static const struct argp_option options[] =
116 {
117   { "fold-case", 'f', NULL, 0, N_("Convert key to lower case") },
118   { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
119   { "quiet", 'q', NULL, 0,
120     N_("Do not print messages while building database") },
121   { "undo", 'u', NULL, 0,
122     N_("Print content of database file, one entry a line") },
123   { "generated", 'g', N_("CHAR"), 0,
124     N_("Generated line not part of iteration") },
125   { NULL, 0, NULL, 0, NULL }
126 };
127
128 /* Short description of program.  */
129 static const char doc[] = N_("Create simple database from textual input.");
130
131 /* Strings for arguments in help texts.  */
132 static const char args_doc[] = N_("\
133 INPUT-FILE OUTPUT-FILE\n-o OUTPUT-FILE INPUT-FILE\n-u INPUT-FILE");
134
135 /* Prototype for option handler.  */
136 static error_t parse_opt (int key, char *arg, struct argp_state *state);
137
138 /* Function to print some extra text in the help message.  */
139 static char *more_help (int key, const char *text, void *input);
140
141 /* Data structure to communicate with argp functions.  */
142 static struct argp argp =
143 {
144   options, parse_opt, args_doc, doc, NULL, more_help
145 };
146
147
148 /* List of databases which are not part of the iteration table.  */
149 static struct db_option
150 {
151   char dbid;
152   struct db_option *next;
153 } *db_options;
154
155
156 /* Prototypes for local functions.  */
157 static int process_input (FILE *input, const char *inname,
158                           int to_lowercase, int be_quiet);
159 static int print_database (int fd);
160 static void compute_tables (void);
161 static int write_output (int fd);
162
163 /* SELinux support.  */
164 #ifdef HAVE_SELINUX
165 /* Set the SELinux file creation context for the given file. */
166 static void set_file_creation_context (const char *outname, mode_t mode);
167 static void reset_file_creation_context (void);
168 #else
169 # define set_file_creation_context(_outname,_mode)
170 # define reset_file_creation_context()
171 #endif
172
173
174 /* External functions.  */
175 extern void *xmalloc (size_t n)
176   __attribute_malloc__ __attribute_alloc_size (1);
177 extern void *xcalloc (size_t n, size_t s)
178   __attribute_malloc__ __attribute_alloc_size (1, 2);
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   switch (key)
366     {
367     case ARGP_KEY_HELP_EXTRA:
368       /* We print some extra information.  */
369       return strdup (gettext ("\
370 For bug reporting instructions, please see:\n\
371 <http://www.gnu.org/software/libc/bugs.html>.\n"));
372     default:
373       break;
374     }
375   return (char *) text;
376 }
377
378 /* Print the version information.  */
379 static void
380 print_version (FILE *stream, struct argp_state *state)
381 {
382   fprintf (stream, "makedb (GNU %s) %s\n", PACKAGE, VERSION);
383   fprintf (stream, gettext ("\
384 Copyright (C) %s Free Software Foundation, Inc.\n\
385 This is free software; see the source for copying conditions.  There is NO\n\
386 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
387 "), "2012");
388   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
389 }
390
391
392 static int
393 dbentry_compare (const void *p1, const void *p2)
394 {
395   const struct dbentry *d1 = (const struct dbentry *) p1;
396   const struct dbentry *d2 = (const struct dbentry *) p2;
397
398   if (d1->hashval != d2->hashval)
399     return d1->hashval < d2->hashval ? -1 : 1;
400
401   return strcmp (d1->str, d2->str);
402 }
403
404
405 static int
406 valstr_compare (const void *p1, const void *p2)
407 {
408   const struct valstrentry *d1 = (const struct valstrentry *) p1;
409   const struct valstrentry *d2 = (const struct valstrentry *) p2;
410
411   return strcmp (d1->str, d2->str);
412 }
413
414
415 static int
416 process_input (input, inname, to_lowercase, be_quiet)
417      FILE *input;
418      const char *inname;
419      int to_lowercase;
420      int be_quiet;
421 {
422   char *line;
423   size_t linelen;
424   int status;
425   size_t linenr;
426
427   line = NULL;
428   linelen = 0;
429   status = EXIT_SUCCESS;
430   linenr = 0;
431
432   struct database *last_database = NULL;
433
434   while (!feof_unlocked (input))
435     {
436       ssize_t n = getline (&line, &linelen, input);
437       if (n < 0)
438         /* This means end of file or some bug.  */
439         break;
440       if (n == 0)
441         /* Short read.  Probably interrupted system call. */
442         continue;
443
444       ++linenr;
445
446       if (line[n - 1] == '\n')
447         /* Remove trailing newline.  */
448         line[--n] = '\0';
449
450       char *cp = line;
451       while (isspace (*cp))
452         ++cp;
453
454       if (*cp == '#' || *cp == '\0')
455         /* First non-space character in line '#': it's a comment.
456            Also go to the next line if it is empty except for whitespaces. */
457         continue;
458
459       /* Skip over the character indicating the database so that it is not
460          affected by TO_LOWERCASE.  */
461       char *key = cp++;
462       while (*cp != '\0' && !isspace (*cp))
463         {
464           if (to_lowercase)
465             *cp = tolower (*cp);
466           ++cp;
467         }
468
469       if (*cp == '\0')
470         /* It's a line without a value field.  */
471         continue;
472
473       *cp++ = '\0';
474       size_t keylen = cp - key;
475
476       while (isspace (*cp))
477         ++cp;
478
479       char *data = cp;
480       size_t datalen = (&line[n] - cp) + 1;
481
482       /* Find the database.  */
483       if (last_database == NULL || last_database->dbid != key[0])
484         {
485           last_database = databases;
486           while (last_database != NULL && last_database->dbid != key[0])
487             last_database = last_database->next;
488
489           if (last_database == NULL)
490             {
491               last_database = xmalloc (sizeof (*last_database));
492               last_database->dbid = key[0];
493               last_database->extra_string = false;
494               last_database->next = databases;
495               last_database->entries = NULL;
496               last_database->nentries = 0;
497               last_database->keystrlen = 0;
498               databases = last_database;
499
500               struct db_option *runp = db_options;
501               while (runp != NULL)
502                 if (runp->dbid == key[0])
503                   {
504                     last_database->extra_string = true;
505                     break;
506                   }
507                 else
508                   runp = runp->next;
509             }
510         }
511
512       /* Skip the database selector.  */
513       ++key;
514       --keylen;
515
516       /* Store the data.  */
517       struct valstrentry *nentry = xmalloc (sizeof (struct valstrentry)
518                                             + datalen);
519       if (last_database->extra_string)
520         nentry->idx = extrastrlen;
521       else
522         nentry->idx = valstrlen;
523       nentry->extra_string = last_database->extra_string;
524       memcpy (nentry->str, data, datalen);
525
526       struct valstrentry **fdata = tsearch (nentry, &valstrtree,
527                                             valstr_compare);
528       if (fdata == NULL)
529         error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
530
531       if (*fdata != nentry)
532         {
533           /* We can reuse a string.  */
534           free (nentry);
535           nentry = *fdata;
536         }
537       else
538         if (last_database->extra_string)
539           extrastrlen += datalen;
540         else
541           valstrlen += datalen;
542
543       /* Store the key.  */
544       struct dbentry *newp = xmalloc (sizeof (struct dbentry) + keylen);
545       newp->validx = nentry->idx;
546       newp->hashval = __hash_string (key);
547       memcpy (newp->str, key, keylen);
548
549       struct dbentry **found = tsearch (newp, &last_database->entries,
550                                         dbentry_compare);
551       if (found == NULL)
552         error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
553
554       if (*found != newp)
555         {
556           free (newp);
557           if (!be_quiet)
558             error_at_line (0, 0, inname, linenr, gettext ("duplicate key"));
559           continue;
560         }
561
562       ++last_database->nentries;
563       last_database->keystrlen += keylen;
564
565       any_dbentry = true;
566     }
567
568   if (ferror_unlocked (input))
569     {
570       error (0, 0, gettext ("problems while reading `%s'"), inname);
571       status = EXIT_FAILURE;
572     }
573
574   return status;
575 }
576
577
578 static void
579 copy_valstr (const void *nodep, const VISIT which, const int depth)
580 {
581   if (which != leaf && which != postorder)
582     return;
583
584   const struct valstrentry *p = *(const struct valstrentry **) nodep;
585
586   strcpy (valstrtab + (p->extra_string ? valstrlen : 0) + p->idx, p->str);
587 }
588
589
590 static int
591 is_prime (size_t candidate)
592 {
593   /* No even number and none less than 10 will be passed here.  */
594   size_t divn = 3;
595   size_t sq = divn * divn;
596
597   while (sq < candidate && candidate % divn != 0)
598     {
599       ++divn;
600       sq += 4 * divn;
601       ++divn;
602     }
603
604   return candidate % divn != 0;
605 }
606
607
608 static size_t
609 next_prime (size_t seed)
610 {
611   /* Make it definitely odd.  */
612   seed |= 1;
613
614   while (!is_prime (seed))
615     seed += 2;
616
617   return seed;
618 }
619
620
621 static void
622 compute_tables (void)
623 {
624   valstrtab = xmalloc (roundup (valstrlen + extrastrlen, sizeof (stridx_t)));
625   while ((valstrlen + extrastrlen) % sizeof (stridx_t) != 0)
626     valstrtab[valstrlen++] = '\0';
627   twalk (valstrtree, copy_valstr);
628
629   static struct database *db;
630   for (db = databases; db != NULL; db = db->next)
631     if (db->nentries != 0)
632       {
633         ++ndatabases;
634
635         /* We simply use an odd number large than twice the number of
636            elements to store in the hash table for the size.  This gives
637            enough efficiency.  */
638 #define TEST_RANGE 30
639         size_t nhashentries_min = next_prime (db->nentries < TEST_RANGE
640                                               ? db->nentries
641                                               : db->nentries * 2 - TEST_RANGE);
642         size_t nhashentries_max = MAX (nhashentries_min, db->nentries * 4);
643         size_t nhashentries_best = nhashentries_min;
644         size_t chainlength_best = db->nentries;
645
646         db->hashtable = xmalloc (2 * nhashentries_max * sizeof (stridx_t)
647                                  + db->keystrlen);
648         db->keyidxtab = db->hashtable + nhashentries_max;
649         db->keystrtab = (char *) (db->keyidxtab + nhashentries_max);
650
651         static size_t max_chainlength;
652         static char *wp;
653         static size_t nhashentries;
654         static bool copy_string;
655
656         void add_key(const void *nodep, const VISIT which, const int depth)
657         {
658           if (which != leaf && which != postorder)
659             return;
660
661           const struct dbentry *dbe = *(const struct dbentry **) nodep;
662
663           ptrdiff_t stridx;
664           if (copy_string)
665             {
666               stridx = wp - db->keystrtab;
667               wp = stpcpy (wp, dbe->str) + 1;
668             }
669           else
670             stridx = 0;
671
672           size_t hidx = dbe->hashval % nhashentries;
673           size_t hval2 = 1 + dbe->hashval % (nhashentries - 2);
674           size_t chainlength = 0;
675
676           while (db->hashtable[hidx] != ~((stridx_t) 0))
677             {
678               ++chainlength;
679               if ((hidx += hval2) >= nhashentries)
680                 hidx -= nhashentries;
681             }
682
683           db->hashtable[hidx] = ((db->extra_string ? valstrlen : 0)
684                                  + dbe->validx);
685           db->keyidxtab[hidx] = stridx;
686
687           max_chainlength = MAX (max_chainlength, chainlength);
688         }
689
690         copy_string = false;
691         nhashentries = nhashentries_min;
692         for (size_t cnt = 0; cnt < TEST_RANGE; ++cnt)
693           {
694             memset (db->hashtable, '\xff', nhashentries * sizeof (stridx_t));
695
696             max_chainlength = 0;
697             wp = db->keystrtab;
698
699             twalk (db->entries, add_key);
700
701             if (max_chainlength == 0)
702               {
703                 /* No need to look further, this is as good as it gets.  */
704                 nhashentries_best = nhashentries;
705                 break;
706               }
707
708             if (max_chainlength < chainlength_best)
709               {
710                 chainlength_best = max_chainlength;
711                 nhashentries_best = nhashentries;
712               }
713
714             nhashentries = next_prime (nhashentries + 1);
715             if (nhashentries > nhashentries_max)
716               break;
717           }
718
719         /* Recompute the best table again, this time fill in the strings.  */
720         nhashentries = nhashentries_best;
721         memset (db->hashtable, '\xff',
722                 2 * nhashentries_max * sizeof (stridx_t));
723         copy_string = true;
724         wp = db->keystrtab;
725
726         twalk (db->entries, add_key);
727
728         db->nhashentries = nhashentries_best;
729         nhashentries_total += nhashentries_best;
730     }
731 }
732
733
734 static int
735 write_output (int fd)
736 {
737   struct nss_db_header *header;
738   uint64_t file_offset = (sizeof (struct nss_db_header)
739                           + (ndatabases * sizeof (header->dbs[0])));
740   header = alloca (file_offset);
741
742   header->magic = NSS_DB_MAGIC;
743   header->ndbs = ndatabases;
744   header->valstroffset = file_offset;
745   header->valstrlen = valstrlen;
746
747   size_t filled_dbs = 0;
748   struct iovec iov[2 + ndatabases * 3];
749   iov[0].iov_base = header;
750   iov[0].iov_len = file_offset;
751
752   iov[1].iov_base = valstrtab;
753   iov[1].iov_len = valstrlen + extrastrlen;
754   file_offset += iov[1].iov_len;
755
756   size_t keydataoffset = file_offset + nhashentries_total * sizeof (stridx_t);
757   for (struct database *db = databases; db != NULL; db = db->next)
758     if (db->entries != NULL)
759       {
760         assert (file_offset % sizeof (stridx_t) == 0);
761         assert (filled_dbs < ndatabases);
762
763         header->dbs[filled_dbs].id = db->dbid;
764         memset (header->dbs[filled_dbs].pad, '\0',
765                 sizeof (header->dbs[0].pad));
766         header->dbs[filled_dbs].hashsize = db->nhashentries;
767
768         iov[2 + filled_dbs].iov_base = db->hashtable;
769         iov[2 + filled_dbs].iov_len = db->nhashentries * sizeof (stridx_t);
770         header->dbs[filled_dbs].hashoffset = file_offset;
771         file_offset += iov[2 + filled_dbs].iov_len;
772
773         iov[2 + ndatabases + filled_dbs * 2].iov_base = db->keyidxtab;
774         iov[2 + ndatabases + filled_dbs * 2].iov_len
775           = db->nhashentries * sizeof (stridx_t);
776         header->dbs[filled_dbs].keyidxoffset = keydataoffset;
777         keydataoffset += iov[2 + ndatabases + filled_dbs * 2].iov_len;
778
779         iov[3 + ndatabases + filled_dbs * 2].iov_base = db->keystrtab;
780         iov[3 + ndatabases + filled_dbs * 2].iov_len = db->keystrlen;
781         header->dbs[filled_dbs].keystroffset = keydataoffset;
782         keydataoffset += iov[3 + ndatabases + filled_dbs * 2].iov_len;
783
784         ++filled_dbs;
785       }
786
787   assert (filled_dbs == ndatabases);
788   assert (file_offset == (iov[0].iov_len + iov[1].iov_len
789                           + nhashentries_total * sizeof (stridx_t)));
790   header->allocate = file_offset;
791
792   if (writev (fd, iov, 2 + ndatabases * 3) != keydataoffset)
793     {
794       error (0, errno, gettext ("failed to write new database file"));
795       return EXIT_FAILURE;
796     }
797
798   return EXIT_SUCCESS;
799 }
800
801
802 static int
803 print_database (int fd)
804 {
805   struct stat64 st;
806   if (fstat64 (fd, &st) != 0)
807     error (EXIT_FAILURE, errno, gettext ("cannot stat database file"));
808
809   const struct nss_db_header *header = mmap (NULL, st.st_size, PROT_READ,
810                                              MAP_PRIVATE|MAP_POPULATE, fd, 0);
811   if (header == MAP_FAILED)
812     error (EXIT_FAILURE, errno, gettext ("cannot map database file"));
813
814   if (header->magic != NSS_DB_MAGIC)
815     error (EXIT_FAILURE, 0, gettext ("file not a database file"));
816
817   const char *valstrtab = (const char *) header + header->valstroffset;
818
819   for (unsigned int dbidx = 0; dbidx < header->ndbs; ++dbidx)
820     {
821       const stridx_t *stridxtab
822         = ((const stridx_t *) ((const char *) header
823                                + header->dbs[dbidx].keyidxoffset));
824       const char *keystrtab
825         = (const char *) header + header->dbs[dbidx].keystroffset;
826       const stridx_t *hashtab
827         = (const stridx_t *) ((const char *) header
828                               + header->dbs[dbidx].hashoffset);
829
830       for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx)
831         if (hashtab[hidx] != ~((stridx_t) 0))
832           printf ("%c%s %s\n",
833                   header->dbs[dbidx].id,
834                   keystrtab + stridxtab[hidx],
835                   valstrtab + hashtab[hidx]);
836     }
837
838   return EXIT_SUCCESS;
839 }
840
841
842 #ifdef HAVE_SELINUX
843 static void
844 set_file_creation_context (const char *outname, mode_t mode)
845 {
846   static int enabled;
847   static int enforcing;
848   security_context_t ctx;
849
850   /* Check if SELinux is enabled, and remember. */
851   if (enabled == 0)
852     enabled = is_selinux_enabled () ? 1 : -1;
853   if (enabled < 0)
854     return;
855
856   /* Check if SELinux is enforcing, and remember. */
857   if (enforcing == 0)
858     enforcing = security_getenforce () ? 1 : -1;
859
860   /* Determine the context which the file should have. */
861   ctx = NULL;
862   if (matchpathcon (outname, S_IFREG | mode, &ctx) == 0 && ctx != NULL)
863     {
864       if (setfscreatecon (ctx) != 0)
865         error (enforcing > 0 ? EXIT_FAILURE : 0, 0,
866                gettext ("cannot set file creation context for `%s'"),
867                outname);
868
869       freecon (ctx);
870     }
871 }
872
873 static void
874 reset_file_creation_context (void)
875 {
876   setfscreatecon (NULL);
877 }
878 #endif