1258945Sroberto/*
2280849Scy * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3258945Sroberto * Copyright (C) 1999-2001  Internet Software Consortium.
4258945Sroberto *
5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any
6258945Sroberto * purpose with or without fee is hereby granted, provided that the above
7258945Sroberto * copyright notice and this permission notice appear in all copies.
8258945Sroberto *
9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11258945Sroberto * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15258945Sroberto * PERFORMANCE OF THIS SOFTWARE.
16258945Sroberto */
17258945Sroberto
18280849Scy/* $Id$ */
19258945Sroberto
20258945Sroberto/*! \file
21258945Sroberto * \author  Principal Authors: DCL */
22258945Sroberto
23258945Sroberto#include <config.h>
24258945Sroberto
25258945Sroberto#include <sys/types.h>
26258945Sroberto#include <sys/stat.h>
27258945Sroberto
28258945Sroberto#include <ctype.h>
29258945Sroberto#include <errno.h>
30258945Sroberto#include <unistd.h>
31258945Sroberto
32258945Sroberto#include <isc/dir.h>
33258945Sroberto#include <isc/magic.h>
34258945Sroberto#include <isc/string.h>
35258945Sroberto#include <isc/util.h>
36258945Sroberto
37258945Sroberto#include "errno2result.h"
38280849Scy#include "ntp_stdlib.h"		/* NTP change for strlcpy, strlcat */
39258945Sroberto
40258945Sroberto#define ISC_DIR_MAGIC		ISC_MAGIC('D', 'I', 'R', '*')
41258945Sroberto#define VALID_DIR(dir)		ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
42258945Sroberto
43258945Srobertovoid
44258945Srobertoisc_dir_init(isc_dir_t *dir) {
45258945Sroberto	REQUIRE(dir != NULL);
46258945Sroberto
47258945Sroberto	dir->entry.name[0] = '\0';
48258945Sroberto	dir->entry.length = 0;
49258945Sroberto
50258945Sroberto	dir->handle = NULL;
51258945Sroberto
52258945Sroberto	dir->magic = ISC_DIR_MAGIC;
53258945Sroberto}
54258945Sroberto
55258945Sroberto/*!
56258945Sroberto * \brief Allocate workspace and open directory stream. If either one fails,
57258945Sroberto * NULL will be returned.
58258945Sroberto */
59258945Srobertoisc_result_t
60258945Srobertoisc_dir_open(isc_dir_t *dir, const char *dirname) {
61258945Sroberto	char *p;
62280849Scy	size_t octets;
63258945Sroberto	isc_result_t result = ISC_R_SUCCESS;
64258945Sroberto
65258945Sroberto	REQUIRE(VALID_DIR(dir));
66258945Sroberto	REQUIRE(dirname != NULL);
67258945Sroberto
68258945Sroberto	/*
69258945Sroberto	 * Copy directory name.  Need to have enough space for the name,
70258945Sroberto	 * a possible path separator, the wildcard, and the final NUL.
71258945Sroberto	 */
72280849Scy	octets = strlen(dirname) + 1;
73280849Scy	if (octets + 2 > sizeof(dir->dirname))
74258945Sroberto		/* XXXDCL ? */
75258945Sroberto		return (ISC_R_NOSPACE);
76280849Scy	strlcpy(dir->dirname, dirname, octets);
77258945Sroberto
78258945Sroberto	/*
79258945Sroberto	 * Append path separator, if needed, and "*".
80258945Sroberto	 */
81258945Sroberto	p = dir->dirname + strlen(dir->dirname);
82258945Sroberto	if (dir->dirname < p && *(p - 1) != '/')
83258945Sroberto		*p++ = '/';
84258945Sroberto	*p++ = '*';
85280849Scy	*p = '\0';
86258945Sroberto
87258945Sroberto	/*
88258945Sroberto	 * Open stream.
89258945Sroberto	 */
90258945Sroberto	dir->handle = opendir(dirname);
91258945Sroberto
92258945Sroberto	if (dir->handle == NULL)
93258945Sroberto		return isc__errno2result(errno);
94258945Sroberto
95258945Sroberto	return (result);
96258945Sroberto}
97258945Sroberto
98258945Sroberto/*!
99258945Sroberto * \brief Return previously retrieved file or get next one.
100258945Sroberto
101258945Sroberto * Unix's dirent has
102258945Sroberto * separate open and read functions, but the Win32 and DOS interfaces open
103258945Sroberto * the dir stream and reads the first file in one operation.
104258945Sroberto */
105258945Srobertoisc_result_t
106258945Srobertoisc_dir_read(isc_dir_t *dir) {
107258945Sroberto	struct dirent *entry;
108280849Scy	size_t octets;
109258945Sroberto
110258945Sroberto	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
111258945Sroberto
112258945Sroberto	/*
113258945Sroberto	 * Fetch next file in directory.
114258945Sroberto	 */
115258945Sroberto	entry = readdir(dir->handle);
116258945Sroberto
117258945Sroberto	if (entry == NULL)
118258945Sroberto		return (ISC_R_NOMORE);
119258945Sroberto
120258945Sroberto	/*
121258945Sroberto	 * Make sure that the space for the name is long enough.
122258945Sroberto	 */
123280849Scy	octets = strlen(entry->d_name) + 1;
124280849Scy	if (sizeof(dir->entry.name) < octets)
125280849Scy		return (ISC_R_UNEXPECTED);
126258945Sroberto
127280849Scy	strlcpy(dir->entry.name, entry->d_name, octets);
128258945Sroberto
129258945Sroberto	/*
130258945Sroberto	 * Some dirents have d_namlen, but it is not portable.
131258945Sroberto	 */
132258945Sroberto	dir->entry.length = strlen(entry->d_name);
133258945Sroberto
134258945Sroberto	return (ISC_R_SUCCESS);
135258945Sroberto}
136258945Sroberto
137258945Sroberto/*!
138258945Sroberto * \brief Close directory stream.
139258945Sroberto */
140258945Srobertovoid
141258945Srobertoisc_dir_close(isc_dir_t *dir) {
142258945Sroberto       REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
143258945Sroberto
144258945Sroberto       (void)closedir(dir->handle);
145258945Sroberto       dir->handle = NULL;
146258945Sroberto}
147258945Sroberto
148258945Sroberto/*!
149258945Sroberto * \brief Reposition directory stream at start.
150258945Sroberto */
151258945Srobertoisc_result_t
152258945Srobertoisc_dir_reset(isc_dir_t *dir) {
153258945Sroberto	REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
154258945Sroberto
155258945Sroberto	rewinddir(dir->handle);
156258945Sroberto
157258945Sroberto	return (ISC_R_SUCCESS);
158258945Sroberto}
159258945Sroberto
160258945Srobertoisc_result_t
161258945Srobertoisc_dir_chdir(const char *dirname) {
162258945Sroberto	/*!
163258945Sroberto	 * \brief Change the current directory to 'dirname'.
164258945Sroberto	 */
165258945Sroberto
166258945Sroberto	REQUIRE(dirname != NULL);
167258945Sroberto
168258945Sroberto	if (chdir(dirname) < 0)
169258945Sroberto		return (isc__errno2result(errno));
170258945Sroberto
171258945Sroberto	return (ISC_R_SUCCESS);
172258945Sroberto}
173258945Sroberto
174258945Srobertoisc_result_t
175258945Srobertoisc_dir_chroot(const char *dirname) {
176258945Sroberto
177258945Sroberto	REQUIRE(dirname != NULL);
178258945Sroberto
179258945Sroberto#ifdef HAVE_CHROOT
180258945Sroberto	if (chroot(dirname) < 0 || chdir("/") < 0)
181258945Sroberto		return (isc__errno2result(errno));
182258945Sroberto
183258945Sroberto	return (ISC_R_SUCCESS);
184258945Sroberto#else
185258945Sroberto	return (ISC_R_NOTIMPLEMENTED);
186258945Sroberto#endif
187258945Sroberto}
188258945Sroberto
189258945Srobertoisc_result_t
190258945Srobertoisc_dir_createunique(char *templet) {
191258945Sroberto	isc_result_t result;
192258945Sroberto	char *x;
193258945Sroberto	char *p;
194258945Sroberto	int i;
195258945Sroberto	int pid;
196258945Sroberto
197258945Sroberto	REQUIRE(templet != NULL);
198258945Sroberto
199258945Sroberto	/*!
200258945Sroberto	 * \brief mkdtemp is not portable, so this emulates it.
201258945Sroberto	 */
202258945Sroberto
203258945Sroberto	pid = getpid();
204258945Sroberto
205258945Sroberto	/*
206258945Sroberto	 * Replace trailing Xs with the process-id, zero-filled.
207258945Sroberto	 */
208258945Sroberto	for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
209258945Sroberto	     x--, pid /= 10)
210258945Sroberto		*x = pid % 10 + '0';
211258945Sroberto
212258945Sroberto	x++;			/* Set x to start of ex-Xs. */
213258945Sroberto
214258945Sroberto	do {
215258945Sroberto		i = mkdir(templet, 0700);
216258945Sroberto		if (i == 0 || errno != EEXIST)
217258945Sroberto			break;
218258945Sroberto
219258945Sroberto		/*
220258945Sroberto		 * The BSD algorithm.
221258945Sroberto		 */
222258945Sroberto		p = x;
223258945Sroberto		while (*p != '\0') {
224258945Sroberto			if (isdigit(*p & 0xff))
225258945Sroberto				*p = 'a';
226258945Sroberto			else if (*p != 'z')
227258945Sroberto				++*p;
228258945Sroberto			else {
229258945Sroberto				/*
230258945Sroberto				 * Reset character and move to next.
231258945Sroberto				 */
232258945Sroberto				*p++ = 'a';
233258945Sroberto				continue;
234258945Sroberto			}
235258945Sroberto
236258945Sroberto			break;
237258945Sroberto		}
238258945Sroberto
239258945Sroberto		if (*p == '\0') {
240258945Sroberto			/*
241258945Sroberto			 * Tried all combinations.  errno should already
242258945Sroberto			 * be EEXIST, but ensure it is anyway for
243258945Sroberto			 * isc__errno2result().
244258945Sroberto			 */
245258945Sroberto			errno = EEXIST;
246258945Sroberto			break;
247258945Sroberto		}
248258945Sroberto	} while (1);
249258945Sroberto
250258945Sroberto	if (i == -1)
251258945Sroberto		result = isc__errno2result(errno);
252258945Sroberto	else
253258945Sroberto		result = ISC_R_SUCCESS;
254258945Sroberto
255258945Sroberto	return (result);
256258945Sroberto}
257