]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/canonicalize.c
flock: initialize timevals [-Werror=maybe-uninitialized]
[thirdparty/util-linux.git] / lib / canonicalize.c
1 /*
2 * canonicalize.c -- canonicalize pathname by removing symlinks
3 *
4 * This file may be distributed under the terms of the
5 * GNU Lesser General Public License.
6 *
7 * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
8 */
9 #include <stdio.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
18
19 #include "canonicalize.h"
20 #include "pathnames.h"
21 #include "all-io.h"
22 #include "strutils.h"
23
24 /*
25 * Converts private "dm-N" names to "/dev/mapper/<name>"
26 *
27 * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
28 * provides the real DM device names in /sys/block/<ptname>/dm/name
29 */
30 char *__canonicalize_dm_name(const char *prefix, const char *ptname)
31 {
32 FILE *f;
33 size_t sz;
34 char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
35
36 if (!ptname || !*ptname)
37 return NULL;
38
39 if (!prefix)
40 prefix = "";
41
42 snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
43 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
44 return NULL;
45
46 /* read "<name>\n" from sysfs */
47 if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
48 name[sz - 1] = '\0';
49 snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
50
51 if ((prefix && *prefix) || access(path, F_OK) == 0)
52 res = strdup(path);
53 }
54 fclose(f);
55 return res;
56 }
57
58 char *canonicalize_dm_name(const char *ptname)
59 {
60 return __canonicalize_dm_name(NULL, ptname);
61 }
62
63 static int is_dm_devname(char *canonical, char **name)
64 {
65 struct stat sb;
66 char *p = strrchr(canonical, '/');
67
68 *name = NULL;
69
70 if (!p
71 || strncmp(p, "/dm-", 4) != 0
72 || !isdigit(*(p + 4))
73 || stat(canonical, &sb) != 0
74 || !S_ISBLK(sb.st_mode))
75 return 0;
76
77 *name = p + 1;
78 return 1;
79 }
80
81 /*
82 * This function does not canonicalize the path! It just prepends CWD before a
83 * relative path. If the path is no relative than returns NULL. The path does
84 * not have to exist.
85 */
86 char *absolute_path(const char *path)
87 {
88 char cwd[PATH_MAX], *res, *p;
89 size_t psz, csz;
90
91 if (!is_relative_path(path)) {
92 errno = EINVAL;
93 return NULL;
94 }
95 if (!getcwd(cwd, sizeof(cwd)))
96 return NULL;
97
98 /* simple clean up */
99 if (startswith(path, "./"))
100 path += 2;
101 else if (strcmp(path, ".") == 0)
102 path = NULL;
103
104 if (!path || !*path)
105 return strdup(cwd);
106
107 csz = strlen(cwd);
108 psz = strlen(path);
109
110 p = res = malloc(csz + 1 + psz + 1);
111 if (!res)
112 return NULL;
113
114 p = mempcpy(p, cwd, csz);
115 *p++ = '/';
116 memcpy(p, path, psz + 1);
117
118 return res;
119 }
120
121 char *canonicalize_path(const char *path)
122 {
123 char *canonical, *dmname;
124
125 if (!path || !*path)
126 return NULL;
127
128 canonical = realpath(path, NULL);
129 if (!canonical)
130 return strdup(path);
131
132 if (is_dm_devname(canonical, &dmname)) {
133 char *dm = canonicalize_dm_name(dmname);
134 if (dm) {
135 free(canonical);
136 return dm;
137 }
138 }
139
140 return canonical;
141 }
142
143 char *canonicalize_path_restricted(const char *path)
144 {
145 char *canonical = NULL;
146 int errsv = 0;
147 int pipes[2];
148 ssize_t len;
149 pid_t pid;
150
151 if (!path || !*path)
152 return NULL;
153
154 if (pipe(pipes) != 0)
155 return NULL;
156
157 /*
158 * To accurately assume identity of getuid() we must use setuid()
159 * but if we do that, we lose ability to reassume euid of 0, so
160 * we fork to do the check to keep euid intact.
161 */
162 pid = fork();
163 switch (pid) {
164 case -1:
165 close(pipes[0]);
166 close(pipes[1]);
167 return NULL; /* fork error */
168 case 0:
169 close(pipes[0]); /* close unused end */
170 pipes[0] = -1;
171 errno = 0;
172
173 if (drop_permissions() != 0)
174 canonical = NULL; /* failed */
175 else {
176 char *dmname = NULL;
177
178 canonical = realpath(path, NULL);
179 if (canonical && is_dm_devname(canonical, &dmname)) {
180 char *dm = canonicalize_dm_name(dmname);
181 if (dm) {
182 free(canonical);
183 canonical = dm;
184 }
185 }
186 }
187
188 len = canonical ? (ssize_t) strlen(canonical) :
189 errno ? -errno : -EINVAL;
190
191 /* send length or errno */
192 write_all(pipes[1], (char *) &len, sizeof(len));
193 if (canonical)
194 write_all(pipes[1], canonical, len);
195 exit(0);
196 default:
197 break;
198 }
199
200 close(pipes[1]); /* close unused end */
201 pipes[1] = -1;
202
203 /* read size or -errno */
204 if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
205 goto done;
206 if (len < 0) {
207 errsv = -len;
208 goto done;
209 }
210
211 canonical = malloc(len + 1);
212 if (!canonical) {
213 errsv = ENOMEM;
214 goto done;
215 }
216 /* read path */
217 if (read_all(pipes[0], canonical, len) != len) {
218 errsv = errno;
219 goto done;
220 }
221 canonical[len] = '\0';
222 done:
223 if (errsv) {
224 free(canonical);
225 canonical = NULL;
226 }
227 close(pipes[0]);
228
229 /* We make a best effort to reap child */
230 ignore_result( waitpid(pid, NULL, 0) );
231
232 errno = errsv;
233 return canonical;
234 }
235
236
237 #ifdef TEST_PROGRAM_CANONICALIZE
238 int main(int argc, char **argv)
239 {
240 if (argc < 2) {
241 fprintf(stderr, "usage: %s <device>\n", argv[0]);
242 exit(EXIT_FAILURE);
243 }
244
245 fprintf(stdout, "orig: %s\n", argv[1]);
246 fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
247 exit(EXIT_SUCCESS);
248 }
249 #endif