/src/net-snmp/snmplib/dir_utils.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Portions of this file are subject to the following copyright(s). See |
2 | | * the Net-SNMP's COPYING file for more details and other copyrights |
3 | | * that may apply: |
4 | | */ |
5 | | /* |
6 | | * Portions of this file are copyrighted by: |
7 | | * Copyright (C) 2007 Apple, Inc. All rights reserved. |
8 | | * Use is subject to license terms specified in the COPYING file |
9 | | * distributed with the Net-SNMP package. |
10 | | */ |
11 | | #include <net-snmp/net-snmp-config.h> |
12 | | #include <net-snmp/net-snmp-features.h> |
13 | | #include <net-snmp/net-snmp-includes.h> |
14 | | |
15 | | #include <stdio.h> |
16 | | #include <ctype.h> |
17 | | #ifdef HAVE_STDLIB_H |
18 | | # include <stdlib.h> |
19 | | #endif |
20 | | #ifdef HAVE_UNISTD_H |
21 | | # include <unistd.h> |
22 | | #endif |
23 | | #ifdef HAVE_STRING_H |
24 | | # include <string.h> |
25 | | #else |
26 | | # include <strings.h> |
27 | | #endif |
28 | | |
29 | | #include <sys/types.h> |
30 | | #ifdef HAVE_LIMITS_H |
31 | | #include <limits.h> |
32 | | #endif |
33 | | #ifdef HAVE_SYS_STAT_H |
34 | | #include <sys/stat.h> |
35 | | #endif |
36 | | #ifdef HAVE_DIRENT_H |
37 | | # include <dirent.h> |
38 | | #endif |
39 | | |
40 | | #include <errno.h> |
41 | | |
42 | | #include <net-snmp/types.h> |
43 | | #include <net-snmp/library/container.h> |
44 | | #include <net-snmp/library/file_utils.h> |
45 | | #include <net-snmp/library/dir_utils.h> |
46 | | |
47 | | netsnmp_feature_child_of(container_directory, container_types); |
48 | | #ifdef NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY |
49 | | netsnmp_feature_require(file_utils); |
50 | | netsnmp_feature_require(container_free_all); |
51 | | #endif /* NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY */ |
52 | | |
53 | | #ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY |
54 | | static int |
55 | | _insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats, |
56 | | u_int flags); |
57 | | |
58 | | /* |
59 | | * read file names in a directory, with an optional filter |
60 | | */ |
61 | | netsnmp_container * |
62 | | netsnmp_directory_container_read_some(netsnmp_container *user_container, |
63 | | const char *dirname, |
64 | | netsnmp_directory_filter *filter, |
65 | | void *filter_ctx, u_int flags) |
66 | 0 | { |
67 | 0 | DIR *dir; |
68 | 0 | netsnmp_container *container = user_container; |
69 | 0 | struct dirent *file; |
70 | 0 | char path[SNMP_MAXPATH]; |
71 | 0 | size_t dirname_len; |
72 | 0 | int rc; |
73 | 0 | struct stat statbuf; |
74 | 0 | netsnmp_file ns_file_tmp; |
75 | |
|
76 | 0 | if ((flags & NETSNMP_DIR_RELATIVE_PATH) && (flags & NETSNMP_DIR_RECURSE)) { |
77 | 0 | DEBUGMSGTL(("directory:container", |
78 | 0 | "no support for relative path with recursion\n")); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | | |
82 | 0 | DEBUGMSGTL(("directory:container", "reading %s\n", dirname)); |
83 | | |
84 | | /* |
85 | | * create the container, if needed |
86 | | */ |
87 | 0 | if (NULL == container) { |
88 | 0 | if (flags & NETSNMP_DIR_NSFILE) { |
89 | 0 | container = netsnmp_container_find("nsfile_directory_container:" |
90 | 0 | "binary_array"); |
91 | 0 | if (container) { |
92 | 0 | container->compare = (netsnmp_container_compare*) |
93 | 0 | netsnmp_file_compare_name; |
94 | 0 | container->free_item = (netsnmp_container_obj_func *) |
95 | 0 | netsnmp_file_container_free; |
96 | 0 | } |
97 | 0 | } |
98 | 0 | else |
99 | 0 | container = netsnmp_container_find("directory_container:cstring"); |
100 | 0 | if (NULL == container) |
101 | 0 | return NULL; |
102 | 0 | container->container_name = strdup(dirname); |
103 | | /** default to unsorted */ |
104 | 0 | if (! (flags & NETSNMP_DIR_SORTED)) |
105 | 0 | CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_UNSORTED, rc); |
106 | | /** default to duplicates not allowed */ |
107 | 0 | if (! (flags & NETSNMP_DIR_ALLOW_DUPLICATES)) |
108 | 0 | CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_ALLOW_DUPLICATES, rc); |
109 | 0 | } |
110 | | |
111 | 0 | dir = opendir(dirname); |
112 | 0 | if (NULL == dir) { |
113 | 0 | DEBUGMSGTL(("directory:container", " not a dir\n")); |
114 | 0 | if (container != user_container) |
115 | 0 | netsnmp_directory_container_free(container); |
116 | 0 | return NULL; |
117 | 0 | } |
118 | | |
119 | | /** copy dirname into path */ |
120 | 0 | if (flags & NETSNMP_DIR_RELATIVE_PATH) |
121 | 0 | dirname_len = 0; |
122 | 0 | else { |
123 | 0 | dirname_len = strlen(dirname); |
124 | 0 | strlcpy(path, dirname, sizeof(path)); |
125 | 0 | if ((dirname_len + 2) > sizeof(path)) { |
126 | | /** not enough room for files */ |
127 | 0 | closedir(dir); |
128 | 0 | if (container != user_container) |
129 | 0 | netsnmp_directory_container_free(container); |
130 | 0 | return NULL; |
131 | 0 | } |
132 | 0 | path[dirname_len] = '/'; |
133 | 0 | path[++dirname_len] = '\0'; |
134 | 0 | } |
135 | | |
136 | | /** iterate over dir */ |
137 | 0 | while ((file = readdir(dir))) { |
138 | 0 | if (file->d_name[0] == 0) |
139 | 0 | continue; |
140 | | |
141 | | /** skip '.' and '..' */ |
142 | 0 | if ((file->d_name[0] == '.') && |
143 | 0 | ((file->d_name[1] == 0) || |
144 | 0 | ((file->d_name[1] == '.') && ((file->d_name[2] == 0))))) |
145 | 0 | continue; |
146 | | |
147 | 0 | strlcpy(&path[dirname_len], file->d_name, sizeof(path) - dirname_len); |
148 | 0 | if (NULL != filter) { |
149 | 0 | if (flags & NETSNMP_DIR_NSFILE_STATS) { |
150 | | /** use local vars for now */ |
151 | 0 | if (stat(path, &statbuf) != 0) { |
152 | 0 | snmp_log(LOG_ERR, "could not stat %s\n", file->d_name); |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | ns_file_tmp.stats = &statbuf; |
156 | 0 | ns_file_tmp.name = path; |
157 | 0 | rc = (*filter)(&ns_file_tmp, filter_ctx); |
158 | 0 | } |
159 | 0 | else |
160 | 0 | rc = (*filter)(path, filter_ctx); |
161 | 0 | if (0 == rc) { |
162 | 0 | DEBUGMSGTL(("directory:container:filtered", "%s\n", |
163 | 0 | file->d_name)); |
164 | 0 | continue; |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | 0 | DEBUGMSGTL(("directory:container:found", "%s\n", path)); |
169 | 0 | if ((flags & NETSNMP_DIR_RECURSE) |
170 | 0 | #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DT_DIR) |
171 | 0 | && (file->d_type == DT_DIR) |
172 | | #elif defined(S_ISDIR) |
173 | | && (stat(file->d_name, &statbuf) != 0) && (S_ISDIR(statbuf.st_mode)) |
174 | | #endif |
175 | 0 | ) { |
176 | | /** xxx add the dir as well? not for now.. maybe another flag? */ |
177 | 0 | netsnmp_directory_container_read(container, path, flags); |
178 | 0 | } |
179 | 0 | else if (flags & NETSNMP_DIR_NSFILE) { |
180 | 0 | if (_insert_nsfile( container, file->d_name, |
181 | 0 | filter ? &statbuf : NULL, flags ) < 0) |
182 | 0 | break; |
183 | 0 | } |
184 | 0 | else { |
185 | 0 | char *dup = strdup(path); |
186 | 0 | if (NULL == dup) { |
187 | 0 | snmp_log(LOG_ERR, |
188 | 0 | "strdup failed while building directory container\n"); |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | rc = CONTAINER_INSERT(container, dup); |
192 | 0 | if (-1 == rc ) { |
193 | 0 | DEBUGMSGTL(("directory:container", " err adding %s\n", path)); |
194 | 0 | free(dup); |
195 | 0 | } |
196 | 0 | } |
197 | 0 | } /* while */ |
198 | |
|
199 | 0 | closedir(dir); |
200 | |
|
201 | 0 | rc = CONTAINER_SIZE(container); |
202 | 0 | DEBUGMSGTL(("directory:container", " container now has %d items\n", rc)); |
203 | 0 | if ((0 == rc) && !(flags & NETSNMP_DIR_EMPTY_OK)) { |
204 | 0 | netsnmp_directory_container_free(container); |
205 | 0 | return NULL; |
206 | 0 | } |
207 | | |
208 | 0 | return container; |
209 | 0 | } |
210 | | |
211 | | void |
212 | | netsnmp_directory_container_free(netsnmp_container *container) |
213 | 0 | { |
214 | 0 | CONTAINER_FREE_ALL(container, NULL); |
215 | 0 | CONTAINER_FREE(container); |
216 | 0 | } |
217 | | |
218 | | static int |
219 | | _insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats, |
220 | | u_int flags) |
221 | 0 | { |
222 | 0 | int rc; |
223 | 0 | netsnmp_file *ns_file = netsnmp_file_new(name, 0, 0, 0); |
224 | 0 | if (NULL == ns_file) { |
225 | 0 | snmp_log(LOG_ERR, "error creating ns_file\n"); |
226 | 0 | return -1; |
227 | 0 | } |
228 | | |
229 | 0 | if (flags & NETSNMP_DIR_NSFILE_STATS) { |
230 | 0 | ns_file->stats = calloc(1,sizeof(*(ns_file->stats))); |
231 | 0 | if (NULL == ns_file->stats) { |
232 | 0 | snmp_log(LOG_ERR, "error creating stats for ns_file\n"); |
233 | 0 | netsnmp_file_release(ns_file); |
234 | 0 | return -1; |
235 | 0 | } |
236 | | |
237 | | /** use stats from earlier if we have them */ |
238 | 0 | if (stats) { |
239 | 0 | memcpy(ns_file->stats, stats, sizeof(*stats)); |
240 | 0 | } else if (stat(ns_file->name, ns_file->stats) < 0) { |
241 | 0 | snmp_log(LOG_ERR, "stat() failed for ns_file\n"); |
242 | 0 | netsnmp_file_release(ns_file); |
243 | 0 | return -1; |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | 0 | rc = CONTAINER_INSERT(c, ns_file); |
248 | 0 | if (-1 == rc ) { |
249 | 0 | DEBUGMSGTL(("directory:container", " err adding %s\n", name)); |
250 | 0 | netsnmp_file_release(ns_file); |
251 | 0 | } |
252 | |
|
253 | 0 | return 0; |
254 | 0 | } |
255 | | #else /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */ |
256 | | netsnmp_feature_unused(container_directory); |
257 | | #endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */ |