1/* $NetBSD$ */ 2 3/* 4 * Copyright (C) 2004, 2005, 2007, 2009 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: dir.c,v 1.25.332.3 2009/02/16 23:47:15 tbox Exp */ 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 41#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') 42#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) 43 44void 45isc_dir_init(isc_dir_t *dir) { 46 REQUIRE(dir != NULL); 47 48 dir->entry.name[0] = '\0'; 49 dir->entry.length = 0; 50 51 dir->handle = NULL; 52 53 dir->magic = ISC_DIR_MAGIC; 54} 55 56/*! 57 * \brief Allocate workspace and open directory stream. If either one fails, 58 * NULL will be returned. 59 */ 60isc_result_t 61isc_dir_open(isc_dir_t *dir, const char *dirname) { 62 char *p; 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 if (strlen(dirname) + 3 > sizeof(dir->dirname)) 73 /* XXXDCL ? */ 74 return (ISC_R_NOSPACE); 75 strcpy(dir->dirname, dirname); 76 77 /* 78 * Append path separator, if needed, and "*". 79 */ 80 p = dir->dirname + strlen(dir->dirname); 81 if (dir->dirname < p && *(p - 1) != '/') 82 *p++ = '/'; 83 *p++ = '*'; 84 *p++ = '\0'; 85 86 /* 87 * Open stream. 88 */ 89 dir->handle = opendir(dirname); 90 91 if (dir->handle == NULL) 92 return isc__errno2result(errno); 93 94 return (result); 95} 96 97/*! 98 * \brief Return previously retrieved file or get next one. 99 100 * Unix's dirent has 101 * separate open and read functions, but the Win32 and DOS interfaces open 102 * the dir stream and reads the first file in one operation. 103 */ 104isc_result_t 105isc_dir_read(isc_dir_t *dir) { 106 struct dirent *entry; 107 108 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 109 110 /* 111 * Fetch next file in directory. 112 */ 113 entry = readdir(dir->handle); 114 115 if (entry == NULL) 116 return (ISC_R_NOMORE); 117 118 /* 119 * Make sure that the space for the name is long enough. 120 */ 121 if (sizeof(dir->entry.name) <= strlen(entry->d_name)) 122 return (ISC_R_UNEXPECTED); 123 124 strcpy(dir->entry.name, entry->d_name); 125 126 /* 127 * Some dirents have d_namlen, but it is not portable. 128 */ 129 dir->entry.length = strlen(entry->d_name); 130 131 return (ISC_R_SUCCESS); 132} 133 134/*! 135 * \brief Close directory stream. 136 */ 137void 138isc_dir_close(isc_dir_t *dir) { 139 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 140 141 (void)closedir(dir->handle); 142 dir->handle = NULL; 143} 144 145/*! 146 * \brief Reposition directory stream at start. 147 */ 148isc_result_t 149isc_dir_reset(isc_dir_t *dir) { 150 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 151 152 rewinddir(dir->handle); 153 154 return (ISC_R_SUCCESS); 155} 156 157isc_result_t 158isc_dir_chdir(const char *dirname) { 159 /*! 160 * \brief Change the current directory to 'dirname'. 161 */ 162 163 REQUIRE(dirname != NULL); 164 165 if (chdir(dirname) < 0) 166 return (isc__errno2result(errno)); 167 168 return (ISC_R_SUCCESS); 169} 170 171isc_result_t 172isc_dir_chroot(const char *dirname) { 173 174 REQUIRE(dirname != NULL); 175 176#ifdef HAVE_CHROOT 177 if (chroot(dirname) < 0 || chdir("/") < 0) 178 return (isc__errno2result(errno)); 179 180 return (ISC_R_SUCCESS); 181#else 182 return (ISC_R_NOTIMPLEMENTED); 183#endif 184} 185 186isc_result_t 187isc_dir_createunique(char *templet) { 188 isc_result_t result; 189 char *x; 190 char *p; 191 int i; 192 int pid; 193 194 REQUIRE(templet != NULL); 195 196 /*! 197 * \brief mkdtemp is not portable, so this emulates it. 198 */ 199 200 pid = getpid(); 201 202 /* 203 * Replace trailing Xs with the process-id, zero-filled. 204 */ 205 for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet; 206 x--, pid /= 10) 207 *x = pid % 10 + '0'; 208 209 x++; /* Set x to start of ex-Xs. */ 210 211 do { 212 i = mkdir(templet, 0700); 213 if (i == 0 || errno != EEXIST) 214 break; 215 216 /* 217 * The BSD algorithm. 218 */ 219 p = x; 220 while (*p != '\0') { 221 if (isdigit(*p & 0xff)) 222 *p = 'a'; 223 else if (*p != 'z') 224 ++*p; 225 else { 226 /* 227 * Reset character and move to next. 228 */ 229 *p++ = 'a'; 230 continue; 231 } 232 233 break; 234 } 235 236 if (*p == '\0') { 237 /* 238 * Tried all combinations. errno should already 239 * be EEXIST, but ensure it is anyway for 240 * isc__errno2result(). 241 */ 242 errno = EEXIST; 243 break; 244 } 245 } while (1); 246 247 if (i == -1) 248 result = isc__errno2result(errno); 249 else 250 result = ISC_R_SUCCESS; 251 252 return (result); 253} 254