b2f7c5250dd970b850d302e983e30da0164606aa
[dragonfly.git] / lib / libutil / 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
46 #include "libutil.h"
47
48 static void finddevlabel(char **pathp, const char *devname);
49 static int xlatedevpath(char **pathp, struct stat *st);
50 static char *dodequote(char *base);
51
52 /*
53  * Acquire device path.
54  *
55  */
56 char *
57 getdevpath(const char *devname, int flags)
58 {
59         const char *ptr;
60         struct stat st;
61         char *path = NULL;
62         int stgood = 0;
63
64         if (devname[0] == '/' || devname[0] == '.') {
65                 asprintf(&path, "%s", devname);
66         } else if ((ptr = strchr(devname, ':')) != NULL) {
67                 asprintf(&path, "/dev/%*.*s/%s",
68                         ptr - devname, ptr - devname, devname,
69                         ptr + 1);
70         } else {
71                 asprintf(&path, "/dev/%s", devname);
72                 if (lstat(path, &st) < 0) {
73                         free(path);
74                         path = NULL;
75                         finddevlabel(&path, devname);
76                 } else {
77                         stgood = 1;
78                 }
79         }
80
81         /*
82          * Translate softlinks if requested.  If the lstat() of the
83          * pre-translated path fails NULL is expected to be returned.
84          * lstat() is not called on the post-translated path.
85          */
86         if ((flags & GETDEVPATH_RAWDEV) && path) {
87                 if (stgood == 0 && lstat(path, &st) == 0)
88                         stgood = 1;
89                 if (stgood)
90                         stgood = xlatedevpath(&path, &st);
91                 if (stgood == 0) {
92                         free(path);
93                         path = NULL;
94                 }
95
96         }
97         if (path == NULL)
98                 errno = ENOENT;
99         return(path);
100 }
101
102 static void
103 finddevlabel(char **pathp, const char *devname)
104 {
105         const char *prefix = _PATH_DEVTAB_PATHS;
106         const char *ptr1;
107         const char *ptr2;
108         char *ptr3;
109         char *dtpath;
110         char *bufp;
111         char buf[256];
112         FILE *fp;
113         size_t len;
114
115         while (*prefix && *pathp == NULL) {
116                 ptr1 = strchr(prefix, ':');
117                 len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
118                 asprintf(&dtpath, "%*.*s/devtab", len, len, prefix);
119                 if ((fp = fopen(dtpath, "r")) != NULL) {
120                         while (fgets(buf, sizeof(buf), fp) != NULL) {
121                                 ptr1 = strtok_r(buf, " \t\r\n", &bufp);
122                                 if (ptr1 == NULL || *ptr1 == 0 || *ptr1 == '#')
123                                         continue;
124                                 if (strcmp(devname, ptr1) != 0)
125                                         continue;
126                                 ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
127                                 ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
128                                 if (ptr2 == NULL || ptr3 == NULL)
129                                         continue;
130                                 if (*ptr2 == 0 || *ptr3 == 0)
131                                         continue;
132                                 ptr3 = dodequote(ptr3);
133                                 if (strcmp(ptr2, "path") == 0) {
134                                         asprintf(pathp, "%s", ptr3);
135                                 } else {
136                                         asprintf(pathp, "/dev/%s/%s",
137                                                  ptr2, ptr3);
138                                 }
139                                 break;
140                         }
141                         fclose(fp);
142                 }
143                 free(dtpath);
144                 prefix += len;
145                 if (*prefix == ':')
146                         ++prefix;
147         }
148 }
149
150 static int
151 xlatedevpath(char **pathp, struct stat *st)
152 {
153         char *path;
154         int n;
155         int len;
156
157         /*
158          * If not a softlink return unchanged.
159          */
160         if (!S_ISLNK(st->st_mode))
161                 return(1);
162
163         /*
164          * If the softlink isn't reasonable return bad (0)
165          */
166         len = (int)st->st_size;
167         if (len < 0 || len > PATH_MAX)
168                 return(0);
169
170         /*
171          * Read the link, return if the result is not what we expected.
172          */
173         path = malloc(len + 1);
174         n = readlink(*pathp, path, len);
175         if (n < 0 || n > len) {
176                 free(path);
177                 return(0);
178         }
179
180         /*
181          * Success, replace (*pathp).
182          */
183         path[n] = 0;
184         free(*pathp);
185         *pathp = path;
186         return(1);
187 }
188
189 static char *
190 dodequote(char *base)
191 {
192         int len = strlen(base);
193
194         if (len && base[0] == '\"' && base[len-1] == '\"') {
195                 base[len - 1] = 0;
196                 ++base;
197         }
198         return(base);
199 }