Home | History | Annotate | Download | only in log
      1 /*-
      2  * See the file LICENSE for redistribution information.
      3  *
      4  * Copyright (c) 1997, 1998
      5  *	Sleepycat Software.  All rights reserved.
      6  */
      7 
      8 #include "config.h"
      9 
     10 #ifndef lint
     11 static const char sccsid[] = "@(#)log_archive.c	10.44 (Sleepycat) 10/9/98";
     12 #endif /* not lint */
     13 
     14 #ifndef NO_SYSTEM_INCLUDES
     15 #include <sys/types.h>
     16 
     17 #include <errno.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <unistd.h>
     21 #endif
     22 
     23 #include "db_int.h"
     24 #include "db_dispatch.h"
     25 #include "shqueue.h"
     26 #include "log.h"
     27 #include "common_ext.h"
     28 #include "clib_ext.h"			/* XXX: needed for getcwd. */
     29 
     30 static int __absname __P((char *, char *, char **));
     31 static int __build_data __P((DB_LOG *, char *, char ***, void *(*)(size_t)));
     32 static int __cmpfunc __P((const void *, const void *));
     33 static int __usermem __P((char ***, void *(*)(size_t)));
     34 
     35 /*
     36  * log_archive --
     37  *	Supporting function for db_archive(1).
     38  */
     39 int
     40 log_archive(dblp, listp, flags, db_malloc)
     41 	DB_LOG *dblp;
     42 	char ***listp;
     43 	u_int32_t flags;
     44 	void *(*db_malloc) __P((size_t));
     45 {
     46 	DBT rec;
     47 	DB_LSN stable_lsn;
     48 	u_int32_t fnum;
     49 	int array_size, n, ret;
     50 	char **array, **arrayp, *name, *p, *pref, buf[MAXPATHLEN];
     51 
     52 	name = NULL;
     53 	COMPQUIET(fnum, 0);
     54 
     55 	LOG_PANIC_CHECK(dblp);
     56 
     57 #define	OKFLAGS	(DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG)
     58 	if (flags != 0) {
     59 		if ((ret =
     60 		    __db_fchk(dblp->dbenv, "log_archive", flags, OKFLAGS)) != 0)
     61 			return (ret);
     62 		if ((ret =
     63 		    __db_fcchk(dblp->dbenv,
     64 		        "log_archive", flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0)
     65 			return (ret);
     66 	}
     67 
     68 	/*
     69 	 * Get the absolute pathname of the current directory.  It would
     70 	 * be nice to get the shortest pathname of the database directory,
     71 	 * but that's just not possible.
     72 	 */
     73 	if (LF_ISSET(DB_ARCH_ABS)) {
     74 		errno = 0;
     75 		if ((pref = getcwd(buf, sizeof(buf))) == NULL)
     76 			return (errno == 0 ? ENOMEM : errno);
     77 	} else
     78 		pref = NULL;
     79 
     80 	switch (LF_ISSET(~DB_ARCH_ABS)) {
     81 	case DB_ARCH_DATA:
     82 		return (__build_data(dblp, pref, listp, db_malloc));
     83 	case DB_ARCH_LOG:
     84 		memset(&rec, 0, sizeof(rec));
     85 		if (F_ISSET(dblp, DB_AM_THREAD))
     86 			F_SET(&rec, DB_DBT_MALLOC);
     87 		if ((ret = log_get(dblp, &stable_lsn, &rec, DB_LAST)) != 0)
     88 			return (ret);
     89 		if (F_ISSET(dblp, DB_AM_THREAD))
     90 			__os_free(rec.data, rec.size);
     91 		fnum = stable_lsn.file;
     92 		break;
     93 	case 0:
     94 		if ((ret = __log_findckp(dblp, &stable_lsn)) != 0) {
     95 			/*
     96 			 * A return of DB_NOTFOUND means that we didn't find
     97 			 * any records in the log (so we are not going to be
     98 			 * deleting any log files).
     99 			 */
    100 			if (ret != DB_NOTFOUND)
    101 				return (ret);
    102 			*listp = NULL;
    103 			return (0);
    104 		}
    105 		/* Remove any log files before the last stable LSN. */
    106 		fnum = stable_lsn.file - 1;
    107 		break;
    108 	}
    109 
    110 #define	LIST_INCREMENT	64
    111 	/* Get some initial space. */
    112 	array_size = 10;
    113 	if ((ret = __os_malloc(sizeof(char *) * array_size, NULL, &array)) != 0)
    114 		return (ret);
    115 	array[0] = NULL;
    116 
    117 	/* Build an array of the file names. */
    118 	for (n = 0; fnum > 0; --fnum) {
    119 		if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0)
    120 			goto err;
    121 		if (__os_exists(name, NULL) != 0) {
    122 			__os_freestr(name);
    123 			name = NULL;
    124 			break;
    125 		}
    126 
    127 		if (n >= array_size - 1) {
    128 			array_size += LIST_INCREMENT;
    129 			if ((ret = __os_realloc(&array,
    130 			    sizeof(char *) * array_size)) != 0)
    131 				goto err;
    132 		}
    133 
    134 		if (LF_ISSET(DB_ARCH_ABS)) {
    135 			if ((ret = __absname(pref, name, &array[n])) != 0)
    136 				goto err;
    137 			__os_freestr(name);
    138 		} else if ((p = __db_rpath(name)) != NULL) {
    139 			if ((ret = __os_strdup(p + 1, &array[n])) != 0)
    140 				goto err;
    141 			__os_freestr(name);
    142 		} else
    143 			array[n] = name;
    144 
    145 		name = NULL;
    146 		array[++n] = NULL;
    147 	}
    148 
    149 	/* If there's nothing to return, we're done. */
    150 	if (n == 0) {
    151 		*listp = NULL;
    152 		ret = 0;
    153 		goto err;
    154 	}
    155 
    156 	/* Sort the list. */
    157 	qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
    158 
    159 	/* Rework the memory. */
    160 	if ((ret = __usermem(&array, db_malloc)) != 0)
    161 		goto err;
    162 
    163 	*listp = array;
    164 	return (0);
    165 
    166 err:	if (array != NULL) {
    167 		for (arrayp = array; *arrayp != NULL; ++arrayp)
    168 			__os_freestr(*arrayp);
    169 		__os_free(array, sizeof(char *) * array_size);
    170 	}
    171 	if (name != NULL)
    172 		__os_freestr(name);
    173 	return (ret);
    174 }
    175 
    176 /*
    177  * __build_data --
    178  *	Build a list of datafiles for return.
    179  */
    180 static int
    181 __build_data(dblp, pref, listp, db_malloc)
    182 	DB_LOG *dblp;
    183 	char *pref, ***listp;
    184 	void *(*db_malloc) __P((size_t));
    185 {
    186 	DBT rec;
    187 	DB_LSN lsn;
    188 	__log_register_args *argp;
    189 	u_int32_t rectype;
    190 	int array_size, last, n, nxt, ret;
    191 	char **array, **arrayp, *p, *real_name;
    192 
    193 	/* Get some initial space. */
    194 	array_size = 10;
    195 	if ((ret = __os_malloc(sizeof(char *) * array_size, NULL, &array)) != 0)
    196 		return (ret);
    197 	array[0] = NULL;
    198 
    199 	memset(&rec, 0, sizeof(rec));
    200 	if (F_ISSET(dblp, DB_AM_THREAD))
    201 		F_SET(&rec, DB_DBT_MALLOC);
    202 	for (n = 0, ret = log_get(dblp, &lsn, &rec, DB_FIRST);
    203 	    ret == 0; ret = log_get(dblp, &lsn, &rec, DB_NEXT)) {
    204 		if (rec.size < sizeof(rectype)) {
    205 			ret = EINVAL;
    206 			__db_err(dblp->dbenv, "log_archive: bad log record");
    207 			goto lg_free;
    208 		}
    209 
    210 		memcpy(&rectype, rec.data, sizeof(rectype));
    211 		if (rectype != DB_log_register) {
    212 			if (F_ISSET(dblp, DB_AM_THREAD)) {
    213 				__os_free(rec.data, rec.size);
    214 				rec.data = NULL;
    215 			}
    216 			continue;
    217 		}
    218 		if ((ret = __log_register_read(rec.data, &argp)) != 0) {
    219 			ret = EINVAL;
    220 			__db_err(dblp->dbenv,
    221 			    "log_archive: unable to read log record");
    222 			goto lg_free;
    223 		}
    224 
    225 		if (n >= array_size - 1) {
    226 			array_size += LIST_INCREMENT;
    227 			if ((ret = __os_realloc(&array,
    228 			    sizeof(char *) * array_size)) != 0)
    229 				goto lg_free;
    230 		}
    231 
    232 		if ((ret = __os_strdup(argp->name.data, &array[n])) != 0) {
    233 lg_free:		if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL)
    234 				__os_free(rec.data, rec.size);
    235 			goto err1;
    236 		}
    237 
    238 		array[++n] = NULL;
    239 		__os_free(argp, 0);
    240 
    241 		if (F_ISSET(dblp, DB_AM_THREAD)) {
    242 			__os_free(rec.data, rec.size);
    243 			rec.data = NULL;
    244 		}
    245 	}
    246 
    247 	/* If there's nothing to return, we're done. */
    248 	if (n == 0) {
    249 		ret = 0;
    250 		*listp = NULL;
    251 		goto err1;
    252 	}
    253 
    254 	/* Sort the list. */
    255 	qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
    256 
    257 	/*
    258 	 * Build the real pathnames, discarding nonexistent files and
    259 	 * duplicates.
    260 	 */
    261 	for (last = nxt = 0; nxt < n;) {
    262 		/*
    263 		 * Discard duplicates.  Last is the next slot we're going
    264 		 * to return to the user, nxt is the next slot that we're
    265 		 * going to consider.
    266 		 */
    267 		if (last != nxt) {
    268 			array[last] = array[nxt];
    269 			array[nxt] = NULL;
    270 		}
    271 		for (++nxt; nxt < n &&
    272 		    strcmp(array[last], array[nxt]) == 0; ++nxt) {
    273 			__os_freestr(array[nxt]);
    274 			array[nxt] = NULL;
    275 		}
    276 
    277 		/* Get the real name. */
    278 		if ((ret = __db_appname(dblp->dbenv,
    279 		    DB_APP_DATA, NULL, array[last], 0, NULL, &real_name)) != 0)
    280 			goto err2;
    281 
    282 		/* If the file doesn't exist, ignore it. */
    283 		if (__os_exists(real_name, NULL) != 0) {
    284 			__os_freestr(real_name);
    285 			__os_freestr(array[last]);
    286 			array[last] = NULL;
    287 			continue;
    288 		}
    289 
    290 		/* Rework the name as requested by the user. */
    291 		__os_freestr(array[last]);
    292 		array[last] = NULL;
    293 		if (pref != NULL) {
    294 			ret = __absname(pref, real_name, &array[last]);
    295 			__os_freestr(real_name);
    296 			if (ret != 0)
    297 				goto err2;
    298 		} else if ((p = __db_rpath(real_name)) != NULL) {
    299 			ret = __os_strdup(p + 1, &array[last]);
    300 			__os_freestr(real_name);
    301 			if (ret != 0)
    302 				goto err2;
    303 		} else
    304 			array[last] = real_name;
    305 		++last;
    306 	}
    307 
    308 	/* NULL-terminate the list. */
    309 	array[last] = NULL;
    310 
    311 	/* Rework the memory. */
    312 	if ((ret = __usermem(&array, db_malloc)) != 0)
    313 		goto err1;
    314 
    315 	*listp = array;
    316 	return (0);
    317 
    318 err2:	/*
    319 	 * XXX
    320 	 * We've possibly inserted NULLs into the array list, so clean up a
    321 	 * bit so that the other error processing works.
    322 	 */
    323 	if (array != NULL)
    324 		for (; nxt < n; ++nxt)
    325 			__os_freestr(array[nxt]);
    326 	/* FALLTHROUGH */
    327 
    328 err1:	if (array != NULL) {
    329 		for (arrayp = array; *arrayp != NULL; ++arrayp)
    330 			__os_freestr(*arrayp);
    331 		__os_free(array, array_size * sizeof(char *));
    332 	}
    333 	return (ret);
    334 }
    335 
    336 /*
    337  * __absname --
    338  *	Return an absolute path name for the file.
    339  */
    340 static int
    341 __absname(pref, name, newnamep)
    342 	char *pref, *name, **newnamep;
    343 {
    344 	size_t l_pref, l_name;
    345 	int isabspath, ret;
    346 	char *newname;
    347 
    348 	l_name = strlen(name);
    349 	isabspath = __os_abspath(name);
    350 	l_pref = isabspath ? 0 : strlen(pref);
    351 
    352 	/* Malloc space for concatenating the two. */
    353 	if ((ret = __os_malloc(l_pref + l_name + 2, NULL, &newname)) != 0)
    354 		return (ret);
    355 	*newnamep = newname;
    356 
    357 	/* Build the name.  If `name' is an absolute path, ignore any prefix. */
    358 	if (!isabspath) {
    359 		memcpy(newname, pref, l_pref);
    360 		if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL)
    361 			newname[l_pref++] = PATH_SEPARATOR[0];
    362 	}
    363 	memcpy(newname + l_pref, name, l_name + 1);
    364 
    365 	return (0);
    366 }
    367 
    368 /*
    369  * __usermem --
    370  *	Create a single chunk of memory that holds the returned information.
    371  *	If the user has their own malloc routine, use it.
    372  */
    373 static int
    374 __usermem(listp, db_malloc)
    375 	char ***listp;
    376 	void *(*db_malloc) __P((size_t));
    377 {
    378 	size_t len;
    379 	int ret;
    380 	char **array, **arrayp, **orig, *strp;
    381 
    382 	/* Find out how much space we need. */
    383 	for (len = 0, orig = *listp; *orig != NULL; ++orig)
    384 		len += sizeof(char *) + strlen(*orig) + 1;
    385 	len += sizeof(char *);
    386 
    387 	/* Allocate it and set up the pointers. */
    388 	if ((ret = __os_malloc(len, db_malloc, &array)) != 0)
    389 		return (ret);
    390 
    391 	strp = (char *)(array + (orig - *listp) + 1);
    392 
    393 	/* Copy the original information into the new memory. */
    394 	for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) {
    395 		len = strlen(*orig);
    396 		memcpy(strp, *orig, len + 1);
    397 		*arrayp = strp;
    398 		strp += len + 1;
    399 
    400 		__os_freestr(*orig);
    401 	}
    402 
    403 	/* NULL-terminate the list. */
    404 	*arrayp = NULL;
    405 
    406 	__os_free(*listp, 0);
    407 	*listp = array;
    408 
    409 	return (0);
    410 }
    411 
    412 static int
    413 __cmpfunc(p1, p2)
    414 	const void *p1, *p2;
    415 {
    416 	return (strcmp(*((char * const *)p1), *((char * const *)p2)));
    417 }
    418