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