1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1998,2008 Oracle. All rights reserved. 5 * 6 * $Id: os_handle.c,v 12.23 2008/01/31 18:40:46 bostic Exp $ 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 */ 20int 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 = __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, you have to use 67 * 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 = EEXIST; 94 goto err; 95 } 96 /* 97 * XXX 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 fhp->fd = creat(name, newflags); 107 DB_END_SINGLE_THREAD; 108 } else 109 /* FALLTHROUGH */ 110#endif 111#ifdef __VMS 112 /* 113 * !!! 114 * Open with full sharing on VMS. 115 * 116 * We use these flags because they are the ones set by the VMS 117 * CRTL mmap() call when it opens a file, and we have to be 118 * able to open files that mmap() has previously opened, e.g., 119 * when we're joining already existing DB regions. 120 */ 121 fhp->fd = open(name, flags, mode, "shr=get,put,upd,del,upi"); 122#else 123 fhp->fd = open(name, flags, mode); 124#endif 125 if (fhp->fd != -1) { 126 ret = 0; 127 break; 128 } 129 130 switch (ret = __os_posix_err(__os_get_syserr())) { 131 case EMFILE: 132 case ENFILE: 133 case ENOSPC: 134 /* 135 * If it's a "temporary" error, we retry up to 3 times, 136 * waiting up to 12 seconds. While it's not a problem 137 * if we can't open a database, an inability to open a 138 * log file is cause for serious dismay. 139 */ 140 __os_yield(env, nrepeat * 2, 0); 141 break; 142 case EAGAIN: 143 case EBUSY: 144 case EINTR: 145 /* 146 * If an EAGAIN, EBUSY or EINTR, retry immediately for 147 * DB_RETRY times. 148 */ 149 if (++retries < DB_RETRY) 150 --nrepeat; 151 break; 152 default: 153 /* Open is silent on error. */ 154 goto err; 155 } 156 } 157 158 if (ret == 0) { 159#if defined(HAVE_FCNTL_F_SETFD) 160 /* Deny file descriptor access to any child process. */ 161 if ((fcntl_flags = fcntl(fhp->fd, F_GETFD)) == -1 || 162 fcntl(fhp->fd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1) { 163 ret = __os_get_syserr(); 164 __db_syserr(env, ret, "fcntl(F_SETFD)"); 165 ret = __os_posix_err(ret); 166 goto err; 167 } 168#else 169 COMPQUIET(fcntl_flags, 0); 170#endif 171 172done: F_SET(fhp, DB_FH_OPENED); 173 *fhpp = fhp; 174 return (0); 175 } 176 177err: (void)__os_closehandle(env, fhp); 178 return (ret); 179} 180 181/* 182 * __os_closehandle -- 183 * Close a file. 184 * 185 * PUBLIC: int __os_closehandle __P((ENV *, DB_FH *)); 186 */ 187int 188__os_closehandle(env, fhp) 189 ENV *env; 190 DB_FH *fhp; 191{ 192 DB_ENV *dbenv; 193 int ret; 194 195 ret = 0; 196 197 /* 198 * If we linked the DB_FH handle into the ENV, it needs to be 199 * unlinked. 200 */ 201 DB_ASSERT(env, env != NULL || !F_ISSET(fhp, DB_FH_ENVLINK)); 202 203 if (env != NULL) { 204 dbenv = env->dbenv; 205 if (fhp->name != NULL && FLD_ISSET( 206 dbenv->verbose, DB_VERB_FILEOPS | DB_VERB_FILEOPS_ALL)) 207 __db_msg(env, "fileops: close %s", fhp->name); 208 209 if (F_ISSET(fhp, DB_FH_ENVLINK)) { 210 /* 211 * Lock the ENV handle and remove this file 212 * handle from the list. 213 */ 214 MUTEX_LOCK(env, env->mtx_env); 215 TAILQ_REMOVE(&env->fdlist, fhp, q); 216 MUTEX_UNLOCK(env, env->mtx_env); 217 } 218 } 219 220 /* Discard any underlying system file reference. */ 221 if (F_ISSET(fhp, DB_FH_OPENED)) { 222 if (DB_GLOBAL(j_close) != NULL) 223 ret = DB_GLOBAL(j_close)(fhp->fd); 224 else 225 RETRY_CHK((close(fhp->fd)), ret); 226 if (ret != 0) { 227 __db_syserr(env, ret, "close"); 228 ret = __os_posix_err(ret); 229 } 230 } 231 232 /* Unlink the file if we haven't already done so. */ 233 if (F_ISSET(fhp, DB_FH_UNLINK)) 234 (void)__os_unlink(env, fhp->name, 0); 235 236 if (fhp->name != NULL) 237 __os_free(env, fhp->name); 238 __os_free(env, fhp); 239 240 return (ret); 241} 242