]>
Commit | Line | Data |
---|---|---|
793bd4d9 | 1 | /* Create simple DB database from textual input. |
bfff8b1b | 2 | Copyright (C) 1996-2017 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 | 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" |
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 | |
50 | # include <selinux/selinux.h> | |
51 | #endif | |
52 | ||
80694780 TS |
53 | #ifndef MAP_POPULATE |
54 | # define MAP_POPULATE 0 | |
55 | #endif | |
56 | ||
793bd4d9 UD |
57 | #define PACKAGE _libc_intl_domainname |
58 | ||
9ee76b5a UD |
59 | /* List of data bases. */ |
60 | struct database | |
61 | { | |
62 | char dbid; | |
9f2da732 | 63 | bool extra_string; |
9ee76b5a UD |
64 | struct database *next; |
65 | void *entries; | |
66 | size_t nentries; | |
9ee76b5a | 67 | size_t nhashentries; |
2666d441 UD |
68 | stridx_t *hashtable; |
69 | size_t keystrlen; | |
70 | stridx_t *keyidxtab; | |
9ee76b5a UD |
71 | char *keystrtab; |
72 | } *databases; | |
73 | static size_t ndatabases; | |
a9e836b0 | 74 | static size_t nhashentries_total; |
9ee76b5a UD |
75 | static size_t valstrlen; |
76 | static void *valstrtree; | |
77 | static char *valstrtab; | |
9f2da732 | 78 | static size_t extrastrlen; |
9ee76b5a UD |
79 | |
80 | /* Database entry. */ | |
81 | struct dbentry | |
82 | { | |
9ee76b5a UD |
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; | |
9f2da732 | 92 | bool extra_string; |
9ee76b5a UD |
93 | char str[0]; |
94 | }; | |
95 | ||
96 | ||
9ee76b5a UD |
97 | /* True if any entry has been added. */ |
98 | static bool any_dbentry; | |
99 | ||
793bd4d9 UD |
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 | ||
793bd4d9 UD |
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") }, | |
9f2da732 UD |
125 | { "generated", 'g', N_("CHAR"), 0, |
126 | N_("Generated line not part of iteration") }, | |
793bd4d9 UD |
127 | { NULL, 0, NULL, 0, NULL } |
128 | }; | |
129 | ||
130 | /* Short description of program. */ | |
9ee76b5a | 131 | static const char doc[] = N_("Create simple database from textual input."); |
793bd4d9 UD |
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. */ | |
cbc85992 | 138 | static error_t parse_opt (int key, char *arg, struct argp_state *state); |
793bd4d9 UD |
139 | |
140 | /* Function to print some extra text in the help message. */ | |
cbc85992 | 141 | static char *more_help (int key, const char *text, void *input); |
793bd4d9 UD |
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 | ||
9f2da732 UD |
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 | ||
793bd4d9 | 158 | /* Prototypes for local functions. */ |
9ee76b5a | 159 | static int process_input (FILE *input, const char *inname, |
cbc85992 | 160 | int to_lowercase, int be_quiet); |
9ee76b5a UD |
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. */ | |
6ff444c4 | 177 | #include <programs/xmalloc.h> |
793bd4d9 UD |
178 | |
179 | ||
180 | int | |
cbc85992 | 181 | main (int argc, char *argv[]) |
793bd4d9 UD |
182 | { |
183 | const char *input_name; | |
184 | FILE *input_file; | |
793bd4d9 | 185 | int remaining; |
2666d441 | 186 | int mode = 0644; |
793bd4d9 UD |
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 | { | |
9ee76b5a UD |
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); | |
793bd4d9 | 229 | |
9ee76b5a | 230 | int status = print_database (fd); |
793bd4d9 | 231 | |
9ee76b5a | 232 | close (fd); |
793bd4d9 UD |
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 | { | |
9ee76b5a | 242 | struct stat64 st; |
793bd4d9 | 243 | |
9ee76b5a | 244 | input_file = fopen64 (input_name, "r"); |
793bd4d9 UD |
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. */ | |
9ee76b5a | 251 | if (fstat64 (fileno (input_file), &st) >= 0) |
793bd4d9 UD |
252 | mode = st.st_mode & ACCESSPERMS; |
253 | } | |
254 | ||
793bd4d9 | 255 | /* Start the real work. */ |
9ee76b5a | 256 | int status = process_input (input_file, input_name, to_lowercase, be_quiet); |
793bd4d9 UD |
257 | |
258 | /* Close files. */ | |
259 | if (input_file != stdin) | |
260 | fclose (input_file); | |
9ee76b5a UD |
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) | |
2666d441 UD |
268 | { |
269 | if (be_quiet) | |
270 | return EXIT_SUCCESS; | |
271 | else | |
272 | error (EXIT_SUCCESS, 0, gettext ("no entries to be processed")); | |
273 | } | |
9ee76b5a UD |
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")); | |
9ee76b5a UD |
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); | |
793bd4d9 UD |
323 | |
324 | return status; | |
325 | } | |
326 | ||
327 | ||
328 | /* Handle program arguments. */ | |
329 | static error_t | |
330 | parse_opt (int key, char *arg, struct argp_state *state) | |
331 | { | |
9f2da732 UD |
332 | struct db_option *newp; |
333 | ||
793bd4d9 UD |
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; | |
9f2da732 UD |
348 | case 'g': |
349 | newp = xmalloc (sizeof (*newp)); | |
350 | newp->dbid = arg[0]; | |
351 | newp->next = db_options; | |
352 | db_options = newp; | |
353 | break; | |
793bd4d9 UD |
354 | default: |
355 | return ARGP_ERR_UNKNOWN; | |
356 | } | |
357 | return 0; | |
358 | } | |
359 | ||
360 | ||
361 | static char * | |
362 | more_help (int key, const char *text, void *input) | |
363 | { | |
8b748aed | 364 | char *tp = NULL; |
793bd4d9 UD |
365 | switch (key) |
366 | { | |
367 | case ARGP_KEY_HELP_EXTRA: | |
368 | /* We print some extra information. */ | |
8b748aed | 369 | if (asprintf (&tp, gettext ("\ |
d40eb37a | 370 | For bug reporting instructions, please see:\n\ |
8b748aed JM |
371 | %s.\n"), REPORT_BUGS_TO) < 0) |
372 | return NULL; | |
373 | return tp; | |
793bd4d9 UD |
374 | default: |
375 | break; | |
376 | } | |
377 | return (char *) text; | |
378 | } | |
379 | ||
380 | /* Print the version information. */ | |
381 | static void | |
382 | print_version (FILE *stream, struct argp_state *state) | |
383 | { | |
8b748aed | 384 | fprintf (stream, "makedb %s%s\n", PKGVERSION, VERSION); |
793bd4d9 UD |
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\ | |
58b587c1 | 389 | "), "2017"); |
793bd4d9 UD |
390 | fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); |
391 | } | |
392 | ||
393 | ||
394 | static int | |
9ee76b5a UD |
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 | ||
2666d441 | 403 | return strcmp (d1->str, d2->str); |
9ee76b5a UD |
404 | } |
405 | ||
406 | ||
407 | static int | |
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 | |
9dd346ff | 418 | process_input (FILE *input, const char *inname, int to_lowercase, int be_quiet) |
793bd4d9 UD |
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 | ||
9ee76b5a | 430 | struct database *last_database = NULL; |
793bd4d9 | 431 | |
9ee76b5a UD |
432 | while (!feof_unlocked (input)) |
433 | { | |
434 | ssize_t n = getline (&line, &linelen, input); | |
793bd4d9 UD |
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 | ||
9ee76b5a | 448 | char *cp = line; |
793bd4d9 UD |
449 | while (isspace (*cp)) |
450 | ++cp; | |
451 | ||
9ee76b5a UD |
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. */ | |
793bd4d9 UD |
455 | continue; |
456 | ||
9ee76b5a UD |
457 | /* Skip over the character indicating the database so that it is not |
458 | affected by TO_LOWERCASE. */ | |
459 | char *key = cp++; | |
793bd4d9 UD |
460 | while (*cp != '\0' && !isspace (*cp)) |
461 | { | |
462 | if (to_lowercase) | |
463 | *cp = tolower (*cp); | |
464 | ++cp; | |
465 | } | |
466 | ||
9ee76b5a UD |
467 | if (*cp == '\0') |
468 | /* It's a line without a value field. */ | |
793bd4d9 UD |
469 | continue; |
470 | ||
9ee76b5a UD |
471 | *cp++ = '\0'; |
472 | size_t keylen = cp - key; | |
793bd4d9 UD |
473 | |
474 | while (isspace (*cp)) | |
475 | ++cp; | |
476 | ||
9ee76b5a UD |
477 | char *data = cp; |
478 | size_t datalen = (&line[n] - cp) + 1; | |
793bd4d9 | 479 | |
9ee76b5a UD |
480 | /* Find the database. */ |
481 | if (last_database == NULL || last_database->dbid != key[0]) | |
793bd4d9 | 482 | { |
9ee76b5a UD |
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) | |
793bd4d9 | 488 | { |
9ee76b5a UD |
489 | last_database = xmalloc (sizeof (*last_database)); |
490 | last_database->dbid = key[0]; | |
9f2da732 | 491 | last_database->extra_string = false; |
9ee76b5a UD |
492 | last_database->next = databases; |
493 | last_database->entries = NULL; | |
494 | last_database->nentries = 0; | |
495 | last_database->keystrlen = 0; | |
496 | databases = last_database; | |
9f2da732 UD |
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; | |
793bd4d9 | 507 | } |
9ee76b5a | 508 | } |
793bd4d9 | 509 | |
9ee76b5a UD |
510 | /* Skip the database selector. */ |
511 | ++key; | |
512 | --keylen; | |
513 | ||
9ee76b5a UD |
514 | /* Store the data. */ |
515 | struct valstrentry *nentry = xmalloc (sizeof (struct valstrentry) | |
516 | + datalen); | |
9f2da732 UD |
517 | if (last_database->extra_string) |
518 | nentry->idx = extrastrlen; | |
519 | else | |
520 | nentry->idx = valstrlen; | |
521 | nentry->extra_string = last_database->extra_string; | |
9ee76b5a | 522 | memcpy (nentry->str, data, datalen); |
793bd4d9 | 523 | |
9ee76b5a UD |
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 | |
9f2da732 UD |
536 | if (last_database->extra_string) |
537 | extrastrlen += datalen; | |
538 | else | |
539 | valstrlen += datalen; | |
9ee76b5a UD |
540 | |
541 | /* Store the key. */ | |
2666d441 UD |
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); | |
9ee76b5a UD |
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; | |
793bd4d9 UD |
564 | } |
565 | ||
9ee76b5a | 566 | if (ferror_unlocked (input)) |
793bd4d9 UD |
567 | { |
568 | error (0, 0, gettext ("problems while reading `%s'"), inname); | |
569 | status = EXIT_FAILURE; | |
570 | } | |
571 | ||
572 | return status; | |
573 | } | |
574 | ||
575 | ||
9ee76b5a UD |
576 | static void |
577 | copy_valstr (const void *nodep, const VISIT which, const int depth) | |
793bd4d9 | 578 | { |
9ee76b5a UD |
579 | if (which != leaf && which != postorder) |
580 | return; | |
581 | ||
582 | const struct valstrentry *p = *(const struct valstrentry **) nodep; | |
583 | ||
9f2da732 | 584 | strcpy (valstrtab + (p->extra_string ? valstrlen : 0) + p->idx, p->str); |
9ee76b5a UD |
585 | } |
586 | ||
587 | ||
0817d63d SP |
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. */ | |
a9e836b0 UD |
590 | static int |
591 | is_prime (size_t candidate) | |
592 | { | |
a9e836b0 UD |
593 | size_t divn = 3; |
594 | size_t sq = divn * divn; | |
595 | ||
0817d63d SP |
596 | assert (candidate > 4 && candidate % 2 != 0); |
597 | ||
a9e836b0 UD |
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 | |
610 | next_prime (size_t seed) | |
611 | { | |
0817d63d SP |
612 | /* Make sure that we're always greater than 4. */ |
613 | seed = (seed + 4) | 1; | |
a9e836b0 UD |
614 | |
615 | while (!is_prime (seed)) | |
616 | seed += 2; | |
617 | ||
618 | return seed; | |
619 | } | |
620 | ||
621 | ||
9ee76b5a UD |
622 | static void |
623 | compute_tables (void) | |
624 | { | |
9f2da732 UD |
625 | valstrtab = xmalloc (roundup (valstrlen + extrastrlen, sizeof (stridx_t))); |
626 | while ((valstrlen + extrastrlen) % sizeof (stridx_t) != 0) | |
9ee76b5a UD |
627 | valstrtab[valstrlen++] = '\0'; |
628 | twalk (valstrtree, copy_valstr); | |
629 | ||
8de79a24 AS |
630 | static struct database *db; |
631 | for (db = databases; db != NULL; db = db->next) | |
9ee76b5a UD |
632 | if (db->nentries != 0) |
633 | { | |
634 | ++ndatabases; | |
635 | ||
9ee76b5a UD |
636 | /* We simply use an odd number large than twice the number of |
637 | elements to store in the hash table for the size. This gives | |
638 | enough efficiency. */ | |
a9e836b0 | 639 | #define TEST_RANGE 30 |
9f2da732 UD |
640 | size_t nhashentries_min = next_prime (db->nentries < TEST_RANGE |
641 | ? db->nentries | |
642 | : db->nentries * 2 - TEST_RANGE); | |
a9e836b0 UD |
643 | size_t nhashentries_max = MAX (nhashentries_min, db->nentries * 4); |
644 | size_t nhashentries_best = nhashentries_min; | |
645 | size_t chainlength_best = db->nentries; | |
646 | ||
647 | db->hashtable = xmalloc (2 * nhashentries_max * sizeof (stridx_t) | |
648 | + db->keystrlen); | |
649 | db->keyidxtab = db->hashtable + nhashentries_max; | |
650 | db->keystrtab = (char *) (db->keyidxtab + nhashentries_max); | |
651 | ||
8de79a24 AS |
652 | static size_t max_chainlength; |
653 | static char *wp; | |
654 | static size_t nhashentries; | |
655 | static bool copy_string; | |
9ee76b5a UD |
656 | |
657 | void add_key(const void *nodep, const VISIT which, const int depth) | |
658 | { | |
659 | if (which != leaf && which != postorder) | |
660 | return; | |
661 | ||
662 | const struct dbentry *dbe = *(const struct dbentry **) nodep; | |
663 | ||
a9e836b0 UD |
664 | ptrdiff_t stridx; |
665 | if (copy_string) | |
666 | { | |
667 | stridx = wp - db->keystrtab; | |
668 | wp = stpcpy (wp, dbe->str) + 1; | |
669 | } | |
670 | else | |
671 | stridx = 0; | |
9ee76b5a | 672 | |
a9e836b0 UD |
673 | size_t hidx = dbe->hashval % nhashentries; |
674 | size_t hval2 = 1 + dbe->hashval % (nhashentries - 2); | |
9ee76b5a UD |
675 | size_t chainlength = 0; |
676 | ||
2666d441 | 677 | while (db->hashtable[hidx] != ~((stridx_t) 0)) |
9ee76b5a UD |
678 | { |
679 | ++chainlength; | |
a9e836b0 UD |
680 | if ((hidx += hval2) >= nhashentries) |
681 | hidx -= nhashentries; | |
9ee76b5a UD |
682 | } |
683 | ||
9f2da732 UD |
684 | db->hashtable[hidx] = ((db->extra_string ? valstrlen : 0) |
685 | + dbe->validx); | |
2666d441 | 686 | db->keyidxtab[hidx] = stridx; |
9ee76b5a UD |
687 | |
688 | max_chainlength = MAX (max_chainlength, chainlength); | |
689 | } | |
793bd4d9 | 690 | |
8de79a24 | 691 | copy_string = false; |
a9e836b0 UD |
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 (db->entries, add_key); | |
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; | |
9ee76b5a | 726 | |
a9e836b0 | 727 | twalk (db->entries, add_key); |
9ee76b5a | 728 | |
a9e836b0 UD |
729 | db->nhashentries = nhashentries_best; |
730 | nhashentries_total += nhashentries_best; | |
9ee76b5a UD |
731 | } |
732 | } | |
733 | ||
734 | ||
735 | static int | |
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; | |
2666d441 | 749 | struct iovec iov[2 + ndatabases * 3]; |
9ee76b5a UD |
750 | iov[0].iov_base = header; |
751 | iov[0].iov_len = file_offset; | |
752 | ||
753 | iov[1].iov_base = valstrtab; | |
9f2da732 UD |
754 | iov[1].iov_len = valstrlen + extrastrlen; |
755 | file_offset += iov[1].iov_len; | |
9ee76b5a | 756 | |
a9e836b0 | 757 | size_t keydataoffset = file_offset + nhashentries_total * sizeof (stridx_t); |
9ee76b5a UD |
758 | for (struct database *db = databases; db != NULL; db = db->next) |
759 | if (db->entries != NULL) | |
760 | { | |
761 | assert (file_offset % sizeof (stridx_t) == 0); | |
762 | assert (filled_dbs < ndatabases); | |
763 | ||
764 | header->dbs[filled_dbs].id = db->dbid; | |
9ee76b5a UD |
765 | memset (header->dbs[filled_dbs].pad, '\0', |
766 | sizeof (header->dbs[0].pad)); | |
767 | header->dbs[filled_dbs].hashsize = db->nhashentries; | |
768 | ||
2666d441 | 769 | iov[2 + filled_dbs].iov_base = db->hashtable; |
a9e836b0 | 770 | iov[2 + filled_dbs].iov_len = db->nhashentries * sizeof (stridx_t); |
9ee76b5a | 771 | header->dbs[filled_dbs].hashoffset = file_offset; |
2666d441 | 772 | file_offset += iov[2 + filled_dbs].iov_len; |
9ee76b5a | 773 | |
2666d441 UD |
774 | iov[2 + ndatabases + filled_dbs * 2].iov_base = db->keyidxtab; |
775 | iov[2 + ndatabases + filled_dbs * 2].iov_len | |
a9e836b0 | 776 | = db->nhashentries * sizeof (stridx_t); |
2666d441 UD |
777 | header->dbs[filled_dbs].keyidxoffset = keydataoffset; |
778 | keydataoffset += iov[2 + ndatabases + filled_dbs * 2].iov_len; | |
779 | ||
780 | iov[3 + ndatabases + filled_dbs * 2].iov_base = db->keystrtab; | |
781 | iov[3 + ndatabases + filled_dbs * 2].iov_len = db->keystrlen; | |
782 | header->dbs[filled_dbs].keystroffset = keydataoffset; | |
783 | keydataoffset += iov[3 + ndatabases + filled_dbs * 2].iov_len; | |
9ee76b5a UD |
784 | |
785 | ++filled_dbs; | |
786 | } | |
787 | ||
788 | assert (filled_dbs == ndatabases); | |
2666d441 | 789 | assert (file_offset == (iov[0].iov_len + iov[1].iov_len |
a9e836b0 | 790 | + nhashentries_total * sizeof (stridx_t))); |
2666d441 | 791 | header->allocate = file_offset; |
9ee76b5a | 792 | |
2666d441 | 793 | if (writev (fd, iov, 2 + ndatabases * 3) != keydataoffset) |
793bd4d9 | 794 | { |
9ee76b5a | 795 | error (0, errno, gettext ("failed to write new database file")); |
793bd4d9 UD |
796 | return EXIT_FAILURE; |
797 | } | |
798 | ||
9ee76b5a UD |
799 | return EXIT_SUCCESS; |
800 | } | |
801 | ||
802 | ||
803 | static int | |
804 | print_database (int fd) | |
805 | { | |
806 | struct stat64 st; | |
807 | if (fstat64 (fd, &st) != 0) | |
808 | error (EXIT_FAILURE, errno, gettext ("cannot stat database file")); | |
809 | ||
810 | const struct nss_db_header *header = mmap (NULL, st.st_size, PROT_READ, | |
811 | MAP_PRIVATE|MAP_POPULATE, fd, 0); | |
812 | if (header == MAP_FAILED) | |
813 | error (EXIT_FAILURE, errno, gettext ("cannot map database file")); | |
814 | ||
815 | if (header->magic != NSS_DB_MAGIC) | |
816 | error (EXIT_FAILURE, 0, gettext ("file not a database file")); | |
817 | ||
818 | const char *valstrtab = (const char *) header + header->valstroffset; | |
819 | ||
820 | for (unsigned int dbidx = 0; dbidx < header->ndbs; ++dbidx) | |
793bd4d9 | 821 | { |
2666d441 UD |
822 | const stridx_t *stridxtab |
823 | = ((const stridx_t *) ((const char *) header | |
824 | + header->dbs[dbidx].keyidxoffset)); | |
9ee76b5a | 825 | const char *keystrtab |
2666d441 UD |
826 | = (const char *) header + header->dbs[dbidx].keystroffset; |
827 | const stridx_t *hashtab | |
828 | = (const stridx_t *) ((const char *) header | |
829 | + header->dbs[dbidx].hashoffset); | |
830 | ||
831 | for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx) | |
832 | if (hashtab[hidx] != ~((stridx_t) 0)) | |
833 | printf ("%c%s %s\n", | |
834 | header->dbs[dbidx].id, | |
835 | keystrtab + stridxtab[hidx], | |
836 | valstrtab + hashtab[hidx]); | |
793bd4d9 UD |
837 | } |
838 | ||
9ee76b5a UD |
839 | return EXIT_SUCCESS; |
840 | } | |
841 | ||
842 | ||
843 | #ifdef HAVE_SELINUX | |
844 | static void | |
845 | set_file_creation_context (const char *outname, mode_t mode) | |
846 | { | |
847 | static int enabled; | |
848 | static int enforcing; | |
849 | security_context_t ctx; | |
850 | ||
851 | /* Check if SELinux is enabled, and remember. */ | |
852 | if (enabled == 0) | |
3d7ba52b | 853 | enabled = is_selinux_enabled () ? 1 : -1; |
9ee76b5a UD |
854 | if (enabled < 0) |
855 | return; | |
856 | ||
857 | /* Check if SELinux is enforcing, and remember. */ | |
858 | if (enforcing == 0) | |
859 | enforcing = security_getenforce () ? 1 : -1; | |
860 | ||
861 | /* Determine the context which the file should have. */ | |
862 | ctx = NULL; | |
863 | if (matchpathcon (outname, S_IFREG | mode, &ctx) == 0 && ctx != NULL) | |
793bd4d9 | 864 | { |
9ee76b5a UD |
865 | if (setfscreatecon (ctx) != 0) |
866 | error (enforcing > 0 ? EXIT_FAILURE : 0, 0, | |
867 | gettext ("cannot set file creation context for `%s'"), | |
868 | outname); | |
869 | ||
870 | freecon (ctx); | |
793bd4d9 | 871 | } |
9ee76b5a | 872 | } |
793bd4d9 | 873 | |
9ee76b5a UD |
874 | static void |
875 | reset_file_creation_context (void) | |
876 | { | |
877 | setfscreatecon (NULL); | |
793bd4d9 | 878 | } |
9ee76b5a | 879 | #endif |