Home | History | Annotate | Download | only in kdb
      1 /*
      2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      7 
      8 #include <sys/stat.h>
      9 #include <sys/types.h>
     10 #include <unistd.h>
     11 #include <fcntl.h>
     12 #include <sys/mman.h>
     13 #include <k5-int.h>
     14 #include <stdlib.h>
     15 #include <limits.h>
     16 #include <syslog.h>
     17 #include "kdb_log.h"
     18 
     19 /*
     20  * This modules includes all the necessary functions that create and
     21  * modify the Kerberos principal update and header logs.
     22  */
     23 
     24 #define	getpagesize()	sysconf(_SC_PAGESIZE)
     25 
     26 static int		pagesize = 0;
     27 
     28 #define	INIT_ULOG(ctx)	log_ctx = ctx->kdblog_context; \
     29 			ulog = log_ctx->ulog
     30 /*
     31  * Sync update entry to disk.
     32  */
     33 krb5_error_code
     34 ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
     35 {
     36 	ulong_t		start, end, size;
     37 	krb5_error_code	retval;
     38 
     39 	if (ulog == NULL)
     40 		return (KRB5_LOG_ERROR);
     41 
     42 	if (!pagesize)
     43 		pagesize = getpagesize();
     44 
     45 	start = ((ulong_t)upd) & (~(pagesize-1));
     46 
     47 	end = (((ulong_t)upd) + ulog->kdb_block +
     48 	    (pagesize-1)) & (~(pagesize-1));
     49 
     50 	size = end - start;
     51 	if (retval = msync((caddr_t)start, size, MS_SYNC)) {
     52 		return (retval);
     53 	}
     54 
     55 	return (0);
     56 }
     57 
     58 /*
     59  * Sync memory to disk for the update log header.
     60  */
     61 void
     62 ulog_sync_header(kdb_hlog_t *ulog)
     63 {
     64 
     65 	if (!pagesize)
     66 		pagesize = getpagesize();
     67 
     68 	if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
     69 		/*
     70 		 * Couldn't sync to disk, let's panic
     71 		 */
     72 		syslog(LOG_ERR, "ulog_sync_header: could not sync to disk");
     73 		abort();
     74 	}
     75 }
     76 
     77 /*
     78  * Resizes the array elements.  We reinitialize the update log rather than
     79  * unrolling the the log and copying it over to a temporary log for obvious
     80  * performance reasons.  Slaves will subsequently do a full resync, but
     81  * the need for resizing should be very small.
     82  */
     83 krb5_error_code
     84 ulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize)
     85 {
     86 	uint_t		new_block, new_size;
     87 
     88 	if (ulog == NULL)
     89 		return (KRB5_LOG_ERROR);
     90 
     91 	new_size = sizeof (kdb_hlog_t);
     92 
     93 	new_block = (recsize / ULOG_BLOCK) + 1;
     94 	new_block *= ULOG_BLOCK;
     95 
     96 	new_size += ulogentries * new_block;
     97 
     98 	if (new_size <= MAXLOGLEN) {
     99 		/*
    100 		 * Reinit log with new block size
    101 		 */
    102 		(void) memset(ulog, 0, sizeof (kdb_hlog_t));
    103 
    104 		ulog->kdb_hmagic = KDB_HMAGIC;
    105 		ulog->db_version_num = KDB_VERSION;
    106 		ulog->kdb_state = KDB_STABLE;
    107 		ulog->kdb_block = new_block;
    108 
    109 		ulog_sync_header(ulog);
    110 
    111 		/*
    112 		 * Time to expand log considering new block size
    113 		 */
    114 		if (lseek(ulogfd, new_size, SEEK_SET) == -1) {
    115 			return (errno);
    116 		}
    117 
    118 		if (write(ulogfd, "+", 1) != 1) {
    119 			return (errno);
    120 		}
    121 	} else {
    122 		/*
    123 		 * Can't map into file larger than MAXLOGLEN
    124 		 */
    125 		return (KRB5_LOG_ERROR);
    126 	}
    127 
    128 	return (0);
    129 }
    130 
    131 /*
    132  * Adds an entry to the update log.
    133  * The layout of the update log looks like:
    134  *
    135  * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
    136  */
    137 krb5_error_code
    138 ulog_add_update(krb5_context context, kdb_incr_update_t *upd)
    139 {
    140 	XDR		xdrs;
    141 	kdbe_time_t	ktime;
    142 	struct timeval	timestamp;
    143 	kdb_ent_header_t *indx_log;
    144 	uint_t		i, recsize;
    145 	ulong_t		upd_size;
    146 	krb5_error_code	retval;
    147 	kdb_sno_t	cur_sno;
    148 	kdb_log_context	*log_ctx;
    149 	kdb_hlog_t	*ulog = NULL;
    150 	uint32_t	ulogentries;
    151 	int		ulogfd;
    152 
    153 	INIT_ULOG(context);
    154 	ulogentries = log_ctx->ulogentries;
    155 	ulogfd = log_ctx->ulogfd;
    156 
    157 	if (upd == NULL)
    158 		return (KRB5_LOG_ERROR);
    159 
    160 	(void) gettimeofday(&timestamp, NULL);
    161 	ktime.seconds = timestamp.tv_sec;
    162 	ktime.useconds = timestamp.tv_usec;
    163 
    164 	upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
    165 
    166 	recsize = sizeof (kdb_ent_header_t) + upd_size;
    167 
    168 	if (recsize > ulog->kdb_block) {
    169 		if (retval = ulog_resize(ulog, ulogentries, ulogfd, recsize)) {
    170 			/* Resize element array failed */
    171 			return (retval);
    172 		}
    173 	}
    174 
    175 	cur_sno = ulog->kdb_last_sno;
    176 
    177 	/*
    178 	 * We need to overflow our sno, replicas will do full
    179 	 * resyncs once they see their sno > than the masters.
    180 	 */
    181 	if (cur_sno == ULONG_MAX)
    182 		cur_sno = 1;
    183 	else
    184 		cur_sno++;
    185 
    186 	/*
    187 	 * We squirrel this away for finish_update() to index
    188 	 */
    189 	upd->kdb_entry_sno = cur_sno;
    190 
    191 	i = (cur_sno - 1) % ulogentries;
    192 
    193 	indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
    194 
    195 	(void) memset(indx_log, 0, ulog->kdb_block);
    196 
    197 	indx_log->kdb_umagic = KDB_UMAGIC;
    198 	indx_log->kdb_entry_size = upd_size;
    199 	indx_log->kdb_entry_sno = cur_sno;
    200 	indx_log->kdb_time = upd->kdb_time = ktime;
    201 	indx_log->kdb_commit = upd->kdb_commit = FALSE;
    202 
    203 	ulog->kdb_state = KDB_UNSTABLE;
    204 
    205 	xdrmem_create(&xdrs, (char *)indx_log->entry_data,
    206 	    indx_log->kdb_entry_size, XDR_ENCODE);
    207 	if (!xdr_kdb_incr_update_t(&xdrs, upd))
    208 		return (KRB5_LOG_CONV);
    209 
    210 	if (retval = ulog_sync_update(ulog, indx_log))
    211 		return (retval);
    212 
    213 	if (ulog->kdb_num < ulogentries)
    214 		ulog->kdb_num++;
    215 
    216 	ulog->kdb_last_sno = cur_sno;
    217 	ulog->kdb_last_time = ktime;
    218 
    219 	/*
    220 	 * Since this is a circular array, once we circled, kdb_first_sno is
    221 	 * always kdb_entry_sno + 1.
    222 	 */
    223 	if (cur_sno > ulogentries) {
    224 		i = upd->kdb_entry_sno % ulogentries;
    225 		indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
    226 		ulog->kdb_first_sno = indx_log->kdb_entry_sno;
    227 		ulog->kdb_first_time = indx_log->kdb_time;
    228 	} else if (cur_sno == 1) {
    229 		ulog->kdb_first_sno = 1;
    230 		ulog->kdb_first_time = indx_log->kdb_time;
    231 	}
    232 
    233 	ulog_sync_header(ulog);
    234 
    235 	return (0);
    236 }
    237 
    238 /*
    239  * Mark the log entry as committed and sync the memory mapped log
    240  * to file.
    241  */
    242 krb5_error_code
    243 ulog_finish_update(krb5_context context, kdb_incr_update_t *upd)
    244 {
    245 	krb5_error_code		retval;
    246 	kdb_ent_header_t	*indx_log;
    247 	uint_t			i;
    248 	kdb_log_context		*log_ctx;
    249 	kdb_hlog_t		*ulog = NULL;
    250 	uint32_t		ulogentries;
    251 
    252 	INIT_ULOG(context);
    253 	ulogentries = log_ctx->ulogentries;
    254 
    255 	i = (upd->kdb_entry_sno - 1) % ulogentries;
    256 
    257 	indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
    258 
    259 	indx_log->kdb_commit = TRUE;
    260 
    261 	ulog->kdb_state = KDB_STABLE;
    262 
    263 	if (retval = ulog_sync_update(ulog, indx_log))
    264 		return (retval);
    265 
    266 	ulog_sync_header(ulog);
    267 
    268 	return (0);
    269 }
    270 
    271 /*
    272  * Set the header log details on the slave and sync it to file.
    273  */
    274 void
    275 ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
    276 {
    277 
    278 	ulog->kdb_last_sno = lastentry.last_sno;
    279 	ulog->kdb_last_time = lastentry.last_time;
    280 
    281 	ulog_sync_header(ulog);
    282 }
    283 
    284 /*
    285  * Delete an entry to the update log.
    286  */
    287 krb5_error_code
    288 ulog_delete_update(krb5_context context, kdb_incr_update_t *upd)
    289 {
    290 
    291 	upd->kdb_deleted = TRUE;
    292 
    293 	return (ulog_add_update(context, upd));
    294 }
    295 
    296 /*
    297  * Used by the slave or master (during ulog_check) to update it's hash db from
    298  * the incr update log.
    299  */
    300 krb5_error_code
    301 ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret)
    302 {
    303 	krb5_db_entry		*entry = NULL;
    304 	kdb_incr_update_t	*upd = NULL, *fupd;
    305 	int			i, no_of_updates;
    306 	krb5_error_code		retval;
    307 	krb5_principal		dbprinc = NULL;
    308 	kdb_last_t		errlast;
    309 	char			*dbprincstr = NULL;
    310 	kdb_log_context		*log_ctx;
    311 	kdb_hlog_t		*ulog = NULL;
    312 	bool_t			fini = FALSE;
    313 
    314 	INIT_ULOG(context);
    315 
    316 	no_of_updates = incr_ret->updates.kdb_ulog_t_len;
    317 	upd = incr_ret->updates.kdb_ulog_t_val;
    318 	fupd = upd;
    319 
    320 	/*
    321 	 * We reset last_sno and last_time to 0, if krb5_db_put_principal
    322 	 * or krb5_db_delete_principal fail.
    323 	 */
    324 	errlast.last_sno = (unsigned int)0;
    325 	errlast.last_time.seconds = (unsigned int)0;
    326 	errlast.last_time.useconds = (unsigned int)0;
    327 
    328 	if (krb5_db_inited(context)) {
    329 		retval = krb5_db_open(context, NULL,
    330 		    KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
    331 		if (retval != 0)
    332 			goto cleanup;
    333 		fini = TRUE;
    334 	}
    335 
    336 	for (i = 0; i < no_of_updates; i++) {
    337 		int nentry = 1;
    338 
    339 		if (!upd->kdb_commit)
    340 			continue;
    341 
    342 		if (upd->kdb_deleted) {
    343 			dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
    344 			    + 1) * sizeof (char));
    345 
    346 			if (dbprincstr == NULL) {
    347 				retval = ENOMEM;
    348 				goto cleanup;
    349 			}
    350 
    351 			(void) strlcpy(dbprincstr,
    352 			    (char *)upd->kdb_princ_name.utf8str_t_val,
    353 			    (upd->kdb_princ_name.utf8str_t_len + 1));
    354 
    355 			if (retval = krb5_parse_name(context, dbprincstr,
    356 			    &dbprinc)) {
    357 				goto cleanup;
    358 			}
    359 
    360 			if (dbprincstr)
    361 				free(dbprincstr);
    362 
    363 			retval = krb5_db_delete_principal(context,
    364 			    dbprinc, &nentry);
    365 
    366 			if (dbprinc)
    367 				krb5_free_principal(context, dbprinc);
    368 
    369 			if (retval)
    370 				goto cleanup;
    371 		} else {
    372 			entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry));
    373 
    374 			if (!entry) {
    375 				retval = errno;
    376 				goto cleanup;
    377 			}
    378 
    379 			(void) memset(entry, 0, sizeof (krb5_db_entry));
    380 
    381 			if (retval = ulog_conv_2dbentry(context, entry, upd, 1))
    382 				goto cleanup;
    383 
    384 			retval = krb5_db_put_principal(context, entry,
    385 			    &nentry);
    386 
    387 			if (entry) {
    388 				krb5_db_free_principal(context, entry, nentry);
    389 				free(entry);
    390 				entry = NULL;
    391 			}
    392 			if (retval)
    393 				goto cleanup;
    394 		}
    395 
    396 		upd++;
    397 	}
    398 
    399 cleanup:
    400 	if (fupd)
    401 		ulog_free_entries(fupd, no_of_updates);
    402 
    403 	if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
    404 		if (retval)
    405 			ulog_finish_update_slave(ulog, errlast);
    406 		else
    407 			ulog_finish_update_slave(ulog, incr_ret->lastentry);
    408 	}
    409 
    410 	if (fini == TRUE)
    411 		krb5_db_fini(context);
    412 
    413 	return (retval);
    414 }
    415 
    416 /*
    417  * Validate the log file and resync any uncommitted update entries
    418  * to the principal database.
    419  */
    420 krb5_error_code
    421 ulog_check(krb5_context context, kdb_hlog_t *ulog)
    422 {
    423 	XDR			xdrs;
    424 	krb5_error_code		retval = 0;
    425 	int			i;
    426 	kdb_ent_header_t	*indx_log;
    427 	kdb_incr_update_t	*upd = NULL;
    428 	kdb_incr_result_t	*incr_ret = NULL;
    429 
    430 	ulog->kdb_state = KDB_STABLE;
    431 
    432 	for (i = 0; i < ulog->kdb_num; i++) {
    433 		indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
    434 
    435 		if (indx_log->kdb_umagic != KDB_UMAGIC) {
    436 			/*
    437 			 * Update entry corrupted we should scream and die
    438 			 */
    439 			ulog->kdb_state = KDB_CORRUPT;
    440 			retval = KRB5_LOG_CORRUPT;
    441 			break;
    442 		}
    443 
    444 		if (indx_log->kdb_commit == FALSE) {
    445 			ulog->kdb_state = KDB_UNSTABLE;
    446 
    447 			incr_ret = (kdb_incr_result_t *)
    448 			    malloc(sizeof (kdb_incr_result_t));
    449 			if (incr_ret == NULL) {
    450 				retval = errno;
    451 				goto error;
    452 			}
    453 
    454 			upd = (kdb_incr_update_t *)
    455 			    malloc(sizeof (kdb_incr_update_t));
    456 			if (upd == NULL) {
    457 				retval = errno;
    458 				goto error;
    459 			}
    460 
    461 			(void) memset(upd, 0, sizeof (kdb_incr_update_t));
    462 			xdrmem_create(&xdrs, (char *)indx_log->entry_data,
    463 			    indx_log->kdb_entry_size, XDR_DECODE);
    464 			if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
    465 				retval = KRB5_LOG_CONV;
    466 				goto error;
    467 			}
    468 
    469 			incr_ret->updates.kdb_ulog_t_len = 1;
    470 			incr_ret->updates.kdb_ulog_t_val = upd;
    471 
    472 			upd->kdb_commit = TRUE;
    473 
    474 			/*
    475 			 * We don't want to readd this update and just use the
    476 			 * existing update to be propagated later on
    477 			 */
    478 			ulog_set_role(context, IPROP_NULL);
    479 			retval = ulog_replay(context, incr_ret);
    480 
    481 			/*
    482 			 * upd was freed by ulog_replay, we NULL
    483 			 * the pointer in case we subsequently break from loop.
    484 			 */
    485 			upd = NULL;
    486 			if (incr_ret) {
    487 				free(incr_ret);
    488 				incr_ret = NULL;
    489 			}
    490 			ulog_set_role(context, IPROP_MASTER);
    491 
    492 			if (retval)
    493 				goto error;
    494 
    495 			/*
    496 			 * We flag this as committed since this was
    497 			 * the last entry before kadmind crashed, ergo
    498 			 * the slaves have not seen this update before
    499 			 */
    500 			indx_log->kdb_commit = TRUE;
    501 			retval = ulog_sync_update(ulog, indx_log);
    502 			if (retval)
    503 				goto error;
    504 
    505 			ulog->kdb_state = KDB_STABLE;
    506 		}
    507 	}
    508 
    509 error:
    510 	if (upd)
    511 		ulog_free_entries(upd, 1);
    512 
    513 	if (incr_ret)
    514 		free(incr_ret);
    515 
    516 	ulog_sync_header(ulog);
    517 
    518 	return (retval);
    519 }
    520 
    521 /*
    522  * Map the log file to memory for performance and simplicity.
    523  *
    524  * Called by: if iprop_enabled then ulog_map();
    525  * Assumes that the caller will terminate on ulog_map, hence munmap and
    526  * closing of the fd are implicitly performed by the caller.
    527  * Returns 0 on success else failure.
    528  */
    529 krb5_error_code
    530 ulog_map(krb5_context context, kadm5_config_params *params, int caller)
    531 {
    532 	struct stat	st;
    533 	krb5_error_code	retval;
    534 	uint32_t	ulog_filesize;
    535 	char		logname[MAX_FILENAME];
    536 	kdb_log_context	*log_ctx;
    537 	kdb_hlog_t	*ulog = NULL;
    538 	uint32_t	ulogentries;
    539 	int		ulogfd = -1;
    540 
    541 	if ((caller == FKADMIND) || (caller == FKCOMMAND))
    542 		ulogentries = params->iprop_ulogsize;
    543 
    544 	ulog_filesize = sizeof (kdb_hlog_t);
    545 
    546 	if (strlcpy(logname, params->dbname, MAX_FILENAME) >= MAX_FILENAME)
    547 		return (KRB5_LOG_ERROR);
    548 	if (strlcat(logname, ".ulog", MAX_FILENAME) >= MAX_FILENAME)
    549 		return (KRB5_LOG_ERROR);
    550 
    551 	if (stat(logname, &st) == -1) {
    552 
    553 		if (caller == FKPROPLOG) {
    554 			/*
    555 			 * File doesn't exist so we exit with kproplog
    556 			 */
    557 			return (errno);
    558 		}
    559 
    560 		if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) {
    561 			return (errno);
    562 		}
    563 
    564 		if (lseek(ulogfd, 0L, SEEK_CUR) == -1) {
    565 			return (errno);
    566 		}
    567 
    568 		if ((caller == FKADMIND) || (caller == FKCOMMAND))
    569 			ulog_filesize += ulogentries * ULOG_BLOCK;
    570 
    571 		if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
    572 			return (errno);
    573 		}
    574 
    575 		if (write(ulogfd, "+", 1) != 1) {
    576 			return (errno);
    577 		}
    578 
    579 	} else {
    580 
    581 		if ((ulogfd = open(logname, O_RDWR, 0600)) == -1) {
    582 			/*
    583 			 * Can't open existing log file
    584 			 */
    585 			return (errno);
    586 		}
    587 	}
    588 
    589 	if (caller == FKPROPLOG) {
    590 		fstat(ulogfd, &st);
    591 		ulog_filesize = st.st_size;
    592 
    593 		ulog = (kdb_hlog_t *)mmap(0, ulog_filesize,
    594 		    PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0);
    595 	} else {
    596 		/*
    597 		 * else kadmind, kpropd, & kcommands should udpate stores
    598 		 */
    599 		ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN,
    600 		    PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0);
    601 	}
    602 
    603 	if ((int)(ulog) == -1) {
    604 		/*
    605 		 * Can't map update log file to memory
    606 		 */
    607 		return (errno);
    608 	}
    609 
    610 	if (!context->kdblog_context) {
    611 		if (!(log_ctx = malloc(sizeof (kdb_log_context))))
    612 			return (errno);
    613 		context->kdblog_context = (void *)log_ctx;
    614 	} else
    615 		log_ctx = context->kdblog_context;
    616 	log_ctx->ulog = ulog;
    617 	log_ctx->ulogentries = ulogentries;
    618 	log_ctx->ulogfd = ulogfd;
    619 
    620 	if (ulog->kdb_hmagic != KDB_HMAGIC) {
    621 		if (ulog->kdb_hmagic == 0) {
    622 			/*
    623 			 * New update log
    624 			 */
    625 			(void) memset(ulog, 0, sizeof (kdb_hlog_t));
    626 
    627 			ulog->kdb_hmagic = KDB_HMAGIC;
    628 			ulog->db_version_num = KDB_VERSION;
    629 			ulog->kdb_state = KDB_STABLE;
    630 			ulog->kdb_block = ULOG_BLOCK;
    631 			if (!(caller == FKPROPLOG))
    632 				ulog_sync_header(ulog);
    633 		} else {
    634 			return (KRB5_LOG_CORRUPT);
    635 		}
    636 	}
    637 
    638 	if (caller == FKADMIND) {
    639 		switch (ulog->kdb_state) {
    640 			case KDB_STABLE:
    641 			case KDB_UNSTABLE:
    642 				/*
    643 				 * Log is currently un/stable, check anyway
    644 				 */
    645 				retval = ulog_check(context, ulog);
    646 				if (retval == KRB5_LOG_CORRUPT) {
    647 					return (retval);
    648 				}
    649 				break;
    650 			case KDB_CORRUPT:
    651 				return (KRB5_LOG_CORRUPT);
    652 			default:
    653 				/*
    654 				 * Invalid db state
    655 				 */
    656 				return (KRB5_LOG_ERROR);
    657 		}
    658 	} else if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
    659 		/*
    660 		 * kproplog and kpropd don't need to do anything else
    661 		 */
    662 		return (0);
    663 	}
    664 
    665 	/*
    666 	 * Reinit ulog if the log is being truncated or expanded after
    667 	 * we have circled.
    668 	 */
    669 	if (ulog->kdb_num != ulogentries) {
    670 		if ((ulog->kdb_num != 0) &&
    671 		    ((ulog->kdb_last_sno > ulog->kdb_num) ||
    672 		    (ulog->kdb_num > ulogentries))) {
    673 			(void) memset(ulog, 0, sizeof (kdb_hlog_t));
    674 
    675 			ulog->kdb_hmagic = KDB_HMAGIC;
    676 			ulog->db_version_num = KDB_VERSION;
    677 			ulog->kdb_state = KDB_STABLE;
    678 			ulog->kdb_block = ULOG_BLOCK;
    679 
    680 			ulog_sync_header(ulog);
    681 		}
    682 
    683 		/*
    684 		 * Expand ulog if we have specified a greater size
    685 		 */
    686 		if (ulog->kdb_num < ulogentries) {
    687 			ulog_filesize += ulogentries * ulog->kdb_block;
    688 
    689 			if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) {
    690 				return (errno);
    691 			}
    692 
    693 			if (write(ulogfd, "+", 1) != 1) {
    694 				return (errno);
    695 			}
    696 		}
    697 	}
    698 
    699 	return (0);
    700 }
    701 
    702 /*
    703  * Get the last set of updates seen, (last+1) to n is returned.
    704  */
    705 krb5_error_code
    706 ulog_get_entries(
    707 	krb5_context context,		/* input - krb5 lib config */
    708 	kdb_last_t last,		/* input - slave's last sno */
    709 	kdb_incr_result_t *ulog_handle)	/* output - incr result for slave */
    710 {
    711 	XDR			xdrs;
    712 	kdb_ent_header_t	*indx_log;
    713 	kdb_incr_update_t	*upd;
    714 	uint_t			indx, count, tdiff;
    715 	uint32_t		sno;
    716 	krb5_error_code		retval;
    717 	struct timeval		timestamp;
    718 	kdb_log_context		*log_ctx;
    719 	kdb_hlog_t		*ulog = NULL;
    720 	uint32_t		ulogentries;
    721 
    722 	INIT_ULOG(context);
    723 	ulogentries = log_ctx->ulogentries;
    724 
    725 	/*
    726 	 * Check to make sure we don't have a corrupt ulog first.
    727 	 */
    728 	if (ulog->kdb_state == KDB_CORRUPT) {
    729 		ulog_handle->ret = UPDATE_ERROR;
    730 		return (KRB5_LOG_CORRUPT);
    731 	}
    732 
    733 	gettimeofday(&timestamp, NULL);
    734 
    735 	tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds;
    736 	if (tdiff <= ULOG_IDLE_TIME) {
    737 		ulog_handle->ret = UPDATE_BUSY;
    738 		return (0);
    739 	}
    740 
    741 	/*
    742 	 * We need to lock out other processes here, such as kadmin.local,
    743 	 * since we are looking at the last_sno and looking up updates.  So
    744 	 * we can share with other readers.
    745 	 */
    746 	retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED);
    747 	if (retval)
    748 		return (retval);
    749 
    750 	/*
    751 	 * We may have overflowed the update log or we shrunk the log, or
    752 	 * the client's ulog has just been created.
    753 	 */
    754 	if ((last.last_sno > ulog->kdb_last_sno) ||
    755 	    (last.last_sno < ulog->kdb_first_sno) ||
    756 	    (last.last_sno == 0)) {
    757 		ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
    758 		(void) krb5_db_unlock(context);
    759 		ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
    760 		return (0);
    761 	} else if (last.last_sno <= ulog->kdb_last_sno) {
    762 		sno = last.last_sno;
    763 
    764 		indx = (sno - 1) % ulogentries;
    765 
    766 		indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
    767 
    768 		/*
    769 		 * Validate the time stamp just to make sure it was the same sno
    770 		 */
    771 		if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
    772 		    (indx_log->kdb_time.useconds == last.last_time.useconds)) {
    773 
    774 			/*
    775 			 * If we have the same sno we return success
    776 			 */
    777 			if (last.last_sno == ulog->kdb_last_sno) {
    778 				(void) krb5_db_unlock(context);
    779 				ulog_handle->ret = UPDATE_NIL;
    780 				return (0);
    781 			}
    782 
    783 			count = ulog->kdb_last_sno - sno;
    784 
    785 			ulog_handle->updates.kdb_ulog_t_val =
    786 			    (kdb_incr_update_t *)malloc(
    787 			    sizeof (kdb_incr_update_t) * count);
    788 
    789 			upd = ulog_handle->updates.kdb_ulog_t_val;
    790 
    791 			if (upd == NULL) {
    792 				(void) krb5_db_unlock(context);
    793 				ulog_handle->ret = UPDATE_ERROR;
    794 				return (errno);
    795 			}
    796 
    797 			while (sno < ulog->kdb_last_sno) {
    798 				indx = sno % ulogentries;
    799 
    800 				indx_log = (kdb_ent_header_t *)
    801 				    INDEX(ulog, indx);
    802 
    803 				(void) memset(upd, 0,
    804 				    sizeof (kdb_incr_update_t));
    805 				xdrmem_create(&xdrs,
    806 				    (char *)indx_log->entry_data,
    807 				    indx_log->kdb_entry_size, XDR_DECODE);
    808 				if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
    809 					(void) krb5_db_unlock(context);
    810 					ulog_handle->ret = UPDATE_ERROR;
    811 					return (KRB5_LOG_CONV);
    812 				}
    813 				/*
    814 				 * Mark commitment since we didn't
    815 				 * want to decode and encode the
    816 				 * incr update record the first time.
    817 				 */
    818 				upd->kdb_commit = indx_log->kdb_commit;
    819 
    820 				upd++;
    821 				sno++;
    822 			} /* while */
    823 
    824 			ulog_handle->updates.kdb_ulog_t_len = count;
    825 
    826 			ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
    827 			ulog_handle->lastentry.last_time.seconds =
    828 			    ulog->kdb_last_time.seconds;
    829 			ulog_handle->lastentry.last_time.useconds =
    830 			    ulog->kdb_last_time.useconds;
    831 			ulog_handle->ret = UPDATE_OK;
    832 
    833 			(void) krb5_db_unlock(context);
    834 
    835 			return (0);
    836 		} else {
    837 			/*
    838 			 * We have time stamp mismatch or we no longer have
    839 			 * the slave's last sno, so we brute force it
    840 			 */
    841 			(void) krb5_db_unlock(context);
    842 			ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
    843 
    844 			return (0);
    845 		}
    846 	}
    847 
    848 	/*
    849 	 * Should never get here, return error
    850 	 */
    851 	ulog_handle->ret = UPDATE_ERROR;
    852 	return (KRB5_LOG_ERROR);
    853 }
    854 
    855 krb5_error_code
    856 ulog_set_role(krb5_context ctx, iprop_role role)
    857 {
    858 	kdb_log_context	*log_ctx;
    859 
    860 	if (!ctx->kdblog_context) {
    861 		if (!(log_ctx = malloc(sizeof (kdb_log_context))))
    862 			return (errno);
    863 		ctx->kdblog_context = (void *)log_ctx;
    864 	} else
    865 		log_ctx = ctx->kdblog_context;
    866 
    867 	log_ctx->iproprole = role;
    868 
    869 	return (0);
    870 }
    871