1 /*-
2 * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file LICENSE for license information.
5 *
6 * $Id$
7 */
8
9 #include "db_config.h"
10
11 #include "db_int.h"
12
13 /*
14 * __os_openhandle --
15 * Open a file, using POSIX 1003.1 open flags.
16 *
17 * PUBLIC: int __os_openhandle
18 * PUBLIC: __P((ENV *, const char *, int, int, DB_FH **));
19 */
20 int
__os_openhandle(env,name,flags,mode,fhpp)21 __os_openhandle(env, name, flags, mode, fhpp)
22 ENV *env;
23 const char *name;
24 int flags, mode;
25 DB_FH **fhpp;
26 {
27 DB_FH *fhp;
28 u_int nrepeat, retries;
29 int fcntl_flags, ret;
30 #ifdef HAVE_VXWORKS
31 int newflags;
32 #endif
33 /*
34 * Allocate the file handle and copy the file name. We generally only
35 * use the name for verbose or error messages, but on systems where we
36 * can't unlink temporary files immediately, we use the name to unlink
37 * the temporary file when the file handle is closed.
38 *
39 * Lock the ENV handle and insert the new file handle on the list.
40 */
41 if ((ret = __os_calloc(env, 1, sizeof(DB_FH), &fhp)) != 0)
42 return (ret);
43 if ((ret = __os_strdup(env, name, &fhp->name)) != 0)
44 goto err;
45 if (env != NULL) {
46 MUTEX_LOCK(env, env->mtx_env);
47 TAILQ_INSERT_TAIL(&env->fdlist, fhp, q);
48 MUTEX_UNLOCK(env, env->mtx_env);
49 F_SET(fhp, DB_FH_ENVLINK);
50 }
51
52 /* If the application specified an interface, use it. */
53 if (DB_GLOBAL(j_open) != NULL) {
54 if ((fhp->fd = DB_GLOBAL(j_open)(name, flags, mode)) == -1) {
55 ret = USR_ERR(env, __os_posix_err(__os_get_syserr()));
56 goto err;
57 }
58 goto done;
59 }
60
61 retries = 0;
62 for (nrepeat = 1; nrepeat < 4; ++nrepeat) {
63 ret = 0;
64 #ifdef HAVE_VXWORKS
65 /*
66 * VxWorks does not support O_CREAT on open previously, you have
67 * to use creat() instead. (It does not support O_EXCL or O_TRUNC
68 * either, even though they are defined "for future support".)
69 * We really want the POSIX behavior that if O_CREAT is set,
70 * we open if it exists, or create it if it doesn't exist.
71 * If O_CREAT is specified, single thread and try to open the
72 * file. If successful, and O_EXCL return EEXIST. If
73 * unsuccessful call creat and then end single threading.
74 */
75 if (LF_ISSET(O_CREAT)) {
76 DB_BEGIN_SINGLE_THREAD;
77 newflags = flags & ~(O_CREAT | O_EXCL);
78 if ((fhp->fd = open(name, newflags, mode)) != -1) {
79 /*
80 * We need to mark the file opened at this
81 * point so that if we get any error below
82 * we will properly close the fd we just
83 * opened on the error path.
84 */
85 F_SET(fhp, DB_FH_OPENED);
86 if (LF_ISSET(O_EXCL)) {
87 /*
88 * If we get here, want O_EXCL create,
89 * and the file exists. Close and
90 * return EEXISTS.
91 */
92 DB_END_SINGLE_THREAD;
93 ret = USR_ERR(env, EEXIST);
94 goto err;
95 }
96 /*
97 * !!!
98 * Assume any error means non-existence.
99 * Unfortunately return values (even for
100 * non-existence) are driver specific so
101 * there is no single error we can use to
102 * verify we truly got the equivalent of
103 * ENOENT.
104 */
105 } else
106 #if defined(_WRS_VXWORKS_MAJOR) && (_WRS_VXWORKS_MAJOR >= 7)
107 fhp->fd = open(name, newflags | O_CREAT | O_EXCL, mode);
108 #else
109 fhp->fd = creat(name, newflags);
110 #endif
111 DB_END_SINGLE_THREAD;
112 } else
113 /* FALLTHROUGH */
114 #endif
115 #ifdef __VMS
116 /*
117 * !!!
118 * Open with full sharing on VMS.
119 *
120 * We use these flags because they are the ones set by the VMS
121 * CRTL mmap() call when it opens a file, and we have to be
122 * able to open files that mmap() has previously opened, e.g.,
123 * when we're joining already existing DB regions.
124 */
125 fhp->fd = open(name, flags, mode, "shr=get,put,upd,del,upi");
126 #else
127 fhp->fd = open(name, flags, mode);
128 #endif
129 if (fhp->fd != -1) {
130 ret = 0;
131 break;
132 }
133
134 ret = USR_ERR(env, __os_posix_err(__os_get_syserr()));
135 switch (ret) {
136 case EMFILE:
137 case ENFILE:
138 case ENOSPC:
139 /*
140 * If it's a "temporary" error, we retry up to 3 times,
141 * waiting up to 12 seconds. While it's not a problem
142 * if we can't open a database, an inability to open a
143 * log file is cause for serious dismay.
144 */
145 __os_yield(env, nrepeat * 2, 0);
146 break;
147 case EAGAIN:
148 case EBUSY:
149 case EINTR:
150 /*
151 * If an EAGAIN, EBUSY or EINTR, retry immediately for
152 * DB_RETRY times.
153 */
154 if (++retries < DB_RETRY)
155 --nrepeat;
156 break;
157 default:
158 /* Open is silent on error. */
159 goto err;
160 }
161 }
162
163 if (ret == 0) {
164 #if defined(HAVE_FCNTL_F_SETFD)
165 /* Deny file descriptor access to any child process. */
166 if ((fcntl_flags = fcntl(fhp->fd, F_GETFD)) == -1 ||
167 fcntl(fhp->fd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1) {
168 ret = USR_ERR(env, __os_get_syserr());
169 __db_syserr(env, ret, DB_STR("0001", "fcntl(F_SETFD)"));
170 ret = __os_posix_err(ret);
171 goto err;
172 }
173 #else
174 COMPQUIET(fcntl_flags, 0);
175 #endif
176
177 done: F_SET(fhp, DB_FH_OPENED);
178 *fhpp = fhp;
179 return (0);
180 }
181
182 err: (void)__os_closehandle(env, fhp);
183 return (ret);
184 }
185
186 /*
187 * __os_closehandle --
188 * Close a file.
189 *
190 * PUBLIC: int __os_closehandle __P((ENV *, DB_FH *));
191 */
192 int
__os_closehandle(env,fhp)193 __os_closehandle(env, fhp)
194 ENV *env;
195 DB_FH *fhp;
196 {
197 DB_ENV *dbenv;
198 int ret;
199
200 ret = 0;
201
202 /*
203 * If we linked the DB_FH handle into the ENV, it needs to be
204 * unlinked.
205 */
206 DB_ASSERT(env, env != NULL || !F_ISSET(fhp, DB_FH_ENVLINK));
207
208 if (env != NULL) {
209 dbenv = env->dbenv;
210 if (fhp->name != NULL && FLD_ISSET(
211 dbenv->verbose, DB_VERB_FILEOPS | DB_VERB_FILEOPS_ALL))
212 __db_msg(env, DB_STR_A("0163",
213 "fileops: close %s", "%s"), fhp->name);
214
215 if (F_ISSET(fhp, DB_FH_ENVLINK)) {
216 /*
217 * Lock the ENV handle and remove this file
218 * handle from the list.
219 */
220 MUTEX_LOCK(env, env->mtx_env);
221 TAILQ_REMOVE(&env->fdlist, fhp, q);
222 MUTEX_UNLOCK(env, env->mtx_env);
223 }
224 }
225
226 /* Discard any underlying system file reference. */
227 if (F_ISSET(fhp, DB_FH_OPENED)) {
228 if (DB_GLOBAL(j_close) != NULL)
229 ret = DB_GLOBAL(j_close)(fhp->fd);
230 else
231 RETRY_CHK((close(fhp->fd)), ret);
232 if (ret != 0) {
233 ret = USR_ERR(env, __os_posix_err(ret));
234 __db_syserr(env, ret, DB_STR("0164", "close"));
235
236 }
237 }
238
239 /* Unlink the file if we haven't already done so. */
240 if (F_ISSET(fhp, DB_FH_UNLINK))
241 (void)__os_unlink(env, fhp->name, 0);
242
243 if (fhp->name != NULL)
244 __os_free(env, fhp->name);
245 __os_free(env, fhp);
246
247 return (ret);
248 }
249