filepath.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. #include "apr.h"
  17. #include "apr_private.h"
  18. #include "apr_arch_file_io.h"
  19. #include "apr_file_io.h"
  20. #include "apr_strings.h"
  21. #define APR_WANT_STRFUNC
  22. #include "apr_want.h"
  23. #if APR_HAVE_UNISTD_H
  24. #include <unistd.h>
  25. #endif
  26. /* Win32 malpropism that can go away once everyone believes this
  27. * code is golden, and I'm not testing it anymore :-)
  28. */
  29. #if APR_HAVE_DIRENT_H
  30. #include <dirent.h>
  31. #endif
  32. /* Any OS that requires/refuses trailing slashes should be dealt with here.
  33. */
  34. APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
  35. apr_pool_t *p)
  36. {
  37. char path[APR_PATH_MAX];
  38. if (!getcwd(path, sizeof(path))) {
  39. if (errno == ERANGE)
  40. return APR_ENAMETOOLONG;
  41. else
  42. return errno;
  43. }
  44. *defpath = apr_pstrdup(p, path);
  45. return APR_SUCCESS;
  46. }
  47. /* Any OS that requires/refuses trailing slashes should be dealt with here
  48. */
  49. APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
  50. {
  51. if (chdir(path) != 0)
  52. return errno;
  53. return APR_SUCCESS;
  54. }
  55. APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
  56. const char **inpath,
  57. apr_int32_t flags,
  58. apr_pool_t *p)
  59. {
  60. if (**inpath == '/') {
  61. *rootpath = apr_pstrdup(p, "/");
  62. do {
  63. ++(*inpath);
  64. } while (**inpath == '/');
  65. return APR_SUCCESS;
  66. }
  67. return APR_ERELATIVE;
  68. }
  69. APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
  70. const char *rootpath,
  71. const char *addpath,
  72. apr_int32_t flags,
  73. apr_pool_t *p)
  74. {
  75. char *path;
  76. apr_size_t rootlen; /* is the length of the src rootpath */
  77. apr_size_t maxlen; /* maximum total path length */
  78. apr_size_t keptlen; /* is the length of the retained rootpath */
  79. apr_size_t pathlen; /* is the length of the result path */
  80. apr_size_t seglen; /* is the end of the current segment */
  81. apr_status_t rv;
  82. /* Treat null as an empty path.
  83. */
  84. if (!addpath)
  85. addpath = "";
  86. if (addpath[0] == '/') {
  87. /* If addpath is rooted, then rootpath is unused.
  88. * Ths violates any APR_FILEPATH_SECUREROOTTEST and
  89. * APR_FILEPATH_NOTABSOLUTE flags specified.
  90. */
  91. if (flags & APR_FILEPATH_SECUREROOTTEST)
  92. return APR_EABOVEROOT;
  93. if (flags & APR_FILEPATH_NOTABSOLUTE)
  94. return APR_EABSOLUTE;
  95. /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
  96. * we won't test the root again, it's ignored.
  97. * Waste no CPU retrieving the working path.
  98. */
  99. if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
  100. rootpath = "";
  101. }
  102. else {
  103. /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
  104. * requires a relative result. If the rootpath is
  105. * ommitted, we do not retrieve the working path,
  106. * if rootpath was supplied as absolute then fail.
  107. */
  108. if (flags & APR_FILEPATH_NOTABSOLUTE) {
  109. if (!rootpath)
  110. rootpath = "";
  111. else if (rootpath[0] == '/')
  112. return APR_EABSOLUTE;
  113. }
  114. }
  115. if (!rootpath) {
  116. /* Start with the current working path. This is bass akwards,
  117. * but required since the compiler (at least vc) doesn't like
  118. * passing the address of a char const* for a char** arg.
  119. */
  120. char *getpath;
  121. rv = apr_filepath_get(&getpath, flags, p);
  122. rootpath = getpath;
  123. if (rv != APR_SUCCESS)
  124. return errno;
  125. /* XXX: Any kernel subject to goofy, uncanonical results
  126. * must run the rootpath against the user's given flags.
  127. * Simplest would be a recursive call to apr_filepath_merge
  128. * with an empty (not null) rootpath and addpath of the cwd.
  129. */
  130. }
  131. rootlen = strlen(rootpath);
  132. maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
  133. * root, and at end, plus trailing
  134. * null */
  135. if (maxlen > APR_PATH_MAX) {
  136. return APR_ENAMETOOLONG;
  137. }
  138. path = (char *)apr_palloc(p, maxlen);
  139. if (addpath[0] == '/') {
  140. /* Ignore the given root path, strip off leading
  141. * '/'s to a single leading '/' from the addpath,
  142. * and leave addpath at the first non-'/' character.
  143. */
  144. keptlen = 0;
  145. while (addpath[0] == '/')
  146. ++addpath;
  147. path[0] = '/';
  148. pathlen = 1;
  149. }
  150. else {
  151. /* If both paths are relative, fail early
  152. */
  153. if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
  154. return APR_ERELATIVE;
  155. /* Base the result path on the rootpath
  156. */
  157. keptlen = rootlen;
  158. memcpy(path, rootpath, rootlen);
  159. /* Always '/' terminate the given root path
  160. */
  161. if (keptlen && path[keptlen - 1] != '/') {
  162. path[keptlen++] = '/';
  163. }
  164. pathlen = keptlen;
  165. }
  166. while (*addpath) {
  167. /* Parse each segment, find the closing '/'
  168. */
  169. const char *next = addpath;
  170. while (*next && (*next != '/')) {
  171. ++next;
  172. }
  173. seglen = next - addpath;
  174. if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
  175. /* noop segment (/ or ./) so skip it
  176. */
  177. }
  178. else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
  179. /* backpath (../) */
  180. if (pathlen == 1 && path[0] == '/') {
  181. /* Attempt to move above root. Always die if the
  182. * APR_FILEPATH_SECUREROOTTEST flag is specified.
  183. */
  184. if (flags & APR_FILEPATH_SECUREROOTTEST) {
  185. return APR_EABOVEROOT;
  186. }
  187. /* Otherwise this is simply a noop, above root is root.
  188. * Flag that rootpath was entirely replaced.
  189. */
  190. keptlen = 0;
  191. }
  192. else if (pathlen == 0
  193. || (pathlen == 3
  194. && !memcmp(path + pathlen - 3, "../", 3))
  195. || (pathlen > 3
  196. && !memcmp(path + pathlen - 4, "/../", 4))) {
  197. /* Path is already backpathed or empty, if the
  198. * APR_FILEPATH_SECUREROOTTEST.was given die now.
  199. */
  200. if (flags & APR_FILEPATH_SECUREROOTTEST) {
  201. return APR_EABOVEROOT;
  202. }
  203. /* Otherwise append another backpath, including
  204. * trailing slash if present.
  205. */
  206. memcpy(path + pathlen, "../", *next ? 3 : 2);
  207. pathlen += *next ? 3 : 2;
  208. }
  209. else {
  210. /* otherwise crop the prior segment
  211. */
  212. do {
  213. --pathlen;
  214. } while (pathlen && path[pathlen - 1] != '/');
  215. }
  216. /* Now test if we are above where we started and back up
  217. * the keptlen offset to reflect the added/altered path.
  218. */
  219. if (pathlen < keptlen) {
  220. if (flags & APR_FILEPATH_SECUREROOTTEST) {
  221. return APR_EABOVEROOT;
  222. }
  223. keptlen = pathlen;
  224. }
  225. }
  226. else {
  227. /* An actual segment, append it to the destination path
  228. */
  229. if (*next) {
  230. seglen++;
  231. }
  232. memcpy(path + pathlen, addpath, seglen);
  233. pathlen += seglen;
  234. }
  235. /* Skip over trailing slash to the next segment
  236. */
  237. if (*next) {
  238. ++next;
  239. }
  240. addpath = next;
  241. }
  242. path[pathlen] = '\0';
  243. /* keptlen will be the rootlen unless the addpath contained
  244. * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
  245. * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
  246. * compare the original root to assure the result path is
  247. * still within given root path.
  248. */
  249. if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
  250. if (strncmp(rootpath, path, rootlen)) {
  251. return APR_EABOVEROOT;
  252. }
  253. if (rootpath[rootlen - 1] != '/'
  254. && path[rootlen] && path[rootlen] != '/') {
  255. return APR_EABOVEROOT;
  256. }
  257. }
  258. *newpath = path;
  259. return APR_SUCCESS;
  260. }
  261. APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
  262. const char *liststr,
  263. apr_pool_t *p)
  264. {
  265. return apr_filepath_list_split_impl(pathelts, liststr, ':', p);
  266. }
  267. APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
  268. apr_array_header_t *pathelts,
  269. apr_pool_t *p)
  270. {
  271. return apr_filepath_list_merge_impl(liststr, pathelts, ':', p);
  272. }
  273. APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
  274. {
  275. #if defined(DARWIN)
  276. *style = APR_FILEPATH_ENCODING_UTF8;
  277. #else
  278. *style = APR_FILEPATH_ENCODING_LOCALE;
  279. #endif
  280. return APR_SUCCESS;
  281. }