filepath.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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 "fspr.h"
  17. #include "fspr_private.h"
  18. #include "fspr_arch_file_io.h"
  19. #include "fspr_strings.h"
  20. #include "fspr_lib.h"
  21. #include <string.h>
  22. #include <ctype.h>
  23. #ifdef NETWARE
  24. #include <unistd.h>
  25. #include <fsio.h>
  26. #endif
  27. /* WinNT accepts several odd forms of a 'root' path. Under Unicode
  28. * calls (ApiFunctionW) the //?/C:/foo or //?/UNC/mach/share/foo forms
  29. * are accepted. Ansi and Unicode functions both accept the //./C:/foo
  30. * form under WinNT/2K. Since these forms are handled in the utf-8 to
  31. * unicode translation phase, we don't want the user confused by them, so
  32. * we will accept them but always return the canonical C:/ or //mach/share/
  33. *
  34. * OS2 appears immune from the nonsense :)
  35. */
  36. APR_DECLARE(fspr_status_t) fspr_filepath_root(const char **rootpath,
  37. const char **inpath,
  38. fspr_int32_t flags,
  39. fspr_pool_t *p)
  40. {
  41. const char *testpath = *inpath;
  42. char *newpath;
  43. #ifdef NETWARE
  44. char seperator[2] = { 0, 0};
  45. char server[APR_PATH_MAX+1];
  46. char volume[APR_PATH_MAX+1];
  47. char file[APR_PATH_MAX+1];
  48. char *volsep = NULL;
  49. int elements;
  50. if (inpath && *inpath)
  51. volsep = strchr (*inpath, ':');
  52. else
  53. return APR_EBADPATH;
  54. if (strlen(*inpath) > APR_PATH_MAX) {
  55. return APR_EBADPATH;
  56. }
  57. seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
  58. /* Allocate and initialize each of the segment buffers
  59. */
  60. server[0] = volume[0] = file[0] = '\0';
  61. /* If we don't have a volume separator then don't bother deconstructing
  62. the path since we won't use the deconstructed information anyway.
  63. */
  64. if (volsep) {
  65. /* Split the inpath into its separate parts. */
  66. deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
  67. /* If we got a volume part then continue splitting out the root.
  68. Otherwise we either have an incomplete or relative path
  69. */
  70. if (volume && strlen(volume) > 0) {
  71. newpath = fspr_pcalloc(p, strlen(server)+strlen(volume)+5);
  72. construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE);
  73. /* NetWare doesn't add the root slash so we need to add it manually.
  74. */
  75. strcat(newpath, seperator);
  76. *rootpath = newpath;
  77. /* Skip the inpath pointer down to the first non-root character
  78. */
  79. newpath = volsep;
  80. do {
  81. ++newpath;
  82. } while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
  83. *inpath = newpath;
  84. /* Need to handle APR_FILEPATH_TRUENAME checking here. */
  85. return APR_SUCCESS;
  86. }
  87. else
  88. return APR_EBADPATH;
  89. }
  90. else if ((**inpath == '/') || (**inpath == '\\')) {
  91. /* if we have a root path without a volume then just split
  92. in same manner as unix although this path will be
  93. incomplete.
  94. */
  95. *rootpath = fspr_pstrdup(p, seperator);
  96. do {
  97. ++(*inpath);
  98. } while ((**inpath == '/') || (**inpath == '\\'));
  99. }
  100. else
  101. return APR_ERELATIVE;
  102. return APR_EINCOMPLETE;
  103. #else /* ndef(NETWARE) */
  104. char seperator[2];
  105. const char *delim1;
  106. const char *delim2;
  107. seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
  108. seperator[1] = 0;
  109. if (testpath[0] == '/' || testpath[0] == '\\') {
  110. if (testpath[1] == '/' || testpath[1] == '\\') {
  111. #ifdef WIN32 /* //server/share isn't the only // delimited syntax */
  112. if ((testpath[2] == '?' || testpath[2] == '.')
  113. && (testpath[3] == '/' || testpath[3] == '\\')) {
  114. if (IS_FNCHAR(testpath[4]) && testpath[5] == ':')
  115. {
  116. fspr_status_t rv;
  117. testpath += 4;
  118. /* given '//?/C: or //./C: let us try this
  119. * all over again from the drive designator
  120. */
  121. rv = fspr_filepath_root(rootpath, &testpath, flags, p);
  122. if (!rv || rv == APR_EINCOMPLETE)
  123. *inpath = testpath;
  124. return rv;
  125. }
  126. else if (strncasecmp(testpath + 4, "UNC", 3) == 0
  127. && (testpath[7] == '/' || testpath[7] == '\\')
  128. && (testpath[2] == '?')) {
  129. /* given '//?/UNC/machine/share, a little magic
  130. * at the end makes this all work out by using
  131. * 'C/machine' as the starting point and replacing
  132. * the UNC delimiters with \'s, including the 'C'
  133. */
  134. testpath += 6;
  135. }
  136. else
  137. /* This must not be a path to a file, but rather
  138. * a volume or device. Die for now.
  139. */
  140. return APR_EBADPATH;
  141. }
  142. #endif /* WIN32 (non - //server/share syntax) */
  143. /* Evaluate path of '//[machine/[share[/]]]' */
  144. delim1 = testpath + 2;
  145. do {
  146. /* Protect against //X/ where X is illegal */
  147. if (*delim1 && !IS_FNCHAR(*(delim1++)))
  148. return APR_EBADPATH;
  149. } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
  150. if (*delim1) {
  151. fspr_status_t rv;
  152. delim2 = delim1 + 1;
  153. while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
  154. /* Protect against //machine/X/ where X is illegal */
  155. if (!IS_FNCHAR(*(delim2++)))
  156. return APR_EBADPATH;
  157. }
  158. /* Copy the '//machine/[share[/]]' path, always providing
  159. * an extra byte for the trailing slash.
  160. */
  161. newpath = fspr_pstrmemdup(p, testpath, delim2 - testpath + 1);
  162. if (delim2 == delim1 + 1) {
  163. /* We found simply \\machine\, so give up already
  164. */
  165. *rootpath = newpath;
  166. *inpath = delim2;
  167. return APR_EINCOMPLETE;
  168. }
  169. if (flags & APR_FILEPATH_TRUENAME) {
  170. /* Validate the \\Machine\Share\ designation,
  171. * Win32 will argue about slashed in UNC paths,
  172. * so use backslashes till we finish testing,
  173. * and add the trailing backslash [required].
  174. * fspr_pstrmemdup above guarentees us the new
  175. * trailing null character.
  176. */
  177. newpath[0] = '\\';
  178. newpath[1] = '\\';
  179. newpath[delim1 - testpath] = '\\';
  180. newpath[delim2 - testpath] = '\\';
  181. rv = filepath_root_test(newpath, p);
  182. if (rv)
  183. return rv;
  184. rv = filepath_root_case(&newpath, newpath, p);
  185. if (rv)
  186. return rv;
  187. newpath[0] = seperator[0];
  188. newpath[1] = seperator[0];
  189. newpath[delim1 - testpath] = seperator[0];
  190. newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
  191. }
  192. else {
  193. /* Give back the caller's own choice of delimiters
  194. */
  195. newpath[0] = testpath[0];
  196. newpath[1] = testpath[1];
  197. newpath[delim1 - testpath] = *delim1;
  198. newpath[delim2 - testpath] = *delim2;
  199. }
  200. /* If this root included the trailing / or \ designation
  201. * then lop off multiple trailing slashes and give back
  202. * appropriate delimiters.
  203. */
  204. if (*delim2) {
  205. *inpath = delim2 + 1;
  206. while (**inpath == '/' || **inpath == '\\')
  207. ++*inpath;
  208. }
  209. else {
  210. *inpath = delim2;
  211. }
  212. *rootpath = newpath;
  213. return APR_SUCCESS;
  214. }
  215. /* Have path of '\\[machine]', if the machine is given,
  216. * append same trailing slash as the leading slash
  217. */
  218. delim1 = strchr(testpath, '\0');
  219. if (delim1 > testpath + 2) {
  220. newpath = fspr_pstrndup(p, testpath, delim1 - testpath + 1);
  221. if (flags & APR_FILEPATH_TRUENAME)
  222. newpath[delim1 - testpath] = seperator[0];
  223. else
  224. newpath[delim1 - testpath] = newpath[0];
  225. newpath[delim1 - testpath + 1] = '\0';
  226. }
  227. else {
  228. newpath = fspr_pstrndup(p, testpath, delim1 - testpath);
  229. }
  230. if (flags & APR_FILEPATH_TRUENAME) {
  231. newpath[0] = seperator[0];
  232. newpath[1] = seperator[0];
  233. }
  234. *rootpath = newpath;
  235. *inpath = delim1;
  236. return APR_EINCOMPLETE;
  237. }
  238. /* Left with a path of '/', what drive are we asking about?
  239. */
  240. *inpath = testpath + 1;
  241. newpath = fspr_palloc(p, 2);
  242. if (flags & APR_FILEPATH_TRUENAME)
  243. newpath[0] = seperator[0];
  244. else
  245. newpath[0] = testpath[0];
  246. newpath[1] = '\0';
  247. *rootpath = newpath;
  248. return APR_EINCOMPLETE;
  249. }
  250. /* Evaluate path of 'd:[/]' */
  251. if (IS_FNCHAR(*testpath) && testpath[1] == ':')
  252. {
  253. fspr_status_t rv;
  254. /* Validate that D:\ drive exists, test must be rooted
  255. * Note that posix/win32 insists a drive letter is upper case,
  256. * so who are we to argue with a 'feature'.
  257. * It is a safe fold, since only A-Z is legal, and has no
  258. * side effects of legal mis-mapped non-us-ascii codes.
  259. */
  260. newpath = fspr_palloc(p, 4);
  261. newpath[0] = testpath[0];
  262. newpath[1] = testpath[1];
  263. newpath[2] = seperator[0];
  264. newpath[3] = '\0';
  265. if (flags & APR_FILEPATH_TRUENAME) {
  266. newpath[0] = fspr_toupper(newpath[0]);
  267. rv = filepath_root_test(newpath, p);
  268. if (rv)
  269. return rv;
  270. }
  271. /* Just give back the root the user handed to us.
  272. */
  273. if (testpath[2] != '/' && testpath[2] != '\\') {
  274. newpath[2] = '\0';
  275. *rootpath = newpath;
  276. *inpath = testpath + 2;
  277. return APR_EINCOMPLETE;
  278. }
  279. /* strip off remaining slashes that designate the root,
  280. * give the caller back their original choice of slash
  281. * unless this is TRUENAME'ed
  282. */
  283. *inpath = testpath + 3;
  284. while (**inpath == '/' || **inpath == '\\')
  285. ++*inpath;
  286. if (!(flags & APR_FILEPATH_TRUENAME))
  287. newpath[2] = testpath[2];
  288. *rootpath = newpath;
  289. return APR_SUCCESS;
  290. }
  291. /* Nothing interesting */
  292. return APR_ERELATIVE;
  293. #endif /* ndef(NETWARE) */
  294. }
  295. APR_DECLARE(fspr_status_t) fspr_filepath_merge(char **newpath,
  296. const char *basepath,
  297. const char *addpath,
  298. fspr_int32_t flags,
  299. fspr_pool_t *p)
  300. {
  301. char path[APR_PATH_MAX]; /* isn't null term */
  302. const char *baseroot = NULL;
  303. const char *addroot;
  304. fspr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
  305. fspr_size_t baselen; /* the length of basepath (excluding baseroot) */
  306. fspr_size_t keptlen; /* the length of the retained basepath (incl root) */
  307. fspr_size_t pathlen; /* the length of the result path */
  308. fspr_size_t segend; /* the end of the current segment */
  309. fspr_size_t seglen; /* the length of the segment (excl trailing chars) */
  310. fspr_status_t basetype = 0; /* from parsing the basepath's baseroot */
  311. fspr_status_t addtype; /* from parsing the addpath's addroot */
  312. fspr_status_t rv;
  313. #ifndef NETWARE
  314. int fixunc = 0; /* flag to complete an incomplete UNC basepath */
  315. #endif
  316. /* Treat null as an empty path, otherwise split addroot from the addpath
  317. */
  318. if (!addpath) {
  319. addpath = addroot = "";
  320. addtype = APR_ERELATIVE;
  321. }
  322. else {
  323. /* This call _should_ test the path
  324. */
  325. addtype = fspr_filepath_root(&addroot, &addpath,
  326. APR_FILEPATH_TRUENAME
  327. | (flags & APR_FILEPATH_NATIVE),
  328. p);
  329. if (addtype == APR_SUCCESS) {
  330. addtype = APR_EABSOLUTE;
  331. }
  332. else if (addtype == APR_ERELATIVE) {
  333. addroot = "";
  334. }
  335. else if (addtype != APR_EINCOMPLETE) {
  336. /* fspr_filepath_root was incomprehensible so fail already
  337. */
  338. return addtype;
  339. }
  340. }
  341. /* If addpath is (even partially) rooted, then basepath is
  342. * unused. Ths violates any APR_FILEPATH_SECUREROOTTEST
  343. * and APR_FILEPATH_NOTABSOLUTE flags specified.
  344. */
  345. if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
  346. {
  347. if (flags & APR_FILEPATH_SECUREROOTTEST)
  348. return APR_EABOVEROOT;
  349. if (flags & APR_FILEPATH_NOTABSOLUTE)
  350. return addtype;
  351. }
  352. /* Optimized tests before we query the current working path
  353. */
  354. if (!basepath) {
  355. /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
  356. * we won't test the root again, it's ignored.
  357. * Waste no CPU retrieving the working path.
  358. */
  359. if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
  360. basepath = baseroot = "";
  361. basetype = APR_ERELATIVE;
  362. }
  363. /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
  364. * requires an absolutely relative result, So do not retrieve
  365. * the working path.
  366. */
  367. if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
  368. basepath = baseroot = "";
  369. basetype = APR_ERELATIVE;
  370. }
  371. }
  372. if (!basepath)
  373. {
  374. /* Start with the current working path. This is bass akwards,
  375. * but required since the compiler (at least vc) doesn't like
  376. * passing the address of a char const* for a char** arg.
  377. * We must grab the current path of the designated drive
  378. * if addroot is given in drive-relative form (e.g. d:foo)
  379. */
  380. char *getpath;
  381. #ifndef NETWARE
  382. if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
  383. rv = filepath_drive_get(&getpath, addroot[0], flags, p);
  384. else
  385. #endif
  386. rv = fspr_filepath_get(&getpath, flags, p);
  387. if (rv != APR_SUCCESS)
  388. return rv;
  389. basepath = getpath;
  390. }
  391. if (!baseroot) {
  392. /* This call should _not_ test the path
  393. */
  394. basetype = fspr_filepath_root(&baseroot, &basepath,
  395. (flags & APR_FILEPATH_NATIVE), p);
  396. if (basetype == APR_SUCCESS) {
  397. basetype = APR_EABSOLUTE;
  398. }
  399. else if (basetype == APR_ERELATIVE) {
  400. baseroot = "";
  401. }
  402. else if (basetype != APR_EINCOMPLETE) {
  403. /* fspr_filepath_root was incomprehensible so fail already
  404. */
  405. return basetype;
  406. }
  407. }
  408. baselen = strlen(basepath);
  409. /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
  410. * requires an absolutely relative result. If the given
  411. * basepath is not relative then fail.
  412. */
  413. if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
  414. return basetype;
  415. /* The Win32 nightmare on unc street... start combining for
  416. * many possible root combinations.
  417. */
  418. if (addtype == APR_EABSOLUTE)
  419. {
  420. /* Ignore the given root path, and start with the addroot
  421. */
  422. if ((flags & APR_FILEPATH_NOTABOVEROOT)
  423. && strncmp(baseroot, addroot, strlen(baseroot)))
  424. return APR_EABOVEROOT;
  425. keptlen = 0;
  426. rootlen = pathlen = strlen(addroot);
  427. memcpy(path, addroot, pathlen);
  428. }
  429. else if (addtype == APR_EINCOMPLETE)
  430. {
  431. /* There are several types of incomplete paths,
  432. * incomplete UNC paths (//foo/ or //),
  433. * drives without rooted paths (d: as in d:foo),
  434. * and simple roots (/ as in /foo).
  435. * Deal with these in significantly different manners...
  436. */
  437. #ifndef NETWARE
  438. if ((addroot[0] == '/' || addroot[0] == '\\') &&
  439. (addroot[1] == '/' || addroot[1] == '\\'))
  440. {
  441. /* Ignore the given root path if the incomplete addpath is UNC,
  442. * (note that the final result will be incomplete).
  443. */
  444. if (flags & APR_FILEPATH_NOTRELATIVE)
  445. return addtype;
  446. if ((flags & APR_FILEPATH_NOTABOVEROOT)
  447. && strncmp(baseroot, addroot, strlen(baseroot)))
  448. return APR_EABOVEROOT;
  449. fixunc = 1;
  450. keptlen = 0;
  451. rootlen = pathlen = strlen(addroot);
  452. memcpy(path, addroot, pathlen);
  453. }
  454. else
  455. #endif
  456. if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1])
  457. {
  458. /* Bring together the drive or UNC root from the baseroot
  459. * if the addpath is a simple root and basepath is rooted,
  460. * otherwise disregard the basepath entirely.
  461. */
  462. if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  463. return basetype;
  464. if (basetype != APR_ERELATIVE) {
  465. #ifndef NETWARE
  466. if (basetype == APR_INCOMPLETE
  467. && (baseroot[0] == '/' || baseroot[0] == '\\')
  468. && (baseroot[1] == '/' || baseroot[1] == '\\'))
  469. fixunc = 1;
  470. #endif
  471. keptlen = rootlen = pathlen = strlen(baseroot);
  472. memcpy(path, baseroot, pathlen);
  473. }
  474. else {
  475. if (flags & APR_FILEPATH_NOTABOVEROOT)
  476. return APR_EABOVEROOT;
  477. keptlen = 0;
  478. rootlen = pathlen = strlen(addroot);
  479. memcpy(path, addroot, pathlen);
  480. }
  481. }
  482. #ifdef NETWARE
  483. else if (filepath_has_drive(addroot, DRIVE_ONLY, p))
  484. {
  485. /* If the addroot is a drive (without a volume root)
  486. * use the basepath _if_ it matches this drive letter!
  487. * Otherwise we must discard the basepath.
  488. */
  489. if (!filepath_compare_drive(addroot, baseroot, p) &&
  490. filepath_has_drive(baseroot, 0, p)) {
  491. #else
  492. else if (addroot[0] && addroot[1] == ':' && !addroot[2])
  493. {
  494. /* If the addroot is a drive (without a volume root)
  495. * use the basepath _if_ it matches this drive letter!
  496. * Otherwise we must discard the basepath.
  497. */
  498. if (addroot[0] == baseroot[0] && baseroot[1] == ':') {
  499. #endif
  500. /* Base the result path on the basepath
  501. */
  502. if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  503. return basetype;
  504. rootlen = strlen(baseroot);
  505. keptlen = pathlen = rootlen + baselen;
  506. if (keptlen >= sizeof(path))
  507. return APR_ENAMETOOLONG;
  508. memcpy(path, baseroot, rootlen);
  509. memcpy(path + rootlen, basepath, baselen);
  510. }
  511. else {
  512. if (flags & APR_FILEPATH_NOTRELATIVE)
  513. return addtype;
  514. if (flags & APR_FILEPATH_NOTABOVEROOT)
  515. return APR_EABOVEROOT;
  516. keptlen = 0;
  517. rootlen = pathlen = strlen(addroot);
  518. memcpy(path, addroot, pathlen);
  519. }
  520. }
  521. else {
  522. /* Now this is unexpected, we aren't aware of any other
  523. * incomplete path forms! Fail now.
  524. */
  525. return APR_EBADPATH;
  526. }
  527. }
  528. else { /* addtype == APR_ERELATIVE */
  529. /* If both paths are relative, fail early
  530. */
  531. if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  532. return basetype;
  533. #ifndef NETWARE
  534. /* An incomplete UNC path must be completed
  535. */
  536. if (basetype == APR_INCOMPLETE
  537. && (baseroot[0] == '/' || baseroot[0] == '\\')
  538. && (baseroot[1] == '/' || baseroot[1] == '\\'))
  539. fixunc = 1;
  540. #endif
  541. /* Base the result path on the basepath
  542. */
  543. rootlen = strlen(baseroot);
  544. keptlen = pathlen = rootlen + baselen;
  545. if (keptlen >= sizeof(path))
  546. return APR_ENAMETOOLONG;
  547. memcpy(path, baseroot, rootlen);
  548. memcpy(path + rootlen, basepath, baselen);
  549. }
  550. /* '/' terminate the given root path unless it's already terminated
  551. * or is an incomplete drive root. Correct the trailing slash unless
  552. * we have an incomplete UNC path still to fix.
  553. */
  554. if (pathlen && path[pathlen - 1] != ':') {
  555. if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
  556. if (pathlen + 1 >= sizeof(path))
  557. return APR_ENAMETOOLONG;
  558. path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
  559. }
  560. /* XXX: wrong, but gotta figure out what I intended;
  561. * else if (!fixunc)
  562. * path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
  563. */
  564. }
  565. while (*addpath)
  566. {
  567. /* Parse each segment, find the closing '/'
  568. */
  569. seglen = 0;
  570. while (addpath[seglen] && addpath[seglen] != '/'
  571. && addpath[seglen] != '\\')
  572. ++seglen;
  573. /* Truncate all trailing spaces and all but the first two dots */
  574. segend = seglen;
  575. while (seglen && (addpath[seglen - 1] == ' '
  576. || addpath[seglen - 1] == '.')) {
  577. if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
  578. --seglen;
  579. else
  580. break;
  581. }
  582. if (seglen == 0 || (seglen == 1 && addpath[0] == '.'))
  583. {
  584. /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
  585. * so eliminate all preconceptions that it is valid.
  586. */
  587. if (seglen < segend)
  588. return APR_EBADPATH;
  589. #ifndef NETWARE
  590. /* This isn't legal unless the unc path is completed
  591. */
  592. if (fixunc)
  593. return APR_EBADPATH;
  594. #endif
  595. /* Otherwise, this is a noop segment (/ or ./) so ignore it
  596. */
  597. }
  598. else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')
  599. {
  600. /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
  601. * and '/..../', some functions treat it as ".", and some
  602. * fail! Eliminate all preconceptions that they are valid.
  603. */
  604. if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
  605. return APR_EBADPATH;
  606. #ifndef NETWARE
  607. /* This isn't legal unless the unc path is completed
  608. */
  609. if (fixunc)
  610. return APR_EBADPATH;
  611. #endif
  612. /* backpath (../) when an absolute path is given */
  613. if (rootlen && (pathlen <= rootlen))
  614. {
  615. /* Attempt to move above root. Always die if the
  616. * APR_FILEPATH_SECUREROOTTEST flag is specified.
  617. */
  618. if (flags & APR_FILEPATH_SECUREROOTTEST)
  619. return APR_EABOVEROOT;
  620. /* Otherwise this is simply a noop, above root is root.
  621. */
  622. }
  623. else if (pathlen == 0
  624. || (pathlen >= 3
  625. && (pathlen == 3
  626. || path[pathlen - 4] == ':'
  627. || path[pathlen - 4] == '/'
  628. || path[pathlen - 4] == '\\')
  629. && path[pathlen - 3] == '.'
  630. && path[pathlen - 2] == '.'
  631. && (path[pathlen - 1] == '/'
  632. || path[pathlen - 1] == '\\')))
  633. {
  634. /* Verified path is empty, exactly "..[/\]", or ends
  635. * in "[:/\]..[/\]" - these patterns we will not back
  636. * over since they aren't 'prior segements'.
  637. *
  638. * If APR_FILEPATH_SECUREROOTTEST.was given, die now.
  639. */
  640. if (flags & APR_FILEPATH_SECUREROOTTEST)
  641. return APR_EABOVEROOT;
  642. /* Otherwise append another backpath.
  643. */
  644. if (pathlen + 3 >= sizeof(path))
  645. return APR_ENAMETOOLONG;
  646. path[pathlen++] = '.';
  647. path[pathlen++] = '.';
  648. if (addpath[segend]) {
  649. path[pathlen++] = ((flags & APR_FILEPATH_NATIVE)
  650. ? '\\' : ((flags & APR_FILEPATH_TRUENAME)
  651. ? '/' : addpath[segend]));
  652. }
  653. /* The 'root' part of this path now includes the ../ path,
  654. * because that backpath will not be parsed by the truename
  655. * code below.
  656. */
  657. keptlen = pathlen;
  658. }
  659. else
  660. {
  661. /* otherwise crop the prior segment
  662. */
  663. do {
  664. --pathlen;
  665. } while (pathlen && path[pathlen - 1] != '/'
  666. && path[pathlen - 1] != '\\');
  667. /* Now test if we are above where we started and back up
  668. * the keptlen offset to reflect the added/altered path.
  669. */
  670. if (pathlen < keptlen)
  671. {
  672. if (flags & APR_FILEPATH_SECUREROOTTEST)
  673. return APR_EABOVEROOT;
  674. keptlen = pathlen;
  675. }
  676. }
  677. }
  678. else /* not empty or dots */
  679. {
  680. #ifndef NETWARE
  681. if (fixunc) {
  682. const char *testpath = path;
  683. const char *testroot;
  684. fspr_status_t testtype;
  685. fspr_size_t i = (addpath[segend] != '\0');
  686. /* This isn't legal unless the unc path is complete!
  687. */
  688. if (seglen < segend)
  689. return APR_EBADPATH;
  690. if (pathlen + seglen + 1 >= sizeof(path))
  691. return APR_ENAMETOOLONG;
  692. memcpy(path + pathlen, addpath, seglen + i);
  693. /* Always add the trailing slash to a UNC segment
  694. */
  695. path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
  696. ? '\\' : '/');
  697. pathlen += seglen + 1;
  698. /* Recanonicalize the UNC root with the new UNC segment,
  699. * and if we succeed, reset this test and the rootlen,
  700. * and replace our path with the canonical UNC root path
  701. */
  702. path[pathlen] = '\0';
  703. /* This call _should_ test the path
  704. */
  705. testtype = fspr_filepath_root(&testroot, &testpath,
  706. APR_FILEPATH_TRUENAME
  707. | (flags & APR_FILEPATH_NATIVE),
  708. p);
  709. if (testtype == APR_SUCCESS) {
  710. rootlen = pathlen = (testpath - path);
  711. memcpy(path, testroot, pathlen);
  712. fixunc = 0;
  713. }
  714. else if (testtype != APR_EINCOMPLETE) {
  715. /* fspr_filepath_root was very unexpected so fail already
  716. */
  717. return testtype;
  718. }
  719. }
  720. else
  721. #endif
  722. {
  723. /* An actual segment, append it to the destination path
  724. */
  725. fspr_size_t i = (addpath[segend] != '\0');
  726. if (pathlen + seglen + i >= sizeof(path))
  727. return APR_ENAMETOOLONG;
  728. memcpy(path + pathlen, addpath, seglen + i);
  729. if (i)
  730. path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
  731. ? '\\' : '/');
  732. pathlen += seglen + i;
  733. }
  734. }
  735. /* Skip over trailing slash to the next segment
  736. */
  737. if (addpath[segend])
  738. ++segend;
  739. addpath += segend;
  740. }
  741. /* keptlen will be the baselen unless the addpath contained
  742. * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
  743. * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
  744. * compare the string beyond the root to assure the result path
  745. * is still within given basepath. Note that the root path
  746. * segment is thoroughly tested prior to path parsing.
  747. */
  748. if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
  749. if (memcmp(basepath, path + rootlen, baselen) != 0)
  750. return APR_EABOVEROOT;
  751. /* Ahem... if we have a basepath without a trailing slash,
  752. * we better be sure that /foo wasn't replaced with /foobar!
  753. */
  754. if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
  755. && path[rootlen + baselen] && path[rootlen + baselen] != '/'
  756. && path[rootlen + baselen] != '\\')
  757. return APR_EABOVEROOT;
  758. }
  759. if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
  760. /* We can always skip the root, it's already true-named. */
  761. if (rootlen > keptlen)
  762. keptlen = rootlen;
  763. if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
  764. /* By rights, keptlen may grown longer than pathlen.
  765. * we wont' use it again (in that case) so we don't care.
  766. */
  767. ++keptlen;
  768. }
  769. /* Go through all the new segments */
  770. while (keptlen < pathlen) {
  771. fspr_finfo_t finfo;
  772. char saveslash = 0;
  773. seglen = 0;
  774. /* find any slash and set it aside for a minute. */
  775. for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
  776. if ((path[keptlen + seglen] == '/') ||
  777. (path[keptlen + seglen] == '\\')) {
  778. saveslash = path[keptlen + seglen];
  779. break;
  780. }
  781. }
  782. /* Null term for stat! */
  783. path[keptlen + seglen] = '\0';
  784. if ((rv = fspr_stat(&finfo, path,
  785. APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
  786. == APR_SUCCESS) {
  787. fspr_size_t namelen = strlen(finfo.name);
  788. #if defined(OS2) /* only has case folding, never aliases that change the length */
  789. if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
  790. memcpy(path + keptlen, finfo.name, namelen);
  791. }
  792. #else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
  793. if ((namelen != seglen) ||
  794. (memcmp(finfo.name, path + keptlen, seglen) != 0))
  795. {
  796. if (namelen <= seglen) {
  797. memcpy(path + keptlen, finfo.name, namelen);
  798. if ((namelen < seglen) && saveslash) {
  799. memmove(path + keptlen + namelen + 1,
  800. path + keptlen + seglen + 1,
  801. pathlen - keptlen - seglen);
  802. pathlen += namelen - seglen;
  803. seglen = namelen;
  804. }
  805. }
  806. else { /* namelen > seglen */
  807. if (pathlen + namelen - seglen >= sizeof(path))
  808. return APR_ENAMETOOLONG;
  809. if (saveslash) {
  810. memmove(path + keptlen + namelen + 1,
  811. path + keptlen + seglen + 1,
  812. pathlen - keptlen - seglen);
  813. }
  814. memcpy(path + keptlen, finfo.name, namelen);
  815. pathlen += namelen - seglen;
  816. seglen = namelen;
  817. }
  818. }
  819. #endif /* !OS2 (Whatever that alias was we're over it) */
  820. /* That's it, the rest is path info.
  821. * I don't know how we aught to handle this. Should
  822. * we define a new error to indicate 'more info'?
  823. * Should we split out the rest of the path?
  824. */
  825. if ((finfo.filetype != APR_DIR) &&
  826. (finfo.filetype != APR_LNK) && saveslash)
  827. rv = APR_ENOTDIR;
  828. #ifdef XXX_FIGURE_THIS_OUT
  829. {
  830. /* the example inserts a null between the end of
  831. * the filename and the next segment, and increments
  832. * the path length so we would return both segments.
  833. */
  834. if (saveslash) {
  835. keptlen += seglen;
  836. path[keptlen] = saveslash;
  837. if (pathlen + 1 >= sizeof(path))
  838. return APR_ENAMETOOLONG;
  839. memmove(path + keptlen + 1,
  840. path + keptlen,
  841. pathlen - keptlen);
  842. path[keptlen] = '\0';
  843. ++pathlen;
  844. break;
  845. }
  846. }
  847. #endif
  848. }
  849. /* put back the '/' */
  850. if (saveslash) {
  851. path[keptlen + seglen] = saveslash;
  852. ++seglen;
  853. }
  854. keptlen += seglen;
  855. if (rv != APR_SUCCESS) {
  856. if (APR_STATUS_IS_ENOENT(rv))
  857. break;
  858. if (APR_STATUS_IS_EPATHWILD(rv))
  859. /* This path included wildcards. The path elements
  860. * that did not contain wildcards are canonicalized,
  861. * so we will return the path, although later elements
  862. * don't necessarily exist, and aren't canonical.
  863. */
  864. break;
  865. else if (APR_STATUS_IS_ENOTDIR(rv))
  866. /* This is a little more serious, we just added a name
  867. * onto a filename (think http's PATH_INFO)
  868. * If the caller is foolish enough to do this, we expect
  869. * the've already canonicalized the root) that they knew
  870. * what they are doing :(
  871. */
  872. break;
  873. else
  874. return rv;
  875. }
  876. }
  877. }
  878. *newpath = fspr_pmemdup(p, path, pathlen + 1);
  879. (*newpath)[pathlen] = '\0';
  880. return APR_SUCCESS;
  881. }
  882. APR_DECLARE(fspr_status_t) fspr_filepath_list_split(fspr_array_header_t **pathelts,
  883. const char *liststr,
  884. fspr_pool_t *p)
  885. {
  886. return fspr_filepath_list_split_impl(pathelts, liststr, ';', p);
  887. }
  888. APR_DECLARE(fspr_status_t) fspr_filepath_list_merge(char **liststr,
  889. fspr_array_header_t *pathelts,
  890. fspr_pool_t *p)
  891. {
  892. return fspr_filepath_list_merge_impl(liststr, pathelts, ';', p);
  893. }
  894. APR_DECLARE(fspr_status_t) fspr_filepath_encoding(int *style, fspr_pool_t *p)
  895. {
  896. #if APR_HAS_UNICODE_FS
  897. IF_WIN_OS_IS_UNICODE
  898. {
  899. *style = APR_FILEPATH_ENCODING_UTF8;
  900. return APR_SUCCESS;
  901. }
  902. #endif
  903. *style = APR_FILEPATH_ENCODING_LOCALE;
  904. return APR_SUCCESS;
  905. }