xref: /dragonfly/lib/libc/gen/getdevpath.c (revision 235099c3)
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 #include <fstab.h>
46 
47 static void finddevlabel(char **pathp, const char *devname);
48 static int xlatedevpath(char **pathp, struct stat *st);
49 static char *dodequote(char *base);
50 
51 /*
52  * Acquire device path.
53  *
54  */
55 char *
56 getdevpath(const char *devname, int flags)
57 {
58 	const char *ptr;
59 	struct stat st;
60 	char *path = NULL;
61 	int stgood = 0;
62 
63 	if (devname[0] == '/' || devname[0] == '.') {
64 		asprintf(&path, "%s", devname);
65 	} else {
66 		asprintf(&path, "/dev/%s", devname);
67 		if (lstat(path, &st) < 0) {
68 			free(path);
69 			path = NULL;
70 			finddevlabel(&path, devname);
71 			if (path == NULL)
72 				asprintf(&path, "%s", devname);
73 		} else {
74 			stgood = 1;
75 		}
76 	}
77 
78 	/*
79 	 * Translate softlinks if requested.  If the lstat() of the
80 	 * pre-translated path fails NULL is expected to be returned.
81 	 * lstat() is not called on the post-translated path.
82 	 */
83 	if ((flags & GETDEVPATH_RAWDEV) && path) {
84 		if (stgood == 0 && lstat(path, &st) == 0)
85 			stgood = 1;
86 		if (stgood)
87 			stgood = xlatedevpath(&path, &st);
88 		if (stgood == 0) {
89 			free(path);
90 			path = NULL;
91 		}
92 
93 	}
94 	if (path == NULL)
95 		errno = ENOENT;
96 	return(path);
97 }
98 
99 static void
100 finddevlabel(char **pathp, const char *devname)
101 {
102 	const char *prefix = _PATH_DEVTAB_PATHS;
103 	const char *ptr1;
104 	const char *trailer;
105 	char *label;
106 	char *ptr2;
107 	char *ptr3;
108 	char *dtpath;
109 	char *bufp;
110 	char buf[256];
111 	FILE *fp;
112 	size_t len;	/* directory prefix length */
113 	size_t dlen;	/* devname length */
114 	size_t tlen;	/* devname length without trailer */
115 
116 	dlen = strlen(devname);
117 	if ((trailer = strrchr(devname, '.')) != NULL)
118 		tlen = trailer - devname;
119 	else
120 		tlen = 0;
121 
122 	while (*prefix && *pathp == NULL) {
123 		/*
124 		 * Directory search path
125 		 */
126 		ptr1 = strchr(prefix, ':');
127 		len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
128 		asprintf(&dtpath, "%*.*s/devtab", len, len, prefix);
129 
130 		/*
131 		 * Each devtab file
132 		 */
133 		if ((fp = fopen(dtpath, "r")) != NULL) {
134 			while (fgets(buf, sizeof(buf), fp) != NULL) {
135 				/*
136 				 * Extract label field, check degenerate
137 				 * cases.
138 				 */
139 				label = strtok_r(buf, " \t\r\n", &bufp);
140 				if (label == NULL || *label == 0 ||
141 				    *label == '#') {
142 					continue;
143 				}
144 
145 				/*
146 				 * Match label, with or without the
147 				 * trailer (aka ".s1a").  The trailer
148 				 * is tacked on if the match is without
149 				 * the trailer.
150 				 */
151 				if (strcmp(devname, label) == 0) {
152 					trailer = "";
153 				} else if (tlen && strlen(label) == tlen &&
154 					   strncmp(devname, label, tlen) == 0) {
155 					trailer = devname + tlen;
156 				} else {
157 					continue;
158 				}
159 
160 				/*
161 				 * Match, extract and process remaining fields.
162 				 */
163 				ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
164 				ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
165 				if (ptr2 == NULL || ptr3 == NULL)
166 					continue;
167 				if (*ptr2 == 0 || *ptr3 == 0)
168 					continue;
169 				ptr3 = dodequote(ptr3);
170 				if (strcmp(ptr2, "path") == 0) {
171 					asprintf(pathp, "%s%s", ptr3, trailer);
172 				} else {
173 					asprintf(pathp, "/dev/%s/%s%s",
174 						 ptr2, ptr3, trailer);
175 				}
176 				break;
177 			}
178 			fclose(fp);
179 		}
180 		free(dtpath);
181 		prefix += len;
182 		if (*prefix == ':')
183 			++prefix;
184 	}
185 }
186 
187 static int
188 xlatedevpath(char **pathp, struct stat *st)
189 {
190 	char *path;
191 	int n;
192 	int len;
193 
194 	/*
195 	 * If not a softlink return unchanged.
196 	 */
197 	if (!S_ISLNK(st->st_mode))
198 		return(1);
199 
200 	/*
201 	 * If the softlink isn't reasonable return bad (0)
202 	 */
203 	len = (int)st->st_size;
204 	if (len < 0 || len > PATH_MAX)
205 		return(0);
206 
207 	/*
208 	 * Read the link, return if the result is not what we expected.
209 	 */
210 	path = malloc(len + 1);
211 	n = readlink(*pathp, path, len);
212 	if (n < 0 || n > len) {
213 		free(path);
214 		return(0);
215 	}
216 
217 	/*
218 	 * Success, replace (*pathp).
219 	 */
220 	path[n] = 0;
221 	free(*pathp);
222 	*pathp = path;
223 	return(1);
224 }
225 
226 static char *
227 dodequote(char *base)
228 {
229 	int len = strlen(base);
230 
231 	if (len && base[0] == '\"' && base[len-1] == '\"') {
232 		base[len - 1] = 0;
233 		++base;
234 	}
235 	return(base);
236 }
237