1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <thread.h>
31 #include <synch.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <dirent.h>
37 #include <regex.h>
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <libdevinfo.h>
41 #include <zone.h>
42 #include <sys/modctl.h>
43 #include <syslog.h>
44 #include <sys/stat.h>
45 #include <assert.h>
46
47
48 struct finddevhdl {
49 int npaths;
50 int curpath;
51 char **paths;
52 };
53
54
55 #define GLOBAL_DEV_PATH(devpath) \
56 ((getzoneid() == GLOBAL_ZONEID) && \
57 ((strcmp(devpath, "/dev") == 0) || \
58 (strncmp(devpath, "/dev/", strlen("/dev/")) == 0)))
59
60 /*
61 * Return true if a device exists
62 * If the path refers into the /dev filesystem, use a
63 * private interface to query if the device exists but
64 * without triggering an implicit reconfig if it does not.
65 * Note: can only function properly with absolute pathnames
66 * and only functions for persisted global /dev names, ie
67 * those managed by devfsadm. For paths other than
68 * /dev, stat(2) is sufficient.
69 */
70 int
device_exists(const char * devname)71 device_exists(const char *devname)
72 {
73 int rv;
74 struct stat st;
75
76 if (GLOBAL_DEV_PATH(devname)) {
77 rv = modctl(MODDEVEXISTS, devname, strlen(devname));
78 return ((rv == 0) ? 1 : 0);
79 }
80 if (stat(devname, &st) == 0)
81 return (1);
82 return (0);
83 }
84
85
86 /*
87 * Use the standard library readdir to read the contents of
88 * directories on alternate root mounted filesystems.
89 * Return results as per dev_readdir_devfs().
90 *
91 * The directory is traversed twice. First, to calculate
92 * the size of the buffer required; second, to copy the
93 * directory contents into the buffer. If the directory
94 * contents grow in between passes, which should almost
95 * never happen, start over again.
96 */
97 static int
finddev_readdir_alt(const char * path,finddevhdl_t * handlep)98 finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
99 {
100 struct finddevhdl *handle;
101 DIR *dir;
102 struct dirent *dp;
103 size_t n;
104
105 *handlep = NULL;
106 if ((dir = opendir(path)) == NULL)
107 return (ENOENT);
108
109 restart:
110 handle = calloc(1, sizeof (struct finddevhdl));
111 if (handle == NULL) {
112 (void) closedir(dir);
113 return (ENOMEM);
114 }
115
116 handle->npaths = 0;
117 handle->curpath = 0;
118 handle->paths = NULL;
119
120 n = 0;
121 rewinddir(dir);
122 while ((dp = readdir(dir)) != NULL) {
123 if ((strcmp(dp->d_name, ".") == 0) ||
124 (strcmp(dp->d_name, "..") == 0))
125 continue;
126 n++;
127 }
128
129 handle->npaths = n;
130 handle->paths = calloc(n, sizeof (char *));
131 if (handle->paths == NULL) {
132 free(handle);
133 (void) closedir(dir);
134 return (ENOMEM);
135 }
136
137 n = 0;
138 rewinddir(dir);
139 while ((dp = readdir(dir)) != NULL) {
140 if ((strcmp(dp->d_name, ".") == 0) ||
141 (strcmp(dp->d_name, "..") == 0))
142 continue;
143 if (n == handle->npaths) {
144 /*
145 * restart if directory contents have out-grown
146 * buffer allocated in the first pass.
147 */
148 finddev_close((finddevhdl_t)handle);
149 goto restart;
150 }
151 handle->paths[n] = strdup(dp->d_name);
152 if (handle->paths[n] == NULL) {
153 (void) closedir(dir);
154 finddev_close((finddevhdl_t)handle);
155 return (ENOMEM);
156 }
157 n++;
158 }
159 (void) closedir(dir);
160 *handlep = (finddevhdl_t)handle;
161 return (0);
162 }
163
164 /*
165 * Use of the dev filesystem's private readdir does not trigger
166 * the implicit device reconfiguration.
167 *
168 * Note: only useable with paths mounted on an instance of the
169 * dev filesystem.
170 *
171 * Does not return the . and .. entries.
172 * Empty directories are returned as an zero-length list.
173 * ENOENT is returned as a NULL list pointer.
174 */
175 static int
finddev_readdir_devfs(const char * path,finddevhdl_t * handlep)176 finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
177 {
178 struct finddevhdl *handle;
179 int n;
180 int rv;
181 int64_t bufsiz;
182 char *pathlist;
183 char *p;
184 int len;
185
186 *handlep = NULL;
187 handle = calloc(1, sizeof (struct finddevhdl));
188 if (handle == NULL)
189 return (ENOMEM);
190
191 handle->npaths = 0;
192 handle->curpath = 0;
193 handle->paths = NULL;
194
195 rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
196 if (rv != 0) {
197 free(handle);
198 return (rv);
199 }
200
201 for (;;) {
202 assert(bufsiz != 0);
203 if ((pathlist = malloc(bufsiz)) == NULL) {
204 free(handle);
205 return (ENOMEM);
206 }
207
208 rv = modctl(MODDEVREADDIR, path, strlen(path),
209 pathlist, &bufsiz);
210 if (rv == 0) {
211 for (n = 0, p = pathlist;
212 (len = strlen(p)) > 0; p += len+1) {
213 n++;
214 }
215 handle->npaths = n;
216 handle->paths = calloc(n, sizeof (char *));
217 if (handle->paths == NULL) {
218 free(handle);
219 free(pathlist);
220 return (ENOMEM);
221 }
222 for (n = 0, p = pathlist;
223 (len = strlen(p)) > 0; p += len+1, n++) {
224 handle->paths[n] = strdup(p);
225 if (handle->paths[n] == NULL) {
226 finddev_close((finddevhdl_t)handle);
227 free(pathlist);
228 return (ENOMEM);
229 }
230 }
231 *handlep = (finddevhdl_t)handle;
232 free(pathlist);
233 return (0);
234 }
235 free(pathlist);
236 switch (errno) {
237 case EAGAIN:
238 break;
239 case ENOENT:
240 default:
241 free(handle);
242 return (errno);
243 }
244 }
245 /*NOTREACHED*/
246 }
247
248 int
finddev_readdir(const char * path,finddevhdl_t * handlep)249 finddev_readdir(const char *path, finddevhdl_t *handlep)
250 {
251 if (GLOBAL_DEV_PATH(path)) {
252 return (finddev_readdir_devfs(path, handlep));
253 }
254 return (finddev_readdir_alt(path, handlep));
255 }
256
257 /*
258 * Return true if a directory is empty
259 * Use the standard library readdir to determine if a directory is
260 * empty.
261 */
262 static int
finddev_emptydir_alt(const char * path)263 finddev_emptydir_alt(const char *path)
264 {
265 DIR *dir;
266 struct dirent *dp;
267
268 if ((dir = opendir(path)) == NULL)
269 return (ENOENT);
270
271 while ((dp = readdir(dir)) != NULL) {
272 if ((strcmp(dp->d_name, ".") == 0) ||
273 (strcmp(dp->d_name, "..") == 0))
274 continue;
275 (void) closedir(dir);
276 return (0); /* not empty */
277 }
278 (void) closedir(dir);
279 return (1); /* empty */
280 }
281
282 /*
283 * Use of the dev filesystem's private readdir does (not trigger
284 * the implicit device reconfiguration) to determine if a directory
285 * is empty.
286 *
287 * Note: only useable with paths mounted on an instance of the
288 * dev filesystem.
289 *
290 * Does not return the . and .. entries.
291 * Empty directories are returned as an zero-length list.
292 * ENOENT is returned as a NULL list pointer.
293 */
294 static int
finddev_emptydir_devfs(const char * path)295 finddev_emptydir_devfs(const char *path)
296 {
297 int rv;
298 int empty;
299
300 rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
301 if (rv == 0) {
302 return (empty);
303 }
304 return (0);
305 }
306
307 int
finddev_emptydir(const char * path)308 finddev_emptydir(const char *path)
309 {
310 if (GLOBAL_DEV_PATH(path)) {
311 return (finddev_emptydir_devfs(path));
312 }
313 return (finddev_emptydir_alt(path));
314 }
315
316 void
finddev_close(finddevhdl_t arg)317 finddev_close(finddevhdl_t arg)
318 {
319 struct finddevhdl *handle = (struct finddevhdl *)arg;
320 int i;
321
322 for (i = 0; i < handle->npaths; i++) {
323 if (handle->paths[i])
324 free(handle->paths[i]);
325 }
326 free(handle->paths);
327 free(handle);
328 }
329
330 const char *
finddev_next(finddevhdl_t arg)331 finddev_next(finddevhdl_t arg)
332 {
333 struct finddevhdl *handle = (struct finddevhdl *)arg;
334 const char *path = NULL;
335
336 if (handle->curpath < handle->npaths) {
337 path = handle->paths[handle->curpath];
338 handle->curpath++;
339 }
340 return (path);
341 }
342