1 /* $NetBSD: dir.c,v 1.3 2025/01/26 16:25:36 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <ctype.h> 19 #include <errno.h> 20 #include <netdb.h> 21 #include <sys/stat.h> 22 #include <sys/types.h> 23 #include <unistd.h> 24 25 #include <isc/dir.h> 26 #include <isc/magic.h> 27 #include <isc/string.h> 28 #include <isc/util.h> 29 30 #include "errno2result.h" 31 32 #define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') 33 #define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) 34 35 void 36 isc_dir_init(isc_dir_t *dir) { 37 REQUIRE(dir != NULL); 38 39 dir->entry.name[0] = '\0'; 40 dir->entry.length = 0; 41 42 dir->handle = NULL; 43 44 dir->magic = ISC_DIR_MAGIC; 45 } 46 47 /*! 48 * \brief Allocate workspace and open directory stream. If either one fails, 49 * NULL will be returned. 50 */ 51 isc_result_t 52 isc_dir_open(isc_dir_t *dir, const char *dirname) { 53 char *p; 54 isc_result_t result = ISC_R_SUCCESS; 55 56 REQUIRE(VALID_DIR(dir)); 57 REQUIRE(dirname != NULL); 58 59 /* 60 * Copy directory name. Need to have enough space for the name, 61 * a possible path separator, the wildcard, and the final NUL. 62 */ 63 if (strlen(dirname) + 3 > sizeof(dir->dirname)) { 64 /* XXXDCL ? */ 65 return ISC_R_NOSPACE; 66 } 67 strlcpy(dir->dirname, dirname, sizeof(dir->dirname)); 68 69 /* 70 * Append path separator, if needed, and "*". 71 */ 72 p = dir->dirname + strlen(dir->dirname); 73 if (dir->dirname < p && *(p - 1) != '/') { 74 *p++ = '/'; 75 } 76 *p++ = '*'; 77 *p = '\0'; 78 79 /* 80 * Open stream. 81 */ 82 dir->handle = opendir(dirname); 83 84 if (dir->handle == NULL) { 85 return isc__errno2result(errno); 86 } 87 88 return result; 89 } 90 91 /*! 92 * \brief Return previously retrieved file or get next one. 93 * 94 * Unix's dirent has 95 * separate open and read functions, but the Win32 and DOS interfaces open 96 * the dir stream and reads the first file in one operation. 97 */ 98 isc_result_t 99 isc_dir_read(isc_dir_t *dir) { 100 struct dirent *entry; 101 102 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 103 104 /* 105 * Fetch next file in directory. 106 */ 107 entry = readdir(dir->handle); 108 109 if (entry == NULL) { 110 return ISC_R_NOMORE; 111 } 112 113 /* 114 * Make sure that the space for the name is long enough. 115 */ 116 if (sizeof(dir->entry.name) <= strlen(entry->d_name)) { 117 return ISC_R_UNEXPECTED; 118 } 119 120 strlcpy(dir->entry.name, entry->d_name, sizeof(dir->entry.name)); 121 122 /* 123 * Some dirents have d_namlen, but it is not portable. 124 */ 125 dir->entry.length = strlen(entry->d_name); 126 127 return ISC_R_SUCCESS; 128 } 129 130 /*! 131 * \brief Close directory stream. 132 */ 133 void 134 isc_dir_close(isc_dir_t *dir) { 135 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 136 137 (void)closedir(dir->handle); 138 dir->handle = NULL; 139 } 140 141 /*! 142 * \brief Reposition directory stream at start. 143 */ 144 isc_result_t 145 isc_dir_reset(isc_dir_t *dir) { 146 REQUIRE(VALID_DIR(dir) && dir->handle != NULL); 147 148 rewinddir(dir->handle); 149 150 return ISC_R_SUCCESS; 151 } 152 153 isc_result_t 154 isc_dir_chdir(const char *dirname) { 155 /*! 156 * \brief Change the current directory to 'dirname'. 157 */ 158 159 REQUIRE(dirname != NULL); 160 161 if (chdir(dirname) < 0) { 162 return isc__errno2result(errno); 163 } 164 165 return ISC_R_SUCCESS; 166 } 167 168 isc_result_t 169 isc_dir_chroot(const char *dirname) { 170 #ifdef HAVE_CHROOT 171 void *tmp; 172 #endif /* ifdef HAVE_CHROOT */ 173 174 REQUIRE(dirname != NULL); 175 176 #ifdef HAVE_CHROOT 177 /* 178 * Try to use getservbyname and getprotobyname before chroot. 179 * If WKS records are used in a zone under chroot, Name Service Switch 180 * may fail to load library in chroot. 181 * Do not report errors if it fails, we do not need any result now. 182 */ 183 tmp = getprotobyname("udp"); 184 if (tmp != NULL) { 185 (void)getservbyname("domain", "udp"); 186 } 187 188 if (chroot(dirname) < 0 || chdir("/") < 0) { 189 return isc__errno2result(errno); 190 } 191 192 return ISC_R_SUCCESS; 193 #else /* ifdef HAVE_CHROOT */ 194 return ISC_R_NOTIMPLEMENTED; 195 #endif /* ifdef HAVE_CHROOT */ 196 } 197