summaryrefslogtreecommitdiff
path: root/src/os_windows/os_handle.c
blob: e6edc3ef834d50ddce162dee5ddb457f60f9c68b (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
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1998, 2012 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.
 */
int
__os_openhandle(env, name, flags, mode, fhpp)
	ENV *env;
	const char *name;
	int flags, mode;
	DB_FH **fhpp;
{
#ifdef DB_WINCE
	/*
	 * __os_openhandle API is not implemented on WinCE.
	 * It is not currently called from within the Berkeley DB library,
	 * so don't log the failure via the __db_err mechanism.
	 */
	return (EFAULT);
#else
	DB_FH *fhp;
	int ret, nrepeat, retries;

	/*
	 * 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);
	}

	retries = 0;
	for (nrepeat = 1; nrepeat < 4; ++nrepeat) {
		fhp->fd = _open(name, flags, mode);

		if (fhp->fd != -1) {
			ret = 0;
			break;
		}

		switch (ret = __os_posix_err(__os_get_syserr())) {
		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) {
		F_SET(fhp, DB_FH_OPENED);
		*fhpp = fhp;
		return (0);
	}

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

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

	ret = 0;

	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("0031",
			    "fileops: %s: close", "%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 (fhp->handle != INVALID_HANDLE_VALUE)
			RETRY_CHK((!CloseHandle(fhp->handle)), ret);
		else
#ifdef DB_WINCE
			ret = EFAULT;
#else
			RETRY_CHK((_close(fhp->fd)), ret);
#endif

		if (fhp->trunc_handle != INVALID_HANDLE_VALUE) {
			RETRY_CHK((!CloseHandle(fhp->trunc_handle)), t_ret);
			if (t_ret != 0 && ret == 0)
				ret = t_ret;
		}

		if (ret != 0) {
			__db_syserr(env, ret, DB_STR("0032",
			    "CloseHandle"));
			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);
}