]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/canonicalize.c
rev: be careful with close()
[thirdparty/util-linux.git] / lib / canonicalize.c
CommitLineData
6dbe3af9 1/*
74a9c6f7 2 * canonicalize.c -- canonicalize pathname by removing symlinks
6dbe3af9 3 *
8a52ef65
KZ
4 * This file may be distributed under the terms of the
5 * GNU Lesser General Public License.
74a9c6f7 6 *
8a52ef65 7 * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
6dbe3af9 8 */
a992137b
KZ
9#include <stdio.h>
10#include <string.h>
11#include <ctype.h>
6dbe3af9 12#include <unistd.h>
6dbe3af9 13#include <errno.h>
74a9c6f7 14#include <stdlib.h>
8a52ef65
KZ
15#include <sys/types.h>
16#include <sys/stat.h>
e101a9eb 17#include <sys/wait.h>
74a9c6f7
KZ
18
19#include "canonicalize.h"
bf99940f 20#include "pathnames.h"
e101a9eb 21#include "all-io.h"
6dbe3af9 22
a992137b
KZ
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 */
5a89aa99 29char *__canonicalize_dm_name(const char *prefix, const char *ptname)
a992137b
KZ
30{
31 FILE *f;
32 size_t sz;
bf99940f 33 char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
a992137b 34
7ba207e7
KZ
35 if (!ptname || !*ptname)
36 return NULL;
37
5a89aa99
KZ
38 if (!prefix)
39 prefix = "";
40
41 snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
b1fa3e22 42 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
a992137b
KZ
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';
bf99940f 48 snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
4e5262e4 49
0ed52c66 50 if ((prefix && *prefix) || access(path, F_OK) == 0)
4e5262e4 51 res = strdup(path);
a992137b
KZ
52 }
53 fclose(f);
54 return res;
55}
56
5a89aa99
KZ
57char *canonicalize_dm_name(const char *ptname)
58{
59 return __canonicalize_dm_name(NULL, ptname);
60}
61
1dbbd85b
KZ
62static 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
2238214d 80/*
9e930041 81 * This function does not canonicalize the path! It just prepends CWD before a
2238214d
KZ
82 * relative path. If the path is no relative than returns NULL. The path does
83 * not have to exist.
84 */
85char *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
8a52ef65 121char *canonicalize_path(const char *path)
a992137b 122{
1dbbd85b 123 char *canonical, *dmname;
74a9c6f7 124
7ba207e7 125 if (!path || !*path)
74a9c6f7
KZ
126 return NULL;
127
8a52ef65
KZ
128 canonical = realpath(path, NULL);
129 if (!canonical)
225b804b
KZ
130 return strdup(path);
131
1dbbd85b
KZ
132 if (is_dm_devname(canonical, &dmname)) {
133 char *dm = canonicalize_dm_name(dmname);
8a52ef65
KZ
134 if (dm) {
135 free(canonical);
136 return dm;
137 }
a992137b 138 }
74a9c6f7 139
8a52ef65 140 return canonical;
74a9c6f7
KZ
141}
142
8a52ef65 143char *canonicalize_path_restricted(const char *path)
33c5fd0c 144{
e101a9eb
RH
145 char *canonical = NULL;
146 int errsv = 0;
147 int pipes[2];
148 ssize_t len;
149 pid_t pid;
33c5fd0c 150
7ba207e7 151 if (!path || !*path)
33c5fd0c
KZ
152 return NULL;
153
e101a9eb 154 if (pipe(pipes) != 0)
33c5fd0c
KZ
155 return NULL;
156
e101a9eb
RH
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 }
1dbbd85b 187 }
e101a9eb 188
348f57d6
KZ
189 len = canonical ? (ssize_t) strlen(canonical) :
190 errno ? -errno : -EINVAL;
e101a9eb 191
e11d0b8c 192 /* send length or errno */
e101a9eb
RH
193 write_all(pipes[1], (char *) &len, sizeof(len));
194 if (canonical)
195 write_all(pipes[1], canonical, len);
196 exit(0);
197 default:
198 break;
1dbbd85b 199 }
33c5fd0c 200
e101a9eb
RH
201 close(pipes[1]); /* close unused end */
202 pipes[1] = -1;
203
204 /* read size or -errno */
205 if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
206 goto done;
207 if (len < 0) {
208 errsv = -len;
209 goto done;
210 }
211
212 canonical = malloc(len + 1);
213 if (!canonical) {
214 errsv = ENOMEM;
215 goto done;
216 }
217 /* read path */
218 if (read_all(pipes[0], canonical, len) != len) {
219 errsv = errno;
220 goto done;
221 }
222 canonical[len] = '\0';
223done:
224 if (errsv) {
8a52ef65 225 free(canonical);
e101a9eb 226 canonical = NULL;
33c5fd0c 227 }
e101a9eb
RH
228 close(pipes[0]);
229
230 /* We make a best effort to reap child */
231 waitpid(pid, NULL, 0);
33c5fd0c
KZ
232
233 errno = errsv;
8a52ef65 234 return canonical;
33c5fd0c
KZ
235}
236
74a9c6f7 237
10ee5932 238#ifdef TEST_PROGRAM_CANONICALIZE
6cfa971e
KZ
239int main(int argc, char **argv)
240{
241 if (argc < 2) {
242 fprintf(stderr, "usage: %s <device>\n", argv[0]);
243 exit(EXIT_FAILURE);
244 }
245
246 fprintf(stdout, "orig: %s\n", argv[1]);
247 fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
6cfa971e
KZ
248 exit(EXIT_SUCCESS);
249}
250#endif