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 2007 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 /* 58 * Return true if a device exists 59 * If the path refers into the /dev filesystem, use a 60 * private interface to query if the device exists but 61 * without triggering an implicit reconfig if it does not. 62 * Note: can only function properly with absolute pathnames 63 * and only functions for persisted global /dev names, ie 64 * those managed by devfsadm. For paths other than 65 * /dev, stat(2) is sufficient. 66 */ 67 int 68 device_exists(const char *devname) 69 { 70 int rv; 71 struct stat st; 72 73 if ((getzoneid() == GLOBAL_ZONEID) && 74 ((strcmp(devname, "/dev") == 0) || 75 (strncmp(devname, "/dev/", 5) == 0))) { 76 rv = modctl(MODDEVEXISTS, devname, strlen(devname)); 77 return ((rv == 0) ? 1 : 0); 78 } 79 if (stat(devname, &st) == 0) 80 return (1); 81 return (0); 82 } 83 84 85 /* 86 * Use the standard library readdir to read the contents of 87 * directories on alternate root mounted filesystems. 88 * Return results as per dev_readdir_devfs(). 89 * 90 * The directory is traversed twice. First, to calculate 91 * the size of the buffer required; second, to copy the 92 * directory contents into the buffer. If the directory 93 * contents grow in between passes, which should almost 94 * never happen, start over again. 95 */ 96 static int 97 finddev_readdir_alt(const char *path, finddevhdl_t *handlep) 98 { 99 struct finddevhdl *handle; 100 DIR *dir; 101 struct dirent *dp; 102 size_t n; 103 104 *handlep = NULL; 105 if ((dir = opendir(path)) == NULL) 106 return (ENOENT); 107 108 restart: 109 handle = calloc(1, sizeof (struct finddevhdl)); 110 if (handle == NULL) { 111 (void) closedir(dir); 112 return (ENOMEM); 113 } 114 115 handle->npaths = 0; 116 handle->curpath = 0; 117 handle->paths = NULL; 118 119 n = 0; 120 rewinddir(dir); 121 while ((dp = readdir(dir)) != NULL) { 122 if ((strcmp(dp->d_name, ".") == 0) || 123 (strcmp(dp->d_name, "..") == 0)) 124 continue; 125 n++; 126 } 127 128 handle->npaths = n; 129 handle->paths = calloc(n, sizeof (char *)); 130 if (handle->paths == NULL) { 131 free(handle); 132 (void) closedir(dir); 133 return (ENOMEM); 134 } 135 136 n = 0; 137 rewinddir(dir); 138 while ((dp = readdir(dir)) != NULL) { 139 if ((strcmp(dp->d_name, ".") == 0) || 140 (strcmp(dp->d_name, "..") == 0)) 141 continue; 142 if (n == handle->npaths) { 143 /* 144 * restart if directory contents have out-grown 145 * buffer allocated in the first pass. 146 */ 147 finddev_close((finddevhdl_t)handle); 148 goto restart; 149 } 150 handle->paths[n] = strdup(dp->d_name); 151 if (handle->paths[n] == NULL) { 152 (void) closedir(dir); 153 finddev_close((finddevhdl_t)handle); 154 return (ENOMEM); 155 } 156 n++; 157 } 158 (void) closedir(dir); 159 *handlep = (finddevhdl_t)handle; 160 return (0); 161 } 162 163 /* 164 * Use of the dev filesystem's private readdir does not trigger 165 * the implicit device reconfiguration. 166 * 167 * Note: only useable with paths mounted on an instance of the 168 * dev filesystem. 169 * 170 * Does not return the . and .. entries. 171 * Empty directories are returned as an zero-length list. 172 * ENOENT is returned as a NULL list pointer. 173 */ 174 static int 175 finddev_readdir_devfs(const char *path, finddevhdl_t *handlep) 176 { 177 struct finddevhdl *handle; 178 int n; 179 int rv; 180 int64_t bufsiz; 181 char *pathlist; 182 char *p; 183 int len; 184 185 *handlep = NULL; 186 handle = calloc(1, sizeof (struct finddevhdl)); 187 if (handle == NULL) 188 return (ENOMEM); 189 190 handle->npaths = 0; 191 handle->curpath = 0; 192 handle->paths = NULL; 193 194 rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz); 195 if (rv != 0) { 196 free(handle); 197 return (rv); 198 } 199 200 for (;;) { 201 assert(bufsiz != 0); 202 if ((pathlist = malloc(bufsiz)) == NULL) { 203 free(handle); 204 return (ENOMEM); 205 } 206 207 rv = modctl(MODDEVREADDIR, path, strlen(path), 208 pathlist, &bufsiz); 209 if (rv == 0) { 210 for (n = 0, p = pathlist; 211 (len = strlen(p)) > 0; p += len+1) { 212 n++; 213 } 214 handle->npaths = n; 215 handle->paths = calloc(n, sizeof (char *)); 216 if (handle->paths == NULL) { 217 free(handle); 218 free(pathlist); 219 return (ENOMEM); 220 } 221 for (n = 0, p = pathlist; 222 (len = strlen(p)) > 0; p += len+1, n++) { 223 handle->paths[n] = strdup(p); 224 if (handle->paths[n] == NULL) { 225 finddev_close((finddevhdl_t)handle); 226 free(pathlist); 227 return (ENOMEM); 228 } 229 } 230 *handlep = (finddevhdl_t)handle; 231 free(pathlist); 232 return (0); 233 } 234 free(pathlist); 235 switch (errno) { 236 case EAGAIN: 237 break; 238 case ENOENT: 239 default: 240 free(handle); 241 return (errno); 242 } 243 } 244 /*NOTREACHED*/ 245 } 246 247 int 248 finddev_readdir(const char *path, finddevhdl_t *handlep) 249 { 250 if ((getzoneid() == GLOBAL_ZONEID) && 251 ((strcmp(path, "/dev") == 0) || 252 (strncmp(path, "/dev/", 4) == 0))) { 253 return (finddev_readdir_devfs(path, handlep)); 254 } 255 return (finddev_readdir_alt(path, handlep)); 256 } 257 258 void 259 finddev_close(finddevhdl_t arg) 260 { 261 struct finddevhdl *handle = (struct finddevhdl *)arg; 262 int i; 263 264 for (i = 0; i < handle->npaths; i++) { 265 if (handle->paths[i]) 266 free(handle->paths[i]); 267 } 268 free(handle->paths); 269 free(handle); 270 } 271 272 const char * 273 finddev_next(finddevhdl_t arg) 274 { 275 struct finddevhdl *handle = (struct finddevhdl *)arg; 276 const char *path = NULL; 277 278 if (handle->curpath < handle->npaths) { 279 path = handle->paths[handle->curpath]; 280 handle->curpath++; 281 } 282 return (path); 283 } 284