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
50struct 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 */
72int
73device_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 */
99static int
100finddev_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
111restart:
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 */
177static int
178finddev_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
250int
251finddev_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 */
264static int
265finddev_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 */
296static int
297finddev_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
309int
310finddev_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
318void
319finddev_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
332const char *
333finddev_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