1258945Sroberto/* 2280849Scy * Copyright (C) 2004, 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 1999-2001 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18280849Scy/* $Id$ */ 19258945Sroberto 20258945Sroberto/* Principal Authors: DCL */ 21258945Sroberto 22258945Sroberto#include <config.h> 23258945Sroberto 24258945Sroberto#include <string.h> 25258945Sroberto#include <direct.h> 26258945Sroberto#include <process.h> 27258945Sroberto#include <io.h> 28258945Sroberto 29258945Sroberto#include <sys/stat.h> 30258945Sroberto 31258945Sroberto#include <isc/dir.h> 32258945Sroberto#include <isc/magic.h> 33258945Sroberto#include <isc/assertions.h> 34258945Sroberto#include <isc/util.h> 35258945Sroberto 36258945Sroberto#include "errno2result.h" 37258945Sroberto 38258945Sroberto#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') 39258945Sroberto#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) 40258945Sroberto 41258945Srobertostatic isc_result_t 42258945Srobertostart_directory(isc_dir_t *p); 43258945Sroberto 44258945Srobertovoid 45258945Srobertoisc_dir_init(isc_dir_t *dir) { 46258945Sroberto REQUIRE(dir != NULL); 47258945Sroberto 48258945Sroberto dir->dirname[0] = '\0'; 49258945Sroberto 50258945Sroberto dir->entry.name[0] = '\0'; 51258945Sroberto dir->entry.length = 0; 52258945Sroberto memset(&(dir->entry.find_data), 0, sizeof(dir->entry.find_data)); 53258945Sroberto 54258945Sroberto dir->entry_filled = ISC_FALSE; 55258945Sroberto dir->search_handle = INVALID_HANDLE_VALUE; 56258945Sroberto 57258945Sroberto dir->magic = ISC_DIR_MAGIC; 58258945Sroberto} 59258945Sroberto 60258945Sroberto/* 61258945Sroberto * Allocate workspace and open directory stream. If either one fails, 62258945Sroberto * NULL will be returned. 63258945Sroberto */ 64258945Srobertoisc_result_t 65258945Srobertoisc_dir_open(isc_dir_t *dir, const char *dirname) { 66258945Sroberto char *p; 67258945Sroberto isc_result_t result; 68258945Sroberto 69258945Sroberto REQUIRE(dirname != NULL); 70258945Sroberto REQUIRE(VALID_DIR(dir) && dir->search_handle == INVALID_HANDLE_VALUE); 71258945Sroberto 72258945Sroberto /* 73258945Sroberto * Copy directory name. Need to have enough space for the name, 74258945Sroberto * a possible path separator, the wildcard, and the final NUL. 75258945Sroberto */ 76258945Sroberto if (strlen(dirname) + 3 > sizeof(dir->dirname)) 77258945Sroberto /* XXXDCL ? */ 78258945Sroberto return (ISC_R_NOSPACE); 79258945Sroberto strcpy(dir->dirname, dirname); 80258945Sroberto 81258945Sroberto /* 82258945Sroberto * Append path separator, if needed, and "*". 83258945Sroberto */ 84258945Sroberto p = dir->dirname + strlen(dir->dirname); 85258945Sroberto if (dir->dirname < p && *(p - 1) != '\\' && *(p - 1) != ':') 86258945Sroberto *p++ = '\\'; 87258945Sroberto *p++ = '*'; 88280849Scy *p = '\0'; 89258945Sroberto 90258945Sroberto /* 91258945Sroberto * Open stream. 92258945Sroberto */ 93258945Sroberto result = start_directory(dir); 94258945Sroberto 95258945Sroberto return (result); 96258945Sroberto} 97258945Sroberto 98258945Sroberto/* 99258945Sroberto * Return previously retrieved file or get next one. Unix's dirent has 100258945Sroberto * separate open and read functions, but the Win32 and DOS interfaces open 101258945Sroberto * the dir stream and reads the first file in one operation. 102258945Sroberto */ 103258945Srobertoisc_result_t 104258945Srobertoisc_dir_read(isc_dir_t *dir) { 105258945Sroberto REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 106258945Sroberto 107258945Sroberto if (dir->entry_filled) 108258945Sroberto /* 109258945Sroberto * start_directory() already filled in the first entry. 110258945Sroberto */ 111258945Sroberto dir->entry_filled = ISC_FALSE; 112258945Sroberto 113258945Sroberto else { 114258945Sroberto /* 115258945Sroberto * Fetch next file in directory. 116258945Sroberto */ 117258945Sroberto if (FindNextFile(dir->search_handle, 118258945Sroberto &dir->entry.find_data) == FALSE) 119258945Sroberto /* 120258945Sroberto * Either the last file has been processed or 121258945Sroberto * an error has occurred. The former is not 122258945Sroberto * really an error, but the latter is. 123258945Sroberto */ 124258945Sroberto if (GetLastError() == ERROR_NO_MORE_FILES) 125258945Sroberto return (ISC_R_NOMORE); 126258945Sroberto else 127258945Sroberto return (ISC_R_UNEXPECTED); 128258945Sroberto } 129258945Sroberto 130258945Sroberto /* 131258945Sroberto * Make sure that the space for the name is long enough. 132258945Sroberto */ 133258945Sroberto strcpy(dir->entry.name, dir->entry.find_data.cFileName); 134258945Sroberto dir->entry.length = strlen(dir->entry.name); 135258945Sroberto 136258945Sroberto return (ISC_R_SUCCESS); 137258945Sroberto} 138258945Sroberto 139258945Sroberto/* 140258945Sroberto * Close directory stream. 141258945Sroberto */ 142258945Srobertovoid 143258945Srobertoisc_dir_close(isc_dir_t *dir) { 144258945Sroberto REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 145258945Sroberto 146258945Sroberto FindClose(dir->search_handle); 147258945Sroberto dir->search_handle = INVALID_HANDLE_VALUE; 148258945Sroberto} 149258945Sroberto 150258945Sroberto/* 151258945Sroberto * Reposition directory stream at start. 152258945Sroberto */ 153258945Srobertoisc_result_t 154258945Srobertoisc_dir_reset(isc_dir_t *dir) { 155258945Sroberto isc_result_t result; 156258945Sroberto 157258945Sroberto REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); 158258945Sroberto REQUIRE(dir->dirname != NULL); 159258945Sroberto 160258945Sroberto /* 161258945Sroberto * NT cannot reposition the seek pointer to the beginning of the 162258945Sroberto * the directory stream, but rather the directory needs to be 163258945Sroberto * closed and reopened. The latter might fail. 164258945Sroberto */ 165258945Sroberto 166258945Sroberto isc_dir_close(dir); 167258945Sroberto 168258945Sroberto result = start_directory(dir); 169258945Sroberto 170258945Sroberto return (result); 171258945Sroberto} 172258945Sroberto 173258945Sroberto/* 174258945Sroberto * Initialize isc_dir_t structure with new directory. The function 175258945Sroberto * returns 0 on failure and nonzero on success. 176258945Sroberto * 177258945Sroberto * Note: 178258945Sroberto * - Be sure to close previous stream before opening new one 179258945Sroberto */ 180258945Srobertostatic isc_result_t 181258945Srobertostart_directory(isc_dir_t *dir) 182258945Sroberto{ 183258945Sroberto REQUIRE(VALID_DIR(dir)); 184258945Sroberto REQUIRE(dir->search_handle == INVALID_HANDLE_VALUE); 185258945Sroberto 186258945Sroberto dir->entry_filled = ISC_FALSE; 187258945Sroberto 188258945Sroberto /* 189258945Sroberto * Open stream and retrieve first file. 190258945Sroberto */ 191258945Sroberto dir->search_handle = FindFirstFile(dir->dirname, 192258945Sroberto &dir->entry.find_data); 193258945Sroberto 194258945Sroberto if (dir->search_handle == INVALID_HANDLE_VALUE) { 195258945Sroberto /* 196258945Sroberto * Something went wrong but we don't know what. GetLastError() 197258945Sroberto * could give us more information about the error, but the 198258945Sroberto * MSDN documentation is frustratingly thin about what 199258945Sroberto * possible errors could have resulted. (Score one for 200258945Sroberto * the Unix manual pages.) So there is just this lame error 201258945Sroberto * instead of being able to differentiate ISC_R_NOTFOUND 202258945Sroberto * from ISC_R_UNEXPECTED. 203258945Sroberto */ 204258945Sroberto return (ISC_R_FAILURE); 205258945Sroberto } 206258945Sroberto 207258945Sroberto /* 208258945Sroberto * Make sure that the space for the name is long enough. 209258945Sroberto */ 210258945Sroberto INSIST(sizeof(dir->entry.name) > 211258945Sroberto strlen(dir->entry.find_data.cFileName)); 212258945Sroberto 213258945Sroberto /* 214258945Sroberto * Fill in the data for the first entry of the directory. 215258945Sroberto */ 216258945Sroberto strcpy(dir->entry.name, dir->entry.find_data.cFileName); 217258945Sroberto dir->entry.length = strlen(dir->entry.name); 218258945Sroberto 219258945Sroberto dir->entry_filled = ISC_TRUE; 220258945Sroberto 221258945Sroberto return (ISC_R_SUCCESS); 222258945Sroberto} 223258945Sroberto 224258945Srobertoisc_result_t 225258945Srobertoisc_dir_chdir(const char *dirname) { 226258945Sroberto /* 227258945Sroberto * Change the current directory to 'dirname'. 228258945Sroberto */ 229258945Sroberto 230258945Sroberto REQUIRE(dirname != NULL); 231258945Sroberto 232258945Sroberto if (chdir(dirname) < 0) 233258945Sroberto return (isc__errno2result(errno)); 234258945Sroberto 235258945Sroberto return (ISC_R_SUCCESS); 236258945Sroberto} 237258945Sroberto 238258945Srobertoisc_result_t 239258945Srobertoisc_dir_chroot(const char *dirname) { 240258945Sroberto return (ISC_R_NOTIMPLEMENTED); 241258945Sroberto} 242258945Sroberto 243258945Srobertoisc_result_t 244258945Srobertoisc_dir_createunique(char *templet) { 245258945Sroberto isc_result_t result; 246258945Sroberto char *x; 247258945Sroberto char *p; 248258945Sroberto int i; 249258945Sroberto int pid; 250258945Sroberto 251258945Sroberto REQUIRE(templet != NULL); 252258945Sroberto 253258945Sroberto /* 254258945Sroberto * mkdtemp is not portable, so this emulates it. 255258945Sroberto */ 256258945Sroberto 257258945Sroberto pid = getpid(); 258258945Sroberto 259258945Sroberto /* 260258945Sroberto * Replace trailing Xs with the process-id, zero-filled. 261258945Sroberto */ 262258945Sroberto for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet; 263258945Sroberto x--, pid /= 10) 264258945Sroberto *x = pid % 10 + '0'; 265258945Sroberto 266258945Sroberto x++; /* Set x to start of ex-Xs. */ 267258945Sroberto 268258945Sroberto do { 269258945Sroberto i = mkdir(templet); 270258945Sroberto i = chmod(templet, 0700); 271258945Sroberto 272258945Sroberto if (i == 0 || errno != EEXIST) 273258945Sroberto break; 274258945Sroberto 275258945Sroberto /* 276258945Sroberto * The BSD algorithm. 277258945Sroberto */ 278258945Sroberto p = x; 279258945Sroberto while (*p != '\0') { 280258945Sroberto if (isdigit(*p & 0xff)) 281258945Sroberto *p = 'a'; 282258945Sroberto else if (*p != 'z') 283258945Sroberto ++*p; 284258945Sroberto else { 285258945Sroberto /* 286258945Sroberto * Reset character and move to next. 287258945Sroberto */ 288258945Sroberto *p++ = 'a'; 289258945Sroberto continue; 290258945Sroberto } 291258945Sroberto 292258945Sroberto break; 293258945Sroberto } 294258945Sroberto 295258945Sroberto if (*p == '\0') { 296258945Sroberto /* 297258945Sroberto * Tried all combinations. errno should already 298258945Sroberto * be EEXIST, but ensure it is anyway for 299258945Sroberto * isc__errno2result(). 300258945Sroberto */ 301258945Sroberto errno = EEXIST; 302258945Sroberto break; 303258945Sroberto } 304258945Sroberto } while (1); 305258945Sroberto 306258945Sroberto if (i == -1) 307258945Sroberto result = isc__errno2result(errno); 308258945Sroberto else 309258945Sroberto result = ISC_R_SUCCESS; 310258945Sroberto 311258945Sroberto return (result); 312258945Sroberto} 313