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