xref: /dflybsd-src/lib/libc/gen/getdevpath.c (revision 6507240b2fcfebaacc0f92f997dad76922e1d8c0)
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <paths.h>
44 #include <limits.h>
45 
46 #include "libutil.h"
47 
48 static void finddevlabel(char **pathp, const char *devname);
49 static int xlatedevpath(char **pathp, struct stat *st);
50 static char *dodequote(char *base);
51 
52 /*
53  * Acquire device path.
54  *
55  */
56 char *
57 getdevpath(const char *devname, int flags)
58 {
59 	const char *ptr;
60 	struct stat st;
61 	char *path = NULL;
62 	int stgood = 0;
63 
64 	if (devname[0] == '/' || devname[0] == '.') {
65 		asprintf(&path, "%s", devname);
66 	} else {
67 		asprintf(&path, "/dev/%s", devname);
68 		if (lstat(path, &st) < 0) {
69 			free(path);
70 			path = NULL;
71 			finddevlabel(&path, devname);
72 			if (path == NULL)
73 				asprintf(&path, "%s", devname);
74 		} else {
75 			stgood = 1;
76 		}
77 	}
78 
79 	/*
80 	 * Translate softlinks if requested.  If the lstat() of the
81 	 * pre-translated path fails NULL is expected to be returned.
82 	 * lstat() is not called on the post-translated path.
83 	 */
84 	if ((flags & GETDEVPATH_RAWDEV) && path) {
85 		if (stgood == 0 && lstat(path, &st) == 0)
86 			stgood = 1;
87 		if (stgood)
88 			stgood = xlatedevpath(&path, &st);
89 		if (stgood == 0) {
90 			free(path);
91 			path = NULL;
92 		}
93 
94 	}
95 	if (path == NULL)
96 		errno = ENOENT;
97 	return(path);
98 }
99 
100 static void
101 finddevlabel(char **pathp, const char *devname)
102 {
103 	const char *prefix = _PATH_DEVTAB_PATHS;
104 	const char *ptr1;
105 	const char *trailer;
106 	char *label;
107 	char *ptr2;
108 	char *ptr3;
109 	char *dtpath;
110 	char *bufp;
111 	char buf[256];
112 	FILE *fp;
113 	size_t len;	/* directory prefix length */
114 	size_t dlen;	/* devname length */
115 	size_t tlen;	/* devname length without trailer */
116 
117 	dlen = strlen(devname);
118 	if ((trailer = strrchr(devname, '.')) != NULL)
119 		tlen = trailer - devname;
120 	else
121 		tlen = 0;
122 
123 	while (*prefix && *pathp == NULL) {
124 		/*
125 		 * Directory search path
126 		 */
127 		ptr1 = strchr(prefix, ':');
128 		len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
129 		asprintf(&dtpath, "%*.*s/devtab", len, len, prefix);
130 
131 		/*
132 		 * Each devtab file
133 		 */
134 		if ((fp = fopen(dtpath, "r")) != NULL) {
135 			while (fgets(buf, sizeof(buf), fp) != NULL) {
136 				/*
137 				 * Extract label field, check degenerate
138 				 * cases.
139 				 */
140 				label = strtok_r(buf, " \t\r\n", &bufp);
141 				if (label == NULL || *label == 0 ||
142 				    *label == '#') {
143 					continue;
144 				}
145 
146 				/*
147 				 * Match label, with or without the
148 				 * trailer (aka ".s1a").  The trailer
149 				 * is tacked on if the match is without
150 				 * the trailer.
151 				 */
152 				if (strcmp(devname, label) == 0) {
153 					trailer = "";
154 				} else if (tlen && strlen(label) == tlen &&
155 					   strncmp(devname, label, tlen) == 0) {
156 					trailer = devname + tlen;
157 				} else {
158 					continue;
159 				}
160 
161 				/*
162 				 * Match, extract and process remaining fields.
163 				 */
164 				ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
165 				ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
166 				if (ptr2 == NULL || ptr3 == NULL)
167 					continue;
168 				if (*ptr2 == 0 || *ptr3 == 0)
169 					continue;
170 				ptr3 = dodequote(ptr3);
171 				if (strcmp(ptr2, "path") == 0) {
172 					asprintf(pathp, "%s%s", ptr3, trailer);
173 				} else {
174 					asprintf(pathp, "/dev/%s/%s%s",
175 						 ptr2, ptr3, trailer);
176 				}
177 				break;
178 			}
179 			fclose(fp);
180 		}
181 		free(dtpath);
182 		prefix += len;
183 		if (*prefix == ':')
184 			++prefix;
185 	}
186 }
187 
188 static int
189 xlatedevpath(char **pathp, struct stat *st)
190 {
191 	char *path;
192 	int n;
193 	int len;
194 
195 	/*
196 	 * If not a softlink return unchanged.
197 	 */
198 	if (!S_ISLNK(st->st_mode))
199 		return(1);
200 
201 	/*
202 	 * If the softlink isn't reasonable return bad (0)
203 	 */
204 	len = (int)st->st_size;
205 	if (len < 0 || len > PATH_MAX)
206 		return(0);
207 
208 	/*
209 	 * Read the link, return if the result is not what we expected.
210 	 */
211 	path = malloc(len + 1);
212 	n = readlink(*pathp, path, len);
213 	if (n < 0 || n > len) {
214 		free(path);
215 		return(0);
216 	}
217 
218 	/*
219 	 * Success, replace (*pathp).
220 	 */
221 	path[n] = 0;
222 	free(*pathp);
223 	*pathp = path;
224 	return(1);
225 }
226 
227 static char *
228 dodequote(char *base)
229 {
230 	int len = strlen(base);
231 
232 	if (len && base[0] == '\"' && base[len-1] == '\"') {
233 		base[len - 1] = 0;
234 		++base;
235 	}
236 	return(base);
237 }
238