1258945Sroberto/*
2280849Scy * Copyright (C) 2004, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 1999-2001  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id$ */
19258945Sroberto
20258945Sroberto/* Principal Authors: DCL */
21258945Sroberto
22258945Sroberto#include <config.h>
23258945Sroberto
24258945Sroberto#include <string.h>
25258945Sroberto#include <direct.h>
26258945Sroberto#include <process.h>
27258945Sroberto#include <io.h>
28258945Sroberto
29258945Sroberto#include <sys/stat.h>
30258945Sroberto
31258945Sroberto#include <isc/dir.h>
32258945Sroberto#include <isc/magic.h>
33258945Sroberto#include <isc/assertions.h>
34258945Sroberto#include <isc/util.h>
35258945Sroberto
36258945Sroberto#include "errno2result.h"
37258945Sroberto
38258945Sroberto#define ISC_DIR_MAGIC		ISC_MAGIC('D', 'I', 'R', '*')
39258945Sroberto#define VALID_DIR(dir)		ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
40258945Sroberto
41258945Srobertostatic isc_result_t
42258945Srobertostart_directory(isc_dir_t *p);
43258945Sroberto
44258945Srobertovoid
45258945Srobertoisc_dir_init(isc_dir_t *dir) {
46258945Sroberto	REQUIRE(dir != NULL);
47258945Sroberto
48258945Sroberto	dir->dirname[0] = '\0';
49258945Sroberto
50258945Sroberto	dir->entry.name[0] = '\0';
51258945Sroberto	dir->entry.length = 0;
52258945Sroberto	memset(&(dir->entry.find_data), 0, sizeof(dir->entry.find_data));
53258945Sroberto
54258945Sroberto	dir->entry_filled = ISC_FALSE;
55258945Sroberto	dir->search_handle = INVALID_HANDLE_VALUE;
56258945Sroberto
57258945Sroberto	dir->magic = ISC_DIR_MAGIC;
58258945Sroberto}
59258945Sroberto
60258945Sroberto/*
61258945Sroberto * Allocate workspace and open directory stream. If either one fails,
62258945Sroberto * NULL will be returned.
63258945Sroberto */
64258945Srobertoisc_result_t
65258945Srobertoisc_dir_open(isc_dir_t *dir, const char *dirname) {
66258945Sroberto	char *p;
67258945Sroberto	isc_result_t result;
68258945Sroberto
69258945Sroberto	REQUIRE(dirname != NULL);
70258945Sroberto	REQUIRE(VALID_DIR(dir) && dir->search_handle == INVALID_HANDLE_VALUE);
71258945Sroberto
72258945Sroberto	/*
73258945Sroberto	 * Copy directory name.  Need to have enough space for the name,
74258945Sroberto	 * a possible path separator, the wildcard, and the final NUL.
75258945Sroberto	 */
76258945Sroberto	if (strlen(dirname) + 3 > sizeof(dir->dirname))
77258945Sroberto		/* XXXDCL ? */
78258945Sroberto		return (ISC_R_NOSPACE);
79258945Sroberto	strcpy(dir->dirname, dirname);
80258945Sroberto
81258945Sroberto	/*
82258945Sroberto	 * Append path separator, if needed, and "*".
83258945Sroberto	 */
84258945Sroberto	p = dir->dirname + strlen(dir->dirname);
85258945Sroberto	if (dir->dirname < p && *(p - 1) != '\\' && *(p - 1) != ':')
86258945Sroberto		*p++ = '\\';
87258945Sroberto	*p++ = '*';
88280849Scy	*p = '\0';
89258945Sroberto
90258945Sroberto	/*
91258945Sroberto	 * Open stream.
92258945Sroberto	 */
93258945Sroberto	result = start_directory(dir);
94258945Sroberto
95258945Sroberto	return (result);
96258945Sroberto}
97258945Sroberto
98258945Sroberto/*
99258945Sroberto * Return previously retrieved file or get next one.  Unix's dirent has
100258945Sroberto * separate open and read functions, but the Win32 and DOS interfaces open
101258945Sroberto * the dir stream and reads the first file in one operation.
102258945Sroberto */
103258945Srobertoisc_result_t
104258945Srobertoisc_dir_read(isc_dir_t *dir) {
105258945Sroberto	REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE);
106258945Sroberto
107258945Sroberto	if (dir->entry_filled)
108258945Sroberto		/*
109258945Sroberto		 * start_directory() already filled in the first entry.
110258945Sroberto		 */
111258945Sroberto		dir->entry_filled = ISC_FALSE;
112258945Sroberto
113258945Sroberto	else {
114258945Sroberto		/*
115258945Sroberto		 * Fetch next file in directory.
116258945Sroberto		 */
117258945Sroberto		if (FindNextFile(dir->search_handle,
118258945Sroberto				 &dir->entry.find_data) == FALSE)
119258945Sroberto			/*
120258945Sroberto			 * Either the last file has been processed or
121258945Sroberto			 * an error has occurred.  The former is not
122258945Sroberto			 * really an error, but the latter is.
123258945Sroberto			 */
124258945Sroberto			if (GetLastError() == ERROR_NO_MORE_FILES)
125258945Sroberto				return (ISC_R_NOMORE);
126258945Sroberto			else
127258945Sroberto				return (ISC_R_UNEXPECTED);
128258945Sroberto	}
129258945Sroberto
130258945Sroberto	/*
131258945Sroberto	 * Make sure that the space for the name is long enough.
132258945Sroberto	 */
133258945Sroberto	strcpy(dir->entry.name, dir->entry.find_data.cFileName);
134258945Sroberto	dir->entry.length = strlen(dir->entry.name);
135258945Sroberto
136258945Sroberto	return (ISC_R_SUCCESS);
137258945Sroberto}
138258945Sroberto
139258945Sroberto/*
140258945Sroberto * Close directory stream.
141258945Sroberto */
142258945Srobertovoid
143258945Srobertoisc_dir_close(isc_dir_t *dir) {
144258945Sroberto       REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE);
145258945Sroberto
146258945Sroberto       FindClose(dir->search_handle);
147258945Sroberto       dir->search_handle = INVALID_HANDLE_VALUE;
148258945Sroberto}
149258945Sroberto
150258945Sroberto/*
151258945Sroberto * Reposition directory stream at start.
152258945Sroberto */
153258945Srobertoisc_result_t
154258945Srobertoisc_dir_reset(isc_dir_t *dir) {
155258945Sroberto	isc_result_t result;
156258945Sroberto
157258945Sroberto	REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE);
158258945Sroberto	REQUIRE(dir->dirname != NULL);
159258945Sroberto
160258945Sroberto	/*
161258945Sroberto	 * NT cannot reposition the seek pointer to the beginning of the
162258945Sroberto	 * the directory stream, but rather the directory needs to be
163258945Sroberto	 * closed and reopened.  The latter might fail.
164258945Sroberto	 */
165258945Sroberto
166258945Sroberto	isc_dir_close(dir);
167258945Sroberto
168258945Sroberto	result = start_directory(dir);
169258945Sroberto
170258945Sroberto	return (result);
171258945Sroberto}
172258945Sroberto
173258945Sroberto/*
174258945Sroberto * Initialize isc_dir_t structure with new directory. The function
175258945Sroberto * returns 0 on failure and nonzero on success.
176258945Sroberto *
177258945Sroberto * Note:
178258945Sroberto * - Be sure to close previous stream before opening new one
179258945Sroberto */
180258945Srobertostatic isc_result_t
181258945Srobertostart_directory(isc_dir_t *dir)
182258945Sroberto{
183258945Sroberto	REQUIRE(VALID_DIR(dir));
184258945Sroberto	REQUIRE(dir->search_handle == INVALID_HANDLE_VALUE);
185258945Sroberto
186258945Sroberto	dir->entry_filled = ISC_FALSE;
187258945Sroberto
188258945Sroberto	/*
189258945Sroberto	 * Open stream and retrieve first file.
190258945Sroberto	 */
191258945Sroberto	dir->search_handle = FindFirstFile(dir->dirname,
192258945Sroberto					    &dir->entry.find_data);
193258945Sroberto
194258945Sroberto	if (dir->search_handle == INVALID_HANDLE_VALUE) {
195258945Sroberto		/*
196258945Sroberto		 * Something went wrong but we don't know what. GetLastError()
197258945Sroberto		 * could give us more information about the error, but the
198258945Sroberto		 * MSDN documentation is frustratingly thin about what
199258945Sroberto		 * possible errors could have resulted.  (Score one for
200258945Sroberto		 * the Unix manual pages.)  So there is just this lame error
201258945Sroberto		 * instead of being able to differentiate ISC_R_NOTFOUND
202258945Sroberto		 * from ISC_R_UNEXPECTED.
203258945Sroberto		 */
204258945Sroberto		return (ISC_R_FAILURE);
205258945Sroberto	}
206258945Sroberto
207258945Sroberto	/*
208258945Sroberto	 * Make sure that the space for the name is long enough.
209258945Sroberto	 */
210258945Sroberto	INSIST(sizeof(dir->entry.name) >
211258945Sroberto	       strlen(dir->entry.find_data.cFileName));
212258945Sroberto
213258945Sroberto	/*
214258945Sroberto	 * Fill in the data for the first entry of the directory.
215258945Sroberto	 */
216258945Sroberto	strcpy(dir->entry.name, dir->entry.find_data.cFileName);
217258945Sroberto	dir->entry.length = strlen(dir->entry.name);
218258945Sroberto
219258945Sroberto	dir->entry_filled = ISC_TRUE;
220258945Sroberto
221258945Sroberto	return (ISC_R_SUCCESS);
222258945Sroberto}
223258945Sroberto
224258945Srobertoisc_result_t
225258945Srobertoisc_dir_chdir(const char *dirname) {
226258945Sroberto	/*
227258945Sroberto	 * Change the current directory to 'dirname'.
228258945Sroberto	 */
229258945Sroberto
230258945Sroberto	REQUIRE(dirname != NULL);
231258945Sroberto
232258945Sroberto	if (chdir(dirname) < 0)
233258945Sroberto		return (isc__errno2result(errno));
234258945Sroberto
235258945Sroberto	return (ISC_R_SUCCESS);
236258945Sroberto}
237258945Sroberto
238258945Srobertoisc_result_t
239258945Srobertoisc_dir_chroot(const char *dirname) {
240258945Sroberto	return (ISC_R_NOTIMPLEMENTED);
241258945Sroberto}
242258945Sroberto
243258945Srobertoisc_result_t
244258945Srobertoisc_dir_createunique(char *templet) {
245258945Sroberto	isc_result_t result;
246258945Sroberto	char *x;
247258945Sroberto	char *p;
248258945Sroberto	int i;
249258945Sroberto	int pid;
250258945Sroberto
251258945Sroberto	REQUIRE(templet != NULL);
252258945Sroberto
253258945Sroberto	/*
254258945Sroberto	 * mkdtemp is not portable, so this emulates it.
255258945Sroberto	 */
256258945Sroberto
257258945Sroberto	pid = getpid();
258258945Sroberto
259258945Sroberto	/*
260258945Sroberto	 * Replace trailing Xs with the process-id, zero-filled.
261258945Sroberto	 */
262258945Sroberto	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
263258945Sroberto	     x--, pid /= 10)
264258945Sroberto		*x = pid % 10 + '0';
265258945Sroberto
266258945Sroberto	x++;			/* Set x to start of ex-Xs. */
267258945Sroberto
268258945Sroberto	do {
269258945Sroberto		i = mkdir(templet);
270258945Sroberto		i = chmod(templet, 0700);
271258945Sroberto
272258945Sroberto		if (i == 0 || errno != EEXIST)
273258945Sroberto			break;
274258945Sroberto
275258945Sroberto		/*
276258945Sroberto		 * The BSD algorithm.
277258945Sroberto		 */
278258945Sroberto		p = x;
279258945Sroberto		while (*p != '\0') {
280258945Sroberto			if (isdigit(*p & 0xff))
281258945Sroberto				*p = 'a';
282258945Sroberto			else if (*p != 'z')
283258945Sroberto				++*p;
284258945Sroberto			else {
285258945Sroberto				/*
286258945Sroberto				 * Reset character and move to next.
287258945Sroberto				 */
288258945Sroberto				*p++ = 'a';
289258945Sroberto				continue;
290258945Sroberto			}
291258945Sroberto
292258945Sroberto			break;
293258945Sroberto		}
294258945Sroberto
295258945Sroberto		if (*p == '\0') {
296258945Sroberto			/*
297258945Sroberto			 * Tried all combinations.  errno should already
298258945Sroberto			 * be EEXIST, but ensure it is anyway for
299258945Sroberto			 * isc__errno2result().
300258945Sroberto			 */
301258945Sroberto			errno = EEXIST;
302258945Sroberto			break;
303258945Sroberto		}
304258945Sroberto	} while (1);
305258945Sroberto
306258945Sroberto	if (i == -1)
307258945Sroberto		result = isc__errno2result(errno);
308258945Sroberto	else
309258945Sroberto		result = ISC_R_SUCCESS;
310258945Sroberto
311258945Sroberto	return (result);
312258945Sroberto}
313