summaryrefslogtreecommitdiff
path: root/src/env/env_name.c
blob: a3a0b371666ccd3005898ff4ee79fd9cf0326ee6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996, 2012 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#include "db_config.h"

#include "db_int.h"

static int __db_fullpath
    __P((ENV *, const char *, const char *, int, int, char **));

#define	DB_ADDSTR(add) {						\
	/*								\
	 * The string might be NULL or zero-length, and the p[-1]	\
	 * might indirect to before the beginning of our buffer.	\
	 */								\
	if ((add) != NULL && (add)[0] != '\0') {			\
		/* If leading slash, start over. */			\
		if (__os_abspath(add)) {				\
			p = str;					\
			slash = 0;					\
		}							\
		/* Append to the current string. */			\
		len = strlen(add);					\
		if (slash)						\
			*p++ = PATH_SEPARATOR[0];			\
		memcpy(p, add, len);					\
		p += len;						\
		slash = strchr(PATH_SEPARATOR, p[-1]) == NULL;		\
	}								\
}

/*
 * __db_fullpath --
 *	Constructs a path name relative to the environment home, and optionally
 *	checks whether the file or directory exist.
 */
static int
__db_fullpath(env, dir, file, check_file, check_dir, namep)
	ENV *env;
	const char *dir;
	const char *file;
	int check_file;
	int check_dir;
	char **namep;
{
	size_t len;
	const char *home;
	char *p, *str;
	int isdir, ret, slash;

	/* All paths are relative to the environment home. */
	home = (env == NULL) ? NULL : env->db_home;

	len =
	    (home == NULL ? 0 : strlen(home) + 1) +
	    (dir == NULL ? 0 : strlen(dir) + 1) +
	    (file == NULL ? 0 : strlen(file) + 1);

	if ((ret = __os_malloc(env, len, &str)) != 0)
		return (ret);

	slash = 0;
	p = str;
	DB_ADDSTR(home);
	DB_ADDSTR(dir);
	*p = '\0';
	if (check_dir && (__os_exists(env, str, &isdir) != 0 || !isdir)) {
		__os_free(env, str);
		return (ENOENT);
	}
	DB_ADDSTR(file);
	*p = '\0';

	/*
	 * If we're opening a data file, see if it exists.  If not, keep
	 * trying.
	 */
	if (check_file && __os_exists(env, str, NULL) != 0) {
		__os_free(env, str);
		return (ENOENT);
	}

	if (namep == NULL)
		__os_free(env, str);
	else
		*namep = str;
	return (0);
}

#define	DB_CHECKFILE(file, dir, check_file, check_dir, namep, ret_dir) do { \
	ret = __db_fullpath(env, dir, file,				\
			check_file, check_dir, namep);			\
	if (ret == 0 && (ret_dir) != NULL)				\
		*(ret_dir) = (dir);					\
	if (ret != ENOENT)						\
		return (ret);						\
} while (0)

/*
 * __db_appname --
 *	Given an optional DB environment, directory and file name and type
 *	of call, build a path based on the ENV->open rules, and return
 *	it in allocated space.  Dirp can be used to specify a data directory
 *	to use.  If not and one is used then drip will contain a pointer
 *	to the directory name.
 *
 * PUBLIC: int __db_appname __P((ENV *, APPNAME,
 * PUBLIC:    const char *, const char **, char **));
 */
int
__db_appname(env, appname, file, dirp, namep)
	ENV *env;
	APPNAME appname;
	const char *file;
	const char **dirp;
	char **namep;
{
	DB_ENV *dbenv;
	char **ddp;
	const char *dir;
	int ret;

	dbenv = env->dbenv;
	dir = NULL;

	if (namep != NULL)
		*namep = NULL;

	/*
	 * Absolute path names are never modified.  If the file is an absolute
	 * path, we're done.
	 */
	if (file != NULL && __os_abspath(file))
		return (__os_strdup(env, file, namep));

	/*
	 * DB_APP_NONE:
	 *      DB_HOME/file
	 * DB_APP_DATA:
	 *      DB_HOME/DB_DATA_DIR/file
	 * DB_APP_LOG:
	 *      DB_HOME/DB_LOG_DIR/file
	 * DB_APP_TMP:
	 *      DB_HOME/DB_TMP_DIR/<create>
	 */
	switch (appname) {
	case DB_APP_NONE:
		break;
	case DB_APP_RECOVER:
	case DB_APP_DATA:
		/*
		 * First, step through the data_dir entries, if any, looking
		 * for the file.
		 */
		if (dbenv != NULL && dbenv->db_data_dir != NULL)
			for (ddp = dbenv->db_data_dir; *ddp != NULL; ddp++)
				DB_CHECKFILE(file, *ddp, 1, 0, namep, dirp);

		/* Second, look in the environment home directory. */
		DB_CHECKFILE(file, NULL, 1, 0, namep, dirp);

		/*
		 * Otherwise, we're going to create.  Use the specified
		 * directory unless we're in recovery and it doesn't exist.
		 */
		if (dirp != NULL && *dirp != NULL)
			DB_CHECKFILE(file, *dirp, 0,
			    appname == DB_APP_RECOVER, namep, dirp);

		/* Finally, use the create directory, if set. */
		if (dbenv != NULL && dbenv->db_create_dir != NULL)
			dir = dbenv->db_create_dir;
		break;
	case DB_APP_LOG:
		if (dbenv != NULL)
			dir = dbenv->db_log_dir;
		break;
	case DB_APP_TMP:
		if (dbenv != NULL)
			dir = dbenv->db_tmp_dir;
		break;
	case DB_APP_META:
		if (dbenv != NULL)
			dir = dbenv->db_md_dir;
		break;
	}

	/*
	 * Construct the full path.  For temporary files, it is an error if the
	 * directory does not exist: if it doesn't, checking whether millions
	 * of temporary files exist inside it takes a *very* long time.
	 */
	DB_CHECKFILE(file, dir, 0, appname == DB_APP_TMP, namep, dirp);

	return (ret);
}

/*
 * __db_tmp_open --
 *	Create a temporary file.
 *
 * PUBLIC: int __db_tmp_open __P((ENV *, u_int32_t, DB_FH **));
 */
int
__db_tmp_open(env, oflags, fhpp)
	ENV *env;
	u_int32_t oflags;
	DB_FH **fhpp;
{
	pid_t pid;
	int filenum, i, ipid, ret;
	char *path;
	char *firstx, *trv;

	DB_ASSERT(env, fhpp != NULL);
	*fhpp = NULL;

#define	DB_TRAIL	"BDBXXXXX"
	if ((ret = __db_appname(env, DB_APP_TMP, DB_TRAIL, NULL, &path)) != 0)
		goto done;

	/* Replace the X's with the process ID (in decimal). */
	__os_id(env->dbenv, &pid, NULL);
	ipid = (int)pid;
	if (ipid < 0)
		ipid = -ipid;
	for (trv = path + strlen(path); *--trv == 'X'; ipid /= 10)
		*trv = '0' + (u_char)(ipid % 10);
	firstx = trv + 1;

	/* Loop, trying to open a file. */
	for (filenum = 1;; filenum++) {
		if ((ret = __os_open(env, path, 0,
		    oflags | DB_OSO_CREATE | DB_OSO_EXCL | DB_OSO_TEMP,
		    DB_MODE_600, fhpp)) == 0) {
			ret = 0;
			goto done;
		}

		/*
		 * !!!:
		 * If we don't get an EEXIST error, then there's something
		 * seriously wrong.  Unfortunately, if the implementation
		 * doesn't return EEXIST for O_CREAT and O_EXCL regardless
		 * of other possible errors, we've lost.
		 */
		if (ret != EEXIST) {
			__db_err(env, ret, DB_STR_A("1586",
			    "temporary open: %s", "%s"), path);
			goto done;
		}

		/*
		 * Generate temporary file names in a backwards-compatible way.
		 * If pid == 12345, the result is:
		 *   <path>/DB12345 (tried above, the first time through).
		 *   <path>/DBa2345 ...  <path>/DBz2345
		 *   <path>/DBaa345 ...  <path>/DBaz345
		 *   <path>/DBba345, and so on.
		 *
		 * XXX
		 * This algorithm is O(n**2) -- that is, creating 100 temporary
		 * files requires 5,000 opens, creating 1000 files requires
		 * 500,000.  If applications open a lot of temporary files, we
		 * could improve performance by switching to timestamp-based
		 * file names.
		 */
		for (i = filenum, trv = firstx; i > 0; i = (i - 1) / 26)
			if (*trv++ == '\0') {
				ret = EINVAL;
				goto done;
			}

		for (i = filenum; i > 0; i = (i - 1) / 26)
			*--trv = 'a' + ((i - 1) % 26);
	}
done:
	__os_free(env, path);
	return (ret);
}