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