diff options
author | Ken Raeburn <raeburn@mit.edu> | 2008-06-24 05:04:29 +0000 |
---|---|---|
committer | Ken Raeburn <raeburn@mit.edu> | 2008-06-24 05:04:29 +0000 |
commit | 5661d1290f74312a405db970aea097da77706f71 (patch) | |
tree | 0ab69c8078ef3275b99a3ad27f3592b607e43f70 /src/lib/kdb/kdb_log.c | |
parent | 6879f371402854465e5276d36e4792938906097f (diff) | |
download | krb5-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.c | 928 |
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(×tamp, 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(×tamp, 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; +} |