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 "testutil.h"
18 #include "apr_file_io.h"
19 #include "apr_file_info.h"
20 #include "apr_errno.h"
21 #include "apr_general.h"
22 #include "apr_pools.h"
23 #include "apr_lib.h"
24 #include "apr_strings.h"
25 
26 #if defined(WIN32)
27 #include <direct.h>
28 #endif
29 
30 #if defined(WIN32) || defined(OS2)
31 #define ABS_ROOT "C:/"
32 #elif defined(NETWARE)
33 #define ABS_ROOT "SYS:/"
34 #else
35 #define ABS_ROOT "/"
36 #endif
37 
merge_aboveroot(abts_case * tc,void * data)38 static void merge_aboveroot(abts_case *tc, void *data)
39 {
40     apr_status_t rv;
41     char *dstpath = NULL;
42     char errmsg[256];
43 
44     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"bar", APR_FILEPATH_NOTABOVEROOT,
45                             p);
46     apr_strerror(rv, errmsg, sizeof(errmsg));
47     ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABOVEROOT(rv));
48     ABTS_PTR_EQUAL(tc, NULL, dstpath);
49     ABTS_STR_EQUAL(tc, "The given path was above the root path", errmsg);
50 }
51 
merge_belowroot(abts_case * tc,void * data)52 static void merge_belowroot(abts_case *tc, void *data)
53 {
54     apr_status_t rv;
55     char *dstpath = NULL;
56 
57     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar",
58                             APR_FILEPATH_NOTABOVEROOT, p);
59     ABTS_PTR_NOTNULL(tc, dstpath);
60     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
61     ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
62 }
63 
merge_noflag(abts_case * tc,void * data)64 static void merge_noflag(abts_case *tc, void *data)
65 {
66     apr_status_t rv;
67     char *dstpath = NULL;
68 
69     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar", 0, p);
70     ABTS_PTR_NOTNULL(tc, dstpath);
71     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
72     ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
73 }
74 
merge_dotdot(abts_case * tc,void * data)75 static void merge_dotdot(abts_case *tc, void *data)
76 {
77     apr_status_t rv;
78     char *dstpath = NULL;
79 
80     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz", 0, p);
81     ABTS_PTR_NOTNULL(tc, dstpath);
82     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
83     ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
84 
85     rv = apr_filepath_merge(&dstpath, "", "../test", 0, p);
86     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
87     ABTS_STR_EQUAL(tc, "../test", dstpath);
88 
89     /* Very dangerous assumptions here about what the cwd is.  However, let's assume
90      * that the testall is invoked from within apr/test/ so the following test should
91      * return ../test unless a previously fixed bug remains or the developer changes
92      * the case of the test directory:
93      */
94     rv = apr_filepath_merge(&dstpath, "", "../test", APR_FILEPATH_TRUENAME, p);
95     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
96     ABTS_STR_EQUAL(tc, "../test", dstpath);
97 }
98 
merge_dotdot_dotdot_dotdot(abts_case * tc,void * data)99 static void merge_dotdot_dotdot_dotdot(abts_case *tc, void *data)
100 {
101     apr_status_t rv;
102     char *dstpath = NULL;
103 
104     rv = apr_filepath_merge(&dstpath, "",
105                             "../../..", APR_FILEPATH_TRUENAME, p);
106     ABTS_PTR_NOTNULL(tc, dstpath);
107     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
108     ABTS_STR_EQUAL(tc, "../../..", dstpath);
109 
110     rv = apr_filepath_merge(&dstpath, "",
111                             "../../../", APR_FILEPATH_TRUENAME, p);
112     ABTS_PTR_NOTNULL(tc, dstpath);
113     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
114     ABTS_STR_EQUAL(tc, "../../../", dstpath);
115 }
116 
merge_secure(abts_case * tc,void * data)117 static void merge_secure(abts_case *tc, void *data)
118 {
119     apr_status_t rv;
120     char *dstpath = NULL;
121 
122     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../bar/baz", 0, p);
123     ABTS_PTR_NOTNULL(tc, dstpath);
124     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
125     ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar/baz", dstpath);
126 }
127 
merge_notrel(abts_case * tc,void * data)128 static void merge_notrel(abts_case *tc, void *data)
129 {
130     apr_status_t rv;
131     char *dstpath = NULL;
132 
133     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
134                             APR_FILEPATH_NOTRELATIVE, p);
135     ABTS_PTR_NOTNULL(tc, dstpath);
136     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
137     ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
138 }
139 
merge_notrelfail(abts_case * tc,void * data)140 static void merge_notrelfail(abts_case *tc, void *data)
141 {
142     apr_status_t rv;
143     char *dstpath = NULL;
144     char errmsg[256];
145 
146     rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
147                             APR_FILEPATH_NOTRELATIVE, p);
148     apr_strerror(rv, errmsg, sizeof(errmsg));
149 
150     ABTS_PTR_EQUAL(tc, NULL, dstpath);
151     ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
152     ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
153 }
154 
merge_notabsfail(abts_case * tc,void * data)155 static void merge_notabsfail(abts_case *tc, void *data)
156 {
157     apr_status_t rv;
158     char *dstpath = NULL;
159     char errmsg[256];
160 
161     rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
162                             APR_FILEPATH_NOTABSOLUTE, p);
163     apr_strerror(rv, errmsg, sizeof(errmsg));
164 
165     ABTS_PTR_EQUAL(tc, NULL, dstpath);
166     ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABSOLUTE(rv));
167     ABTS_STR_EQUAL(tc, "The given path is absolute", errmsg);
168 }
169 
merge_notabs(abts_case * tc,void * data)170 static void merge_notabs(abts_case *tc, void *data)
171 {
172     apr_status_t rv;
173     char *dstpath = NULL;
174 
175     rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
176                             APR_FILEPATH_NOTABSOLUTE, p);
177 
178     ABTS_PTR_NOTNULL(tc, dstpath);
179     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
180     ABTS_STR_EQUAL(tc, "foo/baz", dstpath);
181 }
182 
183 #if defined (WIN32)
merge_lowercasedrive(abts_case * tc,void * data)184 static void merge_lowercasedrive(abts_case *tc, void *data)
185 {
186   char current_dir[1024];
187   char current_dir_on_C[1024];
188   char *dir_on_c;
189   char *testdir;
190   apr_status_t rv;
191 
192   /* Change the current directory on C: from something like "C:\dir"
193      to something like "c:\dir" to replicate the failing case. */
194   ABTS_PTR_NOTNULL(tc, _getcwd(current_dir, sizeof(current_dir)));
195 
196    /* 3 stands for drive C: */
197   ABTS_PTR_NOTNULL(tc, _getdcwd(3, current_dir_on_C,
198 	                            sizeof(current_dir_on_C)));
199 
200   /* Use the same path, but now with a lower case driveletter */
201   dir_on_c = apr_pstrdup(p, current_dir_on_C);
202   dir_on_c[0] = (char)tolower(dir_on_c[0]);
203 
204   chdir(dir_on_c);
205 
206   /* Now merge a drive relative path with an upper case drive letter. */
207   rv = apr_filepath_merge(&testdir, NULL, "C:hi",
208                           APR_FILEPATH_NOTRELATIVE, p);
209 
210   /* Change back to original directory for next tests */
211   chdir("C:\\"); /* Switch to upper case */
212   chdir(current_dir_on_C); /* Switch cwd on C: */
213   chdir(current_dir); /* Switch back to original cwd */
214 
215   ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
216 }
217 
merge_shortname(abts_case * tc,void * data)218 static void merge_shortname(abts_case *tc, void *data)
219 {
220   apr_status_t rv;
221   char *long_path;
222   char short_path[MAX_PATH+1];
223   DWORD short_len;
224   char *result_path;
225 
226   /* 'A b.c' is not a valid short path, so will have multiple representations
227      when short path name generation is enabled... but its 'short' path will
228      most likely be longer than the long path */
229   rv = apr_dir_make_recursive("C:/data/short/A b.c",
230                               APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
231   ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
232 
233   rv = apr_filepath_merge(&long_path, NULL, "C:/data/short/A b.c",
234                           APR_FILEPATH_NOTRELATIVE, p);
235   ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
236 
237   short_len = GetShortPathName(long_path, short_path, sizeof(short_path));
238   if (short_len > MAX_PATH)
239     return; /* Unable to test. Impossible shortname */
240 
241   if (! strcmp(long_path, short_path))
242     return; /* Unable to test. 8dot3name option is probably not enabled */
243 
244   rv = apr_filepath_merge(&result_path, "", short_path, APR_FILEPATH_TRUENAME,
245                           p);
246   ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
247 
248   ABTS_STR_EQUAL(tc, long_path, result_path);
249 }
250 #endif
251 
root_absolute(abts_case * tc,void * data)252 static void root_absolute(abts_case *tc, void *data)
253 {
254     apr_status_t rv;
255     const char *root = NULL;
256     const char *path = ABS_ROOT"foo/bar";
257 
258     rv = apr_filepath_root(&root, &path, 0, p);
259 
260     ABTS_PTR_NOTNULL(tc, root);
261     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
262     ABTS_STR_EQUAL(tc, ABS_ROOT, root);
263 }
264 
root_relative(abts_case * tc,void * data)265 static void root_relative(abts_case *tc, void *data)
266 {
267     apr_status_t rv;
268     const char *root = NULL;
269     const char *path = "foo/bar";
270     char errmsg[256];
271 
272     rv = apr_filepath_root(&root, &path, 0, p);
273     apr_strerror(rv, errmsg, sizeof(errmsg));
274 
275     ABTS_PTR_EQUAL(tc, NULL, root);
276     ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
277     ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
278 }
279 
root_from_slash(abts_case * tc,void * data)280 static void root_from_slash(abts_case *tc, void *data)
281 {
282     apr_status_t rv;
283     const char *root = NULL;
284     const char *path = "//";
285 
286     rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
287 
288 #if defined(WIN32) || defined(OS2)
289     ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
290     ABTS_STR_EQUAL(tc, "//", root);
291 #else
292     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
293     ABTS_STR_EQUAL(tc, "/", root);
294 #endif
295     ABTS_STR_EQUAL(tc, "", path);
296 }
297 
root_from_cwd_and_back(abts_case * tc,void * data)298 static void root_from_cwd_and_back(abts_case *tc, void *data)
299 {
300     apr_status_t rv;
301     const char *root = NULL;
302     const char *path = "//";
303     char *origpath;
304     char *testpath;
305 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
306     int hadfailed;
307 #endif
308 
309     ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_filepath_get(&origpath, 0, p));
310     path = origpath;
311     rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
312 
313 #if defined(WIN32) || defined(OS2)
314     hadfailed = tc->failed;
315     /* It appears some mingw/cygwin and more modern builds can return
316      * a lowercase drive designation, but we canonicalize to uppercase
317      */
318     ABTS_INT_EQUAL(tc, toupper(origpath[0]), root[0]);
319     ABTS_INT_EQUAL(tc, ':', root[1]);
320     ABTS_INT_EQUAL(tc, '/', root[2]);
321     ABTS_INT_EQUAL(tc, 0, root[3]);
322     ABTS_STR_EQUAL(tc, origpath + 3, path);
323 #elif defined(NETWARE)
324     ABTS_INT_EQUAL(tc, origpath[0], root[0]);
325     {
326     char *pt = strchr(root, ':');
327     ABTS_PTR_NOTNULL(tc, pt);
328     ABTS_INT_EQUAL(tc, ':', pt[0]);
329     ABTS_INT_EQUAL(tc, '/', pt[1]);
330     ABTS_INT_EQUAL(tc, 0, pt[2]);
331     pt = strchr(origpath, ':');
332     ABTS_PTR_NOTNULL(tc, pt);
333     ABTS_STR_EQUAL(tc, (pt+2), path);
334     }
335 #else
336     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
337     ABTS_STR_EQUAL(tc, "/", root);
338     ABTS_STR_EQUAL(tc, origpath + 1, path);
339 #endif
340 
341     rv = apr_filepath_merge(&testpath, root, path,
342                             APR_FILEPATH_TRUENAME
343                           | APR_FILEPATH_NOTABOVEROOT
344                           | APR_FILEPATH_NOTRELATIVE, p);
345     ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
346 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
347     hadfailed = tc->failed;
348 #endif
349     /* The API doesn't promise equality!!!
350      * apr_filepath_get never promised a canonical filepath.
351      * We'll emit noise under verbose so the user is aware,
352      * but translate this back to success.
353      */
354     ABTS_STR_EQUAL(tc, origpath, testpath);
355 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
356     if (!hadfailed) tc->failed = 0;
357 #endif
358 }
359 
360 
testnames(abts_suite * suite)361 abts_suite *testnames(abts_suite *suite)
362 {
363     suite = ADD_SUITE(suite)
364 
365     abts_run_test(suite, merge_aboveroot, NULL);
366     abts_run_test(suite, merge_belowroot, NULL);
367     abts_run_test(suite, merge_noflag, NULL);
368     abts_run_test(suite, merge_dotdot, NULL);
369     abts_run_test(suite, merge_secure, NULL);
370     abts_run_test(suite, merge_notrel, NULL);
371     abts_run_test(suite, merge_notrelfail, NULL);
372     abts_run_test(suite, merge_notabs, NULL);
373     abts_run_test(suite, merge_notabsfail, NULL);
374     abts_run_test(suite, merge_dotdot_dotdot_dotdot, NULL);
375 #if defined(WIN32)
376     abts_run_test(suite, merge_lowercasedrive, NULL);
377     abts_run_test(suite, merge_shortname, NULL);
378 #endif
379 
380     abts_run_test(suite, root_absolute, NULL);
381     abts_run_test(suite, root_relative, NULL);
382     abts_run_test(suite, root_from_slash, NULL);
383     abts_run_test(suite, root_from_cwd_and_back, NULL);
384 
385     return suite;
386 }
387 
388