blob: 303703b7c823ce3e68738826248844b8ccda0fda [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001/*
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 */
26char *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")))
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
51static 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
69char *canonicalize_path(const char *path)
70{
71 char *canonical, *dmname;
72
73 if (!path || !*path)
74 return NULL;
75
76 canonical = realpath(path, NULL);
77 if (!canonical)
78 return strdup(path);
79
80 if (is_dm_devname(canonical, &dmname)) {
81 char *dm = canonicalize_dm_name(dmname);
82 if (dm) {
83 free(canonical);
84 return dm;
85 }
86 }
87
88 return canonical;
89}
90
91char *canonicalize_path_restricted(const char *path)
92{
93 char *canonical, *dmname;
94 int errsv;
95 uid_t euid;
96 gid_t egid;
97
98 if (!path || !*path)
99 return NULL;
100
101 euid = geteuid();
102 egid = getegid();
103
104 /* drop permissions */
105 if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
106 return NULL;
107
108 errsv = errno = 0;
109
110 canonical = realpath(path, NULL);
111 if (!canonical)
112 errsv = errno;
113 else if (is_dm_devname(canonical, &dmname)) {
114 char *dm = canonicalize_dm_name(dmname);
115 if (dm) {
116 free(canonical);
117 canonical = dm;
118 }
119 }
120
121 /* restore */
122 if (setegid(egid) < 0 || seteuid(euid) < 0) {
123 free(canonical);
124 return NULL;
125 }
126
127 errno = errsv;
128 return canonical;
129}
130
131
132#ifdef TEST_PROGRAM_CANONICALIZE
133int main(int argc, char **argv)
134{
135 if (argc < 2) {
136 fprintf(stderr, "usage: %s <device>\n", argv[0]);
137 exit(EXIT_FAILURE);
138 }
139
140 fprintf(stdout, "orig: %s\n", argv[1]);
141 fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
142
143 exit(EXIT_SUCCESS);
144}
145#endif