mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-21 01:12:26 +08:00
Rewrite makedb to avoid using db library
This commit is contained in:
parent
c71ca1f89c
commit
9ee76b5ae8
@ -1,3 +1,8 @@
|
||||
2011-06-02 Ulrich Drepper <drepper@gmail.com>
|
||||
|
||||
* nss/makedb.c: Rewritten to not use database library.
|
||||
* nss/Makefile: Update to build new makedb program.
|
||||
|
||||
2011-06-14 Andreas Jaeger <aj@suse.de>
|
||||
|
||||
* sysdeps/unix/sysv/linux/check_native.c: Include <string.h> for
|
||||
|
14
nss/Makefile
14
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
|
||||
|
688
nss/makedb.c
688
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 <drepper@cygnus.com>, 1996.
|
||||
|
||||
@ -19,25 +19,115 @@
|
||||
02111-1307 USA. */
|
||||
|
||||
#include <argp.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#include <search.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#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 <selinux/selinux.h>
|
||||
#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;
|
||||
|
||||
clearerr (input);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
|
||||
status = db->cursor (db->db, NULL, &cursor);
|
||||
if (status != 0)
|
||||
{
|
||||
error (0, status, gettext ("while reading database"));
|
||||
return EXIT_FAILURE;
|
||||
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);
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
key.flags = 0;
|
||||
val.flags = 0;
|
||||
status = cursor->c_get (cursor->cursor, &key, &val, db_first);
|
||||
while (status == 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)
|
||||
{
|
||||
printf ("%.*s %s\n", (int) key.size, (char *) key.data,
|
||||
(char *) val.data);
|
||||
|
||||
status = cursor->c_get (cursor->cursor, &key, &val, db_next);
|
||||
}
|
||||
|
||||
if (status != db_notfound)
|
||||
{
|
||||
error (0, status, gettext ("while reading database"));
|
||||
error (0, errno, gettext ("failed to write new database file"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (setfscreatecon (ctx) != 0)
|
||||
error (enforcing > 0 ? EXIT_FAILURE : 0, 0,
|
||||
gettext ("cannot set file creation context for `%s'"),
|
||||
outname);
|
||||
|
||||
freecon (ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_file_creation_context (void)
|
||||
{
|
||||
setfscreatecon (NULL);
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user