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