1f9ba126cc4fecfaaaf502d79ef380f1a02ed905
[dragonfly.git] / lib / libc / gen / getdevpath.c
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 }