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