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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17#include "apr.h" 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 23#include "apr_want.h" 24#if APR_HAVE_UNISTD_H 25#include <unistd.h> 26#endif 27 28/* Win32 malpropism that can go away once everyone believes this 29 * code is golden, and I'm not testing it anymore :-) 30 */ 31#if APR_HAVE_DIRENT_H 32#include <dirent.h> 33#endif 34 35/* Any OS that requires/refuses trailing slashes should be dealt with here. 36 */ 37APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags, 38 apr_pool_t *p) 39{ 40 char path[APR_PATH_MAX]; 41 42 if (!getcwd(path, sizeof(path))) { 43 if (errno == ERANGE) 44 return APR_ENAMETOOLONG; 45 else 46 return errno; 47 } 48 *defpath = apr_pstrdup(p, path); 49 50 return APR_SUCCESS; 51} 52 53 54/* Any OS that requires/refuses trailing slashes should be dealt with here 55 */ 56APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p) 57{ 58 if (chdir(path) != 0) 59 return errno; 60 61 return APR_SUCCESS; 62} 63 64APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath, 65 const char **inpath, 66 apr_int32_t flags, 67 apr_pool_t *p) 68{ 69 if (**inpath == '/') { 70 *rootpath = apr_pstrdup(p, "/"); 71 do { 72 ++(*inpath); 73 } while (**inpath == '/'); 74 75 return APR_SUCCESS; 76 } 77 78 return APR_ERELATIVE; 79} 80 81APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath, 82 const char *rootpath, 83 const char *addpath, 84 apr_int32_t flags, 85 apr_pool_t *p) 86{ 87 char *path; 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 */ 93 apr_status_t rv; 94 95 /* Treat null as an empty path. 96 */ 97 if (!addpath) 98 addpath = ""; 99 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. 104 */ 105 if (flags & APR_FILEPATH_SECUREROOTTEST) 106 return APR_EABOVEROOT; 107 if (flags & APR_FILEPATH_NOTABSOLUTE) 108 return APR_EABSOLUTE; 109 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. 113 */ 114 if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT)) 115 rootpath = ""; 116 } 117 else { 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. 122 */ 123 if (flags & APR_FILEPATH_NOTABSOLUTE) { 124 if (!rootpath) 125 rootpath = ""; 126 else if (rootpath[0] == '/') 127 return APR_EABSOLUTE; 128 } 129 } 130 131 if (!rootpath) { 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. 135 */ 136 char *getpath; 137 rv = apr_filepath_get(&getpath, flags, p); 138 rootpath = getpath; 139 if (rv != APR_SUCCESS) 140 return errno; 141 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. 146 */ 147 } 148 149 rootlen = strlen(rootpath); 150 maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after 151 * root, and at end, plus trailing 152 * null */ 153 if (maxlen > APR_PATH_MAX) { 154 return APR_ENAMETOOLONG; 155 } 156 path = (char *)apr_palloc(p, maxlen); 157 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. 162 */ 163 keptlen = 0; 164 while (addpath[0] == '/') 165 ++addpath; 166 path[0] = '/'; 167 pathlen = 1; 168 } 169 else { 170 /* If both paths are relative, fail early 171 */ 172 if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE)) 173 return APR_ERELATIVE; 174 175 /* Base the result path on the rootpath 176 */ 177 keptlen = rootlen; 178 memcpy(path, rootpath, rootlen); 179 180 /* Always '/' terminate the given root path 181 */ 182 if (keptlen && path[keptlen - 1] != '/') { 183 path[keptlen++] = '/'; 184 } 185 pathlen = keptlen; 186 } 187 188 while (*addpath) { 189 /* Parse each segment, find the closing '/' 190 */ 191 const char *next = addpath; 192 while (*next && (*next != '/')) { 193 ++next; 194 } 195 seglen = next - addpath; 196 197 if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) { 198 /* noop segment (/ or ./) so skip it 199 */ 200 } 201 else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') { 202 /* backpath (../) */ 203 if (pathlen == 1 && path[0] == '/') { 204 /* Attempt to move above root. Always die if the 205 * APR_FILEPATH_SECUREROOTTEST flag is specified. 206 */ 207 if (flags & APR_FILEPATH_SECUREROOTTEST) { 208 return APR_EABOVEROOT; 209 } 210 211 /* Otherwise this is simply a noop, above root is root. 212 * Flag that rootpath was entirely replaced. 213 */ 214 keptlen = 0; 215 } 216 else if (pathlen == 0 217 || (pathlen == 3 218 && !memcmp(path + pathlen - 3, "../", 3)) 219 || (pathlen > 3 220 && !memcmp(path + pathlen - 4, "/../", 4))) { 221 /* Path is already backpathed or empty, if the 222 * APR_FILEPATH_SECUREROOTTEST.was given die now. 223 */ 224 if (flags & APR_FILEPATH_SECUREROOTTEST) { 225 return APR_EABOVEROOT; 226 } 227 228 /* Otherwise append another backpath, including 229 * trailing slash if present. 230 */ 231 memcpy(path + pathlen, "../", *next ? 3 : 2); 232 pathlen += *next ? 3 : 2; 233 } 234 else { 235 /* otherwise crop the prior segment 236 */ 237 do { 238 --pathlen; 239 } while (pathlen && path[pathlen - 1] != '/'); 240 } 241 242 /* Now test if we are above where we started and back up 243 * the keptlen offset to reflect the added/altered path. 244 */ 245 if (pathlen < keptlen) { 246 if (flags & APR_FILEPATH_SECUREROOTTEST) { 247 return APR_EABOVEROOT; 248 } 249 keptlen = pathlen; 250 } 251 } 252 else { 253 /* An actual segment, append it to the destination path 254 */ 255 if (*next) { 256 seglen++; 257 } 258 memcpy(path + pathlen, addpath, seglen); 259 pathlen += seglen; 260 } 261 262 /* Skip over trailing slash to the next segment 263 */ 264 if (*next) { 265 ++next; 266 } 267 268 addpath = next; 269 } 270 path[pathlen] = '\0'; 271 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. 277 */ 278 if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) { 279 if (strncmp(rootpath, path, rootlen)) { 280 return APR_EABOVEROOT; 281 } 282 if (rootpath[rootlen - 1] != '/' 283 && path[rootlen] && path[rootlen] != '/') { 284 return APR_EABOVEROOT; 285 } 286 } 287 288 *newpath = path; 289 return APR_SUCCESS; 290} 291 292APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts, 293 const char *liststr, 294 apr_pool_t *p) 295{ 296 return apr_filepath_list_split_impl(pathelts, liststr, ':', p); 297} 298 299APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr, 300 apr_array_header_t *pathelts, 301 apr_pool_t *p) 302{ 303 return apr_filepath_list_merge_impl(liststr, pathelts, ':', p); 304} 305 306APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p) 307{ 308#if defined(DARWIN) 309 *style = APR_FILEPATH_ENCODING_UTF8; 310#else 311 *style = APR_FILEPATH_ENCODING_LOCALE; 312#endif 313 return APR_SUCCESS; 314} 315