1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "apr_private.h"
19 #include "apr_arch_file_io.h"
20 #include "apr_file_io.h"
21 #include "apr_strings.h"
22 #define APR_WANT_STRFUNC
28 /* Win32 malpropism that can go away once everyone believes this
29 * code is golden, and I'm not testing it anymore :-)
35 /* Any OS that requires/refuses trailing slashes should be dealt with here.
37 APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
40 char path[APR_PATH_MAX];
42 if (!getcwd(path, sizeof(path))) {
44 return APR_ENAMETOOLONG;
48 *defpath = apr_pstrdup(p, path);
54 /* Any OS that requires/refuses trailing slashes should be dealt with here
56 APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
64 APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
69 if (**inpath == '/') {
70 *rootpath = apr_pstrdup(p, "/");
73 } while (**inpath == '/');
81 APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
88 apr_size_t rootlen; /* is the length of the src rootpath */
89 apr_size_t maxlen; /* maximum total path length */
90 apr_size_t keptlen; /* is the length of the retained rootpath */
91 apr_size_t pathlen; /* is the length of the result path */
92 apr_size_t seglen; /* is the end of the current segment */
95 /* Treat null as an empty path.
100 if (addpath[0] == '/') {
101 /* If addpath is rooted, then rootpath is unused.
102 * Ths violates any APR_FILEPATH_SECUREROOTTEST and
103 * APR_FILEPATH_NOTABSOLUTE flags specified.
105 if (flags & APR_FILEPATH_SECUREROOTTEST)
106 return APR_EABOVEROOT;
107 if (flags & APR_FILEPATH_NOTABSOLUTE)
108 return APR_EABSOLUTE;
110 /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
111 * we won't test the root again, it's ignored.
112 * Waste no CPU retrieving the working path.
114 if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
118 /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
119 * requires a relative result. If the rootpath is
120 * ommitted, we do not retrieve the working path,
121 * if rootpath was supplied as absolute then fail.
123 if (flags & APR_FILEPATH_NOTABSOLUTE) {
126 else if (rootpath[0] == '/')
127 return APR_EABSOLUTE;
132 /* Start with the current working path. This is bass akwards,
133 * but required since the compiler (at least vc) doesn't like
134 * passing the address of a char const* for a char** arg.
137 rv = apr_filepath_get(&getpath, flags, p);
139 if (rv != APR_SUCCESS)
142 /* XXX: Any kernel subject to goofy, uncanonical results
143 * must run the rootpath against the user's given flags.
144 * Simplest would be a recursive call to apr_filepath_merge
145 * with an empty (not null) rootpath and addpath of the cwd.
149 rootlen = strlen(rootpath);
150 maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
151 * root, and at end, plus trailing
153 if (maxlen > APR_PATH_MAX) {
154 return APR_ENAMETOOLONG;
156 path = (char *)apr_palloc(p, maxlen);
158 if (addpath[0] == '/') {
159 /* Ignore the given root path, strip off leading
160 * '/'s to a single leading '/' from the addpath,
161 * and leave addpath at the first non-'/' character.
164 while (addpath[0] == '/')
170 /* If both paths are relative, fail early
172 if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
173 return APR_ERELATIVE;
175 /* Base the result path on the rootpath
178 memcpy(path, rootpath, rootlen);
180 /* Always '/' terminate the given root path
182 if (keptlen && path[keptlen - 1] != '/') {
183 path[keptlen++] = '/';
189 /* Parse each segment, find the closing '/'
191 const char *next = addpath;
192 while (*next && (*next != '/')) {
195 seglen = next - addpath;
197 if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
198 /* noop segment (/ or ./) so skip it
201 else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
203 if (pathlen == 1 && path[0] == '/') {
204 /* Attempt to move above root. Always die if the
205 * APR_FILEPATH_SECUREROOTTEST flag is specified.
207 if (flags & APR_FILEPATH_SECUREROOTTEST) {
208 return APR_EABOVEROOT;
211 /* Otherwise this is simply a noop, above root is root.
212 * Flag that rootpath was entirely replaced.
216 else if (pathlen == 0
218 && !memcmp(path + pathlen - 3, "../", 3))
220 && !memcmp(path + pathlen - 4, "/../", 4))) {
221 /* Path is already backpathed or empty, if the
222 * APR_FILEPATH_SECUREROOTTEST.was given die now.
224 if (flags & APR_FILEPATH_SECUREROOTTEST) {
225 return APR_EABOVEROOT;
228 /* Otherwise append another backpath, including
229 * trailing slash if present.
231 memcpy(path + pathlen, "../", *next ? 3 : 2);
232 pathlen += *next ? 3 : 2;
235 /* otherwise crop the prior segment
239 } while (pathlen && path[pathlen - 1] != '/');
242 /* Now test if we are above where we started and back up
243 * the keptlen offset to reflect the added/altered path.
245 if (pathlen < keptlen) {
246 if (flags & APR_FILEPATH_SECUREROOTTEST) {
247 return APR_EABOVEROOT;
253 /* An actual segment, append it to the destination path
258 memcpy(path + pathlen, addpath, seglen);
262 /* Skip over trailing slash to the next segment
270 path[pathlen] = '\0';
272 /* keptlen will be the rootlen unless the addpath contained
273 * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
274 * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
275 * compare the original root to assure the result path is
276 * still within given root path.
278 if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
279 if (strncmp(rootpath, path, rootlen)) {
280 return APR_EABOVEROOT;
282 if (rootpath[rootlen - 1] != '/'
283 && path[rootlen] && path[rootlen] != '/') {
284 return APR_EABOVEROOT;
292 APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
296 return apr_filepath_list_split_impl(pathelts, liststr, ':', p);
299 APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
300 apr_array_header_t *pathelts,
303 return apr_filepath_list_merge_impl(liststr, pathelts, ':', p);
306 APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
309 *style = APR_FILEPATH_ENCODING_UTF8;
311 *style = APR_FILEPATH_ENCODING_LOCALE;