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