xref: /aosp_15_r20/external/cronet/third_party/apache-portable-runtime/src/test/testlfs.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_file_io.h"
18 #include "apr_file_info.h"
19 #include "apr_errno.h"
20 #include "apr_general.h"
21 #include "apr_poll.h"
22 #include "apr_strings.h"
23 #include "apr_lib.h"
24 #include "apr_mmap.h"
25 #include "testutil.h"
26 
27 /* TODO: in 1.3.0 this becomes APR_HAS_SPARSE_FILES, HOWEVER we will
28  * still need to test csize before proceeding, because having sparse
29  * file support in the OS/APR does not mean this volume supports it!
30  */
31 #if APR_HAS_LARGE_FILES
32 
33 /* Tests which create an 8GB sparse file and then check it can be used
34  * as normal. */
35 
36 static apr_off_t oneMB = APR_INT64_C(2) << 19;
37 static apr_off_t eightGB = APR_INT64_C(2) << 32;
38 
39 static int madefile = 0;
40 
41 #define PRECOND if (!madefile) { ABTS_NOT_IMPL(tc, "Large file tests not enabled"); return; }
42 
43 #define TESTDIR "lfstests"
44 #define TESTFILE "large.bin"
45 #define TESTFN "lfstests/large.bin"
46 
test_open(abts_case * tc,void * data)47 static void test_open(abts_case *tc, void *data)
48 {
49     apr_file_t *f;
50     apr_finfo_t testsize;
51     apr_status_t rv;
52 
53     rv = apr_dir_make(TESTDIR, APR_OS_DEFAULT, p);
54     if (rv && !APR_STATUS_IS_EEXIST(rv)) {
55         APR_ASSERT_SUCCESS(tc, "make test directory", rv);
56     }
57 
58     /* First attempt a 1MB sparse file so we don't tax the poor test box */
59     rv = apr_file_open(&f, TESTFN, APR_FOPEN_CREATE | APR_FOPEN_WRITE
60                                  | APR_FOPEN_TRUNCATE | APR_FOPEN_SPARSE,
61                        APR_OS_DEFAULT, p);
62 
63     APR_ASSERT_SUCCESS(tc, "open file", rv);
64 
65     APR_ASSERT_SUCCESS(tc, "Truncate to 1MB", rv = apr_file_trunc(f, oneMB+1));
66 
67     if (rv == APR_SUCCESS) {
68         rv = apr_file_info_get(&testsize, APR_FINFO_CSIZE, f);
69     }
70 
71     /* give up if we can't determine the allocation size of the file,
72      * or if it's not an obviously small allocation but the allocation
73      * unit doesn't appear insanely large - on most platforms, it's just
74      * zero physical bytes at this point.
75      */
76     if (rv != APR_SUCCESS || (testsize.csize > oneMB
77                               && testsize.csize < oneMB * 2)) {
78         ABTS_NOT_IMPL(tc, "Creation of large file (apparently not sparse)");
79 
80         madefile = 0;
81     }
82     else {
83         /* Proceed with our 8GB sparse file now */
84         rv = apr_file_trunc(f, eightGB);
85 
86         /* 8GB may pass rlimits or filesystem limits */
87 
88         if (APR_STATUS_IS_EINVAL(rv)
89 #ifdef EFBIG
90             || rv == EFBIG
91 #endif
92             ) {
93             ABTS_NOT_IMPL(tc, "Creation of large file (rlimit, quota or fs)");
94         }
95         else {
96             APR_ASSERT_SUCCESS(tc, "truncate file to 8gb", rv);
97         }
98         madefile = rv == APR_SUCCESS;
99     }
100 
101     APR_ASSERT_SUCCESS(tc, "close large file", apr_file_close(f));
102 
103     if (!madefile) {
104         APR_ASSERT_SUCCESS(tc, "remove large file", apr_file_remove(TESTFN, p));
105     }
106 }
107 
test_reopen(abts_case * tc,void * data)108 static void test_reopen(abts_case *tc, void *data)
109 {
110     apr_file_t *fh;
111     apr_finfo_t finfo;
112     apr_status_t rv;
113 
114     PRECOND;
115 
116     rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_READ,
117                        APR_OS_DEFAULT, p);
118     APR_ASSERT_SUCCESS(tc, "re-open 8GB file", rv);
119 
120     APR_ASSERT_SUCCESS(tc, "file_info_get failed",
121                        apr_file_info_get(&finfo, APR_FINFO_NORM, fh));
122 
123     ABTS_ASSERT(tc, "file_info_get gave incorrect size",
124              finfo.size == eightGB);
125 
126     APR_ASSERT_SUCCESS(tc, "re-close large file", apr_file_close(fh));
127 }
128 
test_stat(abts_case * tc,void * data)129 static void test_stat(abts_case *tc, void *data)
130 {
131     apr_finfo_t finfo;
132 
133     PRECOND;
134 
135     APR_ASSERT_SUCCESS(tc, "stat large file",
136                        apr_stat(&finfo, TESTFN, APR_FINFO_NORM, p));
137 
138     ABTS_ASSERT(tc, "stat gave incorrect size", finfo.size == eightGB);
139 }
140 
test_readdir(abts_case * tc,void * data)141 static void test_readdir(abts_case *tc, void *data)
142 {
143     apr_dir_t *dh;
144     apr_status_t rv;
145 
146     PRECOND;
147 
148     APR_ASSERT_SUCCESS(tc, "open test directory",
149                        apr_dir_open(&dh, TESTDIR, p));
150 
151     do {
152         apr_finfo_t finfo;
153 
154         rv = apr_dir_read(&finfo, APR_FINFO_MIN, dh);
155 
156         if (rv == APR_SUCCESS && strcmp(finfo.name, TESTFILE) == 0) {
157             ABTS_ASSERT(tc, "apr_dir_read gave incorrect size for large file",
158                      finfo.size == eightGB);
159         }
160 
161     } while (rv == APR_SUCCESS);
162 
163     if (!APR_STATUS_IS_ENOENT(rv)) {
164         APR_ASSERT_SUCCESS(tc, "apr_dir_read failed", rv);
165     }
166 
167     APR_ASSERT_SUCCESS(tc, "close test directory",
168                        apr_dir_close(dh));
169 }
170 
171 #define TESTSTR "Hello, world."
172 
test_append(abts_case * tc,void * data)173 static void test_append(abts_case *tc, void *data)
174 {
175     apr_file_t *fh;
176     apr_finfo_t finfo;
177     apr_status_t rv;
178 
179     PRECOND;
180 
181     rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE
182                                   | APR_FOPEN_APPEND,
183                        APR_OS_DEFAULT, p);
184     APR_ASSERT_SUCCESS(tc, "open 8GB file for append", rv);
185 
186     APR_ASSERT_SUCCESS(tc, "append to 8GB file",
187                        apr_file_write_full(fh, TESTSTR, strlen(TESTSTR), NULL));
188 
189     APR_ASSERT_SUCCESS(tc, "file_info_get failed",
190                        apr_file_info_get(&finfo, APR_FINFO_NORM, fh));
191 
192     ABTS_ASSERT(tc, "file_info_get gave incorrect size",
193              finfo.size == eightGB + strlen(TESTSTR));
194 
195     APR_ASSERT_SUCCESS(tc, "close 8GB file", apr_file_close(fh));
196 }
197 
test_seek(abts_case * tc,void * data)198 static void test_seek(abts_case *tc, void *data)
199 {
200     apr_file_t *fh;
201     apr_off_t pos;
202     apr_status_t rv;
203 
204     PRECOND;
205 
206     rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE,
207                        APR_OS_DEFAULT, p);
208     APR_ASSERT_SUCCESS(tc, "open 8GB file for writing", rv);
209 
210     pos = 0;
211     APR_ASSERT_SUCCESS(tc, "relative seek to end",
212                        apr_file_seek(fh, APR_END, &pos));
213     ABTS_ASSERT(tc, "seek to END gave 8GB", pos == eightGB);
214 
215     pos = eightGB;
216     APR_ASSERT_SUCCESS(tc, "seek to 8GB", apr_file_seek(fh, APR_SET, &pos));
217     ABTS_ASSERT(tc, "seek gave 8GB offset", pos == eightGB);
218 
219     pos = 0;
220     APR_ASSERT_SUCCESS(tc, "relative seek to 0", apr_file_seek(fh, APR_CUR, &pos));
221     ABTS_ASSERT(tc, "relative seek gave 8GB offset", pos == eightGB);
222 
223     apr_file_close(fh);
224 }
225 
test_write(abts_case * tc,void * data)226 static void test_write(abts_case *tc, void *data)
227 {
228     apr_file_t *fh;
229     apr_off_t pos = eightGB - 4;
230     apr_status_t rv;
231 
232     PRECOND;
233 
234     rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE,
235                        APR_OS_DEFAULT, p);
236     APR_ASSERT_SUCCESS(tc, "re-open 8GB file", rv);
237 
238     APR_ASSERT_SUCCESS(tc, "seek to 8GB - 4",
239                        apr_file_seek(fh, APR_SET, &pos));
240     ABTS_ASSERT(tc, "seek gave 8GB-4 offset", pos == eightGB - 4);
241 
242     APR_ASSERT_SUCCESS(tc, "write magic string to 8GB-4",
243                        apr_file_write_full(fh, "FISH", 4, NULL));
244 
245     APR_ASSERT_SUCCESS(tc, "close 8GB file", apr_file_close(fh));
246 }
247 
248 
249 #if APR_HAS_MMAP
test_mmap(abts_case * tc,void * data)250 static void test_mmap(abts_case *tc, void *data)
251 {
252     apr_mmap_t *map;
253     apr_file_t *fh;
254     apr_size_t len = 65536; /* hopefully a multiple of the page size */
255     apr_off_t off = eightGB - len;
256     apr_status_t rv;
257     void *ptr;
258 
259     PRECOND;
260 
261     rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_READ,
262                        APR_OS_DEFAULT, p);
263     APR_ASSERT_SUCCESS(tc, "open 8gb file for mmap", rv);
264 
265     APR_ASSERT_SUCCESS(tc, "mmap 8GB file",
266                        apr_mmap_create(&map, fh, off, len, APR_MMAP_READ, p));
267 
268     APR_ASSERT_SUCCESS(tc, "close file", apr_file_close(fh));
269 
270     ABTS_ASSERT(tc, "mapped a 64K block", map->size == len);
271 
272     APR_ASSERT_SUCCESS(tc, "get pointer into mmaped region",
273                        apr_mmap_offset(&ptr, map, len - 4));
274     ABTS_ASSERT(tc, "pointer was not NULL", ptr != NULL);
275 
276     ABTS_ASSERT(tc, "found the magic string", memcmp(ptr, "FISH", 4) == 0);
277 
278     APR_ASSERT_SUCCESS(tc, "delete mmap handle", apr_mmap_delete(map));
279 }
280 #endif /* APR_HAS_MMAP */
281 
test_format(abts_case * tc,void * data)282 static void test_format(abts_case *tc, void *data)
283 {
284     apr_off_t off;
285 
286     PRECOND;
287 
288     off = apr_atoi64(apr_off_t_toa(p, eightGB));
289 
290     ABTS_ASSERT(tc, "apr_atoi64 parsed apr_off_t_toa result incorrectly",
291              off == eightGB);
292 }
293 
294 #define TESTBUFFN TESTDIR "/buffer.bin"
295 
test_buffered(abts_case * tc,void * data)296 static void test_buffered(abts_case *tc, void *data)
297 {
298     apr_off_t off;
299     apr_file_t *f;
300     apr_status_t rv;
301 
302     PRECOND;
303 
304     rv = apr_file_open(&f, TESTBUFFN, APR_FOPEN_CREATE | APR_FOPEN_WRITE
305                                     | APR_FOPEN_TRUNCATE | APR_FOPEN_BUFFERED
306                                     | APR_FOPEN_SPARSE,
307                        APR_OS_DEFAULT, p);
308     APR_ASSERT_SUCCESS(tc, "open buffered file", rv);
309 
310     APR_ASSERT_SUCCESS(tc, "truncate to 8GB",
311                        apr_file_trunc(f, eightGB));
312 
313     off = eightGB;
314     APR_ASSERT_SUCCESS(tc, "seek to 8GB",
315                        apr_file_seek(f, APR_SET, &off));
316     ABTS_ASSERT(tc, "returned seek position still 8GB",
317                 off == eightGB);
318 
319     off = 0;
320     APR_ASSERT_SUCCESS(tc, "relative seek",
321                        apr_file_seek(f, APR_CUR, &off));
322     ABTS_ASSERT(tc, "relative seek still at 8GB",
323                 off == eightGB);
324 
325     off = 0;
326     APR_ASSERT_SUCCESS(tc, "end-relative seek",
327                        apr_file_seek(f, APR_END, &off));
328     ABTS_ASSERT(tc, "end-relative seek still at 8GB",
329                 off == eightGB);
330 
331     off = -eightGB;
332     APR_ASSERT_SUCCESS(tc, "relative seek to beginning",
333                        apr_file_seek(f, APR_CUR, &off));
334     ABTS_ASSERT(tc, "seek to beginning got zero",
335                 off == 0);
336 
337     APR_ASSERT_SUCCESS(tc, "close buffered file",
338                        apr_file_close(f));
339 }
340 
341 #else /* !APR_HAS_LARGE_FILES */
342 
test_nolfs(abts_case * tc,void * data)343 static void test_nolfs(abts_case *tc, void *data)
344 {
345     ABTS_NOT_IMPL(tc, "Large Files not supported");
346 }
347 
348 #endif
349 
testlfs(abts_suite * suite)350 abts_suite *testlfs(abts_suite *suite)
351 {
352     suite = ADD_SUITE(suite)
353 
354 #if APR_HAS_LARGE_FILES
355     abts_run_test(suite, test_open, NULL);
356     abts_run_test(suite, test_reopen, NULL);
357     abts_run_test(suite, test_stat, NULL);
358     abts_run_test(suite, test_readdir, NULL);
359     abts_run_test(suite, test_seek, NULL);
360     abts_run_test(suite, test_append, NULL);
361     abts_run_test(suite, test_write, NULL);
362 #if APR_HAS_MMAP
363     abts_run_test(suite, test_mmap, NULL);
364 #endif
365     abts_run_test(suite, test_format, NULL);
366     abts_run_test(suite, test_buffered, NULL);
367 #else
368     abts_run_test(suite, test_nolfs, NULL);
369 #endif
370 
371     return suite;
372 }
373 
374