1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: env_name.c,v 12.90 2008/01/11 20:49:59 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12 13static int __db_tmp_open __P((ENV *, u_int32_t, char *, DB_FH **)); 14 15#define DB_ADDSTR(add) { \ 16 /* \ 17 * The string might be NULL or zero-length, and the p[-1] \ 18 * might indirect to before the beginning of our buffer. \ 19 */ \ 20 if ((add) != NULL && (add)[0] != '\0') { \ 21 /* If leading slash, start over. */ \ 22 if (__os_abspath(add)) { \ 23 p = str; \ 24 slash = 0; \ 25 } \ 26 /* Append to the current string. */ \ 27 len = strlen(add); \ 28 if (slash) \ 29 *p++ = PATH_SEPARATOR[0]; \ 30 memcpy(p, add, len); \ 31 p += len; \ 32 slash = strchr(PATH_SEPARATOR, p[-1]) == NULL; \ 33 } \ 34} 35 36/* 37 * __db_appname -- 38 * Given an optional DB environment, directory and file name and type 39 * of call, build a path based on the ENV->open rules, and return 40 * it in allocated space. 41 * 42 * PUBLIC: int __db_appname __P((ENV *, APPNAME, 43 * PUBLIC: const char *, u_int32_t, DB_FH **, char **)); 44 */ 45int 46__db_appname(env, appname, file, tmp_oflags, fhpp, namep) 47 ENV *env; 48 APPNAME appname; 49 const char *file; 50 u_int32_t tmp_oflags; 51 DB_FH **fhpp; 52 char **namep; 53{ 54 DB_ENV *dbenv; 55 enum { TRY_NOTSET, TRY_DATA_DIR, TRY_ENV_HOME, TRY_CREATE } try_state; 56 size_t len, str_len; 57 int data_entry, ret, slash, tmp_create; 58 const char *a, *b; 59 char *p, *str; 60 61 dbenv = env->dbenv; 62 try_state = TRY_NOTSET; 63 a = b = NULL; 64 data_entry = 0; 65 tmp_create = 0; 66 67 /* 68 * We don't return a name when creating temporary files, just a file 69 * handle. Default to an error now. 70 */ 71 if (fhpp != NULL) 72 *fhpp = NULL; 73 if (namep != NULL) 74 *namep = NULL; 75 76 /* 77 * Absolute path names are never modified. If the file is an absolute 78 * path, we're done. 79 */ 80 if (file != NULL && __os_abspath(file)) 81 return (__os_strdup(env, file, namep)); 82 83 /* Everything else is relative to the environment home. */ 84 if (env != NULL) 85 a = env->db_home; 86 87retry: /* 88 * DB_APP_NONE: 89 * DB_HOME/file 90 * DB_APP_DATA: 91 * DB_HOME/DB_DATA_DIR/file 92 * DB_APP_LOG: 93 * DB_HOME/DB_LOG_DIR/file 94 * DB_APP_TMP: 95 * DB_HOME/DB_TMP_DIR/<create> 96 */ 97 switch (appname) { 98 case DB_APP_NONE: 99 break; 100 case DB_APP_DATA: 101 if (env == NULL || dbenv->db_data_dir == NULL) { 102 try_state = TRY_CREATE; 103 break; 104 } 105 106 /* 107 * First, step through the data_dir entries, if any, looking 108 * for the file. 109 */ 110 if ((b = dbenv->db_data_dir[data_entry]) != NULL) { 111 ++data_entry; 112 try_state = TRY_DATA_DIR; 113 break; 114 } 115 116 /* Second, look in the environment home directory. */ 117 if (try_state != TRY_ENV_HOME) { 118 try_state = TRY_ENV_HOME; 119 break; 120 } 121 122 /* Third, try creation in the first data_dir entry. */ 123 try_state = TRY_CREATE; 124 b = dbenv->db_data_dir[0]; 125 break; 126 case DB_APP_LOG: 127 if (env != NULL) 128 b = dbenv->db_log_dir; 129 break; 130 case DB_APP_TMP: 131 if (env != NULL) 132 b = dbenv->db_tmp_dir; 133 tmp_create = 1; 134 break; 135 } 136 137 len = 138 (a == NULL ? 0 : strlen(a) + 1) + 139 (b == NULL ? 0 : strlen(b) + 1) + 140 (file == NULL ? 0 : strlen(file) + 1); 141 142 /* 143 * Allocate space to hold the current path information, as well as any 144 * temporary space that we're going to need to create a temporary file 145 * name. 146 */ 147#define DB_TRAIL "BDBXXXXX" 148 str_len = len + sizeof(DB_TRAIL) + 10; 149 if ((ret = __os_malloc(env, str_len, &str)) != 0) 150 return (ret); 151 152 slash = 0; 153 p = str; 154 DB_ADDSTR(a); 155 DB_ADDSTR(b); 156 DB_ADDSTR(file); 157 *p = '\0'; 158 159 /* 160 * If we're opening a data file, see if it exists. If it does, 161 * return it, otherwise, try and find another one to open. 162 */ 163 if (appname == DB_APP_DATA && 164 __os_exists(env, str, NULL) != 0 && try_state != TRY_CREATE) { 165 __os_free(env, str); 166 b = NULL; 167 goto retry; 168 } 169 170 /* Create the file if so requested. */ 171 if (tmp_create && 172 (ret = __db_tmp_open(env, tmp_oflags, str, fhpp)) != 0) { 173 __os_free(env, str); 174 return (ret); 175 } 176 177 if (namep == NULL) 178 __os_free(env, str); 179 else 180 *namep = str; 181 return (0); 182} 183 184/* 185 * __db_tmp_open -- 186 * Create a temporary file. 187 */ 188static int 189__db_tmp_open(env, tmp_oflags, path, fhpp) 190 ENV *env; 191 u_int32_t tmp_oflags; 192 char *path; 193 DB_FH **fhpp; 194{ 195 pid_t pid; 196 int filenum, i, isdir, ret; 197 char *firstx, *trv; 198 199 /* 200 * Check the target directory; if you have six X's and it doesn't 201 * exist, this runs for a *very* long time. 202 */ 203 if ((ret = __os_exists(env, path, &isdir)) != 0) { 204 __db_err(env, ret, "%s", path); 205 return (ret); 206 } 207 if (!isdir) { 208 __db_err(env, EINVAL, "%s", path); 209 return (EINVAL); 210 } 211 212 /* Build the path. */ 213 (void)strncat(path, PATH_SEPARATOR, 1); 214 (void)strcat(path, DB_TRAIL); 215 216 /* Replace the X's with the process ID (in decimal). */ 217 __os_id(env->dbenv, &pid, NULL); 218 for (trv = path + strlen(path); *--trv == 'X'; pid /= 10) 219 *trv = '0' + (u_char)(pid % 10); 220 firstx = trv + 1; 221 222 /* Loop, trying to open a file. */ 223 for (filenum = 1;; filenum++) { 224 if ((ret = __os_open(env, path, 0, 225 tmp_oflags | DB_OSO_CREATE | DB_OSO_EXCL | DB_OSO_TEMP, 226 DB_MODE_600, fhpp)) == 0) 227 return (0); 228 229 /* 230 * !!!: 231 * If we don't get an EEXIST error, then there's something 232 * seriously wrong. Unfortunately, if the implementation 233 * doesn't return EEXIST for O_CREAT and O_EXCL regardless 234 * of other possible errors, we've lost. 235 */ 236 if (ret != EEXIST) { 237 __db_err(env, ret, "temporary open: %s", path); 238 return (ret); 239 } 240 241 /* 242 * Generate temporary file names in a backwards-compatible way. 243 * If pid == 12345, the result is: 244 * <path>/DB12345 (tried above, the first time through). 245 * <path>/DBa2345 ... <path>/DBz2345 246 * <path>/DBaa345 ... <path>/DBaz345 247 * <path>/DBba345, and so on. 248 * 249 * XXX 250 * This algorithm is O(n**2) -- that is, creating 100 temporary 251 * files requires 5,000 opens, creating 1000 files requires 252 * 500,000. If applications open a lot of temporary files, we 253 * could improve performance by switching to timestamp-based 254 * file names. 255 */ 256 for (i = filenum, trv = firstx; i > 0; i = (i - 1) / 26) 257 if (*trv++ == '\0') 258 return (EINVAL); 259 260 for (i = filenum; i > 0; i = (i - 1) / 26) 261 *--trv = 'a' + ((i - 1) % 26); 262 } 263 /* NOTREACHED */ 264} 265