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
|
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997-2002
* Sleepycat Software. All rights reserved.
*/
#include "db_config.h"
#ifndef lint
static const char revid[] = "$Id: os_open.c,v 1.1.1.1 2003/11/20 22:13:39 toshok Exp $";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#endif
#include "db_int.h"
/*
* __os_open --
* Open a file descriptor.
*/
int
__os_open(dbenv, name, flags, mode, fhp)
DB_ENV *dbenv;
const char *name;
u_int32_t flags;
int mode;
DB_FH *fhp;
{
DWORD bytesWritten;
u_int32_t log_size, pagesize, sectorsize;
int access, attr, oflags, share, createflag;
int ret, nrepeat;
char *drive, dbuf[4]; /* <letter><colon><slosh><nul> */
#ifdef DIAGNOSTIC
#define OKFLAGS \
(DB_OSO_CREATE | DB_OSO_DIRECT | DB_OSO_EXCL | DB_OSO_LOG | \
DB_OSO_RDONLY | DB_OSO_REGION | DB_OSO_SEQ | DB_OSO_TEMP | \
DB_OSO_TRUNC)
if ((ret = __db_fchk(dbenv, "__os_open", flags, OKFLAGS)) != 0)
return (ret);
#endif
/*
* The "public" interface to the __os_open routine passes around POSIX
* 1003.1 flags, not DB flags. If the user has defined their own open
* interface, use the POSIX flags.
*/
if (DB_GLOBAL(j_open) != NULL) {
oflags = O_BINARY | O_NOINHERIT;
if (LF_ISSET(DB_OSO_CREATE))
oflags |= O_CREAT;
if (LF_ISSET(DB_OSO_EXCL))
oflags |= O_EXCL;
if (LF_ISSET(DB_OSO_RDONLY))
oflags |= O_RDONLY;
else
oflags |= O_RDWR;
if (LF_ISSET(DB_OSO_SEQ))
oflags |= _O_SEQUENTIAL;
else
oflags |= _O_RANDOM;
if (LF_ISSET(DB_OSO_TEMP))
oflags |= _O_TEMPORARY;
if (LF_ISSET(DB_OSO_TRUNC))
oflags |= O_TRUNC;
return (__os_openhandle(dbenv, name, oflags, mode, fhp));
}
ret = 0;
if (LF_ISSET(DB_OSO_LOG))
log_size = fhp->log_size; /* XXX: Gag. */
pagesize = fhp->pagesize;
memset(fhp, 0, sizeof(*fhp));
fhp->fd = -1;
/*
* Otherwise, use the Windows/32 CreateFile interface so that we can
* play magic games with log files to get data flush effects similar
* to the POSIX O_DSYNC flag.
*
* !!!
* We currently ignore the 'mode' argument. It would be possible
* to construct a set of security attributes that we could pass to
* CreateFile that would accurately represents the mode. In worst
* case, this would require looking up user and all group names and
* creating an entry for each. Alternatively, we could call the
* _chmod (partial emulation) function after file creation, although
* this leaves us with an obvious race. However, these efforts are
* largely meaningless on FAT, the most common file system, which
* only has a "readable" and "writeable" flag, applying to all users.
*/
access = GENERIC_READ;
if (!LF_ISSET(DB_OSO_RDONLY))
access |= GENERIC_WRITE;
share = FILE_SHARE_READ | FILE_SHARE_WRITE;
attr = FILE_ATTRIBUTE_NORMAL;
/*
* Reproduce POSIX 1003.1 semantics: if O_CREATE and O_EXCL are both
* specified, fail, returning EEXIST, unless we create the file.
*/
if (LF_ISSET(DB_OSO_CREATE) && LF_ISSET(DB_OSO_EXCL))
createflag = CREATE_NEW; /* create only if !exist*/
else if (!LF_ISSET(DB_OSO_CREATE) && LF_ISSET(DB_OSO_TRUNC))
createflag = TRUNCATE_EXISTING; /* truncate, fail if !exist */
else if (LF_ISSET(DB_OSO_TRUNC))
createflag = CREATE_ALWAYS; /* create and truncate */
else if (LF_ISSET(DB_OSO_CREATE))
createflag = OPEN_ALWAYS; /* open or create */
else
createflag = OPEN_EXISTING; /* open only if existing */
if (LF_ISSET(DB_OSO_LOG)) {
F_SET(fhp, DB_FH_NOSYNC);
attr |= FILE_FLAG_WRITE_THROUGH;
}
if (LF_ISSET(DB_OSO_SEQ))
attr |= FILE_FLAG_SEQUENTIAL_SCAN;
else
attr |= FILE_FLAG_RANDOM_ACCESS;
if (LF_ISSET(DB_OSO_TEMP))
attr |= FILE_FLAG_DELETE_ON_CLOSE;
/*
* We can turn filesystem buffering off if the page size is a
* multiple of the disk's sector size. To find the sector size,
* we call GetDiskFreeSpace, which expects a drive name like "d:\\"
* or NULL for the current disk (i.e., a relative path)
*/
if (LF_ISSET(DB_OSO_DIRECT) && pagesize != 0 && name[0] != '\0') {
if (name[1] == ':') {
drive = dbuf;
snprintf(dbuf, sizeof(dbuf), "%c:\\", name[0]);
} else
drive = NULL;
if (GetDiskFreeSpace(drive, NULL, §orsize, NULL, NULL) &&
pagesize % sectorsize == 0)
attr |= FILE_FLAG_NO_BUFFERING;
}
for (nrepeat = 1;; ++nrepeat) {
fhp->handle =
CreateFile(name, access, share, NULL, createflag, attr, 0);
if (fhp->handle == INVALID_HANDLE_VALUE) {
/*
* 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.
*/
ret = __os_win32_errno();
if ((ret != ENFILE && ret != EMFILE && ret != ENOSPC) ||
nrepeat > 3)
goto err;
(void)__os_sleep(dbenv, nrepeat * 2, 0);
} else
break;
}
/*
* Special handling needed for log files. To get Windows to not update
* the MFT metadata on each write, extend the file to its maximum size.
* Windows will allocate all the data blocks and store them in the MFT
* (inode) area. In addition, flush the MFT area to disk.
* This strategy only works for Win/NT; Win/9X does not
* guarantee that the logs will be zero filled.
*/
if (LF_ISSET(DB_OSO_LOG) && log_size != 0 && __os_is_winnt()) {
if (SetFilePointer(fhp->handle,
log_size - 1, NULL, FILE_BEGIN) == (DWORD)-1)
goto err;
if (WriteFile(fhp->handle, "\x00", 1, &bytesWritten, NULL) == 0)
goto err;
if (bytesWritten != 1)
goto err;
if (SetEndOfFile(fhp->handle) == 0)
goto err;
if (SetFilePointer(
fhp->handle, 0, NULL, FILE_BEGIN) == (DWORD)-1)
goto err;
if (FlushFileBuffers(fhp->handle) == 0)
goto err;
}
F_SET(fhp, DB_FH_VALID);
return (0);
err: if (ret == 0)
ret = __os_win32_errno();
if (fhp->handle != INVALID_HANDLE_VALUE)
(void)CloseHandle(fhp->handle);
return (ret);
}
|