/* 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; unsigned int 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 = INDEX(ulog, i); indx_log->kdb_commit = TRUE; ulog->kdb_state = KDB_STABLE; retval = sync_update(ulog, indx_log); if (retval) return retval; ulog_sync_header(ulog); return 0; }
/* * If any database operations will be invoked while the ulog lock is held, the * caller must explicitly lock the database before locking the ulog, or * deadlock may result. */ static krb5_error_code lock_ulog(krb5_context context, int mode) { kdb_log_context *log_ctx = NULL; kdb_hlog_t *ulog = NULL; INIT_ULOG(context); return krb5_lock_file(context, log_ctx->ulogfd, mode); }
/* Reinitialize the log header. Locking is the caller's responsibility. */ void ulog_init_header(krb5_context context) { kdb_log_context *log_ctx; kdb_hlog_t *ulog; INIT_ULOG(context); reset_header(ulog); ulog_sync_header(ulog); }
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); }
krb5_error_code ulog_get_last(krb5_context context, kdb_last_t *last_out) { krb5_error_code ret; kdb_log_context *log_ctx; kdb_hlog_t *ulog; INIT_ULOG(context); ret = lock_ulog(context, KRB5_LOCKMODE_SHARED); if (ret) return ret; last_out->last_sno = log_ctx->ulog->kdb_last_sno; last_out->last_time = log_ctx->ulog->kdb_last_time; unlock_ulog(context); return 0; }
/* Reinitialize the log header. */ krb5_error_code ulog_init_header(krb5_context context) { krb5_error_code ret; kdb_log_context *log_ctx; kdb_hlog_t *ulog; INIT_ULOG(context); ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (ret) return ret; reset_header(ulog); sync_header(ulog); unlock_ulog(context); return 0; }
krb5_error_code ulog_set_last(krb5_context context, const kdb_last_t *last) { krb5_error_code ret; kdb_log_context *log_ctx; kdb_hlog_t *ulog; INIT_ULOG(context); ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (ret) return ret; ulog->kdb_last_sno = last->last_sno; ulog->kdb_last_time = last->last_time; sync_header(ulog); unlock_ulog(context); return 0; }
/* Add an entry to the update log. */ krb5_error_code ulog_add_update(krb5_context context, kdb_incr_update_t *upd) { krb5_error_code ret; kdb_log_context *log_ctx; kdb_hlog_t *ulog; INIT_ULOG(context); ret = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (ret) return ret; /* If we have reached the last possible serial number, reinitialize the * ulog and start over. Slaves will do a full resync. */ if (ulog->kdb_last_sno == (kdb_sno_t)-1) reset_header(ulog); upd->kdb_entry_sno = ulog->kdb_last_sno + 1; time_current(&upd->kdb_time); ret = store_update(log_ctx, upd); unlock_ulog(context); return ret; }
/* Used by the slave to update its 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; kdb_last_t errlast, *last; char *dbprincstr; 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; last = &errlast; retval = krb5_db_open(context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (retval) goto cleanup; for (i = 0; i < no_of_updates; i++) { if (!upd->kdb_commit) continue; if (upd->kdb_deleted) { dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val, upd->kdb_princ_name.utf8str_t_len, &retval); if (dbprincstr == NULL) goto cleanup; retval = krb5_parse_name(context, dbprincstr, &dbprinc); free(dbprincstr); if (retval) goto cleanup; retval = krb5int_delete_principal_no_log(context, dbprinc); krb5_free_principal(context, dbprinc); if (retval) goto cleanup; } else { entry = k5alloc(sizeof(krb5_db_entry), &retval); if (entry == NULL) goto cleanup; retval = ulog_conv_2dbentry(context, &entry, upd); if (retval) goto cleanup; retval = krb5int_put_principal_no_log(context, entry); krb5_db_free_principal(context, entry); if (retval) goto cleanup; } upd++; } last = &incr_ret->lastentry; cleanup: if (fupd) ulog_free_entries(fupd, no_of_updates); /* Record a new last serial number and timestamp in the ulog header. */ ulog->kdb_last_sno = last->last_sno; ulog->kdb_last_time = last->last_time; ulog_sync_header(ulog); return retval; }
/* * Add 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; kdb_ent_header_t *indx_log; unsigned int i, recsize; unsigned long 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; time_current(&ktime); 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) { retval = resize(ulog, ulogentries, ulogfd, recsize); if (retval) return retval; } /* If we have reached the last possible serial number, reinitialize the * ulog and start over. Slaves will do a full resync. */ if (ulog->kdb_last_sno == (kdb_sno_t)-1) reset_header(ulog); /* Get the next serial number and save it for finish_update() to index. */ cur_sno = ulog->kdb_last_sno + 1; upd->kdb_entry_sno = cur_sno; i = (cur_sno - 1) % ulogentries; indx_log = INDEX(ulog, i); 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; retval = sync_update(ulog, indx_log); if (retval) return retval; if (ulog->kdb_num < ulogentries) ulog->kdb_num++; ulog->kdb_last_sno = cur_sno; ulog->kdb_last_time = ktime; if (cur_sno > ulogentries) { /* Once we've circled, kdb_first_sno is the sno of the next entry. */ i = upd->kdb_entry_sno % ulogentries; indx_log = 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) { /* This is the first update. */ ulog->kdb_first_sno = 1; ulog->kdb_first_time = indx_log->kdb_time; } ulog_sync_header(ulog); return 0; }
/* Get the last set of updates seen, (last+1) to n is returned. */ krb5_error_code ulog_get_entries(krb5_context context, const kdb_last_t *last, kdb_incr_result_t *ulog_handle) { XDR xdrs; kdb_ent_header_t *indx_log; kdb_incr_update_t *upd; unsigned int indx, count; uint32_t sno; krb5_error_code retval; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; uint32_t ulogentries; INIT_ULOG(context); ulogentries = log_ctx->ulogentries; retval = lock_ulog(context, KRB5_LOCKMODE_SHARED); if (retval) return retval; /* If another process terminated mid-update, reset the ulog and force full * resyncs. */ if (ulog->kdb_state != KDB_STABLE) reset_header(ulog); ulog_handle->ret = get_sno_status(log_ctx, last); if (ulog_handle->ret != UPDATE_OK) goto cleanup; sno = last->last_sno; count = ulog->kdb_last_sno - sno; upd = calloc(count, sizeof(kdb_incr_update_t)); if (upd == NULL) { ulog_handle->ret = UPDATE_ERROR; retval = ENOMEM; goto cleanup; } ulog_handle->updates.kdb_ulog_t_val = upd; for (; sno < ulog->kdb_last_sno; sno++) { indx = sno % ulogentries; indx_log = INDEX(ulog, indx); 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)) { ulog_handle->ret = UPDATE_ERROR; retval = KRB5_LOG_CONV; goto cleanup; } /* 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++; } 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; cleanup: unlock_ulog(context); return retval; }
/* * 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++) { 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); if (dbprinc) { krb5_free_principal(context, dbprinc); dbprinc = NULL; } 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))) goto cleanup; retval = krb5int_put_principal_no_log(context, entry); if (entry) { krb5_db_free_principal(context, 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); }
/* Used by the slave to update its hash db from the incr update log. */ 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; char *dbprincstr; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; INIT_ULOG(context); /* Lock the DB before the ulog to avoid deadlock. */ retval = krb5_db_open(context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (retval) return retval; retval = krb5_db_lock(context, KRB5_DB_LOCKMODE_EXCLUSIVE); if (retval) return retval; retval = lock_ulog(context, KRB5_LOCKMODE_EXCLUSIVE); if (retval) { krb5_db_unlock(context); return retval; } no_of_updates = incr_ret->updates.kdb_ulog_t_len; upd = incr_ret->updates.kdb_ulog_t_val; fupd = upd; for (i = 0; i < no_of_updates; i++) { if (!upd->kdb_commit) continue; /* If (unexpectedly) this update does not follow the last one we * stored, discard any previous ulog state. */ if (ulog->kdb_num != 0 && upd->kdb_entry_sno != ulog->kdb_last_sno + 1) reset_header(ulog); if (upd->kdb_deleted) { dbprincstr = k5memdup0(upd->kdb_princ_name.utf8str_t_val, upd->kdb_princ_name.utf8str_t_len, &retval); if (dbprincstr == NULL) goto cleanup; retval = krb5_parse_name(context, dbprincstr, &dbprinc); free(dbprincstr); if (retval) goto cleanup; retval = krb5int_delete_principal_no_log(context, dbprinc); krb5_free_principal(context, dbprinc); if (retval == KRB5_KDB_NOENTRY) retval = 0; if (retval) goto cleanup; } else { entry = k5alloc(sizeof(krb5_db_entry), &retval); if (entry == NULL) goto cleanup; retval = ulog_conv_2dbentry(context, &entry, upd); if (retval) goto cleanup; retval = krb5int_put_principal_no_log(context, entry); krb5_db_free_principal(context, entry); if (retval) goto cleanup; } retval = store_update(log_ctx, upd); if (retval) goto cleanup; upd++; } cleanup: if (fupd) ulog_free_entries(fupd, no_of_updates); if (retval) { reset_header(ulog); sync_header(ulog); } unlock_ulog(context); krb5_db_unlock(context); return retval; }
/* * Add 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; kdb_ent_header_t *indx_log; unsigned int i, recsize; unsigned long 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; time_current(&ktime); 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) { retval = ulog_resize(ulog, ulogentries, ulogfd, recsize); if (retval) return retval; } cur_sno = ulog->kdb_last_sno; /* * If we need to, wrap our sno around to 1. A slaves will do a full resync * since its sno will be out of range of the ulog (or in extreme cases, * its timestamp won't match). */ if (cur_sno == (kdb_sno_t)-1) cur_sno = 1; else cur_sno++; /* 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); 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; retval = ulog_sync_update(ulog, indx_log); if (retval) return retval; if (ulog->kdb_num < ulogentries) ulog->kdb_num++; ulog->kdb_last_sno = cur_sno; ulog->kdb_last_time = ktime; if (cur_sno > ulogentries) { /* Once we've circled, kdb_first_sno is the sno of the next entry. */ 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) { /* This is the first update, or we wrapped. */ ulog->kdb_first_sno = 1; ulog->kdb_first_time = indx_log->kdb_time; } ulog_sync_header(ulog); 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); }
/* Get the last set of updates seen, (last+1) to n is returned. */ krb5_error_code ulog_get_entries(krb5_context context, const kdb_last_t *last, kdb_incr_result_t *ulog_handle) { XDR xdrs; kdb_ent_header_t *indx_log; kdb_incr_update_t *upd; unsigned int indx, count; uint32_t sno; krb5_error_code retval; 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; /* If another process terminated mid-update, reset the ulog and force full * resyncs. */ if (ulog->kdb_state != KDB_STABLE) reset_header(ulog); /* If we have the same sno and timestamp, return a nil update. If a * different timestamp, the sno was reused and we need a full resync. */ if (last->last_sno == ulog->kdb_last_sno) { ulog_handle->ret = time_equal(&last->last_time, &ulog->kdb_last_time) ? UPDATE_NIL : UPDATE_FULL_RESYNC_NEEDED; goto cleanup; } /* We may have overflowed the update log or shrunk the log, or the client * may have created its ulog. */ if (last->last_sno > ulog->kdb_last_sno || last->last_sno < ulog->kdb_first_sno) { ulog_handle->lastentry.last_sno = ulog->kdb_last_sno; ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED; goto cleanup; } sno = last->last_sno; indx = (sno - 1) % ulogentries; indx_log = INDEX(ulog, indx); if (!time_equal(&indx_log->kdb_time, &last->last_time)) { /* We have time stamp mismatch or we no longer have the slave's last * sno, so we brute force it. */ ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED; goto cleanup; } count = ulog->kdb_last_sno - sno; upd = calloc(count, sizeof(kdb_incr_update_t)); if (upd == NULL) { ulog_handle->ret = UPDATE_ERROR; retval = ENOMEM; goto cleanup; } ulog_handle->updates.kdb_ulog_t_val = upd; for (; sno < ulog->kdb_last_sno; sno++) { indx = sno % ulogentries; indx_log = INDEX(ulog, indx); 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)) { ulog_handle->ret = UPDATE_ERROR; retval = KRB5_LOG_CONV; goto cleanup; } /* 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++; } 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; cleanup: (void)ulog_lock(context, KRB5_LOCKMODE_UNLOCK); return retval; }
/* * 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 == (kdb_sno_t)-1) 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); }