1 /* Create simple DB database from textual input.
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <argp.h>
20 #include <assert.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <error.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <libintl.h>
27 #include <locale.h>
28 #include <search.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdint.h>
35 #include <sys/mman.h>
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/uio.h>
39 #include "nss_db/nss_db.h"
40 #include <libc-diag.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. */
60 struct 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;
73 static size_t ndatabases;
74 static size_t nhashentries_total;
75 static size_t valstrlen;
76 static void *valstrtree;
77 static char *valstrtab;
78 static size_t extrastrlen;
79
80 /* Database entry. */
81 struct dbentry
82 {
83 stridx_t validx;
84 uint32_t hashval;
85 char str[0];
86 };
87
88 /* Stored string entry. */
89 struct 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. */
98 static bool any_dbentry;
99
100 /* If non-zero convert key to lower case. */
101 static int to_lowercase;
102
103 /* If non-zero print content of input file, one entry per line. */
104 static int do_undo;
105
106 /* If non-zero do not print informational messages. */
107 static int be_quiet;
108
109 /* Name of output file. */
110 static const char *output_name;
111
112 /* Name and version of program. */
113 static void print_version (FILE *stream, struct argp_state *state);
114 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
115
116 /* Definitions of arguments for argp functions. */
117 static 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. */
131 static const char doc[] = N_("Create simple database from textual input.");
132
133 /* Strings for arguments in help texts. */
134 static const char args_doc[] = N_("\
135 INPUT-FILE OUTPUT-FILE\n-o OUTPUT-FILE INPUT-FILE\n-u INPUT-FILE");
136
137 /* Prototype for option handler. */
138 static error_t parse_opt (int key, char *arg, struct argp_state *state);
139
140 /* Function to print some extra text in the help message. */
141 static char *more_help (int key, const char *text, void *input);
142
143 /* Data structure to communicate with argp functions. */
144 static 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. */
151 static struct db_option
152 {
153 char dbid;
154 struct db_option *next;
155 } *db_options;
156
157
158 /* Prototypes for local functions. */
159 static int process_input (FILE *input, const char *inname,
160 int to_lowercase, int be_quiet);
161 static int print_database (int fd);
162 static void compute_tables (void);
163 static int write_output (int fd);
164
165 /* SELinux support. */
166 #ifdef HAVE_SELINUX
167 /* Set the SELinux file creation context for the given file. */
168 static void set_file_creation_context (const char *outname, mode_t mode);
169 static 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
180 int
main(int argc,char * argv[])181 main (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. */
329 static error_t
parse_opt(int key,char * arg,struct argp_state * state)330 parse_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
361 static char *
more_help(int key,const char * text,void * input)362 more_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 ("\
370 For 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. */
381 static void
print_version(FILE * stream,struct argp_state * state)382 print_version (FILE *stream, struct argp_state *state)
383 {
384 fprintf (stream, "makedb %s%s\n", PKGVERSION, VERSION);
385 fprintf (stream, gettext ("\
386 Copyright (C) %s Free Software Foundation, Inc.\n\
387 This is free software; see the source for copying conditions. There is NO\n\
388 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
389 "), "2022");
390 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
391 }
392
393
394 static int
dbentry_compare(const void * p1,const void * p2)395 dbentry_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
407 static int
valstr_compare(const void * p1,const void * p2)408 valstr_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
417 static int
process_input(FILE * input,const char * inname,int to_lowercase,int be_quiet)418 process_input (FILE *input, const char *inname, int to_lowercase, int be_quiet)
419 {
420 char *line;
421 size_t linelen;
422 int status;
423 size_t linenr;
424
425 line = NULL;
426 linelen = 0;
427 status = EXIT_SUCCESS;
428 linenr = 0;
429
430 struct database *last_database = NULL;
431
432 while (!feof_unlocked (input))
433 {
434 ssize_t n = getline (&line, &linelen, input);
435 if (n < 0)
436 /* This means end of file or some bug. */
437 break;
438 if (n == 0)
439 /* Short read. Probably interrupted system call. */
440 continue;
441
442 ++linenr;
443
444 if (line[n - 1] == '\n')
445 /* Remove trailing newline. */
446 line[--n] = '\0';
447
448 char *cp = line;
449 while (isspace (*cp))
450 ++cp;
451
452 if (*cp == '#' || *cp == '\0')
453 /* First non-space character in line '#': it's a comment.
454 Also go to the next line if it is empty except for whitespaces. */
455 continue;
456
457 /* Skip over the character indicating the database so that it is not
458 affected by TO_LOWERCASE. */
459 char *key = cp++;
460 while (*cp != '\0' && !isspace (*cp))
461 {
462 if (to_lowercase)
463 *cp = tolower (*cp);
464 ++cp;
465 }
466
467 if (*cp == '\0')
468 /* It's a line without a value field. */
469 continue;
470
471 *cp++ = '\0';
472 size_t keylen = cp - key;
473
474 while (isspace (*cp))
475 ++cp;
476
477 char *data = cp;
478 size_t datalen = (&line[n] - cp) + 1;
479
480 /* Find the database. */
481 if (last_database == NULL || last_database->dbid != key[0])
482 {
483 last_database = databases;
484 while (last_database != NULL && last_database->dbid != key[0])
485 last_database = last_database->next;
486
487 if (last_database == NULL)
488 {
489 last_database = xmalloc (sizeof (*last_database));
490 last_database->dbid = key[0];
491 last_database->extra_string = false;
492 last_database->next = databases;
493 last_database->entries = NULL;
494 last_database->nentries = 0;
495 last_database->keystrlen = 0;
496 databases = last_database;
497
498 struct db_option *runp = db_options;
499 while (runp != NULL)
500 if (runp->dbid == key[0])
501 {
502 last_database->extra_string = true;
503 break;
504 }
505 else
506 runp = runp->next;
507 }
508 }
509
510 /* Skip the database selector. */
511 ++key;
512 --keylen;
513
514 /* Store the data. */
515 struct valstrentry *nentry = xmalloc (sizeof (struct valstrentry)
516 + datalen);
517 if (last_database->extra_string)
518 nentry->idx = extrastrlen;
519 else
520 nentry->idx = valstrlen;
521 nentry->extra_string = last_database->extra_string;
522 memcpy (nentry->str, data, datalen);
523
524 struct valstrentry **fdata = tsearch (nentry, &valstrtree,
525 valstr_compare);
526 if (fdata == NULL)
527 error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
528
529 if (*fdata != nentry)
530 {
531 /* We can reuse a string. */
532 free (nentry);
533 nentry = *fdata;
534 }
535 else
536 if (last_database->extra_string)
537 extrastrlen += datalen;
538 else
539 valstrlen += datalen;
540
541 /* Store the key. */
542 struct dbentry *newp = xmalloc (sizeof (struct dbentry) + keylen);
543 newp->validx = nentry->idx;
544 newp->hashval = __hash_string (key);
545 memcpy (newp->str, key, keylen);
546
547 struct dbentry **found = tsearch (newp, &last_database->entries,
548 dbentry_compare);
549 if (found == NULL)
550 error (EXIT_FAILURE, errno, gettext ("cannot create search tree"));
551
552 if (*found != newp)
553 {
554 free (newp);
555 if (!be_quiet)
556 error_at_line (0, 0, inname, linenr, gettext ("duplicate key"));
557 continue;
558 }
559
560 ++last_database->nentries;
561 last_database->keystrlen += keylen;
562
563 any_dbentry = true;
564 }
565
566 if (ferror_unlocked (input))
567 {
568 error (0, 0, gettext ("problems while reading `%s'"), inname);
569 status = EXIT_FAILURE;
570 }
571
572 return status;
573 }
574
575
576 static void
copy_valstr(const void * nodep,const VISIT which,const int depth)577 copy_valstr (const void *nodep, const VISIT which, const int depth)
578 {
579 if (which != leaf && which != postorder)
580 return;
581
582 const struct valstrentry *p = *(const struct valstrentry **) nodep;
583
584 strcpy (valstrtab + (p->extra_string ? valstrlen : 0) + p->idx, p->str);
585 }
586
587
588 /* Determine if the candidate is prime by using a modified trial division
589 algorithm. The candidate must be both odd and greater than 4. */
590 static int
is_prime(size_t candidate)591 is_prime (size_t candidate)
592 {
593 size_t divn = 3;
594 size_t sq = divn * divn;
595
596 assert (candidate > 4 && candidate % 2 != 0);
597
598 while (sq < candidate && candidate % divn != 0)
599 {
600 ++divn;
601 sq += 4 * divn;
602 ++divn;
603 }
604
605 return candidate % divn != 0;
606 }
607
608
609 static size_t
next_prime(size_t seed)610 next_prime (size_t seed)
611 {
612 /* Make sure that we're always greater than 4. */
613 seed = (seed + 4) | 1;
614
615 while (!is_prime (seed))
616 seed += 2;
617
618 return seed;
619 }
620
621 static size_t max_chainlength;
622 static char *wp;
623 static size_t nhashentries;
624 static bool copy_string;
625
add_key(const void * nodep,VISIT which,void * arg)626 void add_key(const void *nodep, VISIT which, void *arg)
627 {
628 if (which != leaf && which != postorder)
629 return;
630
631 const struct database *db = (const struct database *) arg;
632 const struct dbentry *dbe = *(const struct dbentry **) nodep;
633
634 ptrdiff_t stridx;
635 if (copy_string)
636 {
637 stridx = wp - db->keystrtab;
638 wp = stpcpy (wp, dbe->str) + 1;
639 }
640 else
641 stridx = 0;
642
643 size_t hidx = dbe->hashval % nhashentries;
644 size_t hval2 = 1 + dbe->hashval % (nhashentries - 2);
645 size_t chainlength = 0;
646
647 while (db->hashtable[hidx] != ~((stridx_t) 0))
648 {
649 ++chainlength;
650 if ((hidx += hval2) >= nhashentries)
651 hidx -= nhashentries;
652 }
653
654 db->hashtable[hidx] = ((db->extra_string ? valstrlen : 0)
655 + dbe->validx);
656 db->keyidxtab[hidx] = stridx;
657
658 max_chainlength = MAX (max_chainlength, chainlength);
659 }
660
661 static void
compute_tables(void)662 compute_tables (void)
663 {
664 valstrtab = xmalloc (roundup (valstrlen + extrastrlen, sizeof (stridx_t)));
665 while ((valstrlen + extrastrlen) % sizeof (stridx_t) != 0)
666 valstrtab[valstrlen++] = '\0';
667 twalk (valstrtree, copy_valstr);
668
669 static struct database *db;
670 for (db = databases; db != NULL; db = db->next)
671 if (db->nentries != 0)
672 {
673 ++ndatabases;
674
675 /* We simply use an odd number large than twice the number of
676 elements to store in the hash table for the size. This gives
677 enough efficiency. */
678 #define TEST_RANGE 30
679 size_t nhashentries_min = next_prime (db->nentries < TEST_RANGE
680 ? db->nentries
681 : db->nentries * 2 - TEST_RANGE);
682 size_t nhashentries_max = MAX (nhashentries_min, db->nentries * 4);
683 size_t nhashentries_best = nhashentries_min;
684 size_t chainlength_best = db->nentries;
685
686 db->hashtable = xmalloc (2 * nhashentries_max * sizeof (stridx_t)
687 + db->keystrlen);
688 db->keyidxtab = db->hashtable + nhashentries_max;
689 db->keystrtab = (char *) (db->keyidxtab + nhashentries_max);
690
691 copy_string = false;
692 nhashentries = nhashentries_min;
693 for (size_t cnt = 0; cnt < TEST_RANGE; ++cnt)
694 {
695 memset (db->hashtable, '\xff', nhashentries * sizeof (stridx_t));
696
697 max_chainlength = 0;
698 wp = db->keystrtab;
699
700 twalk_r (db->entries, add_key, db);
701
702 if (max_chainlength == 0)
703 {
704 /* No need to look further, this is as good as it gets. */
705 nhashentries_best = nhashentries;
706 break;
707 }
708
709 if (max_chainlength < chainlength_best)
710 {
711 chainlength_best = max_chainlength;
712 nhashentries_best = nhashentries;
713 }
714
715 nhashentries = next_prime (nhashentries + 1);
716 if (nhashentries > nhashentries_max)
717 break;
718 }
719
720 /* Recompute the best table again, this time fill in the strings. */
721 nhashentries = nhashentries_best;
722 memset (db->hashtable, '\xff',
723 2 * nhashentries_max * sizeof (stridx_t));
724 copy_string = true;
725 wp = db->keystrtab;
726
727 twalk_r (db->entries, add_key, db);
728
729 db->nhashentries = nhashentries_best;
730 nhashentries_total += nhashentries_best;
731 }
732 }
733
734
735 static int
write_output(int fd)736 write_output (int fd)
737 {
738 struct nss_db_header *header;
739 uint64_t file_offset = (sizeof (struct nss_db_header)
740 + (ndatabases * sizeof (header->dbs[0])));
741 header = alloca (file_offset);
742
743 header->magic = NSS_DB_MAGIC;
744 header->ndbs = ndatabases;
745 header->valstroffset = file_offset;
746 header->valstrlen = valstrlen;
747
748 size_t filled_dbs = 0;
749 size_t iov_nelts = 2 + ndatabases * 3;
750 struct iovec iov[iov_nelts];
751 iov[0].iov_base = header;
752 iov[0].iov_len = file_offset;
753
754 iov[1].iov_base = valstrtab;
755 iov[1].iov_len = valstrlen + extrastrlen;
756 file_offset += iov[1].iov_len;
757
758 size_t keydataoffset = file_offset + nhashentries_total * sizeof (stridx_t);
759 for (struct database *db = databases; db != NULL; db = db->next)
760 if (db->entries != NULL)
761 {
762 assert (file_offset % sizeof (stridx_t) == 0);
763 assert (filled_dbs < ndatabases);
764
765 header->dbs[filled_dbs].id = db->dbid;
766 memset (header->dbs[filled_dbs].pad, '\0',
767 sizeof (header->dbs[0].pad));
768 header->dbs[filled_dbs].hashsize = db->nhashentries;
769
770 iov[2 + filled_dbs].iov_base = db->hashtable;
771 iov[2 + filled_dbs].iov_len = db->nhashentries * sizeof (stridx_t);
772 header->dbs[filled_dbs].hashoffset = file_offset;
773 file_offset += iov[2 + filled_dbs].iov_len;
774
775 iov[2 + ndatabases + filled_dbs * 2].iov_base = db->keyidxtab;
776 iov[2 + ndatabases + filled_dbs * 2].iov_len
777 = db->nhashentries * sizeof (stridx_t);
778 header->dbs[filled_dbs].keyidxoffset = keydataoffset;
779 keydataoffset += iov[2 + ndatabases + filled_dbs * 2].iov_len;
780
781 iov[3 + ndatabases + filled_dbs * 2].iov_base = db->keystrtab;
782 iov[3 + ndatabases + filled_dbs * 2].iov_len = db->keystrlen;
783 header->dbs[filled_dbs].keystroffset = keydataoffset;
784 keydataoffset += iov[3 + ndatabases + filled_dbs * 2].iov_len;
785
786 ++filled_dbs;
787 }
788
789 assert (filled_dbs == ndatabases);
790 assert (file_offset == (iov[0].iov_len + iov[1].iov_len
791 + nhashentries_total * sizeof (stridx_t)));
792 header->allocate = file_offset;
793
794 #if __GNUC_PREREQ (10, 0) && !__GNUC_PREREQ (11, 0)
795 DIAG_PUSH_NEEDS_COMMENT;
796 /* Avoid GCC 10 false positive warning: specified size exceeds maximum
797 object size. */
798 DIAG_IGNORE_NEEDS_COMMENT (10, "-Wstringop-overflow");
799 #endif
800
801 assert (iov_nelts <= INT_MAX);
802 if (writev (fd, iov, iov_nelts) != keydataoffset)
803 {
804 error (0, errno, gettext ("failed to write new database file"));
805 return EXIT_FAILURE;
806 }
807
808 #if __GNUC_PREREQ (10, 0) && !__GNUC_PREREQ (11, 0)
809 DIAG_POP_NEEDS_COMMENT;
810 #endif
811
812 return EXIT_SUCCESS;
813 }
814
815
816 static int
print_database(int fd)817 print_database (int fd)
818 {
819 struct stat64 st;
820 if (fstat64 (fd, &st) != 0)
821 error (EXIT_FAILURE, errno, gettext ("cannot stat database file"));
822
823 const struct nss_db_header *header = mmap (NULL, st.st_size, PROT_READ,
824 MAP_PRIVATE|MAP_POPULATE, fd, 0);
825 if (header == MAP_FAILED)
826 error (EXIT_FAILURE, errno, gettext ("cannot map database file"));
827
828 if (header->magic != NSS_DB_MAGIC)
829 error (EXIT_FAILURE, 0, gettext ("file not a database file"));
830
831 const char *valstrtab = (const char *) header + header->valstroffset;
832
833 for (unsigned int dbidx = 0; dbidx < header->ndbs; ++dbidx)
834 {
835 const stridx_t *stridxtab
836 = ((const stridx_t *) ((const char *) header
837 + header->dbs[dbidx].keyidxoffset));
838 const char *keystrtab
839 = (const char *) header + header->dbs[dbidx].keystroffset;
840 const stridx_t *hashtab
841 = (const stridx_t *) ((const char *) header
842 + header->dbs[dbidx].hashoffset);
843
844 for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx)
845 if (hashtab[hidx] != ~((stridx_t) 0))
846 printf ("%c%s %s\n",
847 header->dbs[dbidx].id,
848 keystrtab + stridxtab[hidx],
849 valstrtab + hashtab[hidx]);
850 }
851
852 return EXIT_SUCCESS;
853 }
854
855
856 #ifdef HAVE_SELINUX
857
858 /* security_context_t and matchpathcon (along with several other symbols) were
859 marked as deprecated by the SELinux API starting from version 3.1. We use
860 them here, but should eventually switch to the newer API. */
861 DIAG_PUSH_NEEDS_COMMENT
862 DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
863
864 static void
set_file_creation_context(const char * outname,mode_t mode)865 set_file_creation_context (const char *outname, mode_t mode)
866 {
867 static int enabled;
868 static int enforcing;
869 security_context_t ctx;
870
871 /* Check if SELinux is enabled, and remember. */
872 if (enabled == 0)
873 enabled = is_selinux_enabled () ? 1 : -1;
874 if (enabled < 0)
875 return;
876
877 /* Check if SELinux is enforcing, and remember. */
878 if (enforcing == 0)
879 enforcing = security_getenforce () ? 1 : -1;
880
881 /* Determine the context which the file should have. */
882 ctx = NULL;
883 if (matchpathcon (outname, S_IFREG | mode, &ctx) == 0 && ctx != NULL)
884 {
885 if (setfscreatecon (ctx) != 0)
886 error (enforcing > 0 ? EXIT_FAILURE : 0, 0,
887 gettext ("cannot set file creation context for `%s'"),
888 outname);
889
890 freecon (ctx);
891 }
892 }
893 DIAG_POP_NEEDS_COMMENT
894
895 static void
reset_file_creation_context(void)896 reset_file_creation_context (void)
897 {
898 setfscreatecon (NULL);
899 }
900 #endif
901