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