From 9ee76b5ae861ff9891e5586fc6906c94c447a9e0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 2 Jun 2011 14:43:16 -0400 Subject: Rewrite makedb to avoid using db library --- nss/Makefile | 14 +- nss/makedb.c | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 595 insertions(+), 95 deletions(-) (limited to 'nss') diff --git a/nss/Makefile b/nss/Makefile index c49f375..16578a3 100644 --- a/nss/Makefile +++ b/nss/Makefile @@ -37,8 +37,10 @@ routines = nsswitch getnssent getnssent_r digits_dots \ databases = proto service hosts network grp pwd rpc ethers \ spwd netgrp key alias sgrp -others := getent -install-bin := getent +others := getent makedb +install-bin := getent makedb +makedb-modules = xmalloc hash-string +extra-objs += $(makedb-modules:=.o) tests = test-netdb tst-nss-test1 xtests = bug-erange @@ -60,7 +62,7 @@ extra-libs-others = $(extra-libs) # The sources are found in the appropriate subdir. subdir-dirs = $(services:%=nss_%) -vpath %.c $(subdir-dirs) +vpath %.c $(subdir-dirs) ../locale/programs ../intl libnss_files-routines := $(addprefix files-,$(databases)) \ @@ -80,6 +82,10 @@ ifeq (yes,$(build-static-nss)) $(objpfx)getent: $(objpfx)libnss_files.a endif +ifeq (yes,$(have-selinux)) +LDLIBS-makedb := -lselinux +endif + # Depend on libc.so so a DT_NEEDED is generated in the shared objects. # This ensures they will load libc.so for needed symbols if loaded by # a statically-linked program that hasn't already loaded it. @@ -88,6 +94,8 @@ $(services:%=$(objpfx)libnss_%.so): $(libnss-libc) \ $(common-objpfx)libc_nonshared.a +$(objpfx)makedb: $(makedb-modules:%=$(objpfx)%.o) + distribute += nss_test1.c CFLAGS-nss_test1.c = -DNOT_IN_libc=1 diff --git a/nss/makedb.c b/nss/makedb.c index 5390dc1..a01b235 100644 --- a/nss/makedb.c +++ b/nss/makedb.c @@ -1,5 +1,5 @@ /* Create simple DB database from textual input. - Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1996-2000, 2011 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1996. @@ -19,25 +19,115 @@ 02111-1307 USA. */ #include +#include #include -#include #include #include #include +#include #include #include +#include #include -#include #include #include +#include +#include #include #include "nss_db/dummy-db.h" /* Get libc version number. */ #include "../version.h" +/* The hashing function we use. */ +#include "../intl/hash-string.h" + +/* SELinux support. */ +#ifdef HAVE_SELINUX +# include +#endif + #define PACKAGE _libc_intl_domainname +/* String table index type. */ +typedef uint32_t stridx_t; + +/* Database file header. */ +struct nss_db_header +{ + uint32_t magic; +#define NSS_DB_MAGIC 0xdd110601 + uint32_t ndbs; + uint64_t valstroffset; + uint64_t valstrlen; + struct + { + char id; + enum nss_db_type { nss_db_type_hash = 0, + nss_db_type_iterate, + nss_db_type_int4 } type:8; + char pad[sizeof (uint32_t) - 2]; + uint32_t hashsize; + uint64_t hashoffset; + uint64_t stroffset; + } dbs[0]; +}; + +struct nss_db_entry +{ + stridx_t keyidx; + stridx_t dataidx; +}; + + +/* List of data bases. */ +struct database +{ + char dbid; + enum nss_db_type type; + struct database *next; + void *entries; + size_t nentries; + size_t keystrlen; + size_t nhashentries; + struct nss_db_entry *hashtable; + char *keystrtab; +} *databases; +static size_t ndatabases; +static size_t valstrlen; +static void *valstrtree; +static char *valstrtab; + +/* Database entry. */ +struct dbentry +{ + size_t keylen; + stridx_t validx; + uint32_t hashval; + char str[0]; +}; + +/* Stored string entry. */ +struct valstrentry +{ + stridx_t idx; + char str[0]; +}; + + +/* Database type specifiers. */ +struct dbtype +{ + char dbid; + enum nss_db_type type; + struct dbtype *next; +}; +static struct dbtype *dbtypes; + + +/* True if any entry has been added. */ +static bool any_dbentry; + /* If non-zero convert key to lower case. */ static int to_lowercase; @@ -63,11 +153,16 @@ static const struct argp_option options[] = N_("Do not print messages while building database") }, { "undo", 'u', NULL, 0, N_("Print content of database file, one entry a line") }, + { NULL, 0, NULL, 0, N_("Select index type") }, + { "iterate", 'I', "KEY", 0, + N_("Index identified by KEY used to iterate over database") }, + { "binary", 'B', "KEY", 0, + N_("Index identified by KEY has binary key value") }, { NULL, 0, NULL, 0, NULL } }; /* Short description of program. */ -static const char doc[] = N_("Create simple DB database from textual input."); +static const char doc[] = N_("Create simple database from textual input."); /* Strings for arguments in help texts. */ static const char args_doc[] = N_("\ @@ -87,9 +182,26 @@ static struct argp argp = /* Prototypes for local functions. */ -static int process_input (FILE *input, const char *inname, NSS_DB *output, +static int process_input (FILE *input, const char *inname, int to_lowercase, int be_quiet); -static int print_database (NSS_DB *db); +static int print_database (int fd); +static void compute_tables (void); +static int write_output (int fd); + +/* SELinux support. */ +#ifdef HAVE_SELINUX +/* Set the SELinux file creation context for the given file. */ +static void set_file_creation_context (const char *outname, mode_t mode); +static void reset_file_creation_context (void); +#else +# define set_file_creation_context(_outname,_mode) +# define reset_file_creation_context() +#endif + + +/* External functions. */ +extern void *xmalloc (size_t n) __attribute_malloc__; +extern void *xcalloc (size_t n, size_t m) __attribute_malloc__; int @@ -97,8 +209,6 @@ main (int argc, char *argv[]) { const char *input_name; FILE *input_file; - NSS_DB *db_file; - int status; int remaining; int mode = 0666; @@ -136,24 +246,17 @@ main (int argc, char *argv[]) output_name = argv[remaining]; } - /* First load the shared object to initialize version dependend - variables. */ - if (load_db () != NSS_STATUS_SUCCESS) - error (EXIT_FAILURE, 0, gettext ("No usable database library found.")); - /* Special handling if we are asked to print the database. */ if (do_undo) { - dbopen (input_name, db_rdonly, 0666, &db_file); - if (db_file == NULL) - error (EXIT_FAILURE, 0, gettext ("cannot open database file `%s': %s"), - input_name, - (errno == EINVAL ? gettext ("incorrectly formatted file") - : strerror (errno))); + int fd = open (input_name, O_RDONLY); + if (fd == -1) + error (EXIT_FAILURE, errno, gettext ("cannot open database file `%s'"), + input_name); - status = print_database (db_file); + int status = print_database (fd); - db_file->close (db_file->db, 0); + close (fd); return status; } @@ -163,34 +266,83 @@ main (int argc, char *argv[]) input_file = stdin; else { - struct stat st; + struct stat64 st; - input_file = fopen (input_name, "r"); + input_file = fopen64 (input_name, "r"); if (input_file == NULL) error (EXIT_FAILURE, errno, gettext ("cannot open input file `%s'"), input_name); /* Get the access rights from the source file. The output file should have the same. */ - if (fstat (fileno (input_file), &st) >= 0) + if (fstat64 (fileno (input_file), &st) >= 0) mode = st.st_mode & ACCESSPERMS; } - /* Open output file. This must not be standard output so we don't - handle "-" and "/dev/stdout" special. */ - dbopen (output_name, DB_CREATE | db_truncate, mode, &db_file); - if (db_file == NULL) - error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"), - output_name); - /* Start the real work. */ - status = process_input (input_file, input_name, db_file, to_lowercase, - be_quiet); + int status = process_input (input_file, input_name, to_lowercase, be_quiet); /* Close files. */ if (input_file != stdin) fclose (input_file); - db_file->close (db_file->db, 0); + + /* No need to continue when we did not read the file successfully. */ + if (status != EXIT_SUCCESS) + return status; + + /* Bail out if nothing is to be done. */ + if (!any_dbentry) + error (EXIT_SUCCESS, 0, gettext ("no entries to be processed")); + + /* Compute hash and string tables. */ + compute_tables (); + + /* Open output file. This must not be standard output so we don't + handle "-" and "/dev/stdout" special. */ + char *tmp_output_name; + if (asprintf (&tmp_output_name, "%s.XXXXXX", output_name) == -1) + error (EXIT_FAILURE, errno, gettext ("cannot create temporary file name")); + + set_file_creation_context (output_name, mode); + int fd = mkstemp (tmp_output_name); + reset_file_creation_context (); + if (fd == -1) + error (EXIT_FAILURE, errno, gettext ("cannot create temporary file")); + // XXX SELinux context + + status = write_output (fd); + + if (status == EXIT_SUCCESS) + { + struct stat64 st; + + if (fstat64 (fd, &st) == 0) + { + if ((st.st_mode & ACCESSPERMS) != mode) + /* We ignore problems with changing the mode. */ + fchmod (fd, mode); + } + else + { + error (0, errno, gettext ("cannot stat newly created file")); + status = EXIT_FAILURE; + } + } + + close (fd); + + if (status == EXIT_SUCCESS) + { + if (rename (tmp_output_name, output_name) != 0) + { + error (0, errno, gettext ("cannot rename temporary file")); + status = EXIT_FAILURE; + goto do_unlink; + } + } + else + do_unlink: + unlink (tmp_output_name); return status; } @@ -200,6 +352,7 @@ main (int argc, char *argv[]) static error_t parse_opt (int key, char *arg, struct argp_state *state) { + struct dbtype *newtype; switch (key) { case 'f': @@ -214,6 +367,17 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'u': do_undo = 1; break; + case 'I': + case 'B': + if (arg[0] == '\0' || arg[1] != '\0') + error (EXIT_FAILURE, 0, gettext ("\ +argument for option to specify database type must be a single-byte character")); + newtype = xmalloc (sizeof (struct dbtype)); + newtype->dbid = arg[0]; + newtype->type = key == 'I' ? nss_db_type_iterate : nss_db_type_int4; + newtype->next = dbtypes; + dbtypes = newtype; + break; default: return ARGP_ERR_UNKNOWN; } @@ -246,16 +410,44 @@ print_version (FILE *stream, struct argp_state *state) Copyright (C) %s Free Software Foundation, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2000"); +"), "2011"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } static int -process_input (input, inname, output, to_lowercase, be_quiet) +dbentry_compare (const void *p1, const void *p2) +{ + const struct dbentry *d1 = (const struct dbentry *) p1; + const struct dbentry *d2 = (const struct dbentry *) p2; + + if (d1->hashval != d2->hashval) + return d1->hashval < d2->hashval ? -1 : 1; + + if (d1->keylen < d2->keylen) + return -1; + + if (d1->keylen > d2->keylen) + return 1; + + return memcmp (d1->str, d2->str, d1->keylen); +} + + +static int +valstr_compare (const void *p1, const void *p2) +{ + const struct valstrentry *d1 = (const struct valstrentry *) p1; + const struct valstrentry *d2 = (const struct valstrentry *) p2; + + return strcmp (d1->str, d2->str); +} + + +static int +process_input (input, inname, to_lowercase, be_quiet) FILE *input; const char *inname; - NSS_DB *output; int to_lowercase; int be_quiet; { @@ -269,14 +461,11 @@ process_input (input, inname, output, to_lowercase, be_quiet) status = EXIT_SUCCESS; linenr = 0; - while (!feof (input)) - { - DBT key; - DBT val; - char *cp; - int n; + struct database *last_database = NULL; - n = getline (&line, &linelen, input); + while (!feof_unlocked (input)) + { + ssize_t n = getline (&line, &linelen, input); if (n < 0) /* This means end of file or some bug. */ break; @@ -290,15 +479,18 @@ process_input (input, inname, output, to_lowercase, be_quiet) /* Remove trailing newline. */ line[--n] = '\0'; - cp = line; + char *cp = line; while (isspace (*cp)) ++cp; - if (*cp == '#') - /* First non-space character in line '#': it's a comment. */ + if (*cp == '#' || *cp == '\0') + /* First non-space character in line '#': it's a comment. + Also go to the next line if it is empty except for whitespaces. */ continue; - key.data = cp; + /* Skip over the character indicating the database so that it is not + affected by TO_LOWERCASE. */ + char *key = cp++; while (*cp != '\0' && !isspace (*cp)) { if (to_lowercase) @@ -306,44 +498,131 @@ process_input (input, inname, output, to_lowercase, be_quiet) ++cp; } - if (key.data == cp) - /* It's an empty line. */ + if (*cp == '\0') + /* It's a line without a value field. */ continue; - key.size = cp - (char *) key.data; - key.flags = 0; + *cp++ = '\0'; + size_t keylen = cp - key; while (isspace (*cp)) ++cp; - val.data = cp; - val.size = (&line[n] - cp) + 1; - val.flags = 0; + char *data = cp; + size_t datalen = (&line[n] - cp) + 1; - /* Store the value. */ - status = output->put (output->db, NULL, &key, &val, db_nooverwrite); - if (status != 0) + /* Find the database. */ + if (last_database == NULL || last_database->dbid != key[0]) { - if (status == db_keyexist) + last_database = databases; + while (last_database != NULL && last_database->dbid != key[0]) + last_database = last_database->next; + + if (last_database == NULL) { - if (!be_quiet) - error_at_line (0, 0, inname, linenr, - gettext ("duplicate key")); - /* This is no real error. Just give a warning. */ - status = 0; - continue; + last_database = xmalloc (sizeof (*last_database)); + last_database->dbid = key[0]; + last_database->type = nss_db_type_hash; /* Default. */ + last_database->next = databases; + last_database->entries = NULL; + last_database->nentries = 0; + last_database->keystrlen = 0; + databases = last_database; + + struct dbtype *typeit = dbtypes; + while (typeit != NULL) + if (typeit->dbid == last_database->dbid) + { + last_database->type = typeit->type; + break; + } + else + typeit = typeit->next; } - else - error (0, status, gettext ("while writing database file")); + } - status = EXIT_FAILURE; + /* Skip the database selector. */ + ++key; + --keylen; + + /* Check the key value if it has to be numeric. */ + unsigned long int keyvalue = 0; + if (last_database->type != nss_db_type_hash) + { + char *endp; + errno = 0; + keyvalue = strtoul (key, &endp, 0); + if ((keyvalue == ULONG_MAX && errno == ERANGE) + || keyvalue > ~((stridx_t) 0)) + error (EXIT_FAILURE, 0, + gettext ("index value in line %zu too large"), linenr); + + if (*endp != '\0') + error (EXIT_FAILURE, 0, + gettext ("index value in line %zu is not a number"), + linenr); + } + + /* Store the data. */ + struct valstrentry *nentry = xmalloc (sizeof (struct valstrentry) + + datalen); + nentry->idx = valstrlen; + memcpy (nentry->str, data, datalen); - clearerr (input); - break; + struct valstrentry **fdata = tsearch (nentry, &valstrtree, + valstr_compare); + if (fdata == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create search tree")); + + if (*fdata != nentry) + { + /* We can reuse a string. */ + free (nentry); + nentry = *fdata; + } + else + valstrlen += datalen; + + /* Store the key. */ + struct dbentry *newp; + if (last_database->type == nss_db_type_hash) + { + newp = xmalloc (sizeof (struct dbentry) + keylen); + newp->keylen = keylen; + newp->validx = nentry->idx; + newp->hashval = __hash_string (key); + memcpy (newp->str, key, keylen); } + else + { + newp = xmalloc (sizeof (struct dbentry) + sizeof (stridx_t)); + newp->keylen = keylen = sizeof (stridx_t); + newp->validx = nentry->idx; + newp->hashval = keyvalue; + stridx_t value = keyvalue; + memcpy (newp->str, &value, sizeof (stridx_t)); + } + + struct dbentry **found = tsearch (newp, &last_database->entries, + dbentry_compare); + if (found == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot create search tree")); + + if (*found != newp) + { + free (newp); + if (!be_quiet) + error_at_line (0, 0, inname, linenr, gettext ("duplicate key")); + continue; + } + + ++last_database->nentries; + last_database->keystrlen += keylen; + + any_dbentry = true; } - if (ferror (input)) + if (ferror_unlocked (input)) { error (0, 0, gettext ("problems while reading `%s'"), inname); status = EXIT_FAILURE; @@ -353,38 +632,251 @@ process_input (input, inname, output, to_lowercase, be_quiet) } -static int -print_database (db) - NSS_DB *db; +static void +copy_valstr (const void *nodep, const VISIT which, const int depth) { - DBT key; - DBT val; - NSS_DBC *cursor; - int status; + if (which != leaf && which != postorder) + return; + + const struct valstrentry *p = *(const struct valstrentry **) nodep; + + strcpy (valstrtab + p->idx, p->str); +} + + +static void +compute_tables (void) +{ + valstrtab = xmalloc (roundup (valstrlen, sizeof (stridx_t))); + while (valstrlen % sizeof (stridx_t) != 0) + valstrtab[valstrlen++] = '\0'; + twalk (valstrtree, copy_valstr); + + for (struct database *db = databases; db != NULL; db = db->next) + if (db->nentries != 0) + { + ++ndatabases; + + if (db->type == nss_db_type_iterate) + { + /* We need no hash table and no key value table in this case. */ + db->nhashentries = 0; + db->hashtable = NULL; + db->keystrtab = NULL; + db->keystrlen = 0; + continue; + } + + if (db->keystrlen > ~((stridx_t) 0)) + error (EXIT_FAILURE, 0, gettext ("\ +table size too large; recompile with larger stridx_t")); + + /* We simply use an odd number large than twice the number of + elements to store in the hash table for the size. This gives + enough efficiency. */ + db->nhashentries = db->nentries * 2 + 1; + db->hashtable = xmalloc (db->nhashentries + * sizeof (struct nss_db_entry)); + memset (db->hashtable, '\xff', + db->nhashentries * sizeof (struct nss_db_entry)); + db->keystrlen = roundup (db->keystrlen, sizeof (stridx_t)); + db->keystrtab = xmalloc (db->keystrlen); + + size_t max_chainlength = 0; + char *wp = db->keystrtab; + + void add_key(const void *nodep, const VISIT which, const int depth) + { + if (which != leaf && which != postorder) + return; + + const struct dbentry *dbe = *(const struct dbentry **) nodep; + + ptrdiff_t stridx = wp - db->keystrtab; + wp = mempcpy (wp, dbe->str, dbe->keylen); + + size_t hidx = dbe->hashval % db->nhashentries; + size_t hval2 = 1 + dbe->hashval % (db->nhashentries - 2); + size_t chainlength = 0; + + while (db->hashtable[hidx].keyidx != ~((stridx_t) 0)) + { + ++chainlength; + if ((hidx += hval2) >= db->nhashentries) + hidx -= db->nhashentries; + } + + db->hashtable[hidx].keyidx = stridx; + db->hashtable[hidx].dataidx = dbe->validx; + + max_chainlength = MAX (max_chainlength, chainlength); + } - status = db->cursor (db->db, NULL, &cursor); - if (status != 0) + twalk (db->entries, add_key); + + // XXX if hash length is too long resize table and start again + + while ((wp - db->keystrtab) % sizeof (stridx_t) != 0) + *wp++ = '\0'; + } +} + + +static int +write_output (int fd) +{ + struct nss_db_header *header; + uint64_t file_offset = (sizeof (struct nss_db_header) + + (ndatabases * sizeof (header->dbs[0]))); + header = alloca (file_offset); + + header->magic = NSS_DB_MAGIC; + header->ndbs = ndatabases; + header->valstroffset = file_offset; + header->valstrlen = valstrlen; + + size_t filled_dbs = 0; + struct iovec iov[2 + 2 * ndatabases]; + iov[0].iov_base = header; + iov[0].iov_len = file_offset; + + iov[1].iov_base = valstrtab; + iov[1].iov_len = valstrlen; + file_offset += valstrlen; + + for (struct database *db = databases; db != NULL; db = db->next) + if (db->entries != NULL) + { + assert (file_offset % sizeof (stridx_t) == 0); + assert (filled_dbs < ndatabases); + + header->dbs[filled_dbs].id = db->dbid; + header->dbs[filled_dbs].type = db->type; + memset (header->dbs[filled_dbs].pad, '\0', + sizeof (header->dbs[0].pad)); + header->dbs[filled_dbs].hashsize = db->nhashentries; + + iov[2 + filled_dbs * 2].iov_base = db->hashtable; + iov[2 + filled_dbs * 2].iov_len = (db->nhashentries + * sizeof (struct nss_db_entry)); + header->dbs[filled_dbs].hashoffset = file_offset; + file_offset += iov[2 + filled_dbs * 2].iov_len; + + iov[3 + filled_dbs * 2].iov_base = db->keystrtab; + iov[3 + filled_dbs * 2].iov_len = db->keystrlen; + header->dbs[filled_dbs].stroffset = file_offset; + file_offset += iov[3 + filled_dbs * 2].iov_len; + + ++filled_dbs; + } + + assert (filled_dbs == ndatabases); + + if (writev (fd, iov, 2 + 2 * ndatabases) != file_offset) { - error (0, status, gettext ("while reading database")); + error (0, errno, gettext ("failed to write new database file")); return EXIT_FAILURE; } - key.flags = 0; - val.flags = 0; - status = cursor->c_get (cursor->cursor, &key, &val, db_first); - while (status == 0) + return EXIT_SUCCESS; +} + + +static int +print_database (int fd) +{ + struct stat64 st; + if (fstat64 (fd, &st) != 0) + error (EXIT_FAILURE, errno, gettext ("cannot stat database file")); + + const struct nss_db_header *header = mmap (NULL, st.st_size, PROT_READ, + MAP_PRIVATE|MAP_POPULATE, fd, 0); + if (header == MAP_FAILED) + error (EXIT_FAILURE, errno, gettext ("cannot map database file")); + + if (header->magic != NSS_DB_MAGIC) + error (EXIT_FAILURE, 0, gettext ("file not a database file")); + + const char *valstrtab = (const char *) header + header->valstroffset; + + for (unsigned int dbidx = 0; dbidx < header->ndbs; ++dbidx) { - printf ("%.*s %s\n", (int) key.size, (char *) key.data, - (char *) val.data); + const char *keystrtab + = (const char *) header + header->dbs[dbidx].stroffset; + const struct nss_db_entry *hashtab + = (const struct nss_db_entry *) ((const char *) header + + header->dbs[dbidx].hashoffset); - status = cursor->c_get (cursor->cursor, &key, &val, db_next); + if (header->dbs[dbidx].type == nss_db_type_hash) + { + for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx) + if (hashtab[hidx].keyidx != ~((stridx_t) 0)) + printf ("%c%s %s\n", + header->dbs[dbidx].id, + keystrtab + hashtab[hidx].keyidx, + valstrtab + hashtab[hidx].dataidx); + } + else if (header->dbs[dbidx].type == nss_db_type_iterate) + { + const char *endvalstrtab = valstrtab + header->valstrlen; + const char *cp = valstrtab; + unsigned int count = 0; + while (cp < endvalstrtab && *cp != '\0') + { + printf ("%c%u %s\n", header->dbs[dbidx].id, count++, cp); + cp = rawmemchr (cp, '\0') + 1; + } + } + else + { + assert (header->dbs[dbidx].type == nss_db_type_int4); + for (uint32_t hidx = 0; hidx < header->dbs[dbidx].hashsize; ++hidx) + if (hashtab[hidx].keyidx != ~((stridx_t) 0)) + printf ("%c%" PRIu32 " %s\n", + header->dbs[dbidx].id, + *((uint32_t *) (keystrtab + hashtab[hidx].keyidx)), + valstrtab + hashtab[hidx].dataidx); + } } - if (status != db_notfound) + return EXIT_SUCCESS; +} + + +#ifdef HAVE_SELINUX +static void +set_file_creation_context (const char *outname, mode_t mode) +{ + static int enabled; + static int enforcing; + security_context_t ctx; + + /* Check if SELinux is enabled, and remember. */ + if (enabled == 0) + enabled = is_selinux_enabled (); + if (enabled < 0) + return; + + /* Check if SELinux is enforcing, and remember. */ + if (enforcing == 0) + enforcing = security_getenforce () ? 1 : -1; + + /* Determine the context which the file should have. */ + ctx = NULL; + if (matchpathcon (outname, S_IFREG | mode, &ctx) == 0 && ctx != NULL) { - error (0, status, gettext ("while reading database")); - return EXIT_FAILURE; + if (setfscreatecon (ctx) != 0) + error (enforcing > 0 ? EXIT_FAILURE : 0, 0, + gettext ("cannot set file creation context for `%s'"), + outname); + + freecon (ctx); } +} - return EXIT_SUCCESS; +static void +reset_file_creation_context (void) +{ + setfscreatecon (NULL); } +#endif -- cgit v1.1