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 #include <stdio.h> 27 #include <unistd.h> 28 #include <fcntl.h> 29 #include <string.h> 30 #include <thread.h> 31 #include <synch.h> 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <dirent.h> 37 #include <regex.h> 38 #include <errno.h> 39 #include <stdarg.h> 40 #include <libdevinfo.h> 41 #include <zone.h> 42 #include <sys/modctl.h> 43 #include <syslog.h> 44 #include <sys/stat.h> 45 #include <assert.h> 46 47 48 struct finddevhdl { 49 int npaths; 50 int curpath; 51 char **paths; 52 }; 53 54 55 #define GLOBAL_DEV_PATH(devpath) \ 56 ((getzoneid() == GLOBAL_ZONEID) && \ 57 ((strcmp(devpath, "/dev") == 0) || \ 58 (strncmp(devpath, "/dev/", strlen("/dev/")) == 0))) 59 60 /* 61 * Return true if a device exists 62 * If the path refers into the /dev filesystem, use a 63 * private interface to query if the device exists but 64 * without triggering an implicit reconfig if it does not. 65 * Note: can only function properly with absolute pathnames 66 * and only functions for persisted global /dev names, ie 67 * those managed by devfsadm. For paths other than 68 * /dev, stat(2) is sufficient. 69 */ 70 int 71 device_exists(const char *devname) 72 { 73 int rv; 74 struct stat st; 75 76 if (GLOBAL_DEV_PATH(devname)) { 77 rv = modctl(MODDEVEXISTS, devname, strlen(devname)); 78 return ((rv == 0) ? 1 : 0); 79 } 80 if (stat(devname, &st) == 0) 81 return (1); 82 return (0); 83 } 84 85 86 /* 87 * Use the standard library readdir to read the contents of 88 * directories on alternate root mounted filesystems. 89 * Return results as per dev_readdir_devfs(). 90 * 91 * The directory is traversed twice. First, to calculate 92 * the size of the buffer required; second, to copy the 93 * directory contents into the buffer. If the directory 94 * contents grow in between passes, which should almost 95 * never happen, start over again. 96 */ 97 static int 98 finddev_readdir_alt(const char *path, finddevhdl_t *handlep) 99 { 100 struct finddevhdl *handle; 101 DIR *dir; 102 struct dirent *dp; 103 size_t n; 104 105 *handlep = NULL; 106 if ((dir = opendir(path)) == NULL) 107 return (ENOENT); 108 109 restart: 110 handle = calloc(1, sizeof (struct finddevhdl)); 111 if (handle == NULL) { 112 (void) closedir(dir); 113 return (ENOMEM); 114 } 115 116 handle->npaths = 0; 117 handle->curpath = 0; 118 handle->paths = NULL; 119 120 n = 0; 121 rewinddir(dir); 122 while ((dp = readdir(dir)) != NULL) { 123 if ((strcmp(dp->d_name, ".") == 0) || 124 (strcmp(dp->d_name, "..") == 0)) 125 continue; 126 n++; 127 } 128 129 handle->npaths = n; 130 handle->paths = calloc(n, sizeof (char *)); 131 if (handle->paths == NULL) { 132 free(handle); 133 (void) closedir(dir); 134 return (ENOMEM); 135 } 136 137 n = 0; 138 rewinddir(dir); 139 while ((dp = readdir(dir)) != NULL) { 140 if ((strcmp(dp->d_name, ".") == 0) || 141 (strcmp(dp->d_name, "..") == 0)) 142 continue; 143 if (n == handle->npaths) { 144 /* 145 * restart if directory contents have out-grown 146 * buffer allocated in the first pass. 147 */ 148 finddev_close((finddevhdl_t)handle); 149 goto restart; 150 } 151 handle->paths[n] = strdup(dp->d_name); 152 if (handle->paths[n] == NULL) { 153 (void) closedir(dir); 154 finddev_close((finddevhdl_t)handle); 155 return (ENOMEM); 156 } 157 n++; 158 } 159 (void) closedir(dir); 160 *handlep = (finddevhdl_t)handle; 161 return (0); 162 } 163 164 /* 165 * Use of the dev filesystem's private readdir does not trigger 166 * the implicit device reconfiguration. 167 * 168 * Note: only useable with paths mounted on an instance of the 169 * dev filesystem. 170 * 171 * Does not return the . and .. entries. 172 * Empty directories are returned as an zero-length list. 173 * ENOENT is returned as a NULL list pointer. 174 */ 175 static int 176 finddev_readdir_devfs(const char *path, finddevhdl_t *handlep) 177 { 178 struct finddevhdl *handle; 179 int n; 180 int rv; 181 int64_t bufsiz; 182 char *pathlist; 183 char *p; 184 int len; 185 186 *handlep = NULL; 187 handle = calloc(1, sizeof (struct finddevhdl)); 188 if (handle == NULL) 189 return (ENOMEM); 190 191 handle->npaths = 0; 192 handle->curpath = 0; 193 handle->paths = NULL; 194 195 rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz); 196 if (rv != 0) { 197 free(handle); 198 return (rv); 199 } 200 201 for (;;) { 202 assert(bufsiz != 0); 203 if ((pathlist = malloc(bufsiz)) == NULL) { 204 free(handle); 205 return (ENOMEM); 206 } 207 208 rv = modctl(MODDEVREADDIR, path, strlen(path), 209 pathlist, &bufsiz); 210 if (rv == 0) { 211 for (n = 0, p = pathlist; 212 (len = strlen(p)) > 0; p += len+1) { 213 n++; 214 } 215 handle->npaths = n; 216 handle->paths = calloc(n, sizeof (char *)); 217 if (handle->paths == NULL) { 218 free(handle); 219 free(pathlist); 220 return (ENOMEM); 221 } 222 for (n = 0, p = pathlist; 223 (len = strlen(p)) > 0; p += len+1, n++) { 224 handle->paths[n] = strdup(p); 225 if (handle->paths[n] == NULL) { 226 finddev_close((finddevhdl_t)handle); 227 free(pathlist); 228 return (ENOMEM); 229 } 230 } 231 *handlep = (finddevhdl_t)handle; 232 free(pathlist); 233 return (0); 234 } 235 free(pathlist); 236 switch (errno) { 237 case EAGAIN: 238 break; 239 case ENOENT: 240 default: 241 free(handle); 242 return (errno); 243 } 244 } 245 /*NOTREACHED*/ 246 } 247 248 int 249 finddev_readdir(const char *path, finddevhdl_t *handlep) 250 { 251 if (GLOBAL_DEV_PATH(path)) { 252 return (finddev_readdir_devfs(path, handlep)); 253 } 254 return (finddev_readdir_alt(path, handlep)); 255 } 256 257 /* 258 * Return true if a directory is empty 259 * Use the standard library readdir to determine if a directory is 260 * empty. 261 */ 262 static int 263 finddev_emptydir_alt(const char *path) 264 { 265 DIR *dir; 266 struct dirent *dp; 267 268 if ((dir = opendir(path)) == NULL) 269 return (ENOENT); 270 271 while ((dp = readdir(dir)) != NULL) { 272 if ((strcmp(dp->d_name, ".") == 0) || 273 (strcmp(dp->d_name, "..") == 0)) 274 continue; 275 (void) closedir(dir); 276 return (0); /* not empty */ 277 } 278 (void) closedir(dir); 279 return (1); /* empty */ 280 } 281 282 /* 283 * Use of the dev filesystem's private readdir does (not trigger 284 * the implicit device reconfiguration) to determine if a directory 285 * is empty. 286 * 287 * Note: only useable with paths mounted on an instance of the 288 * dev filesystem. 289 * 290 * Does not return the . and .. entries. 291 * Empty directories are returned as an zero-length list. 292 * ENOENT is returned as a NULL list pointer. 293 */ 294 static int 295 finddev_emptydir_devfs(const char *path) 296 { 297 int rv; 298 int empty; 299 300 rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty); 301 if (rv == 0) { 302 return (empty); 303 } 304 return (0); 305 } 306 307 int 308 finddev_emptydir(const char *path) 309 { 310 if (GLOBAL_DEV_PATH(path)) { 311 return (finddev_emptydir_devfs(path)); 312 } 313 return (finddev_emptydir_alt(path)); 314 } 315 316 void 317 finddev_close(finddevhdl_t arg) 318 { 319 struct finddevhdl *handle = (struct finddevhdl *)arg; 320 int i; 321 322 for (i = 0; i < handle->npaths; i++) { 323 if (handle->paths[i]) 324 free(handle->paths[i]); 325 } 326 free(handle->paths); 327 free(handle); 328 } 329 330 const char * 331 finddev_next(finddevhdl_t arg) 332 { 333 struct finddevhdl *handle = (struct finddevhdl *)arg; 334 const char *path = NULL; 335 336 if (handle->curpath < handle->npaths) { 337 path = handle->paths[handle->curpath]; 338 handle->curpath++; 339 } 340 return (path); 341 } 342