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