dir.c revision 234010
1/*
2 * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id$ */
19
20/*! \file
21 * \author  Principal Authors: DCL */
22
23#include <config.h>
24
25#include <sys/types.h>
26#include <sys/stat.h>
27
28#include <ctype.h>
29#include <errno.h>
30#include <unistd.h>
31
32#include <isc/dir.h>
33#include <isc/magic.h>
34#include <isc/string.h>
35#include <isc/util.h>
36
37#include "errno2result.h"
38
39#define ISC_DIR_MAGIC		ISC_MAGIC('D', 'I', 'R', '*')
40#define VALID_DIR(dir)		ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
41
42void
43isc_dir_init(isc_dir_t *dir) {
44	REQUIRE(dir != NULL);
45
46	dir->entry.name[0] = '\0';
47	dir->entry.length = 0;
48
49	dir->handle = NULL;
50
51	dir->magic = ISC_DIR_MAGIC;
52}
53
54/*!
55 * \brief Allocate workspace and open directory stream. If either one fails,
56 * NULL will be returned.
57 */
58isc_result_t
59isc_dir_open(isc_dir_t *dir, const char *dirname) {
60	char *p;
61	isc_result_t result = ISC_R_SUCCESS;
62
63	REQUIRE(VALID_DIR(dir));
64	REQUIRE(dirname != NULL);
65
66	/*
67	 * Copy directory name.  Need to have enough space for the name,
68	 * a possible path separator, the wildcard, and the final NUL.
69	 */
70	if (strlen(dirname) + 3 > sizeof(dir->dirname))
71		/* XXXDCL ? */
72		return (ISC_R_NOSPACE);
73	strcpy(dir->dirname, dirname);
74
75	/*
76	 * Append path separator, if needed, and "*".
77	 */
78	p = dir->dirname + strlen(dir->dirname);
79	if (dir->dirname < p && *(p - 1) != '/')
80		*p++ = '/';
81	*p++ = '*';
82	*p = '\0';
83
84	/*
85	 * Open stream.
86	 */
87	dir->handle = opendir(dirname);
88
89	if (dir->handle == NULL)
90		return isc__errno2result(errno);
91
92	return (result);
93}
94
95/*!
96 * \brief Return previously retrieved file or get next one.
97
98 * Unix's dirent has
99 * separate open and read functions, but the Win32 and DOS interfaces open
100 * the dir stream and reads the first file in one operation.
101 */
102isc_result_t
103isc_dir_read(isc_dir_t *dir) {
104	struct dirent *entry;
105
106	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
107
108	/*
109	 * Fetch next file in directory.
110	 */
111	entry = readdir(dir->handle);
112
113	if (entry == NULL)
114		return (ISC_R_NOMORE);
115
116	/*
117	 * Make sure that the space for the name is long enough.
118	 */
119	if (sizeof(dir->entry.name) <= strlen(entry->d_name))
120	    return (ISC_R_UNEXPECTED);
121
122	strcpy(dir->entry.name, entry->d_name);
123
124	/*
125	 * Some dirents have d_namlen, but it is not portable.
126	 */
127	dir->entry.length = strlen(entry->d_name);
128
129	return (ISC_R_SUCCESS);
130}
131
132/*!
133 * \brief Close directory stream.
134 */
135void
136isc_dir_close(isc_dir_t *dir) {
137       REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
138
139       (void)closedir(dir->handle);
140       dir->handle = NULL;
141}
142
143/*!
144 * \brief Reposition directory stream at start.
145 */
146isc_result_t
147isc_dir_reset(isc_dir_t *dir) {
148	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
149
150	rewinddir(dir->handle);
151
152	return (ISC_R_SUCCESS);
153}
154
155isc_result_t
156isc_dir_chdir(const char *dirname) {
157	/*!
158	 * \brief Change the current directory to 'dirname'.
159	 */
160
161	REQUIRE(dirname != NULL);
162
163	if (chdir(dirname) < 0)
164		return (isc__errno2result(errno));
165
166	return (ISC_R_SUCCESS);
167}
168
169isc_result_t
170isc_dir_chroot(const char *dirname) {
171
172	REQUIRE(dirname != NULL);
173
174#ifdef HAVE_CHROOT
175	if (chroot(dirname) < 0 || chdir("/") < 0)
176		return (isc__errno2result(errno));
177
178	return (ISC_R_SUCCESS);
179#else
180	return (ISC_R_NOTIMPLEMENTED);
181#endif
182}
183
184isc_result_t
185isc_dir_createunique(char *templet) {
186	isc_result_t result;
187	char *x;
188	char *p;
189	int i;
190	int pid;
191
192	REQUIRE(templet != NULL);
193
194	/*!
195	 * \brief mkdtemp is not portable, so this emulates it.
196	 */
197
198	pid = getpid();
199
200	/*
201	 * Replace trailing Xs with the process-id, zero-filled.
202	 */
203	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
204	     x--, pid /= 10)
205		*x = pid % 10 + '0';
206
207	x++;			/* Set x to start of ex-Xs. */
208
209	do {
210		i = mkdir(templet, 0700);
211		if (i == 0 || errno != EEXIST)
212			break;
213
214		/*
215		 * The BSD algorithm.
216		 */
217		p = x;
218		while (*p != '\0') {
219			if (isdigit(*p & 0xff))
220				*p = 'a';
221			else if (*p != 'z')
222				++*p;
223			else {
224				/*
225				 * Reset character and move to next.
226				 */
227				*p++ = 'a';
228				continue;
229			}
230
231			break;
232		}
233
234		if (*p == '\0') {
235			/*
236			 * Tried all combinations.  errno should already
237			 * be EEXIST, but ensure it is anyway for
238			 * isc__errno2result().
239			 */
240			errno = EEXIST;
241			break;
242		}
243	} while (1);
244
245	if (i == -1)
246		result = isc__errno2result(errno);
247	else
248		result = ISC_R_SUCCESS;
249
250	return (result);
251}
252