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