Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #ifndef _GNU_SOURCE
36 : : #define _GNU_SOURCE
37 : : #endif
38 : :
39 : : #include <stdio.h>
40 : : #include <errno.h>
41 : : #include <stdlib.h>
42 : : #include <string.h>
43 : : #include <limits.h>
44 : : #include <unistd.h>
45 : :
46 : : #include "canonpath.h"
47 : : #include "util.h"
48 : :
49 : : char *
50 : 1 : normalize_path(const char *path, size_t path_len)
51 : : {
52 : 1 : size_t res_len = 0;
53 : :
54 : 1 : const char *ptr = path;
55 : 1 : const char *end = path + path_len;
56 : : const char *next;
57 : :
58 : : char *normalized_path;
59 [ + - ][ + - ]: 1 : if (!path_len || *path != '/') {
60 : : // Relative path.
61 : 1 : char *wd = get_current_dir_name();
62 [ + - ]: 1 : if (!wd)
63 : : return NULL;
64 : :
65 : 1 : res_len = strlen(wd);
66 : : /* Add 2 to accomodate the / and the \0 */
67 [ - + ]: 1 : if (!(normalized_path = realloc(wd, res_len + path_len + 2))) {
68 : 0 : free(wd);
69 : 0 : return NULL;
70 : : }
71 [ # # ]: 0 : } else if (!(normalized_path = malloc(path_len + 1)))
72 : : return NULL;
73 : :
74 [ + + ]: 2 : for (ptr = path; ptr < end; ptr = next + 1) {
75 : : size_t len;
76 [ + - ]: 1 : if (!(next = memchr(ptr, '/', end - ptr)))
77 : 1 : next = end;
78 : 1 : len = next - ptr;
79 [ - - - + ]: 1 : switch (len) {
80 : : case 2:
81 [ # # ][ # # ]: 0 : if (ptr[0] == '.' && ptr[1] == '.') {
82 : 0 : const char *slash = memrchr(normalized_path, '/', res_len);
83 [ # # ]: 0 : if (slash)
84 : 0 : res_len = slash - normalized_path;
85 : 0 : continue;
86 : : }
87 : : break;
88 : : case 1:
89 [ # # ]: 0 : if (ptr[0] == '.')
90 : 0 : continue;
91 : : break;
92 : : case 0:
93 : 0 : continue;
94 : : }
95 : 1 : normalized_path[res_len++] = '/';
96 : 1 : memcpy(normalized_path + res_len, ptr, len);
97 : 1 : res_len += len;
98 : : }
99 : :
100 [ - + ]: 1 : if (res_len == 0)
101 : 0 : normalized_path[res_len++] = '/';
102 : 1 : normalized_path[res_len] = '\0';
103 : 1 : return normalized_path;
104 : : }
105 : :
106 : : /*
107 : : * Return a path name which should be used resolving parent.
108 : : * This could be different from realpath as realpath follows all symlinks.
109 : : * Function try to get a file name (even if symlink) contained in /dev/mapper.
110 : : * Symlinks are also kept if a /dev/drbd/by-res/<UUID>/<VOLUME_ID> path is used.
111 : : */
112 : : char *
113 : 1 : canonpath(const char *path, char *resolved_path, size_t dest_size)
114 : : {
115 : : static const char dev_path[] = "/dev/";
116 : : static const size_t dev_len = sizeof(dev_path) - 1;
117 : :
118 : : static const char dev_mapper_path[] = "/dev/mapper/";
119 : : static const size_t dev_mapper_len = sizeof(dev_mapper_path) - 1;
120 : :
121 : : static const char dev_drbd_res_path[] = "/dev/drbd/by-res/";
122 : : static const size_t dev_drbd_res_len = sizeof(dev_drbd_res_path) - 1;
123 : :
124 : : static const char dev_drbd_prefix[] = "/dev/drbd";
125 : : static const size_t dev_drbd_prefix_len = sizeof(dev_drbd_prefix) - 1;
126 : :
127 : : /* make some cleanup */
128 : 1 : char *canon = NULL, *p, *dst;
129 : 1 : size_t len = strlen(path);
130 : :
131 [ + - ]: 1 : if (len >= PATH_MAX)
132 : : goto fallback;
133 : :
134 [ + - ]: 1 : if (!(canon = normalize_path(path, len)))
135 : 1 : return NULL;
136 : :
137 : : /*
138 : : * If path points to a file in /dev/mapper (with no subdirectories)
139 : : * return it without following symlinks.
140 : : */
141 [ - + ][ # # ]: 1 : if (strncmp(canon, dev_mapper_path, dev_mapper_len) == 0 &&
142 [ # # ]: 0 : strchr(canon + dev_mapper_len, '/') == NULL &&
143 : 0 : access(canon, F_OK) == 0) {
144 : 0 : safe_strncpy(resolved_path, canon, dest_size);
145 : : goto end;
146 : : }
147 : :
148 : : /*
149 : : * If path is in a subdirectory of dev (possibly a logical volume)
150 : : * try to find a corresponding file in /dev/mapper and return
151 : : * if found. The path can also be a DRBD volume, try to find it in
152 : : * /dev/drbd/by-res/.
153 : : */
154 [ - + ][ # # ]: 1 : if (strncmp(canon, dev_path, dev_len) == 0 && (p = strchr(canon + dev_len, '/')) != NULL) {
155 [ # # ]: 0 : if (strchr(p+1, '/') == NULL) {
156 : 0 : safe_strncpy(resolved_path, dev_mapper_path, dest_size);
157 : 0 : dst = strchr(resolved_path, 0);
158 [ # # ]: 0 : for (p = canon + dev_len; *p; ++p) {
159 [ # # ]: 0 : if (dst - resolved_path >= PATH_MAX - 2)
160 : : goto fallback;
161 [ # # # ]: 0 : switch (*p) {
162 : : case '/':
163 : 0 : *dst = '-';
164 : 0 : break;
165 : : case '-':
166 : 0 : *dst++ = '-';
167 : : /* fall through */
168 : : default:
169 : 0 : *dst = *p;
170 : : }
171 : 0 : ++dst;
172 : : }
173 : 0 : *dst = 0;
174 [ # # ]: 0 : } else if (strncmp(canon + dev_len, dev_drbd_res_path + dev_len, dev_drbd_res_len - dev_len) == 0) {
175 : : /* If the path is a real DRBD path, it's a symlink that points to /dev/drbdXXXX,
176 : : * where XXXX are digits. */
177 [ # # ]: 0 : if (!realpath(canon, resolved_path)) {
178 : 0 : free(canon);
179 : 0 : return NULL;
180 : : }
181 : :
182 : : /* Try to match /dev/drbd. */
183 [ # # ]: 0 : if (strncmp(resolved_path, dev_drbd_prefix, dev_drbd_prefix_len) != 0)
184 : : goto end;
185 : :
186 : : /* Check the digits. */
187 : 0 : errno = 0;
188 : 0 : strtol(resolved_path + dev_drbd_prefix_len, &p, 10);
189 [ # # ][ # # ]: 0 : if (p == resolved_path + dev_drbd_prefix_len || errno == ERANGE || *p != '\0')
[ # # ]
190 : : goto end; /* Cannot parse correctly pattern. */
191 : :
192 : 0 : safe_strncpy(resolved_path, canon, dest_size);
193 : : } else
194 : : goto fallback;
195 [ # # ]: 0 : if (access(resolved_path, F_OK) == 0)
196 : : goto end;
197 : : }
198 : :
199 : : fallback:
200 : 1 : free(canon);
201 : 1 : return realpath(path, resolved_path);
202 : :
203 : : end:
204 : 0 : free(canon);
205 : 0 : return resolved_path;
206 : : }
207 : :
|