1/* $NetBSD: dir.c,v 1.5 2020/05/25 20:47:23 christos Exp $ */ 2 3/* 4 * Copyright (C) 2004, 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/* Principal Authors: DCL */ 23 24#include <config.h> 25 26#include <string.h> 27#include <direct.h> 28#include <process.h> 29#include <io.h> 30 31#include <sys/stat.h> 32 33#include <isc/dir.h> 34#include <isc/magic.h> 35#include <isc/assertions.h> 36#include <isc/util.h> 37 38#include "errno2result.h" 39 40#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') 41#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) 42 43static isc_result_t 44start_directory(isc_dir_t *p); 45 46void 47isc_dir_init(isc_dir_t *dir) { 48 REQUIRE(dir != NULL); 49 50 dir->dirname[0] = '\0'; 51 52 dir->entry.name[0] = '\0'; 53 dir->entry.length = 0; 54 memset(&(dir->entry.find_data), 0, sizeof(dir->entry.find_data)); 55 56 dir->entry_filled = ISC_FALSE; 57 dir->search_handle = INVALID_HANDLE_VALUE; 58 59 dir->magic = ISC_DIR_MAGIC; 60} 61 62/* 63 * Allocate workspace and open directory stream. If either one fails, 64 * NULL will be returned. 65 */ 66isc_result_t 67isc_dir_open(isc_dir_t *dir, const char *dirname) { 68 char *p; 69 isc_result_t result; 70 71 REQUIRE(dirname != NULL); 72 REQUIRE(VALID_DIR(dir) && dir->search_handle == INVALID_HANDLE_VALUE); 73 74 /* 75 * Copy directory name. Need to have enough space for the name, 76 * a possible path separator, the wildcard, and the final NUL. 77 */ 78 if (strlen(dirname) + 3 > sizeof(dir->dirname)) 79 /* XXXDCL ? */ 80 return (ISC_R_NOSPACE); 81 strcpy(dir->dirname, dirname); 82 83 /* 84 * Append path separator, if needed, and "*". 85 */ 86 p = dir->dirname + strlen(dir->dirname); 87 if (dir->dirname < p && *(p - 1) != '\\' && *(p - 1) != ':') 88 *p++ = '\\'; 89 *p++ = '*'; 90 *p = '\0'; 91 92 /* 93 * Open stream. 94 */ 95 result = start_directory(dir); 96 97 return (result); 98} 99 100/* 101 * Return previously retrieved file or get next one. 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 */ 105isc_result_t 106isc_dir_read(isc_dir_t *dir) { 107 REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 108 109 if (dir->entry_filled) 110 /* 111 * start_directory() already filled in the first entry. 112 */ 113 dir->entry_filled = ISC_FALSE; 114 115 else { 116 /* 117 * Fetch next file in directory. 118 */ 119 if (FindNextFile(dir->search_handle, 120 &dir->entry.find_data) == FALSE) 121 /* 122 * Either the last file has been processed or 123 * an error has occurred. The former is not 124 * really an error, but the latter is. 125 */ 126 if (GetLastError() == ERROR_NO_MORE_FILES) 127 return (ISC_R_NOMORE); 128 else 129 return (ISC_R_UNEXPECTED); 130 } 131 132 /* 133 * Make sure that the space for the name is long enough. 134 */ 135 strcpy(dir->entry.name, dir->entry.find_data.cFileName); 136 dir->entry.length = strlen(dir->entry.name); 137 138 return (ISC_R_SUCCESS); 139} 140 141/* 142 * Close directory stream. 143 */ 144void 145isc_dir_close(isc_dir_t *dir) { 146 REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 147 148 FindClose(dir->search_handle); 149 dir->search_handle = INVALID_HANDLE_VALUE; 150} 151 152/* 153 * Reposition directory stream at start. 154 */ 155isc_result_t 156isc_dir_reset(isc_dir_t *dir) { 157 isc_result_t result; 158 159 REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 160 REQUIRE(dir->dirname != NULL); 161 162 /* 163 * NT cannot reposition the seek pointer to the beginning of the 164 * the directory stream, but rather the directory needs to be 165 * closed and reopened. The latter might fail. 166 */ 167 168 isc_dir_close(dir); 169 170 result = start_directory(dir); 171 172 return (result); 173} 174 175/* 176 * Initialize isc_dir_t structure with new directory. The function 177 * returns 0 on failure and nonzero on success. 178 * 179 * Note: 180 * - Be sure to close previous stream before opening new one 181 */ 182static isc_result_t 183start_directory(isc_dir_t *dir) 184{ 185 REQUIRE(VALID_DIR(dir)); 186 REQUIRE(dir->search_handle == INVALID_HANDLE_VALUE); 187 188 dir->entry_filled = ISC_FALSE; 189 190 /* 191 * Open stream and retrieve first file. 192 */ 193 dir->search_handle = FindFirstFile(dir->dirname, 194 &dir->entry.find_data); 195 196 if (dir->search_handle == INVALID_HANDLE_VALUE) { 197 /* 198 * Something went wrong but we don't know what. GetLastError() 199 * could give us more information about the error, but the 200 * MSDN documentation is frustratingly thin about what 201 * possible errors could have resulted. (Score one for 202 * the Unix manual pages.) So there is just this lame error 203 * instead of being able to differentiate ISC_R_NOTFOUND 204 * from ISC_R_UNEXPECTED. 205 */ 206 return (ISC_R_FAILURE); 207 } 208 209 /* 210 * Make sure that the space for the name is long enough. 211 */ 212 INSIST(sizeof(dir->entry.name) > 213 strlen(dir->entry.find_data.cFileName)); 214 215 /* 216 * Fill in the data for the first entry of the directory. 217 */ 218 strcpy(dir->entry.name, dir->entry.find_data.cFileName); 219 dir->entry.length = strlen(dir->entry.name); 220 221 dir->entry_filled = ISC_TRUE; 222 223 return (ISC_R_SUCCESS); 224} 225 226isc_result_t 227isc_dir_chdir(const char *dirname) { 228 /* 229 * Change the current directory to 'dirname'. 230 */ 231 232 REQUIRE(dirname != NULL); 233 234 if (chdir(dirname) < 0) 235 return (isc__errno2result(errno)); 236 237 return (ISC_R_SUCCESS); 238} 239 240isc_result_t 241isc_dir_chroot(const char *dirname) { 242 return (ISC_R_NOTIMPLEMENTED); 243} 244 245isc_result_t 246isc_dir_createunique(char *templet) { 247 isc_result_t result; 248 char *x; 249 char *p; 250 int i; 251 int pid; 252 253 REQUIRE(templet != NULL); 254 255 /* 256 * mkdtemp is not portable, so this emulates it. 257 */ 258 259 pid = getpid(); 260 261 /* 262 * Replace trailing Xs with the process-id, zero-filled. 263 */ 264 for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet; 265 x--, pid /= 10) 266 *x = pid % 10 + '0'; 267 268 x++; /* Set x to start of ex-Xs. */ 269 270 do { 271 i = mkdir(templet); 272 i = chmod(templet, 0700); 273 274 if (i == 0 || errno != EEXIST) 275 break; 276 277 /* 278 * The BSD algorithm. 279 */ 280 p = x; 281 while (*p != '\0') { 282 if (isdigit(*p & 0xff)) 283 *p = 'a'; 284 else if (*p != 'z') 285 ++*p; 286 else { 287 /* 288 * Reset character and move to next. 289 */ 290 *p++ = 'a'; 291 continue; 292 } 293 294 break; 295 } 296 297 if (*p == '\0') { 298 /* 299 * Tried all combinations. errno should already 300 * be EEXIST, but ensure it is anyway for 301 * isc__errno2result(). 302 */ 303 errno = EEXIST; 304 break; 305 } 306 } while (1); 307 308 if (i == -1) 309 result = isc__errno2result(errno); 310 else 311 result = ISC_R_SUCCESS; 312 313 return (result); 314} 315