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 #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 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 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 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 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 void 260 finddev_close(finddevhdl_t arg) 261 { 262 struct finddevhdl *handle = (struct finddevhdl *)arg; 263 int i; 264 265 for (i = 0; i < handle->npaths; i++) { 266 if (handle->paths[i]) 267 free(handle->paths[i]); 268 } 269 free(handle->paths); 270 free(handle); 271 } 272 273 const char * 274 finddev_next(finddevhdl_t arg) 275 { 276 struct finddevhdl *handle = (struct finddevhdl *)arg; 277 const char *path = NULL; 278 279 if (handle->curpath < handle->npaths) { 280 path = handle->paths[handle->curpath]; 281 handle->curpath++; 282 } 283 return (path); 284 } 285