1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2000,2008 Oracle. All rights reserved. 5 * 6 * $Id: db_setid.c,v 12.32 2008/01/08 20:58:10 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/db_swap.h" 14#include "dbinc/db_am.h" 15#include "dbinc/mp.h" 16 17static int __env_fileid_reset __P((ENV *, 18 DB_THREAD_INFO *, const char *, int)); 19 20/* 21 * __env_fileid_reset_pp -- 22 * ENV->fileid_reset pre/post processing. 23 * 24 * PUBLIC: int __env_fileid_reset_pp __P((DB_ENV *, const char *, u_int32_t)); 25 */ 26int 27__env_fileid_reset_pp(dbenv, name, flags) 28 DB_ENV *dbenv; 29 const char *name; 30 u_int32_t flags; 31{ 32 DB_THREAD_INFO *ip; 33 ENV *env; 34 int ret; 35 36 env = dbenv->env; 37 38 ENV_ILLEGAL_BEFORE_OPEN(env, "DB_ENV->fileid_reset"); 39 40 /* 41 * !!! 42 * The actual argument checking is simple, do it inline, outside of 43 * the replication block. 44 */ 45 if (flags != 0 && flags != DB_ENCRYPT) 46 return (__db_ferr(env, "DB_ENV->fileid_reset", 0)); 47 48 ENV_ENTER(env, ip); 49 REPLICATION_WRAP(env, 50 (__env_fileid_reset(env, ip, name, LF_ISSET(DB_ENCRYPT) ? 1 : 0)), 51 1, ret); 52 ENV_LEAVE(env, ip); 53 return (ret); 54} 55 56/* 57 * __env_fileid_reset -- 58 * Reset the file IDs for every database in the file. 59 */ 60static int 61__env_fileid_reset(env, ip, name, encrypted) 62 ENV *env; 63 DB_THREAD_INFO *ip; 64 const char *name; 65 int encrypted; 66{ 67 DB *dbp; 68 DBC *dbcp; 69 DBT key, data; 70 DB_FH *fhp; 71 DB_MPOOLFILE *mpf; 72 DB_PGINFO cookie; 73 db_pgno_t pgno; 74 int t_ret, ret; 75 size_t n; 76 char *real_name; 77 u_int8_t fileid[DB_FILE_ID_LEN], mbuf[DBMETASIZE]; 78 void *pagep; 79 80 dbp = NULL; 81 dbcp = NULL; 82 fhp = NULL; 83 real_name = NULL; 84 85 /* Get the real backing file name. */ 86 if ((ret = 87 __db_appname(env, DB_APP_DATA, name, 0, NULL, &real_name)) != 0) 88 return (ret); 89 90 /* Get a new file ID. */ 91 if ((ret = __os_fileid(env, real_name, 1, fileid)) != 0) 92 goto err; 93 94 /* 95 * The user may have physically copied a file currently open in the 96 * cache, which means if we open this file through the cache before 97 * updating the file ID on page 0, we might connect to the file from 98 * which the copy was made. 99 */ 100 if ((ret = __os_open(env, real_name, 0, 0, 0, &fhp)) != 0) { 101 __db_err(env, ret, "%s", real_name); 102 goto err; 103 } 104 if ((ret = __os_read(env, fhp, mbuf, sizeof(mbuf), &n)) != 0) 105 goto err; 106 107 if (n != sizeof(mbuf)) { 108 ret = EINVAL; 109 __db_errx(env, 110 "%s: unexpected file type or format", real_name); 111 goto err; 112 } 113 114 /* 115 * Create the DB object. 116 */ 117 if ((ret = __db_create_internal(&dbp, env, 0)) != 0) 118 goto err; 119 120 /* If configured with a password, the databases are encrypted. */ 121 if (encrypted && (ret = __db_set_flags(dbp, DB_ENCRYPT)) != 0) 122 goto err; 123 124 if ((ret = __db_meta_setup(env, 125 dbp, real_name, (DBMETA *)mbuf, 0, DB_CHK_META)) != 0) 126 goto err; 127 memcpy(((DBMETA *)mbuf)->uid, fileid, DB_FILE_ID_LEN); 128 cookie.db_pagesize = sizeof(mbuf); 129 cookie.flags = dbp->flags; 130 cookie.type = dbp->type; 131 key.data = &cookie; 132 133 if ((ret = __db_pgout(env->dbenv, 0, mbuf, &key)) != 0) 134 goto err; 135 if ((ret = __os_seek(env, fhp, 0, 0, 0)) != 0) 136 goto err; 137 if ((ret = __os_write(env, fhp, mbuf, sizeof(mbuf), &n)) != 0) 138 goto err; 139 if ((ret = __os_fsync(env, fhp)) != 0) 140 goto err; 141 142 /* 143 * Page 0 of the file has an updated file ID, and we can open it in 144 * the cache without connecting to a different, existing file. Open 145 * the file in the cache, and update the file IDs for subdatabases. 146 * (No existing code, as far as I know, actually uses the file ID of 147 * a subdatabase, but it's cleaner to get them all.) 148 */ 149 150 /* 151 * Open the DB file. 152 * 153 * !!! 154 * Note DB_RDWRMASTER flag, we need to open the master database file 155 * for writing in this case. 156 */ 157 if ((ret = __db_open(dbp, ip, NULL, 158 name, NULL, DB_UNKNOWN, DB_RDWRMASTER, 0, PGNO_BASE_MD)) != 0) 159 goto err; 160 161 /* 162 * If the database file doesn't support subdatabases, we only have 163 * to update a single metadata page. Otherwise, we have to open a 164 * cursor and step through the master database, and update all of 165 * the subdatabases' metadata pages. 166 */ 167 if (!F_ISSET(dbp, DB_AM_SUBDB)) 168 goto err; 169 170 mpf = dbp->mpf; 171 memset(&key, 0, sizeof(key)); 172 memset(&data, 0, sizeof(data)); 173 if ((ret = __db_cursor(dbp, ip, NULL, &dbcp, 0)) != 0) 174 goto err; 175 while ((ret = __dbc_get(dbcp, &key, &data, DB_NEXT)) == 0) { 176 /* 177 * XXX 178 * We're handling actual data, not on-page meta-data, so it 179 * hasn't been converted to/from opposite endian architectures. 180 * Do it explicitly, now. 181 */ 182 memcpy(&pgno, data.data, sizeof(db_pgno_t)); 183 DB_NTOHL_SWAP(env, &pgno); 184 if ((ret = __memp_fget(mpf, &pgno, ip, NULL, 185 DB_MPOOL_DIRTY, &pagep)) != 0) 186 goto err; 187 memcpy(((DBMETA *)pagep)->uid, fileid, DB_FILE_ID_LEN); 188 if ((ret = __memp_fput(mpf, ip, pagep, dbcp->priority)) != 0) 189 goto err; 190 } 191 if (ret == DB_NOTFOUND) 192 ret = 0; 193 194err: if (dbcp != NULL && (t_ret = __dbc_close(dbcp)) != 0 && ret == 0) 195 ret = t_ret; 196 if (dbp != NULL && (t_ret = __db_close(dbp, NULL, 0)) != 0 && ret == 0) 197 ret = t_ret; 198 if (fhp != NULL && 199 (t_ret = __os_closehandle(env, fhp)) != 0 && ret == 0) 200 ret = t_ret; 201 if (real_name != NULL) 202 __os_free(env, real_name); 203 204 return (ret); 205} 206