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