xref: /netbsd-src/external/bsd/ntp/dist/libntp/lib/isc/unix/dir.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: dir.c,v 1.2 2024/08/18 20:47:15 christos Exp $	*/
2897be3a4Schristos 
3897be3a4Schristos /*
4897be3a4Schristos  * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
5897be3a4Schristos  * Copyright (C) 1999-2001  Internet Software Consortium.
6897be3a4Schristos  *
7897be3a4Schristos  * Permission to use, copy, modify, and/or distribute this software for any
8897be3a4Schristos  * purpose with or without fee is hereby granted, provided that the above
9897be3a4Schristos  * copyright notice and this permission notice appear in all copies.
10897be3a4Schristos  *
11897be3a4Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12897be3a4Schristos  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13897be3a4Schristos  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14897be3a4Schristos  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15897be3a4Schristos  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16897be3a4Schristos  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17897be3a4Schristos  * PERFORMANCE OF THIS SOFTWARE.
18897be3a4Schristos  */
19897be3a4Schristos 
20897be3a4Schristos /* Id */
21897be3a4Schristos 
22897be3a4Schristos /*! \file
23897be3a4Schristos  * \author  Principal Authors: DCL */
24897be3a4Schristos 
25897be3a4Schristos #include <config.h>
26897be3a4Schristos 
27897be3a4Schristos #include <sys/types.h>
28897be3a4Schristos #include <sys/stat.h>
29897be3a4Schristos 
30897be3a4Schristos #include <ctype.h>
31897be3a4Schristos #include <errno.h>
32897be3a4Schristos #include <unistd.h>
33897be3a4Schristos 
34897be3a4Schristos #include <isc/dir.h>
35897be3a4Schristos #include <isc/magic.h>
36897be3a4Schristos #include <isc/string.h>
37897be3a4Schristos #include <isc/util.h>
38897be3a4Schristos 
39897be3a4Schristos #include "errno2result.h"
40897be3a4Schristos #include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
41897be3a4Schristos 
42897be3a4Schristos #define ISC_DIR_MAGIC		ISC_MAGIC('D', 'I', 'R', '*')
43897be3a4Schristos #define VALID_DIR(dir)		ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
44897be3a4Schristos 
45897be3a4Schristos void
46897be3a4Schristos isc_dir_init(isc_dir_t *dir) {
47897be3a4Schristos 	REQUIRE(dir != NULL);
48897be3a4Schristos 
49897be3a4Schristos 	dir->entry.name[0] = '\0';
50897be3a4Schristos 	dir->entry.length = 0;
51897be3a4Schristos 
52897be3a4Schristos 	dir->handle = NULL;
53897be3a4Schristos 
54897be3a4Schristos 	dir->magic = ISC_DIR_MAGIC;
55897be3a4Schristos }
56897be3a4Schristos 
57897be3a4Schristos /*!
58897be3a4Schristos  * \brief Allocate workspace and open directory stream. If either one fails,
59897be3a4Schristos  * NULL will be returned.
60897be3a4Schristos  */
61897be3a4Schristos isc_result_t
62897be3a4Schristos isc_dir_open(isc_dir_t *dir, const char *dirname) {
63897be3a4Schristos 	char *p;
64897be3a4Schristos 	size_t octets;
65897be3a4Schristos 	isc_result_t result = ISC_R_SUCCESS;
66897be3a4Schristos 
67897be3a4Schristos 	REQUIRE(VALID_DIR(dir));
68897be3a4Schristos 	REQUIRE(dirname != NULL);
69897be3a4Schristos 
70897be3a4Schristos 	/*
71897be3a4Schristos 	 * Copy directory name.  Need to have enough space for the name,
72897be3a4Schristos 	 * a possible path separator, the wildcard, and the final NUL.
73897be3a4Schristos 	 */
74897be3a4Schristos 	octets = strlen(dirname) + 1;
75897be3a4Schristos 	if (octets + 2 > sizeof(dir->dirname))
76897be3a4Schristos 		/* XXXDCL ? */
77897be3a4Schristos 		return (ISC_R_NOSPACE);
78897be3a4Schristos 	strlcpy(dir->dirname, dirname, octets);
79897be3a4Schristos 
80897be3a4Schristos 	/*
81897be3a4Schristos 	 * Append path separator, if needed, and "*".
82897be3a4Schristos 	 */
83897be3a4Schristos 	p = dir->dirname + strlen(dir->dirname);
84897be3a4Schristos 	if (dir->dirname < p && *(p - 1) != '/')
85897be3a4Schristos 		*p++ = '/';
86897be3a4Schristos 	*p++ = '*';
87897be3a4Schristos 	*p = '\0';
88897be3a4Schristos 
89897be3a4Schristos 	/*
90897be3a4Schristos 	 * Open stream.
91897be3a4Schristos 	 */
92897be3a4Schristos 	dir->handle = opendir(dirname);
93897be3a4Schristos 
94897be3a4Schristos 	if (dir->handle == NULL)
95897be3a4Schristos 		return isc__errno2result(errno);
96897be3a4Schristos 
97897be3a4Schristos 	return (result);
98897be3a4Schristos }
99897be3a4Schristos 
100897be3a4Schristos /*!
101897be3a4Schristos  * \brief Return previously retrieved file or get next one.
102897be3a4Schristos 
103897be3a4Schristos  * Unix's dirent has
104897be3a4Schristos  * separate open and read functions, but the Win32 and DOS interfaces open
105897be3a4Schristos  * the dir stream and reads the first file in one operation.
106897be3a4Schristos  */
107897be3a4Schristos isc_result_t
108897be3a4Schristos isc_dir_read(isc_dir_t *dir) {
109897be3a4Schristos 	struct dirent *entry;
110897be3a4Schristos 	size_t octets;
111897be3a4Schristos 
112897be3a4Schristos 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
113897be3a4Schristos 
114897be3a4Schristos 	/*
115897be3a4Schristos 	 * Fetch next file in directory.
116897be3a4Schristos 	 */
117897be3a4Schristos 	entry = readdir(dir->handle);
118897be3a4Schristos 
119897be3a4Schristos 	if (entry == NULL)
120897be3a4Schristos 		return (ISC_R_NOMORE);
121897be3a4Schristos 
122897be3a4Schristos 	/*
123897be3a4Schristos 	 * Make sure that the space for the name is long enough.
124897be3a4Schristos 	 */
125897be3a4Schristos 	octets = strlen(entry->d_name) + 1;
126897be3a4Schristos 	if (sizeof(dir->entry.name) < octets)
127897be3a4Schristos 		return (ISC_R_UNEXPECTED);
128897be3a4Schristos 
129897be3a4Schristos 	strlcpy(dir->entry.name, entry->d_name, octets);
130897be3a4Schristos 
131897be3a4Schristos 	/*
132897be3a4Schristos 	 * Some dirents have d_namlen, but it is not portable.
133897be3a4Schristos 	 */
134897be3a4Schristos 	dir->entry.length = strlen(entry->d_name);
135897be3a4Schristos 
136897be3a4Schristos 	return (ISC_R_SUCCESS);
137897be3a4Schristos }
138897be3a4Schristos 
139897be3a4Schristos /*!
140897be3a4Schristos  * \brief Close directory stream.
141897be3a4Schristos  */
142897be3a4Schristos void
143897be3a4Schristos isc_dir_close(isc_dir_t *dir) {
144897be3a4Schristos        REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
145897be3a4Schristos 
146897be3a4Schristos        (void)closedir(dir->handle);
147897be3a4Schristos        dir->handle = NULL;
148897be3a4Schristos }
149897be3a4Schristos 
150897be3a4Schristos /*!
151897be3a4Schristos  * \brief Reposition directory stream at start.
152897be3a4Schristos  */
153897be3a4Schristos isc_result_t
154897be3a4Schristos isc_dir_reset(isc_dir_t *dir) {
155897be3a4Schristos 	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
156897be3a4Schristos 
157897be3a4Schristos 	rewinddir(dir->handle);
158897be3a4Schristos 
159897be3a4Schristos 	return (ISC_R_SUCCESS);
160897be3a4Schristos }
161897be3a4Schristos 
162897be3a4Schristos isc_result_t
163897be3a4Schristos isc_dir_chdir(const char *dirname) {
164897be3a4Schristos 	/*!
165897be3a4Schristos 	 * \brief Change the current directory to 'dirname'.
166897be3a4Schristos 	 */
167897be3a4Schristos 
168897be3a4Schristos 	REQUIRE(dirname != NULL);
169897be3a4Schristos 
170897be3a4Schristos 	if (chdir(dirname) < 0)
171897be3a4Schristos 		return (isc__errno2result(errno));
172897be3a4Schristos 
173897be3a4Schristos 	return (ISC_R_SUCCESS);
174897be3a4Schristos }
175897be3a4Schristos 
176897be3a4Schristos isc_result_t
177897be3a4Schristos isc_dir_chroot(const char *dirname) {
178897be3a4Schristos 
179897be3a4Schristos 	REQUIRE(dirname != NULL);
180897be3a4Schristos 
181897be3a4Schristos #ifdef HAVE_CHROOT
182897be3a4Schristos 	if (chroot(dirname) < 0 || chdir("/") < 0)
183897be3a4Schristos 		return (isc__errno2result(errno));
184897be3a4Schristos 
185897be3a4Schristos 	return (ISC_R_SUCCESS);
186897be3a4Schristos #else
187897be3a4Schristos 	return (ISC_R_NOTIMPLEMENTED);
188897be3a4Schristos #endif
189897be3a4Schristos }
190897be3a4Schristos 
191897be3a4Schristos isc_result_t
192897be3a4Schristos isc_dir_createunique(char *templet) {
193897be3a4Schristos 	isc_result_t result;
194897be3a4Schristos 	char *x;
195897be3a4Schristos 	char *p;
196897be3a4Schristos 	int i;
197897be3a4Schristos 	int pid;
198897be3a4Schristos 
199897be3a4Schristos 	REQUIRE(templet != NULL);
200897be3a4Schristos 
201897be3a4Schristos 	/*!
202897be3a4Schristos 	 * \brief mkdtemp is not portable, so this emulates it.
203897be3a4Schristos 	 */
204897be3a4Schristos 
205897be3a4Schristos 	pid = getpid();
206897be3a4Schristos 
207897be3a4Schristos 	/*
208897be3a4Schristos 	 * Replace trailing Xs with the process-id, zero-filled.
209897be3a4Schristos 	 */
210897be3a4Schristos 	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
211897be3a4Schristos 	     x--, pid /= 10)
212897be3a4Schristos 		*x = pid % 10 + '0';
213897be3a4Schristos 
214897be3a4Schristos 	x++;			/* Set x to start of ex-Xs. */
215897be3a4Schristos 
216897be3a4Schristos 	do {
217897be3a4Schristos 		i = mkdir(templet, 0700);
218897be3a4Schristos 		if (i == 0 || errno != EEXIST)
219897be3a4Schristos 			break;
220897be3a4Schristos 
221897be3a4Schristos 		/*
222897be3a4Schristos 		 * The BSD algorithm.
223897be3a4Schristos 		 */
224897be3a4Schristos 		p = x;
225897be3a4Schristos 		while (*p != '\0') {
226897be3a4Schristos 			if (isdigit(*p & 0xff))
227897be3a4Schristos 				*p = 'a';
228897be3a4Schristos 			else if (*p != 'z')
229897be3a4Schristos 				++*p;
230897be3a4Schristos 			else {
231897be3a4Schristos 				/*
232897be3a4Schristos 				 * Reset character and move to next.
233897be3a4Schristos 				 */
234897be3a4Schristos 				*p++ = 'a';
235897be3a4Schristos 				continue;
236897be3a4Schristos 			}
237897be3a4Schristos 
238897be3a4Schristos 			break;
239897be3a4Schristos 		}
240897be3a4Schristos 
241897be3a4Schristos 		if (*p == '\0') {
242897be3a4Schristos 			/*
243897be3a4Schristos 			 * Tried all combinations.  errno should already
244897be3a4Schristos 			 * be EEXIST, but ensure it is anyway for
245897be3a4Schristos 			 * isc__errno2result().
246897be3a4Schristos 			 */
247897be3a4Schristos 			errno = EEXIST;
248897be3a4Schristos 			break;
249897be3a4Schristos 		}
250897be3a4Schristos 	} while (1);
251897be3a4Schristos 
252897be3a4Schristos 	if (i == -1)
253897be3a4Schristos 		result = isc__errno2result(errno);
254897be3a4Schristos 	else
255897be3a4Schristos 		result = ISC_R_SUCCESS;
256897be3a4Schristos 
257897be3a4Schristos 	return (result);
258897be3a4Schristos }
259