00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 #include "config.h" 00008 00009 #ifndef lint 00010 static const char revid[] = "$Id: log__register_8c-source.html,v 1.1 2008年06月08日 10:20:30 sebdiaz Exp $"; 00011 #endif /* not lint */ 00012 00013 #ifndef NO_SYSTEM_INCLUDES 00014 #include <sys/types.h> 00015 00016 #include <errno.h> 00017 #include <string.h> 00018 #endif 00019 00020 #ifdef HAVE_RPC 00021 #include "db_server.h" 00022 #endif 00023 00024 #include "db_int.h" 00025 #include "log.h" 00026 00027 #ifdef HAVE_RPC 00028 #include "gen_client_ext.h" 00029 #include "rpc_client_ext.h" 00030 #endif 00031 00032 /* 00033 * CDB_log_register -- 00034 * Register a file name. 00035 */ 00036 int 00037 CDB_log_register(dbenv, dbp, name) 00038 DB_ENV *dbenv; 00039 DB *dbp; 00040 const char *name; 00041 { 00042 DBT fid_dbt, r_name; 00043 DB_LOG *dblp; 00044 DB_LSN r_unused; 00045 FNAME *found, *fnp, *recover_fnp, *reuse_fnp; 00046 LOG *lp; 00047 size_t len; 00048 int32_t maxid; 00049 int inserted, ok, ret; 00050 void *namep; 00051 00052 #ifdef HAVE_RPC 00053 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00054 return (__dbcl_log_register(dbenv, dbp, name)); 00055 #endif 00056 00057 PANIC_CHECK(dbenv); 00058 ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG); 00059 00060 dblp = dbenv->lg_handle; 00061 lp = dblp->reginfo.primary; 00062 fnp = reuse_fnp = NULL; 00063 inserted = ret = 0; 00064 namep = NULL; 00065 00066 /* Check the arguments. */ 00067 if (dbp->type != DB_BTREE && dbp->type != DB_QUEUE && 00068 dbp->type != DB_HASH && dbp->type != DB_RECNO) { 00069 CDB___db_err(dbenv, "CDB_log_register: unknown DB file type"); 00070 return (EINVAL); 00071 } 00072 00073 R_LOCK(dbenv, &dblp->reginfo); 00074 00075 /* 00076 * See if we've already got this file in the log, finding the 00077 * (maximum+1) in-use file id and some available file id (if we 00078 * find an available fid, we'll use it, else we'll have to allocate 00079 * one after the maximum that we found). 00080 */ 00081 ok = 0; 00082 found = NULL; 00083 recover_fnp = NULL; 00084 for (maxid = 0, fnp = SH_TAILQ_FIRST(&lp->fq, __fname); 00085 fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { 00086 if (F_ISSET(dblp, DBLOG_RECOVER) && fnp->id == dbp->log_fileid) 00087 recover_fnp = fnp; 00088 if (fnp->ref == 0) { /* Entry is not in use. */ 00089 if (reuse_fnp == NULL) 00090 reuse_fnp = fnp; 00091 continue; 00092 } 00093 if (memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN) == 0) { 00094 if (fnp->meta_pgno == 0) { 00095 if (fnp->locked == 1) { 00096 CDB___db_err(dbenv, "File is locked"); 00097 return (EINVAL); 00098 } 00099 if (found != NULL) 00100 goto found; 00101 ok = 1; 00102 } 00103 if (dbp->meta_pgno == fnp->meta_pgno) { 00104 if (F_ISSET(dblp, DBLOG_RECOVER)) { 00105 if (fnp->id != dbp->log_fileid) { 00106 /* 00107 * If we are in recovery, there 00108 * is only one dbp on the list. 00109 * If the refcount goes to 0, 00110 * we will clear the list. If 00111 * it doesn't, we want to leave 00112 * the dbp where it is, so 00113 * passing a NULL to rem_logid 00114 * is correct. 00115 */ 00116 CDB___log_rem_logid(dblp, 00117 NULL, fnp->id); 00118 if (recover_fnp != NULL) 00119 break; 00120 continue; 00121 } 00122 fnp->ref = 1; 00123 goto found; 00124 } 00125 ++fnp->ref; 00126 if (ok) 00127 goto found; 00128 found = fnp; 00129 } 00130 } 00131 if (maxid <= fnp->id) 00132 maxid = fnp->id + 1; 00133 } 00134 if ((fnp = found) != NULL) 00135 goto found; 00136 00137 /* Fill in fnp structure. */ 00138 if (recover_fnp != NULL) /* This has the right number */ 00139 fnp = recover_fnp; 00140 else if (reuse_fnp != NULL) /* Reuse existing one. */ 00141 fnp = reuse_fnp; 00142 else { /* Allocate a new one. */ 00143 if ((ret = CDB___db_shalloc(dblp->reginfo.addr, 00144 sizeof(FNAME), 0, &fnp)) != 0) 00145 goto mem_err; 00146 fnp->id = maxid; 00147 } 00148 00149 if (F_ISSET(dblp, DBLOG_RECOVER)) 00150 fnp->id = dbp->log_fileid; 00151 00152 fnp->ref = 1; 00153 fnp->locked = 0; 00154 fnp->s_type = dbp->type; 00155 memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN); 00156 fnp->meta_pgno = dbp->meta_pgno; 00157 00158 if (name != NULL) { 00159 len = strlen(name) + 1; 00160 if ((ret = 00161 CDB___db_shalloc(dblp->reginfo.addr, len, 0, &namep)) != 0) { 00162 mem_err: CDB___db_err(dbenv, 00163 "Unable to allocate memory to register %s", namep); 00164 goto err; 00165 } 00166 fnp->name_off = R_OFFSET(&dblp->reginfo, namep); 00167 memcpy(namep, name, len); 00168 } else 00169 fnp->name_off = INVALID_ROFF; 00170 00171 /* Only do the insert if we allocated a new fnp. */ 00172 if (reuse_fnp == NULL && recover_fnp == NULL) 00173 SH_TAILQ_INSERT_HEAD(&lp->fq, fnp, q, __fname); 00174 inserted = 1; 00175 00176 /* Log the registry. */ 00177 if (!F_ISSET(dblp, DBLOG_RECOVER)) { 00178 /* 00179 * We allow logging on in-memory databases, so the name here 00180 * could be NULL. 00181 */ 00182 if (name != NULL) { 00183 r_name.data = (void *)name; 00184 r_name.size = strlen(name) + 1; 00185 } 00186 memset(&fid_dbt, 0, sizeof(fid_dbt)); 00187 fid_dbt.data = dbp->fileid; 00188 fid_dbt.size = DB_FILE_ID_LEN; 00189 if ((ret = CDB___log_register_log(dbenv, NULL, &r_unused, 00190 0, LOG_OPEN, name == NULL ? NULL : &r_name, 00191 &fid_dbt, fnp->id, dbp->type, dbp->meta_pgno)) != 0) 00192 goto err; 00193 } 00194 00195 found: /* 00196 * If we found the entry in the shared area, then the file is 00197 * already open, so there is no need to log the open. We only 00198 * log the open and closes on the first open and last close. 00199 */ 00200 if (!F_ISSET(dblp, DBLOG_RECOVER) && 00201 (ret = CDB___log_add_logid(dbenv, dblp, dbp, fnp->id)) != 0) 00202 goto err; 00203 00204 if (!F_ISSET(dblp, DBLOG_RECOVER)) 00205 dbp->log_fileid = fnp->id; 00206 00207 if (0) { 00208 err: if (inserted) 00209 SH_TAILQ_REMOVE(&lp->fq, fnp, q, __fname); 00210 if (namep != NULL) 00211 CDB___db_shalloc_free(dblp->reginfo.addr, namep); 00212 if (fnp != NULL) 00213 CDB___db_shalloc_free(dblp->reginfo.addr, fnp); 00214 } 00215 00216 R_UNLOCK(dbenv, &dblp->reginfo); 00217 00218 return (ret); 00219 } 00220 00221 /* 00222 * CDB_log_unregister -- 00223 * Discard a registered file name. 00224 */ 00225 int 00226 CDB_log_unregister(dbenv, dbp) 00227 DB_ENV *dbenv; 00228 DB *dbp; 00229 { 00230 int ret; 00231 00232 #ifdef HAVE_RPC 00233 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00234 return (__dbcl_log_unregister(dbenv, dbp)); 00235 #endif 00236 00237 PANIC_CHECK(dbenv); 00238 ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG); 00239 00240 ret = CDB___log_filelist_update(dbenv, dbp, dbp->log_fileid, NULL, NULL); 00241 dbp->log_fileid = DB_LOGFILEID_INVALID; 00242 return (ret); 00243 } 00244 00245 /* 00246 * PUBLIC: int CDB___log_filelist_update 00247 * PUBLIC: __P((DB_ENV *, DB *, int32_t, const char *, int *)); 00248 * 00249 * Utility player for updating and logging the file list. Called 00250 * for 3 reasons: 00251 * 1) mark file closed: newname == NULL. 00252 * 2) change filename: newname != NULL. 00253 * 3) from recovery to verify and change filename if 00254 * neessary, set != NULL. 00255 */ 00256 int CDB___log_filelist_update(dbenv, dbp, fid, newname, set) 00257 DB_ENV *dbenv; 00258 DB *dbp; 00259 int32_t fid; 00260 const char *newname; 00261 int *set; 00262 { 00263 DBT fid_dbt, r_name; 00264 DB_LOG *dblp; 00265 DB_LSN r_unused; 00266 FNAME *fnp; 00267 LOG *lp; 00268 u_int32_t len, newlen; 00269 int ret; 00270 void *namep; 00271 00272 ret = 0; 00273 dblp = dbenv->lg_handle; 00274 lp = dblp->reginfo.primary; 00275 00276 R_LOCK(dbenv, &dblp->reginfo); 00277 00278 /* Find the entry in the log. */ 00279 for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); 00280 fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) 00281 if (fid == fnp->id) 00282 break; 00283 if (fnp == NULL) { 00284 CDB___db_err(dbenv, "CDB_log_unregister: non-existent file id"); 00285 ret = EINVAL; 00286 goto ret1; 00287 } 00288 00289 /* 00290 * Log the unregistry only if this is the last one and we are 00291 * really closing the file or if this is an abort of a created 00292 * file and we need to make sure that there is a record in the 00293 * log. 00294 */ 00295 namep = NULL; 00296 len = 0; 00297 if (fnp->name_off != INVALID_ROFF) { 00298 namep = R_ADDR(&dblp->reginfo, fnp->name_off); 00299 len = strlen(namep) + 1; 00300 } 00301 if (!F_ISSET(dblp, DBLOG_RECOVER) && fnp->ref == 1) { 00302 if (namep != NULL) { 00303 memset(&r_name, 0, sizeof(r_name)); 00304 r_name.data = namep; 00305 r_name.size = len; 00306 } 00307 memset(&fid_dbt, 0, sizeof(fid_dbt)); 00308 fid_dbt.data = fnp->ufid; 00309 fid_dbt.size = DB_FILE_ID_LEN; 00310 if ((ret = CDB___log_register_log(dbenv, NULL, &r_unused, 00311 0, LOG_CLOSE, 00312 fnp->name_off == INVALID_ROFF ? NULL : &r_name, 00313 &fid_dbt, fid, fnp->s_type, fnp->meta_pgno)) 00314 != 0) 00315 goto ret1; 00316 } 00317 00318 /* 00319 * If we are changing the name we must log this fact. 00320 */ 00321 if (newname != NULL) { 00322 DB_ASSERT(fnp->ref == 1); 00323 newlen = strlen(newname) + 1; 00324 if (!F_ISSET(dblp, DBLOG_RECOVER)) { 00325 r_name.data = (void *) newname; 00326 r_name.size = newlen; 00327 if ((ret = CDB___log_register_log(dbenv, 00328 NULL, &r_unused, 0, LOG_OPEN, &r_name, &fid_dbt, 00329 fnp->id, fnp->s_type, fnp->meta_pgno)) != 0) 00330 goto ret1; 00331 } 00332 00333 /* 00334 * Check to see if the name is already correct. 00335 */ 00336 if (set != NULL) { 00337 if (len != newlen || memcmp(namep, newname, len) != 0) 00338 *set = 1; 00339 else { 00340 *set = 0; 00341 goto ret1; 00342 } 00343 } 00344 00345 /* 00346 * Change the name, realloc memory if necessary 00347 */ 00348 if (len < newlen) { 00349 CDB___db_shalloc_free(dblp->reginfo.addr, 00350 R_ADDR(&dblp->reginfo, fnp->name_off)); 00351 if ((ret = CDB___db_shalloc( 00352 dblp->reginfo.addr, newlen, 0, &namep)) != 0) { 00353 CDB___db_err(dbenv, 00354 "Unable to allocate memory to register %s", 00355 namep); 00356 goto ret1; 00357 } 00358 fnp->name_off = R_OFFSET(&dblp->reginfo, namep); 00359 } else 00360 namep = R_ADDR(&dblp->reginfo, fnp->name_off); 00361 memcpy(namep, newname, newlen); 00362 } else { 00363 00364 /* 00365 * If more than 1 reference, just decrement the reference 00366 * and return. Otherwise, free the name if one exists. 00367 */ 00368 DB_ASSERT(fnp->ref >= 1); 00369 --fnp->ref; 00370 if (fnp->ref == 0) { 00371 if (fnp->name_off != INVALID_ROFF) 00372 CDB___db_shalloc_free(dblp->reginfo.addr, 00373 R_ADDR(&dblp->reginfo, fnp->name_off)); 00374 fnp->name_off = INVALID_ROFF; 00375 } 00376 00377 /* 00378 * Remove from the process local table. If this 00379 * operation is taking place during recovery, then 00380 * the logid was never added to the table, so do not remove it. 00381 */ 00382 if (!F_ISSET(dblp, DBLOG_RECOVER)) 00383 CDB___log_rem_logid(dblp, dbp, fid); 00384 } 00385 00386 ret1: R_UNLOCK(dbenv, &dblp->reginfo); 00387 return (ret); 00388 } 00389 00390 /* 00391 * CDB___log_file_lock -- lock a file for single access 00392 * This only works if logging is on. 00393 * 00394 * PUBLIC: int CDB___log_file_lock __P((DB *)); 00395 */ 00396 int 00397 CDB___log_file_lock(dbp) 00398 DB *dbp; 00399 { 00400 DB_ENV *dbenv; 00401 DB_LOG *dblp; 00402 FNAME *found, *fnp; 00403 LOG *lp; 00404 int ret; 00405 00406 dbenv = dbp->dbenv; 00407 dblp = dbenv->lg_handle; 00408 lp = dblp->reginfo.primary; 00409 00410 found = NULL; 00411 ret = 0; 00412 R_LOCK(dbenv, &dblp->reginfo); 00413 00414 for (fnp = SH_TAILQ_FIRST(&lp->fq, __fname); 00415 fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) { 00416 if (fnp->ref == 0) 00417 continue; 00418 00419 if (!memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN)) { 00420 if (fnp->meta_pgno == 0) { 00421 if (fnp->ref != 1) 00422 goto err; 00423 00424 fnp->locked = 1; 00425 found = fnp; 00426 } else { 00427 err: CDB___db_err(dbp->dbenv, "File is open"); 00428 ret = EINVAL; 00429 goto done; 00430 } 00431 00432 } 00433 } 00434 done: R_UNLOCK(dbenv, &dblp->reginfo); 00435 return (ret); 00436 }