]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/canonicalize.c
93782e894075375fbd8e2e111d4be929c6360fa7
[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
18 #include "canonicalize.h"
19
20 /*
21 * Converts private "dm-N" names to "/dev/mapper/<name>"
22 *
23 * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
24 * provides the real DM device names in /sys/block/<ptname>/dm/name
25 */
26 char *canonicalize_dm_name(const char *ptname)
27 {
28 FILE *f;
29 size_t sz;
30 char path[256], name[256], *res = NULL;
31
32 if (!ptname || !*ptname)
33 return NULL;
34
35 snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
36 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
37 return NULL;
38
39 /* read "<name>\n" from sysfs */
40 if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
41 name[sz - 1] = '\0';
42 snprintf(path, sizeof(path), "/dev/mapper/%s", name);
43
44 if (access(path, F_OK) == 0)
45 res = strdup(path);
46 }
47 fclose(f);
48 return res;
49 }
50
51 static int is_dm_devname(char *canonical, char **name)
52 {
53 struct stat sb;
54 char *p = strrchr(canonical, '/');
55
56 *name = NULL;
57
58 if (!p
59 || strncmp(p, "/dm-", 4) != 0
60 || !isdigit(*(p + 4))
61 || stat(canonical, &sb) != 0
62 || !S_ISBLK(sb.st_mode))
63 return 0;
64
65 *name = p + 1;
66 return 1;
67 }
68
69 /*
70 * This function does not cannonicalize the path! It just prepends CWD before a
71 * relative path. If the path is no relative than returns NULL. The path does
72 * not have to exist.
73 */
74 char *absolute_path(const char *path)
75 {
76 char cwd[PATH_MAX], *res, *p;
77 size_t psz, csz;
78
79 if (!is_relative_path(path)) {
80 errno = EINVAL;
81 return NULL;
82 }
83 if (!getcwd(cwd, sizeof(cwd)))
84 return NULL;
85
86 /* simple clean up */
87 if (startswith(path, "./"))
88 path += 2;
89 else if (strcmp(path, ".") == 0)
90 path = NULL;
91
92 if (!path || !*path)
93 return strdup(cwd);
94
95 csz = strlen(cwd);
96 psz = strlen(path);
97
98 p = res = malloc(csz + 1 + psz + 1);
99 if (!res)
100 return NULL;
101
102 memcpy(p, cwd, csz);
103 p += csz;
104 *p++ = '/';
105 memcpy(p, path, psz + 1);
106
107 return res;
108 }
109
110 char *canonicalize_path(const char *path)
111 {
112 char *canonical, *dmname;
113
114 if (!path || !*path)
115 return NULL;
116
117 canonical = realpath(path, NULL);
118 if (!canonical)
119 return strdup(path);
120
121 if (is_dm_devname(canonical, &dmname)) {
122 char *dm = canonicalize_dm_name(dmname);
123 if (dm) {
124 free(canonical);
125 return dm;
126 }
127 }
128
129 return canonical;
130 }
131
132 char *canonicalize_path_restricted(const char *path)
133 {
134 char *canonical, *dmname;
135 int errsv;
136 uid_t euid;
137 gid_t egid;
138
139 if (!path || !*path)
140 return NULL;
141
142 euid = geteuid();
143 egid = getegid();
144
145 /* drop permissions */
146 if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
147 return NULL;
148
149 errsv = errno = 0;
150
151 canonical = realpath(path, NULL);
152 if (!canonical)
153 errsv = errno;
154 else if (is_dm_devname(canonical, &dmname)) {
155 char *dm = canonicalize_dm_name(dmname);
156 if (dm) {
157 free(canonical);
158 canonical = dm;
159 }
160 }
161
162 /* restore */
163 if (setegid(egid) < 0 || seteuid(euid) < 0) {
164 free(canonical);
165 return NULL;
166 }
167
168 errno = errsv;
169 return canonical;
170 }
171
172
173 #ifdef TEST_PROGRAM_CANONICALIZE
174 int main(int argc, char **argv)
175 {
176 if (argc < 2) {
177 fprintf(stderr, "usage: %s <device>\n", argv[0]);
178 exit(EXIT_FAILURE);
179 }
180
181 fprintf(stdout, "orig: %s\n", argv[1]);
182 fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
183 exit(EXIT_SUCCESS);
184 }
185 #endif