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