1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997,2008 Oracle.  All rights reserved.
5 *
6 * $Id: os_unlink.c,v 12.26 2008/05/07 12:27:35 bschmeck Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13/*
14 * __os_unlink --
15 *	Remove a file.
16 */
17int
18__os_unlink(env, path, overwrite_test)
19	ENV *env;
20	const char *path;
21	int overwrite_test;
22{
23	DB_ENV *dbenv;
24	HANDLE h;
25	_TCHAR *tpath, *orig_tpath, buf[DB_MAXPATHLEN];
26	u_int32_t id;
27	int ret, t_ret;
28
29	dbenv = env == NULL ? NULL : env->dbenv;
30
31	if (dbenv != NULL &&
32	    FLD_ISSET(dbenv->verbose, DB_VERB_FILEOPS | DB_VERB_FILEOPS_ALL))
33		__db_msg(env, "fileops: unlink %s", path);
34
35	/* Optionally overwrite the contents of the file to enhance security. */
36	if (dbenv != NULL && overwrite_test && F_ISSET(dbenv, DB_ENV_OVERWRITE))
37		(void)__db_file_multi_write(env, path);
38
39	TO_TSTRING(env, path, tpath, ret);
40	if (ret != 0)
41		return (ret);
42	orig_tpath = tpath;
43
44	LAST_PANIC_CHECK_BEFORE_IO(env);
45
46	/*
47	 * Windows NT and its descendants allow removal of open files, but the
48	 * DeleteFile Win32 system call isn't equivalent to a POSIX unlink.
49	 * Firstly, it only succeeds if FILE_SHARE_DELETE is set when the file
50	 * is opened.  Secondly, it leaves the file in a "zombie" state, where
51	 * it can't be opened again, but a new file with the same name can't be
52	 * created either.
53	 *
54	 * Since we depend on being able to recreate files (during recovery,
55	 * say), we have to first rename the file, and then delete it.  It
56	 * still hangs around, but with a name we don't care about.  The rename
57	 * will fail if the file doesn't exist, which isn't a problem, but if
58	 * it fails for some other reason, we need to know about it or a
59	 * subsequent open may fail for no apparent reason.
60	 */
61	if (__os_is_winnt()) {
62		__os_unique_id(env, &id);
63		_sntprintf(buf, DB_MAXPATHLEN, _T("%s.del.%010u"), tpath, id);
64		if (MoveFile(tpath, buf))
65			tpath = buf;
66		else {
67			ret = __os_get_syserr();
68			if (__os_posix_err(ret) != ENOENT)
69				__db_err(env, ret,
70				    "MoveFile: rename %s to temporary file",
71				    path);
72		}
73
74		/*
75		 * Try removing the file using the delete-on-close flag.  This
76		 * plays nicer with files that are still open than DeleteFile.
77		 */
78		h = CreateFile(tpath, 0,
79		    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
80		    NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
81		if (h != INVALID_HANDLE_VALUE) {
82			(void)CloseHandle (h);
83			if (GetFileAttributes(tpath) == INVALID_FILE_ATTRIBUTES)
84				goto skipdel;
85		}
86	}
87
88	RETRY_CHK((!DeleteFile(tpath)), ret);
89
90skipdel:
91	FREE_STRING(env, orig_tpath);
92
93	/*
94	 * XXX
95	 * We shouldn't be testing for an errno of ENOENT here, but ENOENT
96	 * signals that a file is missing, and we attempt to unlink things
97	 * (such as v. 2.x environment regions, in ENV->remove) that we
98	 * are expecting not to be there.  Reporting errors in these cases
99	 * is annoying.
100	 */
101	if (ret != 0) {
102		if ((t_ret = __os_posix_err(ret)) != ENOENT)
103			__db_syserr(env, ret, "DeleteFile: %s", path);
104		ret = t_ret;
105	}
106
107	return (ret);
108}
109