12621Sllai1 /*
22621Sllai1 * CDDL HEADER START
32621Sllai1 *
42621Sllai1 * The contents of this file are subject to the terms of the
52621Sllai1 * Common Development and Distribution License (the "License").
62621Sllai1 * You may not use this file except in compliance with the License.
72621Sllai1 *
82621Sllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1 * or http://www.opensolaris.org/os/licensing.
102621Sllai1 * See the License for the specific language governing permissions
112621Sllai1 * and limitations under the License.
122621Sllai1 *
132621Sllai1 * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1 * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1 * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1 * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1 *
192621Sllai1 * CDDL HEADER END
202621Sllai1 */
212621Sllai1 /*
22*6065Scth * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
232621Sllai1 * Use is subject to license terms.
242621Sllai1 */
252621Sllai1
262621Sllai1 #pragma ident "%Z%%M% %I% %E% SMI"
272621Sllai1
282621Sllai1 #include <stdio.h>
292621Sllai1 #include <unistd.h>
302621Sllai1 #include <fcntl.h>
312621Sllai1 #include <string.h>
322621Sllai1 #include <thread.h>
332621Sllai1 #include <synch.h>
342621Sllai1 #include <limits.h>
352621Sllai1 #include <stdlib.h>
362621Sllai1 #include <string.h>
372621Sllai1 #include <strings.h>
382621Sllai1 #include <dirent.h>
392621Sllai1 #include <regex.h>
402621Sllai1 #include <errno.h>
412621Sllai1 #include <stdarg.h>
422621Sllai1 #include <libdevinfo.h>
433702Sjg #include <zone.h>
442621Sllai1 #include <sys/modctl.h>
452621Sllai1 #include <syslog.h>
463702Sjg #include <sys/stat.h>
472621Sllai1 #include <assert.h>
482621Sllai1
492621Sllai1
502621Sllai1 struct finddevhdl {
512621Sllai1 int npaths;
522621Sllai1 int curpath;
532621Sllai1 char **paths;
542621Sllai1 };
552621Sllai1
562621Sllai1
573747Sjg #define GLOBAL_DEV_PATH(devpath) \
583747Sjg ((getzoneid() == GLOBAL_ZONEID) && \
593747Sjg ((strcmp(devpath, "/dev") == 0) || \
603747Sjg (strncmp(devpath, "/dev/", strlen("/dev/")) == 0)))
613747Sjg
623702Sjg /*
633702Sjg * Return true if a device exists
643702Sjg * If the path refers into the /dev filesystem, use a
653702Sjg * private interface to query if the device exists but
663702Sjg * without triggering an implicit reconfig if it does not.
673702Sjg * Note: can only function properly with absolute pathnames
683702Sjg * and only functions for persisted global /dev names, ie
693702Sjg * those managed by devfsadm. For paths other than
703702Sjg * /dev, stat(2) is sufficient.
713702Sjg */
722621Sllai1 int
device_exists(const char * devname)732621Sllai1 device_exists(const char *devname)
742621Sllai1 {
752621Sllai1 int rv;
763702Sjg struct stat st;
772621Sllai1
783747Sjg if (GLOBAL_DEV_PATH(devname)) {
793702Sjg rv = modctl(MODDEVEXISTS, devname, strlen(devname));
803702Sjg return ((rv == 0) ? 1 : 0);
813702Sjg }
823702Sjg if (stat(devname, &st) == 0)
833702Sjg return (1);
843702Sjg return (0);
852621Sllai1 }
862621Sllai1
873702Sjg
883702Sjg /*
893702Sjg * Use the standard library readdir to read the contents of
903702Sjg * directories on alternate root mounted filesystems.
913702Sjg * Return results as per dev_readdir_devfs().
923702Sjg *
933702Sjg * The directory is traversed twice. First, to calculate
943702Sjg * the size of the buffer required; second, to copy the
953702Sjg * directory contents into the buffer. If the directory
963702Sjg * contents grow in between passes, which should almost
973702Sjg * never happen, start over again.
983702Sjg */
993702Sjg static int
finddev_readdir_alt(const char * path,finddevhdl_t * handlep)1003702Sjg finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
1013702Sjg {
1023702Sjg struct finddevhdl *handle;
1033702Sjg DIR *dir;
1043702Sjg struct dirent *dp;
1053702Sjg size_t n;
1063702Sjg
1073702Sjg *handlep = NULL;
1083702Sjg if ((dir = opendir(path)) == NULL)
1093702Sjg return (ENOENT);
1103702Sjg
1113702Sjg restart:
1123702Sjg handle = calloc(1, sizeof (struct finddevhdl));
1133702Sjg if (handle == NULL) {
1143702Sjg (void) closedir(dir);
1153702Sjg return (ENOMEM);
1163702Sjg }
1173702Sjg
1183702Sjg handle->npaths = 0;
1193702Sjg handle->curpath = 0;
1203702Sjg handle->paths = NULL;
1213702Sjg
1223702Sjg n = 0;
1233702Sjg rewinddir(dir);
1243702Sjg while ((dp = readdir(dir)) != NULL) {
1253702Sjg if ((strcmp(dp->d_name, ".") == 0) ||
1263702Sjg (strcmp(dp->d_name, "..") == 0))
1273702Sjg continue;
1283702Sjg n++;
1293702Sjg }
1303702Sjg
1313702Sjg handle->npaths = n;
1323702Sjg handle->paths = calloc(n, sizeof (char *));
1333702Sjg if (handle->paths == NULL) {
1343702Sjg free(handle);
1353702Sjg (void) closedir(dir);
1363702Sjg return (ENOMEM);
1373702Sjg }
1383702Sjg
1393702Sjg n = 0;
1403702Sjg rewinddir(dir);
1413702Sjg while ((dp = readdir(dir)) != NULL) {
1423702Sjg if ((strcmp(dp->d_name, ".") == 0) ||
1433702Sjg (strcmp(dp->d_name, "..") == 0))
1443702Sjg continue;
1453702Sjg if (n == handle->npaths) {
1463702Sjg /*
1473702Sjg * restart if directory contents have out-grown
1483702Sjg * buffer allocated in the first pass.
1493702Sjg */
1503702Sjg finddev_close((finddevhdl_t)handle);
1513702Sjg goto restart;
1523702Sjg }
1533702Sjg handle->paths[n] = strdup(dp->d_name);
1543702Sjg if (handle->paths[n] == NULL) {
1553702Sjg (void) closedir(dir);
1563702Sjg finddev_close((finddevhdl_t)handle);
1573702Sjg return (ENOMEM);
1583702Sjg }
1593702Sjg n++;
1603702Sjg }
1613702Sjg (void) closedir(dir);
1623702Sjg *handlep = (finddevhdl_t)handle;
1633702Sjg return (0);
1643702Sjg }
1653702Sjg
1663702Sjg /*
1673702Sjg * Use of the dev filesystem's private readdir does not trigger
1683702Sjg * the implicit device reconfiguration.
1693702Sjg *
1703702Sjg * Note: only useable with paths mounted on an instance of the
1713702Sjg * dev filesystem.
1723702Sjg *
1733702Sjg * Does not return the . and .. entries.
1743702Sjg * Empty directories are returned as an zero-length list.
1753702Sjg * ENOENT is returned as a NULL list pointer.
1763702Sjg */
1773702Sjg static int
finddev_readdir_devfs(const char * path,finddevhdl_t * handlep)1783702Sjg finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
1792621Sllai1 {
1802621Sllai1 struct finddevhdl *handle;
1812621Sllai1 int n;
1822621Sllai1 int rv;
1832621Sllai1 int64_t bufsiz;
1842621Sllai1 char *pathlist;
1852621Sllai1 char *p;
1862621Sllai1 int len;
1872621Sllai1
1882621Sllai1 *handlep = NULL;
1892621Sllai1 handle = calloc(1, sizeof (struct finddevhdl));
1902621Sllai1 if (handle == NULL)
1912621Sllai1 return (ENOMEM);
1922621Sllai1
1932621Sllai1 handle->npaths = 0;
1942621Sllai1 handle->curpath = 0;
1952621Sllai1 handle->paths = NULL;
1962621Sllai1
1973702Sjg rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
1982621Sllai1 if (rv != 0) {
1992621Sllai1 free(handle);
2002621Sllai1 return (rv);
2012621Sllai1 }
2022621Sllai1
2032621Sllai1 for (;;) {
2042621Sllai1 assert(bufsiz != 0);
2052621Sllai1 if ((pathlist = malloc(bufsiz)) == NULL) {
2062621Sllai1 free(handle);
2072621Sllai1 return (ENOMEM);
2082621Sllai1 }
2092621Sllai1
2103702Sjg rv = modctl(MODDEVREADDIR, path, strlen(path),
2112621Sllai1 pathlist, &bufsiz);
2122621Sllai1 if (rv == 0) {
2132621Sllai1 for (n = 0, p = pathlist;
2142621Sllai1 (len = strlen(p)) > 0; p += len+1) {
2152621Sllai1 n++;
2162621Sllai1 }
2172621Sllai1 handle->npaths = n;
2182621Sllai1 handle->paths = calloc(n, sizeof (char *));
2192621Sllai1 if (handle->paths == NULL) {
2202621Sllai1 free(handle);
2212621Sllai1 free(pathlist);
2222621Sllai1 return (ENOMEM);
2232621Sllai1 }
2242621Sllai1 for (n = 0, p = pathlist;
2252621Sllai1 (len = strlen(p)) > 0; p += len+1, n++) {
2262621Sllai1 handle->paths[n] = strdup(p);
2272621Sllai1 if (handle->paths[n] == NULL) {
2282621Sllai1 finddev_close((finddevhdl_t)handle);
2292621Sllai1 free(pathlist);
2302621Sllai1 return (ENOMEM);
2312621Sllai1 }
2322621Sllai1 }
2332621Sllai1 *handlep = (finddevhdl_t)handle;
2342621Sllai1 free(pathlist);
2352621Sllai1 return (0);
2362621Sllai1 }
2372621Sllai1 free(pathlist);
2382621Sllai1 switch (errno) {
2392621Sllai1 case EAGAIN:
2402621Sllai1 break;
2412621Sllai1 case ENOENT:
2422621Sllai1 default:
2432621Sllai1 free(handle);
2442621Sllai1 return (errno);
2452621Sllai1 }
2462621Sllai1 }
2472621Sllai1 /*NOTREACHED*/
2482621Sllai1 }
2492621Sllai1
2503702Sjg int
finddev_readdir(const char * path,finddevhdl_t * handlep)2513702Sjg finddev_readdir(const char *path, finddevhdl_t *handlep)
2523702Sjg {
2533747Sjg if (GLOBAL_DEV_PATH(path)) {
2543702Sjg return (finddev_readdir_devfs(path, handlep));
2553702Sjg }
2563702Sjg return (finddev_readdir_alt(path, handlep));
2573702Sjg }
2583702Sjg
259*6065Scth /*
260*6065Scth * Return true if a directory is empty
261*6065Scth * Use the standard library readdir to determine if a directory is
262*6065Scth * empty.
263*6065Scth */
264*6065Scth static int
finddev_emptydir_alt(const char * path)265*6065Scth finddev_emptydir_alt(const char *path)
266*6065Scth {
267*6065Scth DIR *dir;
268*6065Scth struct dirent *dp;
269*6065Scth
270*6065Scth if ((dir = opendir(path)) == NULL)
271*6065Scth return (ENOENT);
272*6065Scth
273*6065Scth while ((dp = readdir(dir)) != NULL) {
274*6065Scth if ((strcmp(dp->d_name, ".") == 0) ||
275*6065Scth (strcmp(dp->d_name, "..") == 0))
276*6065Scth continue;
277*6065Scth (void) closedir(dir);
278*6065Scth return (0); /* not empty */
279*6065Scth }
280*6065Scth (void) closedir(dir);
281*6065Scth return (1); /* empty */
282*6065Scth }
283*6065Scth
284*6065Scth /*
285*6065Scth * Use of the dev filesystem's private readdir does (not trigger
286*6065Scth * the implicit device reconfiguration) to determine if a directory
287*6065Scth * is empty.
288*6065Scth *
289*6065Scth * Note: only useable with paths mounted on an instance of the
290*6065Scth * dev filesystem.
291*6065Scth *
292*6065Scth * Does not return the . and .. entries.
293*6065Scth * Empty directories are returned as an zero-length list.
294*6065Scth * ENOENT is returned as a NULL list pointer.
295*6065Scth */
296*6065Scth static int
finddev_emptydir_devfs(const char * path)297*6065Scth finddev_emptydir_devfs(const char *path)
298*6065Scth {
299*6065Scth int rv;
300*6065Scth int empty;
301*6065Scth
302*6065Scth rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
303*6065Scth if (rv == 0) {
304*6065Scth return (empty);
305*6065Scth }
306*6065Scth return (0);
307*6065Scth }
308*6065Scth
309*6065Scth int
finddev_emptydir(const char * path)310*6065Scth finddev_emptydir(const char *path)
311*6065Scth {
312*6065Scth if (GLOBAL_DEV_PATH(path)) {
313*6065Scth return (finddev_emptydir_devfs(path));
314*6065Scth }
315*6065Scth return (finddev_emptydir_alt(path));
316*6065Scth }
317*6065Scth
3182621Sllai1 void
finddev_close(finddevhdl_t arg)3192621Sllai1 finddev_close(finddevhdl_t arg)
3202621Sllai1 {
3212621Sllai1 struct finddevhdl *handle = (struct finddevhdl *)arg;
3222621Sllai1 int i;
3232621Sllai1
3242621Sllai1 for (i = 0; i < handle->npaths; i++) {
3252621Sllai1 if (handle->paths[i])
3262621Sllai1 free(handle->paths[i]);
3272621Sllai1 }
3282621Sllai1 free(handle->paths);
3292621Sllai1 free(handle);
3302621Sllai1 }
3312621Sllai1
3322621Sllai1 const char *
finddev_next(finddevhdl_t arg)3332621Sllai1 finddev_next(finddevhdl_t arg)
3342621Sllai1 {
3352621Sllai1 struct finddevhdl *handle = (struct finddevhdl *)arg;
3362621Sllai1 const char *path = NULL;
3372621Sllai1
3382621Sllai1 if (handle->curpath < handle->npaths) {
3392621Sllai1 path = handle->paths[handle->curpath];
3402621Sllai1 handle->curpath++;
3412621Sllai1 }
3422621Sllai1 return (path);
3432621Sllai1 }
344