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