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