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