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 https://opensource.org/licenses/CDDL-1.0. 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <errno.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include <libzutil.h> 33 34 /* Substring from after the last slash, or the string itself if none */ 35 const char * 36 zfs_basename(const char *path) 37 { 38 const char *bn = strrchr(path, '/'); 39 return (bn ? bn + 1 : path); 40 } 41 42 /* Return index of last slash or -1 if none */ 43 ssize_t 44 zfs_dirnamelen(const char *path) 45 { 46 const char *end = strrchr(path, '/'); 47 return (end ? end - path : -1); 48 } 49 50 /* 51 * Given a shorthand device name check if a file by that name exists in any 52 * of the 'zpool_default_import_path' or ZPOOL_IMPORT_PATH directories. If 53 * one is found, store its fully qualified path in the 'path' buffer passed 54 * by the caller and return 0, otherwise return an error. 55 */ 56 int 57 zfs_resolve_shortname(const char *name, char *path, size_t len) 58 { 59 const char *env = getenv("ZPOOL_IMPORT_PATH"); 60 char resolved_path[PATH_MAX]; 61 62 if (env) { 63 for (;;) { 64 env += strspn(env, ":"); 65 size_t dirlen = strcspn(env, ":"); 66 if (dirlen) { 67 (void) snprintf(path, len, "%.*s/%s", 68 (int)dirlen, env, name); 69 if (access(path, F_OK) == 0) 70 return (0); 71 72 env += dirlen; 73 } else 74 break; 75 } 76 } else { 77 size_t count; 78 const char *const *zpool_default_import_path = 79 zpool_default_search_paths(&count); 80 81 for (size_t i = 0; i < count; ++i) { 82 (void) snprintf(path, len, "%s/%s", 83 zpool_default_import_path[i], name); 84 if (access(path, F_OK) == 0) 85 return (0); 86 } 87 } 88 89 /* 90 * The user can pass a relative path like ./file1 for the vdev. The path 91 * must contain a directory prefix like './file1' or '../file1'. Simply 92 * passing 'file1' is not allowed, as it may match a block device name. 93 */ 94 if ((strncmp(name, "./", 2) == 0 || strncmp(name, "../", 3) == 0) && 95 realpath(name, resolved_path) != NULL) { 96 if (access(resolved_path, F_OK) == 0) { 97 if (strlen(resolved_path) + 1 <= len) { 98 if (strlcpy(path, resolved_path, len) < len) 99 return (0); /* success */ 100 } 101 } 102 } 103 return (errno = ENOENT); 104 } 105 106 /* 107 * Given a shorthand device name look for a match against 'cmp_name'. This 108 * is done by checking all prefix expansions using either the default 109 * 'zpool_default_import_paths' or the ZPOOL_IMPORT_PATH environment 110 * variable. Proper partition suffixes will be appended if this is a 111 * whole disk. When a match is found 0 is returned otherwise ENOENT. 112 */ 113 static int 114 zfs_strcmp_shortname(const char *name, const char *cmp_name, int wholedisk) 115 { 116 int path_len, cmp_len, i = 0, error = ENOENT; 117 char *dir, *env, *envdup = NULL, *tmp = NULL; 118 char path_name[MAXPATHLEN]; 119 const char *const *zpool_default_import_path = NULL; 120 size_t count; 121 122 cmp_len = strlen(cmp_name); 123 env = getenv("ZPOOL_IMPORT_PATH"); 124 125 if (env) { 126 envdup = strdup(env); 127 dir = strtok_r(envdup, ":", &tmp); 128 } else { 129 zpool_default_import_path = zpool_default_search_paths(&count); 130 dir = (char *)zpool_default_import_path[i]; 131 } 132 133 while (dir) { 134 /* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */ 135 if (env) { 136 while (dir[strlen(dir)-1] == '/') 137 dir[strlen(dir)-1] = '\0'; 138 } 139 140 path_len = snprintf(path_name, MAXPATHLEN, "%s/%s", dir, name); 141 if (wholedisk) 142 path_len = zfs_append_partition(path_name, MAXPATHLEN); 143 144 if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0) { 145 error = 0; 146 break; 147 } 148 149 if (env) { 150 dir = strtok_r(NULL, ":", &tmp); 151 } else if (++i < count) { 152 dir = (char *)zpool_default_import_path[i]; 153 } else { 154 dir = NULL; 155 } 156 } 157 158 if (env) 159 free(envdup); 160 161 return (error); 162 } 163 164 /* 165 * Given either a shorthand or fully qualified path name look for a match 166 * against 'cmp'. The passed name will be expanded as needed for comparison 167 * purposes and redundant slashes stripped to ensure an accurate match. 168 */ 169 int 170 zfs_strcmp_pathname(const char *name, const char *cmp, int wholedisk) 171 { 172 int path_len, cmp_len; 173 char path_name[MAXPATHLEN]; 174 char cmp_name[MAXPATHLEN]; 175 char *dir, *tmp = NULL; 176 177 /* Strip redundant slashes if they exist due to ZPOOL_IMPORT_PATH */ 178 cmp_name[0] = '\0'; 179 (void) strlcpy(path_name, cmp, sizeof (path_name)); 180 for (dir = strtok_r(path_name, "/", &tmp); 181 dir != NULL; 182 dir = strtok_r(NULL, "/", &tmp)) { 183 strlcat(cmp_name, "/", sizeof (cmp_name)); 184 strlcat(cmp_name, dir, sizeof (cmp_name)); 185 } 186 187 if (name[0] != '/') 188 return (zfs_strcmp_shortname(name, cmp_name, wholedisk)); 189 190 (void) strlcpy(path_name, name, MAXPATHLEN); 191 path_len = strlen(path_name); 192 cmp_len = strlen(cmp_name); 193 194 if (wholedisk) { 195 path_len = zfs_append_partition(path_name, MAXPATHLEN); 196 if (path_len == -1) 197 return (ENOMEM); 198 } 199 200 if ((path_len != cmp_len) || strcmp(path_name, cmp_name)) 201 return (ENOENT); 202 203 return (0); 204 } 205