]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/canonicalize.c
libmount: add version and features to debug output
[thirdparty/util-linux.git] / lib / canonicalize.c
CommitLineData
6dbe3af9 1/*
74a9c6f7 2 * canonicalize.c -- canonicalize pathname by removing symlinks
6dbe3af9
KZ
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library Public License for more details.
74a9c6f7 14 *
6dbe3af9
KZ
15 */
16
17/*
fd6b7a7f
KZ
18 * This routine is part of libc. We include it nevertheless,
19 * since the libc version has some security flaws.
74a9c6f7
KZ
20 *
21 * TODO: use canonicalize_file_name() when exist in glibc
6dbe3af9 22 */
a992137b
KZ
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
6dbe3af9 26#include <unistd.h>
6dbe3af9 27#include <errno.h>
74a9c6f7
KZ
28#include <stdlib.h>
29
30#include "canonicalize.h"
6dbe3af9 31
4717ea4a
KZ
32#ifndef MAXSYMLINKS
33# define MAXSYMLINKS 256
34#endif
6dbe3af9 35
74a9c6f7 36static char *
eb63b9b8 37myrealpath(const char *path, char *resolved_path, int maxreslth) {
6dbe3af9 38 int readlinks = 0;
364cda48
KZ
39 char *npath;
40 char link_path[PATH_MAX+1];
41 int n;
d26aa358 42 char *buf = NULL;
6dbe3af9 43
eb63b9b8 44 npath = resolved_path;
fd6b7a7f 45
eb63b9b8 46 /* If it's a relative pathname use getcwd for starters. */
6dbe3af9 47 if (*path != '/') {
22853e4a
KZ
48 if (!getcwd(npath, maxreslth-2))
49 return NULL;
eb63b9b8
KZ
50 npath += strlen(npath);
51 if (npath[-1] != '/')
52 *npath++ = '/';
53 } else {
54 *npath++ = '/';
6dbe3af9
KZ
55 path++;
56 }
eb63b9b8 57
6dbe3af9
KZ
58 /* Expand each slash-separated pathname component. */
59 while (*path != '\0') {
eb63b9b8 60 /* Ignore stray "/" */
6dbe3af9
KZ
61 if (*path == '/') {
62 path++;
63 continue;
64 }
eb63b9b8
KZ
65 if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
66 /* Ignore "." */
67 path++;
68 continue;
69 }
70 if (*path == '.' && path[1] == '.' &&
71 (path[2] == '\0' || path[2] == '/')) {
72 /* Backup for ".." */
73 path += 2;
74 while (npath > resolved_path+1 &&
75 (--npath)[-1] != '/')
76 ;
77 continue;
6dbe3af9
KZ
78 }
79 /* Safely copy the next pathname component. */
80 while (*path != '\0' && *path != '/') {
eb63b9b8 81 if (npath-resolved_path > maxreslth-2) {
6dbe3af9 82 errno = ENAMETOOLONG;
d26aa358 83 goto err;
6dbe3af9 84 }
eb63b9b8 85 *npath++ = *path++;
6dbe3af9 86 }
eb63b9b8 87
6dbe3af9 88 /* Protect against infinite loops. */
4717ea4a 89 if (readlinks++ > MAXSYMLINKS) {
6dbe3af9 90 errno = ELOOP;
d26aa358 91 goto err;
6dbe3af9 92 }
eb63b9b8 93
364cda48 94 /* See if last pathname component is a symlink. */
eb63b9b8
KZ
95 *npath = '\0';
96 n = readlink(resolved_path, link_path, PATH_MAX);
6dbe3af9
KZ
97 if (n < 0) {
98 /* EINVAL means the file exists but isn't a symlink. */
99 if (errno != EINVAL)
d26aa358 100 goto err;
eb63b9b8 101 } else {
d26aa358 102 int m;
a9d6150d 103 char *newbuf;
d26aa358 104
6dbe3af9
KZ
105 /* Note: readlink doesn't add the null byte. */
106 link_path[n] = '\0';
107 if (*link_path == '/')
108 /* Start over for an absolute symlink. */
eb63b9b8 109 npath = resolved_path;
6dbe3af9
KZ
110 else
111 /* Otherwise back up over this component. */
eb63b9b8 112 while (*(--npath) != '/')
6dbe3af9 113 ;
22853e4a 114
6dbe3af9 115 /* Insert symlink contents into path. */
22853e4a 116 m = strlen(path);
74a9c6f7
KZ
117 newbuf = malloc(m + n + 1);
118 if (!newbuf)
119 goto err;
a9d6150d
NB
120 memcpy(newbuf, link_path, n);
121 memcpy(newbuf + n, path, m + 1);
71c445db 122 free(buf);
a9d6150d 123 path = buf = newbuf;
6dbe3af9 124 }
eb63b9b8 125 *npath++ = '/';
6dbe3af9
KZ
126 }
127 /* Delete trailing slash but don't whomp a lone slash. */
eb63b9b8
KZ
128 if (npath != resolved_path+1 && npath[-1] == '/')
129 npath--;
6dbe3af9 130 /* Make sure it's null terminated. */
eb63b9b8 131 *npath = '\0';
d26aa358 132
71c445db 133 free(buf);
6dbe3af9 134 return resolved_path;
d26aa358
KZ
135
136 err:
71c445db 137 free(buf);
d26aa358 138 return NULL;
6dbe3af9 139}
74a9c6f7 140
a992137b
KZ
141/*
142 * Converts private "dm-N" names to "/dev/mapper/<name>"
143 *
144 * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
145 * provides the real DM device names in /sys/block/<ptname>/dm/name
146 */
74a9c6f7 147char *
a992137b
KZ
148canonicalize_dm_name(const char *ptname)
149{
150 FILE *f;
151 size_t sz;
152 char path[256], name[256], *res = NULL;
153
154 snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
155 if (!(f = fopen(path, "r")))
156 return NULL;
157
158 /* read "<name>\n" from sysfs */
159 if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
160 name[sz - 1] = '\0';
161 snprintf(path, sizeof(path), "/dev/mapper/%s", name);
162 res = strdup(path);
163 }
164 fclose(f);
165 return res;
166}
167
168char *
169canonicalize_path(const char *path)
170{
74a9c6f7 171 char canonical[PATH_MAX+2];
a992137b 172 char *p;
74a9c6f7
KZ
173
174 if (path == NULL)
175 return NULL;
176
225b804b
KZ
177 if (!myrealpath(path, canonical, PATH_MAX+1))
178 return strdup(path);
179
a992137b
KZ
180
181 p = strrchr(canonical, '/');
182 if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) {
183 p = canonicalize_dm_name(p+1);
184 if (p)
185 return p;
186 }
74a9c6f7 187
a992137b 188 return strdup(canonical);
74a9c6f7
KZ
189}
190
33c5fd0c
KZ
191char *
192canonicalize_path_restricted(const char *path)
193{
194 char canonical[PATH_MAX+2];
195 char *p = NULL;
196 int errsv;
197 uid_t euid;
198 gid_t egid;
199
200 if (path == NULL)
201 return NULL;
202
203 euid = geteuid();
204 egid = getegid();
205
206 /* drop permissions */
207 if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
208 return NULL;
209
210 errsv = errno = 0;
211
212 if (myrealpath(path, canonical, PATH_MAX+1)) {
213 p = strrchr(canonical, '/');
214 if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4)))
215 p = canonicalize_dm_name(p+1);
216 else
217 p = NULL;
218 if (!p)
219 p = strdup(canonical);
220 } else
221 errsv = errno;
222
223 /* restore */
224 if (setegid(egid) < 0 || seteuid(euid) < 0) {
225 free(p);
226 return NULL;
227 }
228
229 errno = errsv;
230 return p;
231}
232
74a9c6f7 233
10ee5932 234#ifdef TEST_PROGRAM_CANONICALIZE
6cfa971e
KZ
235int main(int argc, char **argv)
236{
237 if (argc < 2) {
238 fprintf(stderr, "usage: %s <device>\n", argv[0]);
239 exit(EXIT_FAILURE);
240 }
241
242 fprintf(stdout, "orig: %s\n", argv[1]);
243 fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
244
245 exit(EXIT_SUCCESS);
246}
247#endif