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