]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/canonicalize.c
libblkid: Fix probe_ioctl_tp assigning BLKGETDISKSEQ as physical sector size
[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>
74a9c6f7
KZ
17
18#include "canonicalize.h"
bf99940f 19#include "pathnames.h"
e101a9eb 20#include "all-io.h"
cba33452 21#include "strutils.h"
63d14a2e 22#include "fileutils.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
65858415
KZ
29 *
30 * The @prefix allows /sys to be mounted or stored outside the system root
31 * (/prefix/sys/...).
a992137b 32 */
65858415 33char *ul_canonicalize_dm_name_prefixed(const char *prefix, const char *ptname)
a992137b
KZ
34{
35 FILE *f;
36 size_t sz;
bf99940f 37 char path[256], name[sizeof(path) - sizeof(_PATH_DEV_MAPPER)], *res = NULL;
a992137b 38
7ba207e7
KZ
39 if (!ptname || !*ptname)
40 return NULL;
41
5a89aa99
KZ
42 if (!prefix)
43 prefix = "";
44
45 snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", prefix, ptname);
b1fa3e22 46 if (!(f = fopen(path, "r" UL_CLOEXECSTR)))
a992137b
KZ
47 return NULL;
48
49 /* read "<name>\n" from sysfs */
50 if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
51 name[sz - 1] = '\0';
bf99940f 52 snprintf(path, sizeof(path), _PATH_DEV_MAPPER "/%s", name);
4e5262e4 53
0ed52c66 54 if ((prefix && *prefix) || access(path, F_OK) == 0)
4e5262e4 55 res = strdup(path);
a992137b
KZ
56 }
57 fclose(f);
58 return res;
59}
60
65858415 61char *ul_canonicalize_dm_name(const char *ptname)
5a89aa99 62{
65858415 63 return ul_canonicalize_dm_name_prefixed(NULL, ptname);
5a89aa99
KZ
64}
65
1dbbd85b
KZ
66static int is_dm_devname(char *canonical, char **name)
67{
68 struct stat sb;
69 char *p = strrchr(canonical, '/');
70
71 *name = NULL;
72
73 if (!p
74 || strncmp(p, "/dm-", 4) != 0
75 || !isdigit(*(p + 4))
76 || stat(canonical, &sb) != 0
77 || !S_ISBLK(sb.st_mode))
78 return 0;
79
80 *name = p + 1;
81 return 1;
82}
83
2238214d 84/*
9e930041 85 * This function does not canonicalize the path! It just prepends CWD before a
2238214d
KZ
86 * relative path. If the path is no relative than returns NULL. The path does
87 * not have to exist.
88 */
b9f21dbd 89char *ul_absolute_path(const char *path)
2238214d
KZ
90{
91 char cwd[PATH_MAX], *res, *p;
92 size_t psz, csz;
93
65858415 94 if (!ul_is_relative_path(path)) {
2238214d
KZ
95 errno = EINVAL;
96 return NULL;
97 }
98 if (!getcwd(cwd, sizeof(cwd)))
99 return NULL;
100
101 /* simple clean up */
aa07db0a 102 if (ul_startswith(path, "./"))
2238214d
KZ
103 path += 2;
104 else if (strcmp(path, ".") == 0)
105 path = NULL;
106
107 if (!path || !*path)
108 return strdup(cwd);
109
110 csz = strlen(cwd);
111 psz = strlen(path);
112
113 p = res = malloc(csz + 1 + psz + 1);
114 if (!res)
115 return NULL;
116
cba33452 117 p = mempcpy(p, cwd, csz);
2238214d
KZ
118 *p++ = '/';
119 memcpy(p, path, psz + 1);
120
121 return res;
122}
123
ccae72b4
KZ
124/*
125 * Returns: <0 on error, 1 is cannot be canonicalized (errno is set); 0 on success
126 */
127static int __attribute__((nonnull(2)))
63d14a2e
KZ
128do_canonicalize(const char *path, char **result,
129 void *data __attribute__((__unused__)))
a992137b 130{
1dbbd85b 131 char *canonical, *dmname;
74a9c6f7 132
ccae72b4
KZ
133 *result = NULL;
134
135 if (!path || !*path) {
136 errno = EINVAL;
137 return -errno;
138 }
74a9c6f7 139
ccae72b4 140 errno = 0;
8a52ef65
KZ
141 canonical = realpath(path, NULL);
142 if (!canonical)
ccae72b4 143 return 1;
225b804b 144
1dbbd85b 145 if (is_dm_devname(canonical, &dmname)) {
65858415 146 char *dm = ul_canonicalize_dm_name(dmname);
8a52ef65
KZ
147 if (dm) {
148 free(canonical);
ccae72b4 149 canonical = dm;
8a52ef65 150 }
a992137b 151 }
74a9c6f7 152
ccae72b4
KZ
153 if (canonical)
154 *result = canonical;
155 return 0;
74a9c6f7
KZ
156}
157
ccae72b4
KZ
158/*
159 * Always returns a newly allocated string or NULL in case of an error. An
160 * unreachable path is not an error (!), and in this case, it just duplicates
161 * @path.
162 */
65858415 163char *ul_canonicalize_path(const char *path)
ccae72b4
KZ
164{
165 char *canonical = NULL;
166
63d14a2e 167 if (do_canonicalize(path, &canonical, NULL) == 1)
ccae72b4
KZ
168 return strdup(path);
169
170 return canonical;
171}
63d14a2e 172
ccae72b4 173/*
63d14a2e
KZ
174 * Drop permissions (e.g., suid) and canonicalize the path. If the path is
175 * unreadable (for example, due to missing permissions), it returns NULL.
ccae72b4 176 */
65858415 177char *ul_canonicalize_path_restricted(const char *path)
33c5fd0c 178{
63d14a2e 179 return ul_restricted_path_oper(path, do_canonicalize, NULL);
33c5fd0c
KZ
180}
181
10ee5932 182#ifdef TEST_PROGRAM_CANONICALIZE
6cfa971e
KZ
183int main(int argc, char **argv)
184{
d44a1ee0
KZ
185 char *p;
186
6cfa971e
KZ
187 if (argc < 2) {
188 fprintf(stderr, "usage: %s <device>\n", argv[0]);
189 exit(EXIT_FAILURE);
190 }
191
d44a1ee0
KZ
192 fprintf(stdout, "orig: %s\n", argv[1]);
193
65858415 194 p = ul_canonicalize_path(argv[1]);
d44a1ee0
KZ
195 fprintf(stdout, "real: %s\n", p);
196 free(p);
197
65858415 198 p = ul_canonicalize_path_restricted(argv[1]);
d44a1ee0
KZ
199 fprintf(stdout, "real-restricted: %s\n", p);
200 free(p);
201
6cfa971e
KZ
202 exit(EXIT_SUCCESS);
203}
204#endif