00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: log__archive_8c-source.html,v 1.1 2008年06月08日 10:20:14 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 00017 #include <errno.h> 00018 #include <stdlib.h> 00019 #include <string.h> 00020 #include <unistd.h> 00021 #endif 00022 00023 #ifdef HAVE_RPC 00024 #include "db_server.h" 00025 #endif 00026 00027 #include "db_int.h" 00028 #include "db_dispatch.h" 00029 #include "log.h" 00030 00031 #ifdef HAVE_RPC 00032 #include "gen_client_ext.h" 00033 #include "rpc_client_ext.h" 00034 #endif 00035 00036 static int __absname __P((DB_ENV *, char *, char *, char **)); 00037 static int __build_data __P((DB_ENV *, char *, char ***, void *(*)(size_t))); 00038 static int __cmpfunc __P((const void *, const void *)); 00039 static int __usermem __P((DB_ENV *, char ***, void *(*)(size_t))); 00040 00041 /* 00042 * CDB_log_archive -- 00043 * Supporting function for db_archive(1). 00044 */ 00045 int 00046 CDB_log_archive(dbenv, listp, flags, db_malloc) 00047 DB_ENV *dbenv; 00048 char ***listp; 00049 u_int32_t flags; 00050 void *(*db_malloc) __P((size_t)); 00051 { 00052 DBT rec; 00053 DB_LOG *dblp; 00054 DB_LSN stable_lsn; 00055 u_int32_t fnum; 00056 int array_size, n, ret; 00057 char **array, **arrayp, *name, *p, *pref, buf[MAXPATHLEN]; 00058 00059 #ifdef HAVE_RPC 00060 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00061 return (__dbcl_log_archive(dbenv, listp, flags, db_malloc)); 00062 #endif 00063 00064 PANIC_CHECK(dbenv); 00065 ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG); 00066 00067 name = NULL; 00068 dblp = dbenv->lg_handle; 00069 COMPQUIET(fnum, 0); 00070 00071 #define OKFLAGS (DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG) 00072 if (flags != 0) { 00073 if ((ret = 00074 CDB___db_fchk(dbenv, "CDB_log_archive", flags, OKFLAGS)) != 0) 00075 return (ret); 00076 if ((ret = 00077 CDB___db_fcchk(dbenv, 00078 "CDB_log_archive", flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0) 00079 return (ret); 00080 } 00081 00082 /* 00083 * Get the absolute pathname of the current directory. It would 00084 * be nice to get the shortest pathname of the database directory, 00085 * but that's just not possible. 00086 * 00087 * XXX 00088 * Can't trust getcwd(3) to set a valid errno. If it doesn't, just 00089 * guess that we ran out of memory. 00090 */ 00091 if (LF_ISSET(DB_ARCH_ABS)) { 00092 CDB___os_set_errno(0); 00093 if ((pref = getcwd(buf, sizeof(buf))) == NULL) { 00094 if (CDB___os_get_errno() == 0) 00095 CDB___os_set_errno(ENOMEM); 00096 return (CDB___os_get_errno()); 00097 } 00098 } else 00099 pref = NULL; 00100 00101 switch (LF_ISSET(~DB_ARCH_ABS)) { 00102 case DB_ARCH_DATA: 00103 return (__build_data(dbenv, pref, listp, db_malloc)); 00104 case DB_ARCH_LOG: 00105 memset(&rec, 0, sizeof(rec)); 00106 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00107 F_SET(&rec, DB_DBT_MALLOC); 00108 if ((ret = CDB_log_get(dbenv, &stable_lsn, &rec, DB_LAST)) != 0) 00109 return (ret); 00110 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00111 CDB___os_free(rec.data, rec.size); 00112 fnum = stable_lsn.file; 00113 break; 00114 case 0: 00115 if ((ret = CDB___log_findckp(dbenv, &stable_lsn)) != 0) { 00116 /* 00117 * A return of DB_NOTFOUND means that we didn't find 00118 * any records in the log (so we are not going to be 00119 * deleting any log files). 00120 */ 00121 if (ret != DB_NOTFOUND) 00122 return (ret); 00123 *listp = NULL; 00124 return (0); 00125 } 00126 /* Remove any log files before the last stable LSN. */ 00127 fnum = stable_lsn.file - 1; 00128 break; 00129 } 00130 00131 #define LIST_INCREMENT 64 00132 /* Get some initial space. */ 00133 array_size = 10; 00134 if ((ret = CDB___os_malloc(dbenv, 00135 sizeof(char *) * array_size, NULL, &array)) != 0) 00136 return (ret); 00137 array[0] = NULL; 00138 00139 /* Build an array of the file names. */ 00140 for (n = 0; fnum > 0; --fnum) { 00141 if ((ret = CDB___log_name(dblp, fnum, &name, NULL, 0)) != 0) 00142 goto err; 00143 if (CDB___os_exists(name, NULL) != 0) { 00144 if (LF_ISSET(DB_ARCH_LOG) && fnum == stable_lsn.file) 00145 continue; 00146 CDB___os_freestr(name); 00147 name = NULL; 00148 break; 00149 } 00150 00151 if (n >= array_size - 1) { 00152 array_size += LIST_INCREMENT; 00153 if ((ret = CDB___os_realloc(dbenv, 00154 sizeof(char *) * array_size, NULL, &array)) != 0) 00155 goto err; 00156 } 00157 00158 if (LF_ISSET(DB_ARCH_ABS)) { 00159 if ((ret = __absname(dbenv, 00160 pref, name, &array[n])) != 0) 00161 goto err; 00162 CDB___os_freestr(name); 00163 } else if ((p = CDB___db_rpath(name)) != NULL) { 00164 if ((ret = CDB___os_strdup(dbenv, p + 1, &array[n])) != 0) 00165 goto err; 00166 CDB___os_freestr(name); 00167 } else 00168 array[n] = name; 00169 00170 name = NULL; 00171 array[++n] = NULL; 00172 } 00173 00174 /* If there's nothing to return, we're done. */ 00175 if (n == 0) { 00176 *listp = NULL; 00177 ret = 0; 00178 goto err; 00179 } 00180 00181 /* Sort the list. */ 00182 qsort(array, (size_t)n, sizeof(char *), __cmpfunc); 00183 00184 /* Rework the memory. */ 00185 if ((ret = __usermem(dbenv, &array, db_malloc)) != 0) 00186 goto err; 00187 00188 *listp = array; 00189 return (0); 00190 00191 err: if (array != NULL) { 00192 for (arrayp = array; *arrayp != NULL; ++arrayp) 00193 CDB___os_freestr(*arrayp); 00194 CDB___os_free(array, sizeof(char *) * array_size); 00195 } 00196 if (name != NULL) 00197 CDB___os_freestr(name); 00198 return (ret); 00199 } 00200 00201 /* 00202 * __build_data -- 00203 * Build a list of datafiles for return. 00204 */ 00205 static int 00206 __build_data(dbenv, pref, listp, db_malloc) 00207 DB_ENV *dbenv; 00208 char *pref, ***listp; 00209 void *(*db_malloc) __P((size_t)); 00210 { 00211 DBT rec; 00212 DB_LSN lsn; 00213 __log_register_args *argp; 00214 u_int32_t rectype; 00215 int array_size, last, n, nxt, ret; 00216 char **array, **arrayp, *p, *real_name; 00217 00218 /* Get some initial space. */ 00219 array_size = 10; 00220 if ((ret = CDB___os_malloc(dbenv, 00221 sizeof(char *) * array_size, NULL, &array)) != 0) 00222 return (ret); 00223 array[0] = NULL; 00224 00225 memset(&rec, 0, sizeof(rec)); 00226 if (F_ISSET(dbenv, DB_ENV_THREAD)) 00227 F_SET(&rec, DB_DBT_MALLOC); 00228 for (n = 0, ret = CDB_log_get(dbenv, &lsn, &rec, DB_FIRST); 00229 ret == 0; ret = CDB_log_get(dbenv, &lsn, &rec, DB_NEXT)) { 00230 if (rec.size < sizeof(rectype)) { 00231 ret = EINVAL; 00232 CDB___db_err(dbenv, "CDB_log_archive: bad log record"); 00233 goto lg_free; 00234 } 00235 00236 memcpy(&rectype, rec.data, sizeof(rectype)); 00237 if (rectype != DB_log_register) { 00238 if (F_ISSET(dbenv, DB_ENV_THREAD)) { 00239 CDB___os_free(rec.data, rec.size); 00240 rec.data = NULL; 00241 } 00242 continue; 00243 } 00244 if ((ret = CDB___log_register_read(dbenv, rec.data, &argp)) != 0) { 00245 ret = EINVAL; 00246 CDB___db_err(dbenv, 00247 "CDB_log_archive: unable to read log record"); 00248 goto lg_free; 00249 } 00250 00251 if (n >= array_size - 1) { 00252 array_size += LIST_INCREMENT; 00253 if ((ret = CDB___os_realloc(dbenv, 00254 sizeof(char *) * array_size, NULL, &array)) != 0) 00255 goto lg_free; 00256 } 00257 00258 if ((ret = CDB___os_strdup(dbenv, 00259 argp->name.data, &array[n])) != 0) { 00260 lg_free: if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL) 00261 CDB___os_free(rec.data, rec.size); 00262 goto err1; 00263 } 00264 00265 array[++n] = NULL; 00266 CDB___os_free(argp, 0); 00267 00268 if (F_ISSET(dbenv, DB_ENV_THREAD)) { 00269 CDB___os_free(rec.data, rec.size); 00270 rec.data = NULL; 00271 } 00272 } 00273 00274 /* If there's nothing to return, we're done. */ 00275 if (n == 0) { 00276 ret = 0; 00277 *listp = NULL; 00278 goto err1; 00279 } 00280 00281 /* Sort the list. */ 00282 qsort(array, (size_t)n, sizeof(char *), __cmpfunc); 00283 00284 /* 00285 * Build the real pathnames, discarding nonexistent files and 00286 * duplicates. 00287 */ 00288 for (last = nxt = 0; nxt < n;) { 00289 /* 00290 * Discard duplicates. Last is the next slot we're going 00291 * to return to the user, nxt is the next slot that we're 00292 * going to consider. 00293 */ 00294 if (last != nxt) { 00295 array[last] = array[nxt]; 00296 array[nxt] = NULL; 00297 } 00298 for (++nxt; nxt < n && 00299 strcmp(array[last], array[nxt]) == 0; ++nxt) { 00300 CDB___os_freestr(array[nxt]); 00301 array[nxt] = NULL; 00302 } 00303 00304 /* Get the real name. */ 00305 if ((ret = CDB___db_appname(dbenv, 00306 DB_APP_DATA, NULL, array[last], 0, NULL, &real_name)) != 0) 00307 goto err2; 00308 00309 /* If the file doesn't exist, ignore it. */ 00310 if (CDB___os_exists(real_name, NULL) != 0) { 00311 CDB___os_freestr(real_name); 00312 CDB___os_freestr(array[last]); 00313 array[last] = NULL; 00314 continue; 00315 } 00316 00317 /* Rework the name as requested by the user. */ 00318 CDB___os_freestr(array[last]); 00319 array[last] = NULL; 00320 if (pref != NULL) { 00321 ret = __absname(dbenv, pref, real_name, &array[last]); 00322 CDB___os_freestr(real_name); 00323 if (ret != 0) 00324 goto err2; 00325 } else if ((p = CDB___db_rpath(real_name)) != NULL) { 00326 ret = CDB___os_strdup(dbenv, p + 1, &array[last]); 00327 CDB___os_freestr(real_name); 00328 if (ret != 0) 00329 goto err2; 00330 } else 00331 array[last] = real_name; 00332 ++last; 00333 } 00334 00335 /* NULL-terminate the list. */ 00336 array[last] = NULL; 00337 00338 /* Rework the memory. */ 00339 if ((ret = __usermem(dbenv, &array, db_malloc)) != 0) 00340 goto err1; 00341 00342 *listp = array; 00343 return (0); 00344 00345 err2: /* 00346 * XXX 00347 * We've possibly inserted NULLs into the array list, so clean up a 00348 * bit so that the other error processing works. 00349 */ 00350 if (array != NULL) 00351 for (; nxt < n; ++nxt) 00352 CDB___os_freestr(array[nxt]); 00353 /* FALLTHROUGH */ 00354 00355 err1: if (array != NULL) { 00356 for (arrayp = array; *arrayp != NULL; ++arrayp) 00357 CDB___os_freestr(*arrayp); 00358 CDB___os_free(array, array_size * sizeof(char *)); 00359 } 00360 return (ret); 00361 } 00362 00363 /* 00364 * __absname -- 00365 * Return an absolute path name for the file. 00366 */ 00367 static int 00368 __absname(dbenv, pref, name, newnamep) 00369 DB_ENV *dbenv; 00370 char *pref, *name, **newnamep; 00371 { 00372 size_t l_pref, l_name; 00373 int isabspath, ret; 00374 char *newname; 00375 00376 l_name = strlen(name); 00377 isabspath = CDB___os_abspath(name); 00378 l_pref = isabspath ? 0 : strlen(pref); 00379 00380 /* Malloc space for concatenating the two. */ 00381 if ((ret = CDB___os_malloc(dbenv, 00382 l_pref + l_name + 2, NULL, &newname)) != 0) 00383 return (ret); 00384 *newnamep = newname; 00385 00386 /* Build the name. If `name' is an absolute path, ignore any prefix. */ 00387 if (!isabspath) { 00388 memcpy(newname, pref, l_pref); 00389 if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL) 00390 newname[l_pref++] = PATH_SEPARATOR[0]; 00391 } 00392 memcpy(newname + l_pref, name, l_name + 1); 00393 00394 return (0); 00395 } 00396 00397 /* 00398 * __usermem -- 00399 * Create a single chunk of memory that holds the returned information. 00400 * If the user has their own malloc routine, use it. 00401 */ 00402 static int 00403 __usermem(dbenv, listp, db_malloc) 00404 DB_ENV *dbenv; 00405 char ***listp; 00406 void *(*db_malloc) __P((size_t)); 00407 { 00408 size_t len; 00409 int ret; 00410 char **array, **arrayp, **orig, *strp; 00411 00412 /* Find out how much space we need. */ 00413 for (len = 0, orig = *listp; *orig != NULL; ++orig) 00414 len += sizeof(char *) + strlen(*orig) + 1; 00415 len += sizeof(char *); 00416 00417 /* Allocate it and set up the pointers. */ 00418 if ((ret = CDB___os_malloc(dbenv, len, db_malloc, &array)) != 0) 00419 return (ret); 00420 00421 strp = (char *)(array + (orig - *listp) + 1); 00422 00423 /* Copy the original information into the new memory. */ 00424 for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) { 00425 len = strlen(*orig); 00426 memcpy(strp, *orig, len + 1); 00427 *arrayp = strp; 00428 strp += len + 1; 00429 00430 CDB___os_freestr(*orig); 00431 } 00432 00433 /* NULL-terminate the list. */ 00434 *arrayp = NULL; 00435 00436 CDB___os_free(*listp, 0); 00437 *listp = array; 00438 00439 return (0); 00440 } 00441 00442 static int 00443 __cmpfunc(p1, p2) 00444 const void *p1, *p2; 00445 { 00446 return (strcmp(*((char * const *)p1), *((char * const *)p2))); 00447 }