xref: /dragonfly/lib/libc/gen/getdevpath.c (revision 52f9f0d9)
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 dlen;	/* devname length */
113 	size_t tlen;	/* devname length without trailer */
114 
115 	dlen = strlen(devname);
116 	if ((trailer = strrchr(devname, '.')) != NULL)
117 		tlen = trailer - devname;
118 	else
119 		tlen = 0;
120 
121 	while (*prefix && *pathp == NULL) {
122 		/*
123 		 * Directory search path
124 		 */
125 		ptr1 = strchr(prefix, ':');
126 		len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
127 		asprintf(&dtpath, "%*.*s/devtab", len, len, prefix);
128 
129 		/*
130 		 * Each devtab file
131 		 */
132 		if ((fp = fopen(dtpath, "r")) != NULL) {
133 			while (fgets(buf, sizeof(buf), fp) != NULL) {
134 				/*
135 				 * Extract label field, check degenerate
136 				 * cases.
137 				 */
138 				label = strtok_r(buf, " \t\r\n", &bufp);
139 				if (label == NULL || *label == 0 ||
140 				    *label == '#') {
141 					continue;
142 				}
143 
144 				/*
145 				 * Match label, with or without the
146 				 * trailer (aka ".s1a").  The trailer
147 				 * is tacked on if the match is without
148 				 * the trailer.
149 				 */
150 				if (strcmp(devname, label) == 0) {
151 					trailer = "";
152 				} else if (tlen && strlen(label) == tlen &&
153 					   strncmp(devname, label, tlen) == 0) {
154 					trailer = devname + tlen;
155 				} else {
156 					continue;
157 				}
158 
159 				/*
160 				 * Match, extract and process remaining fields.
161 				 */
162 				ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
163 				ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
164 				if (ptr2 == NULL || ptr3 == NULL)
165 					continue;
166 				if (*ptr2 == 0 || *ptr3 == 0)
167 					continue;
168 				ptr3 = dodequote(ptr3);
169 				if (strcmp(ptr2, "path") == 0) {
170 					asprintf(pathp, "%s%s", ptr3, trailer);
171 				} else {
172 					asprintf(pathp, "/dev/%s/%s%s",
173 						 ptr2, ptr3, trailer);
174 				}
175 				break;
176 			}
177 			fclose(fp);
178 		}
179 		free(dtpath);
180 		prefix += len;
181 		if (*prefix == ':')
182 			++prefix;
183 	}
184 }
185 
186 static int
187 xlatedevpath(char **pathp, struct stat *st)
188 {
189 	char *path;
190 	int n;
191 	int len;
192 
193 	/*
194 	 * If not a softlink return unchanged.
195 	 */
196 	if (!S_ISLNK(st->st_mode))
197 		return(1);
198 
199 	/*
200 	 * If the softlink isn't reasonable return bad (0)
201 	 */
202 	len = (int)st->st_size;
203 	if (len < 0 || len > PATH_MAX)
204 		return(0);
205 
206 	/*
207 	 * Read the link, return if the result is not what we expected.
208 	 */
209 	path = malloc(len + 1);
210 	n = readlink(*pathp, path, len);
211 	if (n < 0 || n > len) {
212 		free(path);
213 		return(0);
214 	}
215 
216 	/*
217 	 * Success, replace (*pathp).
218 	 */
219 	path[n] = 0;
220 	free(*pathp);
221 	*pathp = path;
222 	return(1);
223 }
224 
225 static char *
226 dodequote(char *base)
227 {
228 	int len = strlen(base);
229 
230 	if (len && base[0] == '\"' && base[len-1] == '\"') {
231 		base[len - 1] = 0;
232 		++base;
233 	}
234 	return(base);
235 }
236