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