2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* #pragma ident "@(#)kdb_log.c 1.3 04/02/23 SMI" */
2N/A
2N/A#include <sys/stat.h>
2N/A#include <sys/types.h>
2N/A#include <unistd.h>
2N/A#include <fcntl.h>
2N/A#include <sys/mman.h>
2N/A#include <k5-int.h>
2N/A#include <stdlib.h>
2N/A#include <limits.h>
2N/A#include <syslog.h>
2N/A#include "kdb5.h"
2N/A#include "kdb_log.h"
2N/A#include "kdb5int.h"
2N/A
2N/A/*
2N/A * This modules includes all the necessary functions that create and
2N/A * modify the Kerberos principal update and header logs.
2N/A */
2N/A
2N/A#define getpagesize() sysconf(_SC_PAGESIZE)
2N/A
2N/Astatic int pagesize = 0;
2N/A
2N/A#define INIT_ULOG(ctx) \
2N/A log_ctx = ctx->kdblog_context; \
2N/A assert(log_ctx != NULL); \
2N/A ulog = log_ctx->ulog; \
2N/A assert(ulog != NULL)
2N/A
2N/A#if 0 /* Solaris Kerberos */
2N/A/* XXX */
2N/Atypedef unsigned long ulong_t;
2N/Atypedef unsigned int uint_t;
2N/A#endif
2N/A
2N/Astatic int extend_file_to(int fd, uint_t new_size);
2N/A
2N/Akrb5_error_code
2N/Aulog_lock(krb5_context ctx, int mode)
2N/A{
2N/A kdb_log_context *log_ctx = NULL;
2N/A kdb_hlog_t *ulog = NULL;
2N/A
2N/A if (ctx == NULL)
2N/A return KRB5_LOG_ERROR;
2N/A if (ctx->kdblog_context == NULL || ctx->kdblog_context->iproprole == IPROP_NULL)
2N/A return 0;
2N/A INIT_ULOG(ctx);
2N/A return krb5_lock_file(ctx, log_ctx->ulogfd, mode);
2N/A}
2N/A
2N/A/*
2N/A * Sync update entry to disk.
2N/A */
2N/Astatic krb5_error_code
2N/Aulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
2N/A{
2N/A ulong_t start, end, size;
2N/A krb5_error_code retval;
2N/A
2N/A if (ulog == NULL)
2N/A return (KRB5_LOG_ERROR);
2N/A
2N/A if (!pagesize)
2N/A pagesize = getpagesize();
2N/A
2N/A start = ((ulong_t)upd) & (~(pagesize-1));
2N/A
2N/A end = (((ulong_t)upd) + ulog->kdb_block +
2N/A (pagesize-1)) & (~(pagesize-1));
2N/A
2N/A size = end - start;
2N/A if ((retval = msync((caddr_t)start, size, MS_SYNC))) {
2N/A return (retval);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Sync memory to disk for the update log header.
2N/A */
2N/Astatic void
2N/Aulog_sync_header(kdb_hlog_t *ulog)
2N/A{
2N/A
2N/A if (!pagesize)
2N/A pagesize = getpagesize();
2N/A
2N/A if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
2N/A /*
2N/A * Couldn't sync to disk, let's panic
2N/A */
2N/A syslog(LOG_ERR, "ulog_sync_header: could not sync to disk");
2N/A abort();
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Resizes the array elements. We reinitialize the update log rather than
2N/A * unrolling the the log and copying it over to a temporary log for obvious
2N/A * performance reasons. Slaves will subsequently do a full resync, but
2N/A * the need for resizing should be very small.
2N/A */
2N/Astatic krb5_error_code
2N/Aulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize)
2N/A{
2N/A uint_t new_block, new_size;
2N/A
2N/A if (ulog == NULL)
2N/A return (KRB5_LOG_ERROR);
2N/A
2N/A new_size = sizeof (kdb_hlog_t);
2N/A
2N/A new_block = (recsize / ULOG_BLOCK) + 1;
2N/A new_block *= ULOG_BLOCK;
2N/A
2N/A new_size += ulogentries * new_block;
2N/A
2N/A if (new_size <= MAXLOGLEN) {
2N/A /*
2N/A * Reinit log with new block size
2N/A */
2N/A (void) memset(ulog, 0, sizeof (kdb_hlog_t));
2N/A
2N/A ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
2N/A ulog->db_version_num = KDB_VERSION;
2N/A ulog->kdb_state = KDB_STABLE;
2N/A ulog->kdb_block = new_block;
2N/A
2N/A ulog_sync_header(ulog);
2N/A
2N/A /*
2N/A * Time to expand log considering new block size
2N/A */
2N/A if (extend_file_to(ulogfd, new_size) < 0)
2N/A return errno;
2N/A } else {
2N/A /*
2N/A * Can't map into file larger than MAXLOGLEN
2N/A */
2N/A return (KRB5_LOG_ERROR);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Adds an entry to the update log.
2N/A * The layout of the update log looks like:
2N/A *
2N/A * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
2N/A */
2N/Akrb5_error_code
2N/Aulog_add_update(krb5_context context, kdb_incr_update_t *upd)
2N/A{
2N/A XDR xdrs;
2N/A kdbe_time_t ktime;
2N/A struct timeval timestamp;
2N/A kdb_ent_header_t *indx_log;
2N/A uint_t i, recsize;
2N/A ulong_t upd_size;
2N/A krb5_error_code retval;
2N/A kdb_sno_t cur_sno;
2N/A kdb_log_context *log_ctx;
2N/A kdb_hlog_t *ulog = NULL;
2N/A uint32_t ulogentries;
2N/A int ulogfd;
2N/A
2N/A INIT_ULOG(context);
2N/A ulogentries = log_ctx->ulogentries;
2N/A ulogfd = log_ctx->ulogfd;
2N/A
2N/A if (upd == NULL)
2N/A return (KRB5_LOG_ERROR);
2N/A
2N/A (void) gettimeofday(&timestamp, NULL);
2N/A ktime.seconds = timestamp.tv_sec;
2N/A ktime.useconds = timestamp.tv_usec;
2N/A
2N/A upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
2N/A
2N/A recsize = sizeof (kdb_ent_header_t) + upd_size;
2N/A
2N/A if (recsize > ulog->kdb_block) {
2N/A if ((retval = ulog_resize(ulog, ulogentries, ulogfd, recsize))) {
2N/A /* Resize element array failed */
2N/A return (retval);
2N/A }
2N/A }
2N/A
2N/A cur_sno = ulog->kdb_last_sno;
2N/A
2N/A /*
2N/A * We need to overflow our sno, replicas will do full
2N/A * resyncs once they see their sno > than the masters.
2N/A */
2N/A if (cur_sno == ULONG_MAX)
2N/A cur_sno = 1;
2N/A else
2N/A cur_sno++;
2N/A
2N/A /*
2N/A * We squirrel this away for finish_update() to index
2N/A */
2N/A upd->kdb_entry_sno = cur_sno;
2N/A
2N/A i = (cur_sno - 1) % ulogentries;
2N/A
2N/A indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
2N/A
2N/A (void) memset(indx_log, 0, ulog->kdb_block);
2N/A
2N/A indx_log->kdb_umagic = KDB_ULOG_MAGIC;
2N/A indx_log->kdb_entry_size = upd_size;
2N/A indx_log->kdb_entry_sno = cur_sno;
2N/A indx_log->kdb_time = upd->kdb_time = ktime;
2N/A indx_log->kdb_commit = upd->kdb_commit = FALSE;
2N/A
2N/A ulog->kdb_state = KDB_UNSTABLE;
2N/A
2N/A xdrmem_create(&xdrs, (char *)indx_log->entry_data,
2N/A indx_log->kdb_entry_size, XDR_ENCODE);
2N/A if (!xdr_kdb_incr_update_t(&xdrs, upd))
2N/A return (KRB5_LOG_CONV);
2N/A
2N/A if ((retval = ulog_sync_update(ulog, indx_log)))
2N/A return (retval);
2N/A
2N/A if (ulog->kdb_num < ulogentries)
2N/A ulog->kdb_num++;
2N/A
2N/A ulog->kdb_last_sno = cur_sno;
2N/A ulog->kdb_last_time = ktime;
2N/A
2N/A /*
2N/A * Since this is a circular array, once we circled, kdb_first_sno is
2N/A * always kdb_entry_sno + 1.
2N/A */
2N/A if (cur_sno > ulogentries) {
2N/A i = upd->kdb_entry_sno % ulogentries;
2N/A indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
2N/A ulog->kdb_first_sno = indx_log->kdb_entry_sno;
2N/A ulog->kdb_first_time = indx_log->kdb_time;
2N/A } else if (cur_sno == 1) {
2N/A ulog->kdb_first_sno = 1;
2N/A ulog->kdb_first_time = indx_log->kdb_time;
2N/A }
2N/A
2N/A ulog_sync_header(ulog);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Mark the log entry as committed and sync the memory mapped log
2N/A * to file.
2N/A */
2N/Akrb5_error_code
2N/Aulog_finish_update(krb5_context context, kdb_incr_update_t *upd)
2N/A{
2N/A krb5_error_code retval;
2N/A kdb_ent_header_t *indx_log;
2N/A uint_t i;
2N/A kdb_log_context *log_ctx;
2N/A kdb_hlog_t *ulog = NULL;
2N/A uint32_t ulogentries;
2N/A
2N/A INIT_ULOG(context);
2N/A ulogentries = log_ctx->ulogentries;
2N/A
2N/A i = (upd->kdb_entry_sno - 1) % ulogentries;
2N/A
2N/A indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
2N/A
2N/A indx_log->kdb_commit = TRUE;
2N/A
2N/A ulog->kdb_state = KDB_STABLE;
2N/A
2N/A if ((retval = ulog_sync_update(ulog, indx_log)))
2N/A return (retval);
2N/A
2N/A ulog_sync_header(ulog);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Set the header log details on the slave and sync it to file.
2N/A */
2N/Astatic void
2N/Aulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
2N/A{
2N/A
2N/A ulog->kdb_last_sno = lastentry.last_sno;
2N/A ulog->kdb_last_time = lastentry.last_time;
2N/A
2N/A ulog_sync_header(ulog);
2N/A}
2N/A
2N/A/*
2N/A * Delete an entry to the update log.
2N/A */
2N/Akrb5_error_code
2N/Aulog_delete_update(krb5_context context, kdb_incr_update_t *upd)
2N/A{
2N/A
2N/A upd->kdb_deleted = TRUE;
2N/A
2N/A return (ulog_add_update(context, upd));
2N/A}
2N/A
2N/A/*
2N/A * Used by the slave or master (during ulog_check) to update it's hash db from
2N/A * the incr update log.
2N/A *
2N/A * Must be called with lock held.
2N/A */
2N/Akrb5_error_code
2N/Aulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args)
2N/A{
2N/A krb5_db_entry *entry = NULL;
2N/A kdb_incr_update_t *upd = NULL, *fupd;
2N/A int i, no_of_updates;
2N/A krb5_error_code retval;
2N/A krb5_principal dbprinc = NULL;
2N/A kdb_last_t errlast;
2N/A char *dbprincstr = NULL;
2N/A kdb_log_context *log_ctx;
2N/A kdb_hlog_t *ulog = NULL;
2N/A
2N/A INIT_ULOG(context);
2N/A
2N/A no_of_updates = incr_ret->updates.kdb_ulog_t_len;
2N/A upd = incr_ret->updates.kdb_ulog_t_val;
2N/A fupd = upd;
2N/A
2N/A /*
2N/A * We reset last_sno and last_time to 0, if krb5_db2_db_put_principal
2N/A * or krb5_db2_db_delete_principal fail.
2N/A */
2N/A errlast.last_sno = (unsigned int)0;
2N/A errlast.last_time.seconds = (unsigned int)0;
2N/A errlast.last_time.useconds = (unsigned int)0;
2N/A
2N/A if ((retval = krb5_db_open(context, db_args,
2N/A KRB5_KDB_OPEN_RW|KRB5_KDB_SRV_TYPE_ADMIN)))
2N/A goto cleanup;
2N/A
2N/A for (i = 0; i < no_of_updates; i++) {
2N/A int nentry = 1;
2N/A
2N/A if (!upd->kdb_commit)
2N/A continue;
2N/A
2N/A if (upd->kdb_deleted) {
2N/A dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
2N/A + 1) * sizeof (char));
2N/A
2N/A if (dbprincstr == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A (void) strncpy(dbprincstr,
2N/A (char *)upd->kdb_princ_name.utf8str_t_val,
2N/A (upd->kdb_princ_name.utf8str_t_len + 1));
2N/A dbprincstr[upd->kdb_princ_name.utf8str_t_len] = 0;
2N/A
2N/A if ((retval = krb5_parse_name(context, dbprincstr,
2N/A &dbprinc))) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A free(dbprincstr);
2N/A
2N/A retval = krb5int_delete_principal_no_log(context,
2N/A dbprinc,
2N/A &nentry);
2N/A
2N/A if (dbprinc) {
2N/A krb5_free_principal(context, dbprinc);
2N/A dbprinc = NULL;
2N/A }
2N/A
2N/A if (retval)
2N/A goto cleanup;
2N/A } else {
2N/A entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry));
2N/A
2N/A if (!entry) {
2N/A retval = errno;
2N/A goto cleanup;
2N/A }
2N/A
2N/A (void) memset(entry, 0, sizeof (krb5_db_entry));
2N/A
2N/A if ((retval = ulog_conv_2dbentry(context, entry, upd, 1)))
2N/A goto cleanup;
2N/A
2N/A retval = krb5int_put_principal_no_log(context, entry,
2N/A &nentry);
2N/A
2N/A if (entry) {
2N/A krb5_db_free_principal(context, entry, nentry);
2N/A free(entry);
2N/A entry = NULL;
2N/A }
2N/A if (retval)
2N/A goto cleanup;
2N/A }
2N/A
2N/A upd++;
2N/A }
2N/A
2N/Acleanup:
2N/A if (fupd)
2N/A ulog_free_entries(fupd, no_of_updates);
2N/A
2N/A if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
2N/A if (retval)
2N/A ulog_finish_update_slave(ulog, errlast);
2N/A else
2N/A ulog_finish_update_slave(ulog, incr_ret->lastentry);
2N/A }
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Validate the log file and resync any uncommitted update entries
2N/A * to the principal database.
2N/A *
2N/A * Must be called with lock held.
2N/A */
2N/Astatic krb5_error_code
2N/Aulog_check(krb5_context context, kdb_hlog_t *ulog, char **db_args)
2N/A{
2N/A XDR xdrs;
2N/A krb5_error_code retval = 0;
2N/A unsigned int i;
2N/A kdb_ent_header_t *indx_log;
2N/A kdb_incr_update_t *upd = NULL;
2N/A kdb_incr_result_t *incr_ret = NULL;
2N/A
2N/A ulog->kdb_state = KDB_STABLE;
2N/A
2N/A for (i = 0; i < ulog->kdb_num; i++) {
2N/A indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
2N/A
2N/A if (indx_log->kdb_umagic != KDB_ULOG_MAGIC) {
2N/A /*
2N/A * Update entry corrupted we should scream and die
2N/A */
2N/A ulog->kdb_state = KDB_CORRUPT;
2N/A retval = KRB5_LOG_CORRUPT;
2N/A break;
2N/A }
2N/A
2N/A if (indx_log->kdb_commit == FALSE) {
2N/A ulog->kdb_state = KDB_UNSTABLE;
2N/A
2N/A incr_ret = (kdb_incr_result_t *)
2N/A malloc(sizeof (kdb_incr_result_t));
2N/A if (incr_ret == NULL) {
2N/A retval = errno;
2N/A goto error;
2N/A }
2N/A
2N/A upd = (kdb_incr_update_t *)
2N/A malloc(sizeof (kdb_incr_update_t));
2N/A if (upd == NULL) {
2N/A retval = errno;
2N/A goto error;
2N/A }
2N/A
2N/A (void) memset(upd, 0, sizeof (kdb_incr_update_t));
2N/A xdrmem_create(&xdrs, (char *)indx_log->entry_data,
2N/A indx_log->kdb_entry_size, XDR_DECODE);
2N/A if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
2N/A retval = KRB5_LOG_CONV;
2N/A goto error;
2N/A }
2N/A
2N/A incr_ret->updates.kdb_ulog_t_len = 1;
2N/A incr_ret->updates.kdb_ulog_t_val = upd;
2N/A
2N/A upd->kdb_commit = TRUE;
2N/A
2N/A /*
2N/A * We don't want to readd this update and just use the
2N/A * existing update to be propagated later on
2N/A */
2N/A ulog_set_role(context, IPROP_NULL);
2N/A retval = ulog_replay(context, incr_ret, db_args);
2N/A
2N/A /*
2N/A * upd was freed by ulog_replay, we NULL
2N/A * the pointer in case we subsequently break from loop.
2N/A */
2N/A upd = NULL;
2N/A if (incr_ret) {
2N/A free(incr_ret);
2N/A incr_ret = NULL;
2N/A }
2N/A ulog_set_role(context, IPROP_MASTER);
2N/A
2N/A if (retval)
2N/A goto error;
2N/A
2N/A /*
2N/A * We flag this as committed since this was
2N/A * the last entry before kadmind crashed, ergo
2N/A * the slaves have not seen this update before
2N/A */
2N/A indx_log->kdb_commit = TRUE;
2N/A retval = ulog_sync_update(ulog, indx_log);
2N/A if (retval)
2N/A goto error;
2N/A
2N/A ulog->kdb_state = KDB_STABLE;
2N/A }
2N/A }
2N/A
2N/Aerror:
2N/A if (upd)
2N/A ulog_free_entries(upd, 1);
2N/A
2N/A free(incr_ret);
2N/A
2N/A ulog_sync_header(ulog);
2N/A
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * Map the log file to memory for performance and simplicity.
2N/A *
2N/A * Called by: if iprop_enabled then ulog_map();
2N/A * Assumes that the caller will terminate on ulog_map, hence munmap and
2N/A * closing of the fd are implicitly performed by the caller.
2N/A * Returns 0 on success else failure.
2N/A */
2N/Akrb5_error_code
2N/Aulog_map(krb5_context context, const char *logname, uint32_t ulogentries,
2N/A int caller, char **db_args)
2N/A{
2N/A struct stat st;
2N/A krb5_error_code retval;
2N/A uint32_t ulog_filesize;
2N/A kdb_log_context *log_ctx;
2N/A kdb_hlog_t *ulog = NULL;
2N/A int ulogfd = -1;
2N/A
2N/A ulog_filesize = sizeof (kdb_hlog_t);
2N/A
2N/A if (stat(logname, &st) == -1) {
2N/A
2N/A if (caller == FKPROPLOG) {
2N/A /*
2N/A * File doesn't exist so we exit with kproplog
2N/A */
2N/A return (errno);
2N/A }
2N/A
2N/A if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) {
2N/A return (errno);
2N/A }
2N/A
2N/A if (lseek(ulogfd, 0L, SEEK_CUR) == -1) {
2N/A return (errno);
2N/A }
2N/A
2N/A if ((caller == FKADMIND) || (caller == FKCOMMAND))
2N/A ulog_filesize += ulogentries * ULOG_BLOCK;
2N/A
2N/A if (extend_file_to(ulogfd, ulog_filesize) < 0)
2N/A return errno;
2N/A } else {
2N/A
2N/A ulogfd = open(logname, O_RDWR, 0600);
2N/A if (ulogfd == -1)
2N/A /*
2N/A * Can't open existing log file
2N/A */
2N/A return errno;
2N/A }
2N/A
2N/A if (caller == FKPROPLOG) {
2N/A if (fstat(ulogfd, &st) < 0) {
2N/A close(ulogfd);
2N/A return errno;
2N/A }
2N/A ulog_filesize = st.st_size;
2N/A
2N/A ulog = (kdb_hlog_t *)mmap(0, ulog_filesize,
2N/A PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0);
2N/A } else {
2N/A /*
2N/A * else kadmind, kpropd, & kcommands should udpate stores
2N/A */
2N/A ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN,
2N/A PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0);
2N/A }
2N/A
2N/A if ((int)(ulog) == -1) {
2N/A /*
2N/A * Can't map update log file to memory
2N/A */
2N/A close(ulogfd);
2N/A return (errno);
2N/A }
2N/A
2N/A if (!context->kdblog_context) {
2N/A if (!(log_ctx = malloc(sizeof (kdb_log_context))))
2N/A return (errno);
2N/A memset(log_ctx, 0, sizeof(*log_ctx));
2N/A context->kdblog_context = log_ctx;
2N/A } else
2N/A log_ctx = context->kdblog_context;
2N/A log_ctx->ulog = ulog;
2N/A log_ctx->ulogentries = ulogentries;
2N/A log_ctx->ulogfd = ulogfd;
2N/A
2N/A if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) {
2N/A if (ulog->kdb_hmagic == 0) {
2N/A /*
2N/A * New update log
2N/A */
2N/A (void) memset(ulog, 0, sizeof (kdb_hlog_t));
2N/A
2N/A ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
2N/A ulog->db_version_num = KDB_VERSION;
2N/A ulog->kdb_state = KDB_STABLE;
2N/A ulog->kdb_block = ULOG_BLOCK;
2N/A if (!(caller == FKPROPLOG))
2N/A ulog_sync_header(ulog);
2N/A } else {
2N/A return (KRB5_LOG_CORRUPT);
2N/A }
2N/A }
2N/A
2N/A if (caller == FKADMIND) {
2N/A retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE);
2N/A if (retval)
2N/A return retval;
2N/A switch (ulog->kdb_state) {
2N/A case KDB_STABLE:
2N/A case KDB_UNSTABLE:
2N/A /*
2N/A * Log is currently un/stable, check anyway
2N/A */
2N/A retval = ulog_check(context, ulog, db_args);
2N/A ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A if (retval == KRB5_LOG_CORRUPT) {
2N/A return (retval);
2N/A }
2N/A break;
2N/A case KDB_CORRUPT:
2N/A ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return (KRB5_LOG_CORRUPT);
2N/A default:
2N/A /*
2N/A * Invalid db state
2N/A */
2N/A ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return (KRB5_LOG_ERROR);
2N/A }
2N/A } else if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
2N/A /*
2N/A * kproplog and kpropd don't need to do anything else
2N/A */
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Reinit ulog if the log is being truncated or expanded after
2N/A * we have circled.
2N/A */
2N/A retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE);
2N/A if (retval)
2N/A return retval;
2N/A if (ulog->kdb_num != ulogentries) {
2N/A if ((ulog->kdb_num != 0) &&
2N/A ((ulog->kdb_last_sno > ulog->kdb_num) ||
2N/A (ulog->kdb_num > ulogentries))) {
2N/A
2N/A (void) memset(ulog, 0, sizeof (kdb_hlog_t));
2N/A
2N/A ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
2N/A ulog->db_version_num = KDB_VERSION;
2N/A ulog->kdb_state = KDB_STABLE;
2N/A ulog->kdb_block = ULOG_BLOCK;
2N/A
2N/A ulog_sync_header(ulog);
2N/A }
2N/A
2N/A /*
2N/A * Expand ulog if we have specified a greater size
2N/A */
2N/A if (ulog->kdb_num < ulogentries) {
2N/A ulog_filesize += ulogentries * ulog->kdb_block;
2N/A
2N/A if (extend_file_to(ulogfd, ulog_filesize) < 0) {
2N/A ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return errno;
2N/A }
2N/A }
2N/A }
2N/A ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get the last set of updates seen, (last+1) to n is returned.
2N/A */
2N/Akrb5_error_code
2N/Aulog_get_entries(krb5_context context, /* input - krb5 lib config */
2N/A kdb_last_t last, /* input - slave's last sno */
2N/A kdb_incr_result_t *ulog_handle) /* output - incr result for slave */
2N/A{
2N/A XDR xdrs;
2N/A kdb_ent_header_t *indx_log;
2N/A kdb_incr_update_t *upd;
2N/A uint_t indx, count, tdiff;
2N/A uint32_t sno;
2N/A krb5_error_code retval;
2N/A struct timeval timestamp;
2N/A kdb_log_context *log_ctx;
2N/A kdb_hlog_t *ulog = NULL;
2N/A uint32_t ulogentries;
2N/A
2N/A INIT_ULOG(context);
2N/A ulogentries = log_ctx->ulogentries;
2N/A
2N/A retval = ulog_lock(context, KRB5_LOCKMODE_SHARED);
2N/A if (retval)
2N/A return retval;
2N/A
2N/A /*
2N/A * Check to make sure we don't have a corrupt ulog first.
2N/A */
2N/A if (ulog->kdb_state == KDB_CORRUPT) {
2N/A ulog_handle->ret = UPDATE_ERROR;
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return (KRB5_LOG_CORRUPT);
2N/A }
2N/A
2N/A gettimeofday(&timestamp, NULL);
2N/A
2N/A tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds;
2N/A if (tdiff <= ULOG_IDLE_TIME) {
2N/A ulog_handle->ret = UPDATE_BUSY;
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * We need to lock out other processes here, such as kadmin.local,
2N/A * since we are looking at the last_sno and looking up updates. So
2N/A * we can share with other readers.
2N/A */
2N/A retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED);
2N/A if (retval) {
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A return (retval);
2N/A }
2N/A
2N/A /*
2N/A * We may have overflowed the update log or we shrunk the log, or
2N/A * the client's ulog has just been created.
2N/A */
2N/A if ((last.last_sno > ulog->kdb_last_sno) ||
2N/A (last.last_sno < ulog->kdb_first_sno) ||
2N/A (last.last_sno == 0)) {
2N/A ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
2N/A return (0);
2N/A } else if (last.last_sno <= ulog->kdb_last_sno) {
2N/A sno = last.last_sno;
2N/A
2N/A indx = (sno - 1) % ulogentries;
2N/A
2N/A indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
2N/A
2N/A /*
2N/A * Validate the time stamp just to make sure it was the same sno
2N/A */
2N/A if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
2N/A (indx_log->kdb_time.useconds == last.last_time.useconds)) {
2N/A
2N/A /*
2N/A * If we have the same sno we return success
2N/A */
2N/A if (last.last_sno == ulog->kdb_last_sno) {
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A ulog_handle->ret = UPDATE_NIL;
2N/A return (0);
2N/A }
2N/A
2N/A count = ulog->kdb_last_sno - sno;
2N/A
2N/A ulog_handle->updates.kdb_ulog_t_val =
2N/A (kdb_incr_update_t *)malloc(
2N/A sizeof (kdb_incr_update_t) * count);
2N/A
2N/A upd = ulog_handle->updates.kdb_ulog_t_val;
2N/A
2N/A if (upd == NULL) {
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A ulog_handle->ret = UPDATE_ERROR;
2N/A return (errno);
2N/A }
2N/A
2N/A while (sno < ulog->kdb_last_sno) {
2N/A indx = sno % ulogentries;
2N/A
2N/A indx_log = (kdb_ent_header_t *)
2N/A INDEX(ulog, indx);
2N/A
2N/A (void) memset(upd, 0,
2N/A sizeof (kdb_incr_update_t));
2N/A xdrmem_create(&xdrs,
2N/A (char *)indx_log->entry_data,
2N/A indx_log->kdb_entry_size, XDR_DECODE);
2N/A if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A ulog_handle->ret = UPDATE_ERROR;
2N/A return (KRB5_LOG_CONV);
2N/A }
2N/A /*
2N/A * Mark commitment since we didn't
2N/A * want to decode and encode the
2N/A * incr update record the first time.
2N/A */
2N/A upd->kdb_commit = indx_log->kdb_commit;
2N/A
2N/A upd++;
2N/A sno++;
2N/A } /* while */
2N/A
2N/A ulog_handle->updates.kdb_ulog_t_len = count;
2N/A
2N/A ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
2N/A ulog_handle->lastentry.last_time.seconds =
2N/A ulog->kdb_last_time.seconds;
2N/A ulog_handle->lastentry.last_time.useconds =
2N/A ulog->kdb_last_time.useconds;
2N/A ulog_handle->ret = UPDATE_OK;
2N/A
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A
2N/A return (0);
2N/A } else {
2N/A /*
2N/A * We have time stamp mismatch or we no longer have
2N/A * the slave's last sno, so we brute force it
2N/A */
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A (void) krb5_db_unlock(context);
2N/A ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
2N/A
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Should never get here, return error
2N/A */
2N/A (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
2N/A ulog_handle->ret = UPDATE_ERROR;
2N/A return (KRB5_LOG_ERROR);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Aulog_set_role(krb5_context ctx, iprop_role role)
2N/A{
2N/A kdb_log_context *log_ctx;
2N/A
2N/A if (!ctx->kdblog_context) {
2N/A if (!(log_ctx = malloc(sizeof (kdb_log_context))))
2N/A return (errno);
2N/A memset(log_ctx, 0, sizeof(*log_ctx));
2N/A ctx->kdblog_context = log_ctx;
2N/A } else
2N/A log_ctx = ctx->kdblog_context;
2N/A
2N/A log_ctx->iproprole = role;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Extend update log file.
2N/A */
2N/Astatic int extend_file_to(int fd, uint_t new_size)
2N/A{
2N/A int current_offset;
2N/A static const char zero[512] = { 0, };
2N/A
2N/A current_offset = lseek(fd, 0, SEEK_END);
2N/A if (current_offset < 0)
2N/A return -1;
2N/A if (new_size > INT_MAX) {
2N/A errno = EINVAL;
2N/A return -1;
2N/A }
2N/A while (current_offset < new_size) {
2N/A int write_size, wrote_size;
2N/A write_size = new_size - current_offset;
2N/A if (write_size > 512)
2N/A write_size = 512;
2N/A wrote_size = write(fd, zero, write_size);
2N/A if (wrote_size < 0)
2N/A return -1;
2N/A if (wrote_size == 0) {
2N/A errno = EINVAL; /* XXX ?? */
2N/A return -1;
2N/A }
2N/A current_offset += wrote_size;
2N/A write_size = new_size - current_offset;
2N/A }
2N/A return 0;
2N/A}