1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: ex_apprec.c,v 12.9 2008/04/16 13:27:33 margo Exp $ 7 */ 8 9#include <sys/types.h> 10#include <sys/stat.h> 11 12#include <errno.h> 13#include <stddef.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17#include <unistd.h> 18 19#include "db_config.h" 20#include "db.h" 21#include "db_int.h" 22#include "dbinc/db_swap.h" 23 24#include "ex_apprec.h" 25 26int apprec_dispatch __P((DB_ENV *, DBT *, DB_LSN *, db_recops)); 27int open_env __P((const char *, FILE *, const char *, DB_ENV **)); 28int verify_absence __P((DB_ENV *, const char *)); 29int verify_presence __P((DB_ENV *, const char *)); 30 31int 32main(argc, argv) 33 int argc; 34 char *argv[]; 35{ 36 extern char *optarg; 37 DB_ENV *dbenv; 38 DB_LSN lsn; 39 DB_TXN *txn; 40 DBT dirnamedbt; 41 int ret; 42 const char *home; 43 char ch, dirname[256]; 44 const char *progname = "ex_apprec"; /* Program name. */ 45 46 /* Default home. */ 47 home = "TESTDIR"; 48 49 while ((ch = getopt(argc, argv, "h:")) != EOF) 50 switch (ch) { 51 case 'h': 52 home = optarg; 53 break; 54 default: 55 fprintf(stderr, "usage: %s [-h home]", progname); 56 exit(EXIT_FAILURE); 57 } 58 59 printf("Set up environment.\n"); 60 if ((ret = open_env(home, stderr, progname, &dbenv)) != 0) 61 return (EXIT_FAILURE); 62 63 printf("Create a directory in a transaction.\n"); 64 /* 65 * This application's convention is to log the full directory name, 66 * including trailing nul. 67 */ 68 memset(&dirnamedbt, 0, sizeof(dirnamedbt)); 69 sprintf(dirname, "%s/MYDIRECTORY", home); 70 dirnamedbt.data = dirname; 71 dirnamedbt.size = strlen(dirname) + 1; 72 73 if ((ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0) { 74 dbenv->err(dbenv, ret, "txn_begin"); 75 return (EXIT_FAILURE); 76 } 77 78 /* 79 * Remember, always log actions before you execute them! 80 * Since this log record is describing a file system operation and 81 * we have no control over when file system operations go to disk, 82 * we need to flush the log record immediately to ensure that the 83 * log record is on disk before the operation it describes. The 84 * flush would not be necessary were we doing an operation into the 85 * BDB mpool and using LSNs that mpool knew about. 86 */ 87 memset(&lsn, 0, sizeof(lsn)); 88 if ((ret = 89 ex_apprec_mkdir_log(dbenv, 90 txn, &lsn, DB_FLUSH, &dirnamedbt)) != 0) { 91 dbenv->err(dbenv, ret, "mkdir_log"); 92 return (EXIT_FAILURE); 93 } 94 if (mkdir(dirname, 0755) != 0) { 95 dbenv->err(dbenv, errno, "mkdir"); 96 return (EXIT_FAILURE); 97 } 98 99 printf("Verify the directory's presence: "); 100 verify_presence(dbenv, dirname); 101 printf("check.\n"); 102 103 /* Now abort the transaction and verify that the directory goes away. */ 104 printf("Abort the transaction.\n"); 105 if ((ret = txn->abort(txn)) != 0) { 106 dbenv->err(dbenv, ret, "txn_abort"); 107 return (EXIT_FAILURE); 108 } 109 110 printf("Verify the directory's absence: "); 111 verify_absence(dbenv, dirname); 112 printf("check.\n"); 113 114 /* Now do the same thing over again, only with a commit this time. */ 115 printf("Create a directory in a transaction.\n"); 116 memset(&dirnamedbt, 0, sizeof(dirnamedbt)); 117 sprintf(dirname, "%s/MYDIRECTORY", home); 118 dirnamedbt.data = dirname; 119 dirnamedbt.size = strlen(dirname) + 1; 120 if ((ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0) { 121 dbenv->err(dbenv, ret, "txn_begin"); 122 return (EXIT_FAILURE); 123 } 124 125 memset(&lsn, 0, sizeof(lsn)); 126 if ((ret = 127 ex_apprec_mkdir_log(dbenv, txn, &lsn, 0, &dirnamedbt)) != 0) { 128 dbenv->err(dbenv, ret, "mkdir_log"); 129 return (EXIT_FAILURE); 130 } 131 if (mkdir(dirname, 0755) != 0) { 132 dbenv->err(dbenv, errno, "mkdir"); 133 return (EXIT_FAILURE); 134 } 135 136 printf("Verify the directory's presence: "); 137 verify_presence(dbenv, dirname); 138 printf("check.\n"); 139 140 /* Now abort the transaction and verify that the directory goes away. */ 141 printf("Commit the transaction.\n"); 142 if ((ret = txn->commit(txn, 0)) != 0) { 143 dbenv->err(dbenv, ret, "txn_commit"); 144 return (EXIT_FAILURE); 145 } 146 147 printf("Verify the directory's presence: "); 148 verify_presence(dbenv, dirname); 149 printf("check.\n"); 150 151 printf("Now remove the directory, then run recovery.\n"); 152 if ((ret = dbenv->close(dbenv, 0)) != 0) { 153 fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret)); 154 return (EXIT_FAILURE); 155 } 156 if (rmdir(dirname) != 0) { 157 fprintf(stderr, 158 "%s: rmdir failed with error %s", progname, 159 strerror(errno)); 160 } 161 verify_absence(dbenv, dirname); 162 163 /* Opening with DB_RECOVER runs recovery. */ 164 if ((ret = open_env(home, stderr, progname, &dbenv)) != 0) 165 return (EXIT_FAILURE); 166 167 printf("Verify the directory's presence: "); 168 verify_presence(dbenv, dirname); 169 printf("check.\n"); 170 171 /* Close the handle. */ 172 if ((ret = dbenv->close(dbenv, 0)) != 0) { 173 fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret)); 174 return (EXIT_FAILURE); 175 } 176 177 return (EXIT_SUCCESS); 178} 179 180int 181open_env(home, errfp, progname, dbenvp) 182 const char *home, *progname; 183 FILE *errfp; 184 DB_ENV **dbenvp; 185{ 186 DB_ENV *dbenv; 187 int ret; 188 189 /* 190 * Create an environment object and initialize it for error 191 * reporting. 192 */ 193 if ((ret = db_env_create(&dbenv, 0)) != 0) { 194 fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); 195 return (ret); 196 } 197 dbenv->set_errfile(dbenv, errfp); 198 dbenv->set_errpfx(dbenv, progname); 199 200 /* Set up our custom recovery dispatch function. */ 201 if ((ret = dbenv->set_app_dispatch(dbenv, apprec_dispatch)) != 0) { 202 dbenv->err(dbenv, ret, "set_app_dispatch"); 203 return (ret); 204 } 205 206 /* 207 * Open the environment with full transactional support, running 208 * recovery. 209 */ 210 if ((ret = 211 dbenv->open(dbenv, home, DB_CREATE | DB_RECOVER | DB_INIT_LOCK | 212 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN, 0)) != 0) { 213 dbenv->err(dbenv, ret, "environment open: %s", home); 214 dbenv->close(dbenv, 0); 215 return (ret); 216 } 217 218 *dbenvp = dbenv; 219 return (0); 220} 221 222/* 223 * Sample application dispatch function to handle user-specified log record 224 * types. 225 */ 226int 227apprec_dispatch(dbenv, dbt, lsn, op) 228 DB_ENV *dbenv; 229 DBT *dbt; 230 DB_LSN *lsn; 231 db_recops op; 232{ 233 u_int32_t rectype; 234 235 /* Pull the record type out of the log record. */ 236 LOGCOPY_32(dbenv->env, &rectype, dbt->data); 237 238 switch (rectype) { 239 case DB_ex_apprec_mkdir: 240 return (ex_apprec_mkdir_recover(dbenv, dbt, lsn, op)); 241 default: 242 /* 243 * We've hit an unexpected, allegedly user-defined record 244 * type. 245 */ 246 dbenv->errx(dbenv, "Unexpected log record type encountered"); 247 return (EINVAL); 248 } 249} 250 251int 252verify_absence(dbenv, dirname) 253 DB_ENV *dbenv; 254 const char *dirname; 255{ 256 257 if (access(dirname, F_OK) == 0) { 258 dbenv->errx(dbenv, "Error--directory present!"); 259 exit(EXIT_FAILURE); 260 } 261 262 return (0); 263} 264 265int 266verify_presence(dbenv, dirname) 267 DB_ENV *dbenv; 268 const char *dirname; 269{ 270 271 if (access(dirname, F_OK) != 0) { 272 dbenv->errx(dbenv, "Error--directory not present!"); 273 exit(EXIT_FAILURE); 274 } 275 276 return (0); 277} 278