Merge branch 'vendor/BZIP'
[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 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 }