xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/unix/dir.c (revision 63372caa2f74032c7c1cb34e7cd32f28ad65b703)
1 /*	$NetBSD: dir.c,v 1.2 2024/08/18 20:47:15 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 #include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
41 
42 #define ISC_DIR_MAGIC		ISC_MAGIC('D', 'I', 'R', '*')
43 #define VALID_DIR(dir)		ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
44 
45 void
46 isc_dir_init(isc_dir_t *dir) {
47 	REQUIRE(dir != NULL);
48 
49 	dir->entry.name[0] = '\0';
50 	dir->entry.length = 0;
51 
52 	dir->handle = NULL;
53 
54 	dir->magic = ISC_DIR_MAGIC;
55 }
56 
57 /*!
58  * \brief Allocate workspace and open directory stream. If either one fails,
59  * NULL will be returned.
60  */
61 isc_result_t
62 isc_dir_open(isc_dir_t *dir, const char *dirname) {
63 	char *p;
64 	size_t octets;
65 	isc_result_t result = ISC_R_SUCCESS;
66 
67 	REQUIRE(VALID_DIR(dir));
68 	REQUIRE(dirname != NULL);
69 
70 	/*
71 	 * Copy directory name.  Need to have enough space for the name,
72 	 * a possible path separator, the wildcard, and the final NUL.
73 	 */
74 	octets = strlen(dirname) + 1;
75 	if (octets + 2 > sizeof(dir->dirname))
76 		/* XXXDCL ? */
77 		return (ISC_R_NOSPACE);
78 	strlcpy(dir->dirname, dirname, octets);
79 
80 	/*
81 	 * Append path separator, if needed, and "*".
82 	 */
83 	p = dir->dirname + strlen(dir->dirname);
84 	if (dir->dirname < p && *(p - 1) != '/')
85 		*p++ = '/';
86 	*p++ = '*';
87 	*p = '\0';
88 
89 	/*
90 	 * Open stream.
91 	 */
92 	dir->handle = opendir(dirname);
93 
94 	if (dir->handle == NULL)
95 		return isc__errno2result(errno);
96 
97 	return (result);
98 }
99 
100 /*!
101  * \brief Return previously retrieved file or get next one.
102 
103  * Unix's dirent has
104  * separate open and read functions, but the Win32 and DOS interfaces open
105  * the dir stream and reads the first file in one operation.
106  */
107 isc_result_t
108 isc_dir_read(isc_dir_t *dir) {
109 	struct dirent *entry;
110 	size_t octets;
111 
112 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
113 
114 	/*
115 	 * Fetch next file in directory.
116 	 */
117 	entry = readdir(dir->handle);
118 
119 	if (entry == NULL)
120 		return (ISC_R_NOMORE);
121 
122 	/*
123 	 * Make sure that the space for the name is long enough.
124 	 */
125 	octets = strlen(entry->d_name) + 1;
126 	if (sizeof(dir->entry.name) < octets)
127 		return (ISC_R_UNEXPECTED);
128 
129 	strlcpy(dir->entry.name, entry->d_name, octets);
130 
131 	/*
132 	 * Some dirents have d_namlen, but it is not portable.
133 	 */
134 	dir->entry.length = strlen(entry->d_name);
135 
136 	return (ISC_R_SUCCESS);
137 }
138 
139 /*!
140  * \brief Close directory stream.
141  */
142 void
143 isc_dir_close(isc_dir_t *dir) {
144        REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
145 
146        (void)closedir(dir->handle);
147        dir->handle = NULL;
148 }
149 
150 /*!
151  * \brief Reposition directory stream at start.
152  */
153 isc_result_t
154 isc_dir_reset(isc_dir_t *dir) {
155 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
156 
157 	rewinddir(dir->handle);
158 
159 	return (ISC_R_SUCCESS);
160 }
161 
162 isc_result_t
163 isc_dir_chdir(const char *dirname) {
164 	/*!
165 	 * \brief Change the current directory to 'dirname'.
166 	 */
167 
168 	REQUIRE(dirname != NULL);
169 
170 	if (chdir(dirname) < 0)
171 		return (isc__errno2result(errno));
172 
173 	return (ISC_R_SUCCESS);
174 }
175 
176 isc_result_t
177 isc_dir_chroot(const char *dirname) {
178 
179 	REQUIRE(dirname != NULL);
180 
181 #ifdef HAVE_CHROOT
182 	if (chroot(dirname) < 0 || chdir("/") < 0)
183 		return (isc__errno2result(errno));
184 
185 	return (ISC_R_SUCCESS);
186 #else
187 	return (ISC_R_NOTIMPLEMENTED);
188 #endif
189 }
190 
191 isc_result_t
192 isc_dir_createunique(char *templet) {
193 	isc_result_t result;
194 	char *x;
195 	char *p;
196 	int i;
197 	int pid;
198 
199 	REQUIRE(templet != NULL);
200 
201 	/*!
202 	 * \brief mkdtemp is not portable, so this emulates it.
203 	 */
204 
205 	pid = getpid();
206 
207 	/*
208 	 * Replace trailing Xs with the process-id, zero-filled.
209 	 */
210 	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
211 	     x--, pid /= 10)
212 		*x = pid % 10 + '0';
213 
214 	x++;			/* Set x to start of ex-Xs. */
215 
216 	do {
217 		i = mkdir(templet, 0700);
218 		if (i == 0 || errno != EEXIST)
219 			break;
220 
221 		/*
222 		 * The BSD algorithm.
223 		 */
224 		p = x;
225 		while (*p != '\0') {
226 			if (isdigit(*p & 0xff))
227 				*p = 'a';
228 			else if (*p != 'z')
229 				++*p;
230 			else {
231 				/*
232 				 * Reset character and move to next.
233 				 */
234 				*p++ = 'a';
235 				continue;
236 			}
237 
238 			break;
239 		}
240 
241 		if (*p == '\0') {
242 			/*
243 			 * Tried all combinations.  errno should already
244 			 * be EEXIST, but ensure it is anyway for
245 			 * isc__errno2result().
246 			 */
247 			errno = EEXIST;
248 			break;
249 		}
250 	} while (1);
251 
252 	if (i == -1)
253 		result = isc__errno2result(errno);
254 	else
255 		result = ISC_R_SUCCESS;
256 
257 	return (result);
258 }
259