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