makedb.c 23 KB

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