]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/canonicalize.c
Merge branch 'login' of https://github.com/stoeckmann/util-linux
[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"
cba33452 22#include "strutils.h"
6dbe3af9 23
a992137b
KZ
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 */
5a89aa99 30char *__canonicalize_dm_name(const char *prefix, const char *ptname)
a992137b
KZ
31{
32 FILE *f;
33 size_t sz;
bf99940f 34 char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
a992137b 35
7ba207e7
KZ
36 if (!ptname || !*ptname)
37 return NULL;
38
5a89aa99
KZ
39 if (!prefix)
40 prefix = "";
41
42 snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
b1fa3e22 43 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
a992137b
KZ
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';
bf99940f 49 snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
4e5262e4 50
0ed52c66 51 if ((prefix && *prefix) || access(path, F_OK) == 0)
4e5262e4 52 res = strdup(path);
a992137b
KZ
53 }
54 fclose(f);
55 return res;
56}
57
5a89aa99
KZ
58char *canonicalize_dm_name(const char *ptname)
59{
60 return __canonicalize_dm_name(NULL, ptname);
61}
62
1dbbd85b
KZ
63static 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
2238214d 81/*
9e930041 82 * This function does not canonicalize the path! It just prepends CWD before a
2238214d
KZ
83 * relative path. If the path is no relative than returns NULL. The path does
84 * not have to exist.
85 */
86char *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
cba33452 114 p = mempcpy(p, cwd, csz);
2238214d
KZ
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
17fc8693 173 if (drop_permissions() != 0)
e101a9eb
RH
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 }
1dbbd85b 186 }
e101a9eb 187
348f57d6
KZ
188 len = canonical ? (ssize_t) strlen(canonical) :
189 errno ? -errno : -EINVAL;
e101a9eb 190
e11d0b8c 191 /* send length or errno */
e101a9eb
RH
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;
1dbbd85b 198 }
33c5fd0c 199
e101a9eb
RH
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';
222done:
223 if (errsv) {
8a52ef65 224 free(canonical);
e101a9eb 225 canonical = NULL;
33c5fd0c 226 }
e101a9eb
RH
227 close(pipes[0]);
228
229 /* We make a best effort to reap child */
88e5bad6 230 ignore_result( waitpid(pid, NULL, 0) );
33c5fd0c
KZ
231
232 errno = errsv;
8a52ef65 233 return canonical;
33c5fd0c
KZ
234}
235
74a9c6f7 236
10ee5932 237#ifdef TEST_PROGRAM_CANONICALIZE
6cfa971e
KZ
238int 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]));
6cfa971e
KZ
247 exit(EXIT_SUCCESS);
248}
249#endif