changeset 3702:0c044e9df56a

6517848 dev_readdir() in devfsadm fails with alternate root
author jg
date Thu, 22 Feb 2007 19:40:48 -0800
parents fec29e0d1d48
children 914e4929dd48
files usr/src/cmd/devfsadm/devfsadm.c usr/src/cmd/devfsadm/devfsadm_impl.h usr/src/lib/libdevinfo/devinfo_devperm.c usr/src/lib/libdevinfo/devinfo_finddev.c usr/src/lib/libdevinfo/libdevinfo.h
diffstat 5 files changed, 171 insertions(+), 140 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/devfsadm/devfsadm.c	Thu Feb 22 19:22:48 2007 -0800
+++ b/usr/src/cmd/devfsadm/devfsadm.c	Thu Feb 22 19:40:48 2007 -0800
@@ -3103,8 +3103,9 @@
 {
 	char *ptr, path[PATH_MAX + 1];
 	char *fcn = "rm_parent_dir_if_empty: ";
-	char *pathlist;
-	int len;
+	finddevhdl_t fhandle;
+	const char *f;
+	int rv;
 
 	vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
 
@@ -3122,17 +3123,17 @@
 
 		*ptr = '\0';
 
-		if ((pathlist = dev_readdir(path)) == NULL) {
-			err_print(OPENDIR_FAILED, path, strerror(errno));
+		if ((rv = finddev_readdir(path, &fhandle)) != 0) {
+			err_print(OPENDIR_FAILED, path, strerror(rv));
 			return;
 		}
 
 		/*
 		 * An empty pathlist implies an empty directory
 		 */
-		len = strlen(pathlist);
-		free(pathlist);
-		if (len == 0) {
+		f = finddev_next(fhandle);
+		finddev_close(fhandle);
+		if (f == NULL) {
 			if (s_rmdir(path) == 0) {
 				vprint(REMOVE_MID,
 				    "%sremoving empty dir %s\n", fcn, path);
@@ -4219,13 +4220,13 @@
 	char new_path[PATH_MAX + 1];
 	char *anchored_path_re;
 	size_t len;
-	char *pathlist;
-	char *listp;
+	finddevhdl_t fhandle;
+	const char *fp;
 
 	vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
 		current_dir, path_re);
 
-	if ((pathlist = dev_readdir(current_dir)) == NULL)
+	if (finddev_readdir(current_dir, &fhandle) != 0)
 		return;
 
 	len = strlen(path_re);
@@ -4243,13 +4244,13 @@
 
 	free(anchored_path_re);
 
-	for (listp = pathlist; (len = strlen(listp)) > 0; listp += len+1) {
-
-		if (regexec(&re1, listp, 0, NULL, 0) == 0) {
+	while ((fp = finddev_next(fhandle)) != NULL) {
+
+		if (regexec(&re1, fp, 0, NULL, 0) == 0) {
 			/* match */
 			(void) strcpy(new_path, current_dir);
 			(void) strcat(new_path, "/");
-			(void) strcat(new_path, listp);
+			(void) strcat(new_path, fp);
 
 			vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
 				"path = %s\n", new_path);
@@ -4268,7 +4269,7 @@
 	regfree(&re1);
 
 out:
-	free(pathlist);
+	finddev_close(fhandle);
 }
 
 /*
@@ -5016,9 +5017,8 @@
 get_stat_info(char *namebuf, struct stat *sb)
 {
 	char *cp;
-	char *pathlist;
-	char *listp;
-	int len;
+	finddevhdl_t fhandle;
+	const char *fp;
 
 	if (lstat(namebuf, sb) < 0) {
 		(void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
@@ -5035,7 +5035,7 @@
 	 */
 	if ((sb->st_mode & S_IFMT) == S_IFDIR) {
 
-		if ((pathlist = dev_readdir(namebuf)) == NULL) {
+		if (finddev_readdir(namebuf, &fhandle) != 0) {
 			return (DEVFSADM_FAILURE);
 		}
 
@@ -5043,21 +5043,21 @@
 		 *  Search each dir entry looking for a symlink.  Return
 		 *  the first symlink found in namebuf.  Recurse dirs.
 		 */
-		for (listp = pathlist;
-		    (len = strlen(listp)) > 0; listp += len+1) {
+		while ((fp = finddev_next(fhandle)) != NULL) {
 			cp = namebuf + strlen(namebuf);
 			if ((strlcat(namebuf, "/", PATH_MAX) >= PATH_MAX) ||
-			    (strlcat(namebuf, listp, PATH_MAX) >= PATH_MAX)) {
+			    (strlcat(namebuf, fp, PATH_MAX) >= PATH_MAX)) {
 				*cp = '\0';
+				finddev_close(fhandle);
 				return (DEVFSADM_FAILURE);
 			}
 			if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
-				free(pathlist);
+				finddev_close(fhandle);
 				return (DEVFSADM_SUCCESS);
 			}
 			*cp = '\0';
 		}
-		free(pathlist);
+		finddev_close(fhandle);
 	}
 
 	/* no symlink found, so return error */
@@ -5182,11 +5182,10 @@
 	char *slash;
 	char *new_path;
 	char *numeral_id;
-	char *pathlist;
-	char *listp;
-	int len;
-
-	if ((pathlist = dev_readdir(current_dir)) == NULL) {
+	finddevhdl_t fhandle;
+	const char *fp;
+
+	if (finddev_readdir(current_dir, &fhandle) != 0) {
 		return;
 	}
 
@@ -5199,7 +5198,7 @@
 		*slash = '\0';
 	}
 
-	for (listp = pathlist; (len = strlen(listp)) > 0; listp += len+1) {
+	while ((fp = finddev_next(fhandle)) != NULL) {
 
 		/*
 		 *  Returns true if path_left matches the list entry.
@@ -5208,15 +5207,15 @@
 		 *  numeral_id.
 		 */
 		numeral_id = NULL;
-		if (match_path_component(path_left, listp, &numeral_id,
+		if (match_path_component(path_left, (char *)fp, &numeral_id,
 				    slash ? 0 : rules[index].subexp)) {
 
 			new_path = s_malloc(strlen(current_dir) +
-			    strlen(listp) + 2);
+			    strlen(fp) + 2);
 
 			(void) strcpy(new_path, current_dir);
 			(void) strcat(new_path, "/");
-			(void) strcat(new_path, listp);
+			(void) strcat(new_path, fp);
 
 			if (slash != NULL) {
 				enumerate_recurse(new_path, slash + 1,
@@ -5235,7 +5234,7 @@
 	if (slash != NULL) {
 		*slash = '/';
 	}
-	free(pathlist);
+	finddev_close(fhandle);
 }
 
 
@@ -8372,58 +8371,3 @@
 	(void) door_return((char *)&res, sizeof (struct sdev_door_res),
 	    NULL, 0);
 }
-
-/*
- * Use of the dev filesystem's private readdir does not trigger
- * the implicit device reconfiguration.
- *
- * Note: only useable with paths mounted on an instance of the
- * dev filesystem.
- *
- * Does not return the . and .. entries.
- * Empty directories are returned as an zero-length list.
- * ENOENT is returned as a NULL list pointer.
- */
-static char *
-dev_readdir(char *path)
-{
-	int	rv;
-	int64_t	bufsiz;
-	char	*pathlist;
-	char	*p;
-	int	len;
-
-	assert((strcmp(path, "/dev") == 0) ||
-		(strncmp(path, "/dev/", 4) == 0));
-
-	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
-	if (rv != 0) {
-		vprint(READDIR_MID, "%s: %s\n", path, strerror(errno));
-		return (NULL);
-	}
-
-	for (;;) {
-		assert(bufsiz != 0);
-		pathlist = s_malloc(bufsiz);
-
-		rv = modctl(MODDEVREADDIR, path, strlen(path),
-		    pathlist, &bufsiz);
-		if (rv == 0) {
-			vprint(READDIR_MID, "%s\n", path);
-			vprint(READDIR_ALL_MID, "%s:\n", path);
-			for (p = pathlist; (len = strlen(p)) > 0; p += len+1) {
-				vprint(READDIR_ALL_MID, "    %s\n", p);
-			}
-			return (pathlist);
-		}
-		free(pathlist);
-		switch (errno) {
-		case EAGAIN:
-			break;
-		case ENOENT:
-		default:
-			vprint(READDIR_MID, "%s: %s\n", path, strerror(errno));
-			return (NULL);
-		}
-	}
-}
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h	Thu Feb 22 19:22:48 2007 -0800
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h	Thu Feb 22 19:40:48 2007 -0800
@@ -494,7 +494,6 @@
     di_node_t, char *, int);
 static void log_event(char *, char *, nvlist_t *);
 static void build_and_log_event(char *, char *, char *, di_node_t);
-static char *dev_readdir(char *);
 
 static void read_logindevperm_file(void);
 static void set_logindev_perms(char *devlink);
--- a/usr/src/lib/libdevinfo/devinfo_devperm.c	Thu Feb 22 19:22:48 2007 -0800
+++ b/usr/src/lib/libdevinfo/devinfo_devperm.c	Thu Feb 22 19:40:48 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -384,8 +384,6 @@
 {
 	struct stat stat_buf;
 	int err = 0;
-	DIR *dirp;
-	struct dirent *direntp;
 	char errstring[MAX_LINELEN];
 	char *p;
 	regex_t regex;
@@ -393,7 +391,6 @@
 	char *match;
 	char *name, *newpath, *remainder_path;
 	finddevhdl_t handle;
-	int find_method;
 
 	/*
 	 * Determine if the search needs to be performed via finddev,
@@ -403,13 +400,7 @@
 	 * reconfig for names managed by logindevperm but not present
 	 * on the system.
 	 */
-	find_method = ((getzoneid() == GLOBAL_ZONEID) &&
-	    ((strcmp(path, "/dev") == 0) ||
-	    (strncmp(path, "/dev/", 5) == 0))) ?
-		FLAG_USE_FINDDEV : FLAG_USE_READDIR;
-
-	/* path must be a valid name */
-	if (find_method == FLAG_USE_FINDDEV && !device_exists(path)) {
+	if (!device_exists(path)) {
 		return (-1);
 	}
 	if (stat(path, &stat_buf) == -1) {
@@ -444,24 +435,20 @@
 		}
 	}
 
-	if (find_method == FLAG_USE_READDIR) {
-		dirp = opendir(path);
-		if (dirp == NULL)
-			return (0);
-	} else {
-		if (finddev_readdir(path, &handle) != 0)
-			return (0);
-	}
+	if (finddev_readdir(path, &handle) != 0)
+		return (0);
 
 	p = strchr(left_to_do, '/');
 	alwaysmatch = 0;
 
 	newpath = (char *)malloc(MAXPATHLEN);
 	if (newpath == NULL) {
+		finddev_close(handle);
 		return (-1);
 	}
 	match = (char *)calloc(MAXPATHLEN, 1);
 	if (match == NULL) {
+		finddev_close(handle);
 		free(newpath);
 		return (-1);
 	}
@@ -478,23 +465,12 @@
 		if (regcomp(&regex, match, REG_EXTENDED) != 0) {
 			free(newpath);
 			free(match);
+			finddev_close(handle);
 			return (-1);
 		}
 	}
 
-	for (;;) {
-		if (find_method == FLAG_USE_READDIR) {
-			if ((direntp = readdir(dirp)) == NULL)
-				break;
-			name = direntp->d_name;
-			if ((strcmp(name, ".") == 0) ||
-			    (strcmp(name, "..") == 0))
-				continue;
-		} else {
-			if ((name = (char *)finddev_next(handle)) == NULL)
-				break;
-		}
-
+	while ((name = (char *)finddev_next(handle)) != NULL) {
 		if (alwaysmatch ||
 		    regexec(&regex, name, 0, NULL, 0) == 0) {
 			if (strcmp(path, "/") == 0) {
@@ -518,11 +494,7 @@
 		}
 	}
 
-	if (find_method == FLAG_USE_READDIR) {
-		(void) closedir(dirp);
-	} else {
-		finddev_close(handle);
-	}
+	finddev_close(handle);
 	free(newpath);
 	free(match);
 	if (!alwaysmatch) {
--- a/usr/src/lib/libdevinfo/devinfo_finddev.c	Thu Feb 22 19:22:48 2007 -0800
+++ b/usr/src/lib/libdevinfo/devinfo_finddev.c	Thu Feb 22 19:40:48 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -40,9 +40,10 @@
 #include <errno.h>
 #include <stdarg.h>
 #include <libdevinfo.h>
+#include <zone.h>
 #include <sys/modctl.h>
 #include <syslog.h>
-
+#include <sys/stat.h>
 #include <assert.h>
 
 
@@ -53,17 +54,125 @@
 };
 
 
+/*
+ * Return true if a device exists
+ * If the path refers into the /dev filesystem, use a
+ * private interface to query if the device exists but
+ * without triggering an implicit reconfig if it does not.
+ * Note: can only function properly with absolute pathnames
+ * and only functions for persisted global /dev names, ie
+ * those managed by devfsadm.  For paths other than
+ * /dev, stat(2) is sufficient.
+ */
 int
 device_exists(const char *devname)
 {
 	int	rv;
+	struct stat st;
 
-	rv = modctl(MODDEVEXISTS, devname, strlen(devname));
-	return ((rv == 0) ? 1 : 0);
+	if ((getzoneid() == GLOBAL_ZONEID) &&
+	    ((strcmp(devname, "/dev") == 0) ||
+	    (strncmp(devname, "/dev/", 5) == 0))) {
+		rv = modctl(MODDEVEXISTS, devname, strlen(devname));
+		return ((rv == 0) ? 1 : 0);
+	}
+	if (stat(devname, &st) == 0)
+		return (1);
+	return (0);
 }
 
-int
-finddev_readdir(const char *dir, finddevhdl_t *handlep)
+
+/*
+ * Use the standard library readdir to read the contents of
+ * directories on alternate root mounted filesystems.
+ * Return results as per dev_readdir_devfs().
+ *
+ * The directory is traversed twice.  First, to calculate
+ * the size of the buffer required; second, to copy the
+ * directory contents into the buffer.  If the directory
+ * contents grow in between passes, which should almost
+ * never happen, start over again.
+ */
+static int
+finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
+{
+	struct finddevhdl *handle;
+	DIR *dir;
+	struct dirent *dp;
+	size_t n;
+
+	*handlep = NULL;
+	if ((dir = opendir(path)) == NULL)
+		return (ENOENT);
+
+restart:
+	handle = calloc(1, sizeof (struct finddevhdl));
+	if (handle == NULL) {
+		(void) closedir(dir);
+		return (ENOMEM);
+	}
+
+	handle->npaths = 0;
+	handle->curpath = 0;
+	handle->paths = NULL;
+
+	n = 0;
+	rewinddir(dir);
+	while ((dp = readdir(dir)) != NULL) {
+		if ((strcmp(dp->d_name, ".") == 0) ||
+		    (strcmp(dp->d_name, "..") == 0))
+			continue;
+		n++;
+	}
+
+	handle->npaths = n;
+	handle->paths = calloc(n, sizeof (char *));
+	if (handle->paths == NULL) {
+		free(handle);
+		(void) closedir(dir);
+		return (ENOMEM);
+	}
+
+	n = 0;
+	rewinddir(dir);
+	while ((dp = readdir(dir)) != NULL) {
+		if ((strcmp(dp->d_name, ".") == 0) ||
+		    (strcmp(dp->d_name, "..") == 0))
+			continue;
+		if (n == handle->npaths) {
+			/*
+			 * restart if directory contents have out-grown
+			 * buffer allocated in the first pass.
+			 */
+			finddev_close((finddevhdl_t)handle);
+			goto restart;
+		}
+		handle->paths[n] = strdup(dp->d_name);
+		if (handle->paths[n] == NULL) {
+			(void) closedir(dir);
+			finddev_close((finddevhdl_t)handle);
+			return (ENOMEM);
+		}
+		n++;
+	}
+	(void) closedir(dir);
+	*handlep = (finddevhdl_t)handle;
+	return (0);
+}
+
+/*
+ * Use of the dev filesystem's private readdir does not trigger
+ * the implicit device reconfiguration.
+ *
+ * Note: only useable with paths mounted on an instance of the
+ * dev filesystem.
+ *
+ * Does not return the . and .. entries.
+ * Empty directories are returned as an zero-length list.
+ * ENOENT is returned as a NULL list pointer.
+ */
+static int
+finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
 {
 	struct finddevhdl	*handle;
 	int			n;
@@ -82,7 +191,7 @@
 	handle->curpath = 0;
 	handle->paths = NULL;
 
-	rv = modctl(MODDEVREADDIR, dir, strlen(dir), NULL, &bufsiz);
+	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
 	if (rv != 0) {
 		free(handle);
 		return (rv);
@@ -95,7 +204,7 @@
 			return (ENOMEM);
 		}
 
-		rv = modctl(MODDEVREADDIR, dir, strlen(dir),
+		rv = modctl(MODDEVREADDIR, path, strlen(path),
 		    pathlist, &bufsiz);
 		if (rv == 0) {
 			for (n = 0, p = pathlist;
@@ -135,6 +244,17 @@
 	/*NOTREACHED*/
 }
 
+int
+finddev_readdir(const char *path, finddevhdl_t *handlep)
+{
+	if ((getzoneid() == GLOBAL_ZONEID) &&
+	    ((strcmp(path, "/dev") == 0) ||
+	    (strncmp(path, "/dev/", 4) == 0))) {
+		return (finddev_readdir_devfs(path, handlep));
+	}
+	return (finddev_readdir_alt(path, handlep));
+}
+
 void
 finddev_close(finddevhdl_t arg)
 {
--- a/usr/src/lib/libdevinfo/libdevinfo.h	Thu Feb 22 19:22:48 2007 -0800
+++ b/usr/src/lib/libdevinfo/libdevinfo.h	Thu Feb 22 19:40:48 2007 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -413,10 +413,6 @@
 extern void		finddev_close(finddevhdl_t);
 extern const char	*finddev_next(finddevhdl_t);
 
-/* For interfaces implementing search either by readdir or finddev */
-#define	FLAG_USE_READDIR	0
-#define	FLAG_USE_FINDDEV	1
-
 
 /*
  * Private interfaces for non-global /dev profile