]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/canonicalize.c
setterm: disallow "default" for --ulcolor/--hbcolor
[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
23 /*
24 * Converts private "dm-N" names to "/dev/mapper/<name>"
25 *
26 * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
27 * provides the real DM device names in /sys/block/<ptname>/dm/name
28 */
29 char *__canonicalize_dm_name(const char *prefix, const char *ptname)
30 {
31 FILE *f;
32 size_t sz;
33 char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
34
35 if (!ptname || !*ptname)
36 return NULL;
37
38 if (!prefix)
39 prefix = "";
40
41 snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
42 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
43 return NULL;
44
45 /* read "<name>\n" from sysfs */
46 if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
47 name[sz - 1] = '\0';
48 snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
49
50 if (prefix || access(path, F_OK) == 0)
51 res = strdup(path);
52 }
53 fclose(f);
54 return res;
55 }
56
57 char *canonicalize_dm_name(const char *ptname)
58 {
59 return __canonicalize_dm_name(NULL, ptname);
60 }
61
62 static int is_dm_devname(char *canonical, char **name)
63 {
64 struct stat sb;
65 char *p = strrchr(canonical, '/');
66
67 *name = NULL;
68
69 if (!p
70 || strncmp(p, "/dm-", 4) != 0
71 || !isdigit(*(p + 4))
72 || stat(canonical, &sb) != 0
73 || !S_ISBLK(sb.st_mode))
74 return 0;
75
76 *name = p + 1;
77 return 1;
78 }
79
80 /*
81 * This function does not canonicalize the path! It just prepends CWD before a
82 * relative path. If the path is no relative than returns NULL. The path does
83 * not have to exist.
84 */
85 char *absolute_path(const char *path)
86 {
87 char cwd[PATH_MAX], *res, *p;
88 size_t psz, csz;
89
90 if (!is_relative_path(path)) {
91 errno = EINVAL;
92 return NULL;
93 }
94 if (!getcwd(cwd, sizeof(cwd)))
95 return NULL;
96
97 /* simple clean up */
98 if (startswith(path, "./"))
99 path += 2;
100 else if (strcmp(path, ".") == 0)
101 path = NULL;
102
103 if (!path || !*path)
104 return strdup(cwd);
105
106 csz = strlen(cwd);
107 psz = strlen(path);
108
109 p = res = malloc(csz + 1 + psz + 1);
110 if (!res)
111 return NULL;
112
113 memcpy(p, cwd, csz);
114 p += 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 /* drop permissions */
174 if (setgid(getgid()) < 0 || setuid(getuid()) < 0)
175 canonical = NULL; /* failed */
176 else {
177 char *dmname = NULL;
178
179 canonical = realpath(path, NULL);
180 if (canonical && is_dm_devname(canonical, &dmname)) {
181 char *dm = canonicalize_dm_name(dmname);
182 if (dm) {
183 free(canonical);
184 canonical = dm;
185 }
186 }
187 }
188
189 len = canonical ? strlen(canonical) : 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 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