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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2019 Joyent, Inc. 28 */ 29 30 #include <sun_sas.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <dirent.h> 36 #include <libdevinfo.h> 37 38 /* 39 * structure for di_devlink_walk 40 */ 41 typedef struct walk_devlink { 42 char *path; 43 size_t len; 44 char **linkpp; 45 } walk_devlink_t; 46 47 /* 48 * callback funtion for di_devlink_walk 49 * Find matching /dev link for the given path argument. 50 * devlink element and callback function argument. 51 * The input path is expected to not have "/devices". 52 */ 53 static int 54 get_devlink(di_devlink_t devlink, void *arg) 55 { 56 const char ROUTINE[] = "get_devlink"; 57 walk_devlink_t *warg = (walk_devlink_t *)arg; 58 59 /* 60 * When path is specified, it doesn't have minor 61 * name. Therefore, the ../.. prefixes needs to be stripped. 62 */ 63 if (warg->path) { 64 char *content = (char *)di_devlink_content(devlink); 65 char *start = strstr(content, "/devices"); 66 67 if (start == NULL || 68 strncmp(start, warg->path, warg->len) != 0 || 69 /* make it sure the device path has minor name */ 70 start[warg->len] != ':') { 71 return (DI_WALK_CONTINUE); 72 } 73 } 74 75 *(warg->linkpp) = strdup(di_devlink_path(devlink)); 76 log(LOG_DEBUG, ROUTINE, "Walk terminate"); 77 return (DI_WALK_TERMINATE); 78 } 79 80 /* 81 * Convert /devices paths to /dev sym-link paths. 82 * The mapping buffer OSDeviceName paths will be 83 * converted to short names. 84 * mappings The target mappings data to convert to short names 85 * 86 * If no link is found, the long path is left as is. 87 * Note: The NumberOfEntries field MUST not be greater than the size 88 * of the array passed in. 89 */ 90 void 91 convertDevpathToDevlink(PSMHBA_TARGETMAPPING mappings) 92 { 93 const char ROUTINE[] = "convertDevpathToLink"; 94 di_devlink_handle_t hdl; 95 walk_devlink_t warg; 96 int j; 97 char *minor_path, *devlinkp; 98 99 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 100 log(LOG_DEBUG, ROUTINE, "di_devlink failed: errno:%d", 101 strerror(errno)); 102 return; 103 } 104 105 for (j = 0; j < mappings->NumberOfEntries; j++) { 106 if (strchr(mappings->entry[j].ScsiId.OSDeviceName, ':')) { 107 /* search link for minor node */ 108 minor_path = mappings->entry[j].ScsiId.OSDeviceName; 109 if (strstr(minor_path, "/devices") != NULL) { 110 minor_path = mappings->entry[j].ScsiId. 111 OSDeviceName + strlen("/devices"); 112 } 113 warg.path = NULL; 114 } else { 115 minor_path = NULL; 116 if (strstr(mappings->entry[j].ScsiId.OSDeviceName, 117 "/devices") != NULL) { 118 warg.len = strlen(mappings->entry[j].ScsiId. 119 OSDeviceName) - strlen("/devices"); 120 warg.path = mappings->entry[j]. 121 ScsiId.OSDeviceName + strlen("/devices"); 122 } else { 123 warg.len = strlen(mappings->entry[j].ScsiId. 124 OSDeviceName); 125 warg.path = mappings->entry[j].ScsiId. 126 OSDeviceName; 127 } 128 } 129 130 devlinkp = NULL; 131 warg.linkpp = &devlinkp; 132 (void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK, 133 (void *)&warg, get_devlink); 134 135 if (devlinkp != NULL) { 136 (void) snprintf(mappings->entry[j].ScsiId.OSDeviceName, 137 sizeof (mappings->entry[j].ScsiId.OSDeviceName), 138 "%s", devlinkp); 139 free(devlinkp); 140 } 141 142 } 143 144 (void) di_devlink_fini(&hdl); 145 } 146 147 /* 148 * Finds controller path for a give device path. 149 * 150 * Return value: /dev link for dir and minor name. 151 */ 152 static HBA_STATUS 153 lookupLink(char *path, char *link, const char *dir, const char *mname) 154 { 155 const char ROUTINE[] = "lookupLink"; 156 DIR *dp; 157 char buf[MAXPATHLEN]; 158 char node[MAXPATHLEN]; 159 char *charptr; 160 struct dirent *newdirp, *dirp; 161 ssize_t count; 162 int dirplen; 163 char *subpath; 164 char tmpPath[MAXPATHLEN]; 165 166 if ((dp = opendir(dir)) == NULL) { 167 log(LOG_DEBUG, ROUTINE, 168 "Unable to open %s to find controller number.", dir); 169 return (HBA_STATUS_ERROR); 170 } 171 172 if (link == NULL) { 173 log(LOG_DEBUG, ROUTINE, 174 "Invalid argument for storing the link."); 175 return (HBA_STATUS_ERROR); 176 } 177 178 /* 179 * dirplen is large enough to fit the largest path- 180 * struct dirent includes one byte (the terminator) 181 * so we don't add 1 to the calculation here. 182 */ 183 dirplen = pathconf(dir, _PC_NAME_MAX); 184 dirplen = ((dirplen <= 0) ? MAXNAMELEN : dirplen) + 185 sizeof (struct dirent); 186 dirp = (struct dirent *)malloc(dirplen); 187 if (dirp == NULL) { 188 OUT_OF_MEMORY(ROUTINE); 189 return (HBA_STATUS_ERROR); 190 } 191 192 while ((readdir_r(dp, dirp, &newdirp)) == 0 && newdirp != NULL) { 193 if (strcmp(dirp->d_name, ".") == 0 || 194 strcmp(dirp->d_name, "..") == 0) { 195 continue; 196 } 197 /* 198 * set to another pointer since dirp->d_name length is 1 199 * that will store only the first char 'c' from the name. 200 */ 201 charptr = dirp->d_name; 202 (void) snprintf(node, strlen(charptr) + strlen(dir) + 2, 203 "%s/%s", dir, charptr); 204 if ((count = readlink(node, buf, sizeof (buf))) > 0) { 205 subpath = NULL; 206 subpath = strstr(buf, path); 207 buf[count] = '\0'; 208 if (subpath != NULL) { 209 (void) strlcpy(tmpPath, path, MAXPATHLEN); 210 (void) strlcat(tmpPath, mname, MAXPATHLEN); 211 /* 212 * if device path has substring of path 213 * and exactally matching with :scsi suffix 214 */ 215 if (strcmp(subpath, tmpPath) == 0) { 216 (void) strlcpy(link, node, MAXPATHLEN); 217 (void) closedir(dp); 218 S_FREE(dirp); 219 return (HBA_STATUS_OK); 220 } 221 } 222 } 223 } 224 225 (void) closedir(dp); 226 S_FREE(dirp); 227 return (HBA_STATUS_ERROR); 228 } 229 230 /* 231 * Finds controller path for a give device path. 232 * 233 * Return vale:i smp devlink. 234 */ 235 HBA_STATUS 236 lookupControllerLink(char *path, char *link) 237 { 238 const char dir[] = "/dev/cfg"; 239 const char mname[] = ":scsi"; 240 return (lookupLink(path, link, dir, mname)); 241 } 242 243 /* 244 * Finds smp devlink for a give smp path. 245 * 246 * Return vale: smp devlink. 247 */ 248 HBA_STATUS 249 lookupSMPLink(char *path, char *link) 250 { 251 const char dir[] = "/dev/smp"; 252 const char mname[] = ":smp"; 253 return (lookupLink(path, link, dir, mname)); 254 } 255