summaryrefslogtreecommitdiff
path: root/src/os/os_handle.c
blob: 7dbe31e10d0a4a7eac4e3b54d2953e2de4c3919a (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
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1998, 2015 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id$
 */

#include "db_config.h"

#include "db_int.h"

/*
 * __os_openhandle --
 *	Open a file, using POSIX 1003.1 open flags.
 *
 * PUBLIC: int __os_openhandle
 * PUBLIC:     __P((ENV *, const char *, int, int, DB_FH **));
 */
int
__os_openhandle(env, name, flags, mode, fhpp)
	ENV *env;
	const char *name;
	int flags, mode;
	DB_FH **fhpp;
{
	DB_FH *fhp;
	u_int nrepeat, retries;
	int fcntl_flags, ret;
#ifdef HAVE_VXWORKS
	int newflags;
#endif
	/*
	 * Allocate the file handle and copy the file name.  We generally only
	 * use the name for verbose or error messages, but on systems where we
	 * can't unlink temporary files immediately, we use the name to unlink
	 * the temporary file when the file handle is closed.
	 *
	 * Lock the ENV handle and insert the new file handle on the list.
	 */
	if ((ret = __os_calloc(env, 1, sizeof(DB_FH), &fhp)) != 0)
		return (ret);
	if ((ret = __os_strdup(env, name, &fhp->name)) != 0)
		goto err;
	if (env != NULL) {
		MUTEX_LOCK(env, env->mtx_env);
		TAILQ_INSERT_TAIL(&env->fdlist, fhp, q);
		MUTEX_UNLOCK(env, env->mtx_env);
		F_SET(fhp, DB_FH_ENVLINK);
	}

	/* If the application specified an interface, use it. */
	if (DB_GLOBAL(j_open) != NULL) {
		if ((fhp->fd = DB_GLOBAL(j_open)(name, flags, mode)) == -1) {
			ret = __os_posix_err(__os_get_syserr());
			goto err;
		}
		goto done;
	}

	retries = 0;
	for (nrepeat = 1; nrepeat < 4; ++nrepeat) {
		ret = 0;
#ifdef	HAVE_VXWORKS
		/*
		 * VxWorks does not support O_CREAT on open, you have to use
		 * creat() instead.  (It does not support O_EXCL or O_TRUNC
		 * either, even though they are defined "for future support".)
		 * We really want the POSIX behavior that if O_CREAT is set,
		 * we open if it exists, or create it if it doesn't exist.
		 * If O_CREAT is specified, single thread and try to open the
		 * file.  If successful, and O_EXCL return EEXIST.  If
		 * unsuccessful call creat and then end single threading.
		 */
		if (LF_ISSET(O_CREAT)) {
			DB_BEGIN_SINGLE_THREAD;
			newflags = flags & ~(O_CREAT | O_EXCL);
			if ((fhp->fd = open(name, newflags, mode)) != -1) {
				/*
				 * We need to mark the file opened at this
				 * point so that if we get any error below
				 * we will properly close the fd we just
				 * opened on the error path.
				 */
				F_SET(fhp, DB_FH_OPENED);
				if (LF_ISSET(O_EXCL)) {
					/*
					 * If we get here, want O_EXCL create,
					 * and the file exists.  Close and
					 * return EEXISTS.
					 */
					DB_END_SINGLE_THREAD;
					ret = USR_ERR(env, EEXIST);
					goto err;
				}
				/*
				 * XXX
				 * Assume any error means non-existence.
				 * Unfortunately return values (even for
				 * non-existence) are driver specific so
				 * there is no single error we can use to
				 * verify we truly got the equivalent of
				 * ENOENT.
				 */
			} else
				fhp->fd = creat(name, newflags);
			DB_END_SINGLE_THREAD;
		} else
		/* FALLTHROUGH */
#endif
#ifdef __VMS
		/*
		 * !!!
		 * Open with full sharing on VMS.
		 *
		 * We use these flags because they are the ones set by the VMS
		 * CRTL mmap() call when it opens a file, and we have to be
		 * able to open files that mmap() has previously opened, e.g.,
		 * when we're joining already existing DB regions.
		 */
		fhp->fd = open(name, flags, mode, "shr=get,put,upd,del,upi");
#else
		fhp->fd = open(name, flags, mode);
#endif
		if (fhp->fd != -1) {
			ret = 0;
			break;
		}

		ret = __os_posix_err(__os_get_syserr());
		if (ret != ENOENT)
			(void)USR_ERR(env, ret);
		switch (ret) {
		case EMFILE:
		case ENFILE:
		case ENOSPC:
			/*
			 * If it's a "temporary" error, we retry up to 3 times,
			 * waiting up to 12 seconds.  While it's not a problem
			 * if we can't open a database, an inability to open a
			 * log file is cause for serious dismay.
			 */
			__os_yield(env, nrepeat * 2, 0);
			break;
		case EAGAIN:
		case EBUSY:
		case EINTR:
			/*
			 * If an EAGAIN, EBUSY or EINTR, retry immediately for
			 * DB_RETRY times.
			 */
			if (++retries < DB_RETRY)
				--nrepeat;
			break;
		default:
			/* Open is silent on error. */
			goto err;
		}
	}

	if (ret == 0) {
#if defined(HAVE_FCNTL_F_SETFD)
		/* Deny file descriptor access to any child process. */
		if ((fcntl_flags = fcntl(fhp->fd, F_GETFD)) == -1 ||
		    fcntl(fhp->fd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1) {
			ret = USR_ERR(env, __os_get_syserr());
			__db_syserr(env, ret, DB_STR("0162", "fcntl(F_SETFD)"));
			ret = __os_posix_err(ret);
			goto err;
		}
#else
		COMPQUIET(fcntl_flags, 0);
#endif

done:		F_SET(fhp, DB_FH_OPENED);
		*fhpp = fhp;
		return (0);
	}

err:	(void)__os_closehandle(env, fhp);
	return (ret);
}

/*
 * __os_closehandle --
 *	Close a file.
 *
 * PUBLIC: int __os_closehandle __P((ENV *, DB_FH *));
 */
int
__os_closehandle(env, fhp)
	ENV *env;
	DB_FH *fhp;
{
	DB_ENV *dbenv;
	int ret;

	ret = 0;

	/*
	 * If we linked the DB_FH handle into the ENV, it needs to be
	 * unlinked.
	 */
	DB_ASSERT(env, env != NULL || !F_ISSET(fhp, DB_FH_ENVLINK));

	if (env != NULL) {
		dbenv = env->dbenv;
		if (fhp->name != NULL && FLD_ISSET(
		    dbenv->verbose, DB_VERB_FILEOPS | DB_VERB_FILEOPS_ALL))
			__db_msg(env, DB_STR_A("0163",
			    "fileops: close %s", "%s"), fhp->name);

		if (F_ISSET(fhp, DB_FH_ENVLINK)) {
			/*
			 * Lock the ENV handle and remove this file
			 * handle from the list.
			 */
			MUTEX_LOCK(env, env->mtx_env);
			TAILQ_REMOVE(&env->fdlist, fhp, q);
			MUTEX_UNLOCK(env, env->mtx_env);
		}
	}

	/* Discard any underlying system file reference. */
	if (F_ISSET(fhp, DB_FH_OPENED)) {
		if (DB_GLOBAL(j_close) != NULL)
			ret = DB_GLOBAL(j_close)(fhp->fd);
		else
			RETRY_CHK((close(fhp->fd)), ret);
		if (ret != 0) {
			ret = USR_ERR(env, ret);
			__db_syserr(env, ret, DB_STR("0164", "close"));
			ret = __os_posix_err(ret);
		}
	}

	/* Unlink the file if we haven't already done so. */
	if (F_ISSET(fhp, DB_FH_UNLINK))
		(void)__os_unlink(env, fhp->name, 0);

	if (fhp->name != NULL)
		__os_free(env, fhp->name);
	__os_free(env, fhp);

	return (ret);
}