1 /* io-test.c --- tests for some i/o functions
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22 
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <stdio.h>
27 
28 #include <apr.h>
29 #include <apr_version.h>
30 
31 #include "svn_pools.h"
32 #include "svn_string.h"
33 #include "svn_io.h"
34 #include "private/svn_skel.h"
35 #include "private/svn_dep_compat.h"
36 #include "private/svn_io_private.h"
37 
38 #include "../svn_test.h"
39 #include "../svn_test_fs.h"
40 
41 
42 /* Helpers to create the test data directory. */
43 
44 #define TEST_DIR_PREFIX "io-test-temp"
45 
46 /* The definition for the test data files. */
47 struct test_file_definition_t
48   {
49     /* The name of the test data file. */
50     const char* const name;
51 
52     /* The string needs to contain up to 5 bytes, they
53      * are interpreded as:
54      * - first byte
55      * - filler between first and medium byte
56      * - medium byte (the byte in the middle of the file)
57      * - filler between medium and last byte
58      * - last byte.
59      * If the string is shorter than the file length,
60      * the test will fail. */
61     const char* const data;
62 
63     /* The size of the file actually to create. */
64     const apr_off_t size;
65 
66     /* The created path of the file. Will be filled in
67      * by create_test_file() */
68     char* created_path;
69   };
70 
71 static struct test_file_definition_t test_file_definitions_template[] =
72   {
73     {"empty",                 "",      0},
74     {"single_a",              "a",     1},
75     {"single_b",              "b",     1},
76     {"hundred_a",             "aaaaa", 100},
77     {"hundred_b",             "bbbbb", 100},
78     {"hundred_b1",            "baaaa", 100},
79     {"hundred_b2",            "abaaa", 100},
80     {"hundred_b3",            "aabaa", 100},
81     {"hundred_b4",            "aaaba", 100},
82     {"hundred_b5",            "aaaab", 100},
83     {"chunk_minus_one_a",     "aaaaa", SVN__STREAM_CHUNK_SIZE - 1},
84     {"chunk_minus_one_b1",    "baaaa", SVN__STREAM_CHUNK_SIZE - 1},
85     {"chunk_minus_one_b2",    "abaaa", SVN__STREAM_CHUNK_SIZE - 1},
86     {"chunk_minus_one_b3",    "aabaa", SVN__STREAM_CHUNK_SIZE - 1},
87     {"chunk_minus_one_b4",    "aaaba", SVN__STREAM_CHUNK_SIZE - 1},
88     {"chunk_minus_one_b5",    "aaaab", SVN__STREAM_CHUNK_SIZE - 1},
89     {"chunk_a",               "aaaaa", SVN__STREAM_CHUNK_SIZE},
90     {"chunk_b1",              "baaaa", SVN__STREAM_CHUNK_SIZE},
91     {"chunk_b2",              "abaaa", SVN__STREAM_CHUNK_SIZE},
92     {"chunk_b3",              "aabaa", SVN__STREAM_CHUNK_SIZE},
93     {"chunk_b4",              "aaaba", SVN__STREAM_CHUNK_SIZE},
94     {"chunk_b5",              "aaaab", SVN__STREAM_CHUNK_SIZE},
95     {"chunk_plus_one_a",      "aaaaa", SVN__STREAM_CHUNK_SIZE + 1},
96     {"chunk_plus_one_b1",     "baaaa", SVN__STREAM_CHUNK_SIZE + 1},
97     {"chunk_plus_one_b2",     "abaaa", SVN__STREAM_CHUNK_SIZE + 1},
98     {"chunk_plus_one_b3",     "aabaa", SVN__STREAM_CHUNK_SIZE + 1},
99     {"chunk_plus_one_b4",     "aaaba", SVN__STREAM_CHUNK_SIZE + 1},
100     {"chunk_plus_one_b5",     "aaaab", SVN__STREAM_CHUNK_SIZE + 1},
101     {"twochunk_minus_one_a",  "aaaaa", SVN__STREAM_CHUNK_SIZE*2 - 1},
102     {"twochunk_minus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2 - 1},
103     {"twochunk_minus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2 - 1},
104     {"twochunk_minus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2 - 1},
105     {"twochunk_minus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2 - 1},
106     {"twochunk_minus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2 - 1},
107     {"twochunk_a",            "aaaaa", SVN__STREAM_CHUNK_SIZE*2},
108     {"twochunk_b1",           "baaaa", SVN__STREAM_CHUNK_SIZE*2},
109     {"twochunk_b2",           "abaaa", SVN__STREAM_CHUNK_SIZE*2},
110     {"twochunk_b3",           "aabaa", SVN__STREAM_CHUNK_SIZE*2},
111     {"twochunk_b4",           "aaaba", SVN__STREAM_CHUNK_SIZE*2},
112     {"twochunk_b5",           "aaaab", SVN__STREAM_CHUNK_SIZE*2},
113     {"twochunk_plus_one_a",   "aaaaa", SVN__STREAM_CHUNK_SIZE*2 + 1},
114     {"twochunk_plus_one_b1",  "baaaa", SVN__STREAM_CHUNK_SIZE*2 + 1},
115     {"twochunk_plus_one_b2",  "abaaa", SVN__STREAM_CHUNK_SIZE*2 + 1},
116     {"twochunk_plus_one_b3",  "aabaa", SVN__STREAM_CHUNK_SIZE*2 + 1},
117     {"twochunk_plus_one_b4",  "aaaba", SVN__STREAM_CHUNK_SIZE*2 + 1},
118     {"twochunk_plus_one_b5",  "aaaab", SVN__STREAM_CHUNK_SIZE*2 + 1},
119     {0},
120   };
121 
122 /* Function to prepare a single test file */
123 
124 static svn_error_t *
create_test_file(struct test_file_definition_t * definition,const char * test_dir,apr_pool_t * pool,apr_pool_t * scratch_pool)125 create_test_file(struct test_file_definition_t* definition,
126                  const char *test_dir,
127                  apr_pool_t *pool,
128                  apr_pool_t *scratch_pool)
129 {
130   apr_status_t status = 0;
131   apr_file_t *file_h;
132   apr_off_t midpos = definition->size / 2;
133   svn_error_t *err = NULL;
134   int i;
135 
136   if (definition->size < 5)
137     SVN_ERR_ASSERT(strlen(definition->data) >= (apr_size_t)definition->size);
138   else
139     SVN_ERR_ASSERT(strlen(definition->data) >= 5);
140 
141 
142   definition->created_path = svn_dirent_join(test_dir,
143                                              definition->name,
144                                              pool);
145 
146   SVN_ERR(svn_io_file_open(&file_h,
147                            definition->created_path,
148                            (APR_WRITE | APR_CREATE | APR_EXCL | APR_BUFFERED),
149                            APR_OS_DEFAULT,
150                            scratch_pool));
151 
152   for (i=1; i <= definition->size; i += 1)
153     {
154       char c;
155       if (i == 1)
156         c = definition->data[0];
157       else if (i < midpos)
158         c = definition->data[1];
159       else if (i == midpos)
160         c = definition->data[2];
161       else if (i < definition->size)
162         c = definition->data[3];
163       else
164         c = definition->data[4];
165 
166       status = apr_file_putc(c, file_h);
167 
168       if (status)
169         break;
170     }
171 
172   if (status)
173     err = svn_error_wrap_apr(status, "Can't write to file '%s'",
174                               definition->name);
175 
176   return svn_error_compose_create(err,
177                         svn_io_file_close(file_h, scratch_pool));
178 }
179 
180 /* Function to prepare the whole set of on-disk files to be compared. */
181 static svn_error_t *
create_comparison_candidates(struct test_file_definition_t ** definitions,const char * testname,apr_pool_t * pool)182 create_comparison_candidates(struct test_file_definition_t **definitions,
183                              const char *testname,
184                              apr_pool_t *pool)
185 {
186   apr_pool_t *iterpool = svn_pool_create(pool);
187   struct test_file_definition_t *candidate;
188   svn_error_t *err = SVN_NO_ERROR;
189   apr_size_t count = 0;
190   const char *test_dir;
191 
192   SVN_ERR(svn_test_make_sandbox_dir(&test_dir, testname, pool));
193 
194   for (candidate = test_file_definitions_template;
195        candidate->name != NULL;
196        candidate += 1)
197     count++;
198 
199   *definitions = apr_pmemdup(pool, test_file_definitions_template,
200                              (count + 1) * sizeof(**definitions));
201   for (candidate = *definitions; candidate->name != NULL; candidate += 1)
202     {
203       svn_pool_clear(iterpool);
204       err = create_test_file(candidate, test_dir, pool, iterpool);
205       if (err)
206         break;
207     }
208 
209   svn_pool_destroy(iterpool);
210 
211   return err;
212 }
213 
214 /* Create an on-disk tree with optional read-only attributes on some
215    files and/or directories. */
216 static svn_error_t *
create_dir_tree(const char ** dir_path,const char * testname,svn_boolean_t dir_readonly,svn_boolean_t file_readonly,apr_pool_t * pool)217 create_dir_tree(const char **dir_path,
218                 const char *testname,
219                 svn_boolean_t dir_readonly,
220                 svn_boolean_t file_readonly,
221                 apr_pool_t *pool)
222 {
223   const char *test_dir_path;
224   const char *sub_dir_path;
225   const char *file_path;
226 
227   SVN_ERR(svn_test_make_sandbox_dir(&test_dir_path, testname, pool));
228 
229   sub_dir_path = svn_dirent_join(test_dir_path, "dir", pool);
230   SVN_ERR(svn_io_dir_make(sub_dir_path, APR_OS_DEFAULT, pool));
231 
232   file_path = svn_dirent_join(sub_dir_path, "file", pool);
233   SVN_ERR(svn_io_file_create_empty(file_path, pool));
234 
235   if (file_readonly)
236     SVN_ERR(svn_io_set_file_read_only(file_path, FALSE, pool));
237 
238   if (dir_readonly)
239     SVN_ERR(svn_io_set_file_read_only(sub_dir_path, FALSE, pool));
240 
241   *dir_path = sub_dir_path;
242   return SVN_NO_ERROR;
243 }
244 
245 
246 
247 /* Functions to check the 2-way and 3-way file comparison functions.  */
248 
249 /* Test 2-way file size checking */
250 static svn_error_t *
test_two_file_size_comparison(apr_pool_t * scratch_pool)251 test_two_file_size_comparison(apr_pool_t *scratch_pool)
252 {
253   struct test_file_definition_t *inner, *outer;
254   svn_boolean_t actual;
255   svn_boolean_t expected;
256   svn_error_t *err = SVN_NO_ERROR;
257   svn_error_t *cmp_err;
258   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
259   struct test_file_definition_t *test_file_definitions;
260 
261   SVN_ERR(create_comparison_candidates(&test_file_definitions,
262                                        "test_two_file_size_comparison",
263                                        scratch_pool));
264 
265   for (outer = test_file_definitions; outer->name != NULL; outer += 1)
266     {
267 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
268       inner = test_file_definitions;
269 #else
270       inner = outer;
271 #endif
272       for (; inner->name != NULL; inner += 1)
273         {
274           svn_pool_clear(iterpool);
275 
276           expected = inner->size != outer->size;
277 
278           cmp_err = svn_io_filesizes_different_p(&actual,
279                                                  inner->created_path,
280                                                  outer->created_path,
281                                                  iterpool);
282 
283           if (cmp_err)
284             {
285               err = svn_error_compose_create(err, cmp_err);
286             }
287           else if (expected != actual)
288             {
289               err = svn_error_compose_create(err,
290                   svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
291                                    "size comparison problem: '%s' and '%s'",
292                                    inner->created_path,
293                                    outer->created_path));
294             }
295         }
296     }
297 
298   svn_pool_destroy(iterpool);
299   return err;
300 }
301 
302 
303 /* Test 2-way file content checking */
304 static svn_error_t *
test_two_file_content_comparison(apr_pool_t * scratch_pool)305 test_two_file_content_comparison(apr_pool_t *scratch_pool)
306 {
307   struct test_file_definition_t *inner, *outer;
308   svn_boolean_t actual;
309   svn_boolean_t expected;
310   svn_error_t *err = SVN_NO_ERROR;
311   svn_error_t *cmp_err;
312   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
313   struct test_file_definition_t *test_file_definitions;
314 
315   SVN_ERR(create_comparison_candidates(&test_file_definitions,
316                                        "test_two_file_content_comparison",
317                                        scratch_pool));
318 
319   for (outer = test_file_definitions; outer->name != NULL; outer += 1)
320     {
321 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
322       inner = test_file_definitions;
323 #else
324       inner = outer;
325 #endif
326       for (; inner->name != NULL; inner += 1)
327         {
328           svn_pool_clear(iterpool);
329 
330           expected = inner->size == outer->size
331             && strcmp(inner->data, outer->data) == 0;
332 
333           cmp_err = svn_io_files_contents_same_p(&actual,
334                                                  inner->created_path,
335                                                  outer->created_path,
336                                                  iterpool);
337 
338           if (cmp_err)
339             {
340               err = svn_error_compose_create(err, cmp_err);
341             }
342           else
343             {
344               if (expected != actual)
345                   err = svn_error_compose_create(err,
346                       svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
347                                  "content comparison problem: '%s' and '%s'",
348                                  inner->created_path,
349                                  outer->created_path));
350             }
351         }
352     }
353 
354   svn_pool_destroy(iterpool);
355   return err;
356 }
357 
358 
359 /* Test 3-way file size checking */
360 static svn_error_t *
test_three_file_size_comparison(apr_pool_t * scratch_pool)361 test_three_file_size_comparison(apr_pool_t *scratch_pool)
362 {
363   struct test_file_definition_t *inner, *middle, *outer;
364   svn_boolean_t actual12, actual23, actual13;
365   svn_boolean_t expected12, expected23, expected13;
366   svn_error_t *err = SVN_NO_ERROR;
367   svn_error_t *cmp_err;
368   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
369   struct test_file_definition_t *test_file_definitions;
370 
371   SVN_ERR(create_comparison_candidates(&test_file_definitions,
372                                        "test_three_file_size_comparison",
373                                        scratch_pool));
374 
375   for (outer = test_file_definitions; outer->name != NULL; outer += 1)
376     {
377 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
378       middle = test_file_definitions;
379 #else
380       middle = outer;
381 #endif
382       for (; middle->name != NULL; middle += 1)
383         {
384 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
385           inner = test_file_definitions;
386 #else
387           inner = middle;
388 #endif
389           for (; inner->name != NULL; inner += 1)
390             {
391               svn_pool_clear(iterpool);
392 
393               expected12 = inner->size != middle->size;
394               expected23 = middle->size != outer->size;
395               expected13 = inner->size != outer->size;
396 
397               cmp_err = svn_io_filesizes_three_different_p(&actual12,
398                                 &actual23,
399                                 &actual13,
400                                 inner->created_path,
401                                 middle->created_path,
402                                 outer->created_path,
403                                 iterpool);
404 
405               if (cmp_err)
406                 {
407                   err = svn_error_compose_create(err, cmp_err);
408                 }
409               else
410                 {
411                   if (expected12 != actual12)
412                       err = svn_error_compose_create(err,
413                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
414                                      "size comparison problem: '%s' and '%s'",
415                                      inner->created_path,
416                                      middle->created_path));
417 
418                   if (expected23 != actual23)
419                       err = svn_error_compose_create(err,
420                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
421                                      "size comparison problem: '%s' and '%s'",
422                                      middle->created_path,
423                                      outer->created_path));
424 
425                   if (expected13 != actual13)
426                       err = svn_error_compose_create(err,
427                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
428                                      "size comparison problem: '%s' and '%s'",
429                                      inner->created_path,
430                                      outer->created_path));
431                 }
432             }
433         }
434     }
435 
436   svn_pool_destroy(iterpool);
437 
438   return err;
439 }
440 
441 
442 /* Test 3-way file content checking */
443 static svn_error_t *
test_three_file_content_comparison(apr_pool_t * scratch_pool)444 test_three_file_content_comparison(apr_pool_t *scratch_pool)
445 {
446   struct test_file_definition_t *inner, *middle, *outer;
447   svn_boolean_t actual12, actual23, actual13;
448   svn_boolean_t expected12, expected23, expected13;
449   svn_error_t *err = SVN_NO_ERROR;
450   svn_error_t *cmp_err;
451   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
452   struct test_file_definition_t *test_file_definitions;
453 
454   SVN_ERR(create_comparison_candidates(&test_file_definitions,
455                                        "test_three_file_content_comparison",
456                                        scratch_pool));
457 
458   for (outer = test_file_definitions; outer->name != NULL; outer += 1)
459     {
460 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
461       middle = test_file_definitions;
462 #else
463       middle = outer;
464 #endif
465       for (; middle->name != NULL; middle += 1)
466         {
467 #ifdef SVN_IO_TEST_ALL_PERMUTATIONS
468           inner = test_file_definitions;
469 #else
470           inner = middle;
471 #endif
472           for (; inner->name != NULL; inner += 1)
473             {
474               svn_pool_clear(iterpool);
475 
476               expected12 = outer->size == middle->size
477                 && strcmp(outer->data, middle->data) == 0;
478               expected23 = middle->size == inner->size
479                 && strcmp(middle->data, inner->data) == 0;
480               expected13 = outer->size == inner->size
481                 && strcmp(outer->data, inner->data) == 0;
482 
483               cmp_err = svn_io_files_contents_three_same_p(&actual12,
484                                 &actual23,
485                                 &actual13,
486                                 outer->created_path,
487                                 middle->created_path,
488                                 inner->created_path,
489                                 iterpool);
490 
491               if (cmp_err)
492                 {
493                   err = svn_error_compose_create(err, cmp_err);
494                 }
495               else
496                 {
497                   if (expected12 != actual12)
498                       err = svn_error_compose_create(err,
499                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
500                                      "size comparison problem: '%s' and '%s'",
501                                      inner->created_path,
502                                      middle->created_path));
503 
504                   if (expected23 != actual23)
505                       err = svn_error_compose_create(err,
506                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
507                                      "size comparison problem: '%s' and '%s'",
508                                      middle->created_path,
509                                      outer->created_path));
510 
511                   if (expected13 != actual13)
512                       err = svn_error_compose_create(err,
513                           svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
514                                      "size comparison problem: '%s' and '%s'",
515                                      inner->created_path,
516                                      outer->created_path));
517                 }
518             }
519         }
520     }
521 
522   return err;
523 }
524 
525 static svn_error_t *
read_length_line_shouldnt_loop(apr_pool_t * pool)526 read_length_line_shouldnt_loop(apr_pool_t *pool)
527 {
528   const char *tmp_dir;
529   const char *tmp_file;
530   char buffer[4];
531   apr_size_t buffer_limit = sizeof(buffer);
532   apr_file_t *f;
533 
534   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "read_length_tmp", pool));
535 
536   SVN_ERR(svn_io_write_unique(&tmp_file, tmp_dir, "1234\r\n", 6,
537                               svn_io_file_del_on_pool_cleanup, pool));
538 
539   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ, APR_OS_DEFAULT, pool));
540 
541   SVN_TEST_ASSERT_ERROR(svn_io_read_length_line(f, buffer, &buffer_limit,
542                                                 pool), SVN_ERR_MALFORMED_FILE);
543   SVN_TEST_ASSERT(buffer_limit == 4);
544 
545   return SVN_NO_ERROR;
546 }
547 
548 static svn_error_t *
test_read_length_line(apr_pool_t * pool)549 test_read_length_line(apr_pool_t *pool)
550 {
551   const char *tmp_dir;
552   const char *tmp_file;
553   char buffer[80];
554   apr_size_t buffer_limit;
555   apr_file_t *f;
556   svn_error_t *err;
557 
558   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_read_length_line",
559                                     pool));
560 
561   /* Test 1: Read empty file. */
562   tmp_file = svn_dirent_join(tmp_dir, "empty", pool);
563   SVN_ERR(svn_io_file_create(tmp_file, "", pool));
564 
565   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
566                            APR_OS_DEFAULT, pool));
567   buffer_limit = sizeof(buffer);
568   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
569   SVN_TEST_ASSERT_ERROR(err, APR_EOF);
570 
571   SVN_ERR(svn_io_file_close(f, pool));
572 
573   /* Test 2: Read empty line.*/
574   tmp_file = svn_dirent_join(tmp_dir, "empty-line", pool);
575   SVN_ERR(svn_io_file_create(tmp_file, "\n", pool));
576 
577   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
578                            APR_OS_DEFAULT, pool));
579   buffer_limit = sizeof(buffer);
580   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
581   SVN_ERR(err);
582   SVN_TEST_ASSERT(buffer_limit == 0);
583   SVN_TEST_STRING_ASSERT(buffer, "");
584   SVN_ERR(svn_io_file_close(f, pool));
585 
586   /* Test 3: Read two lines.*/
587   tmp_file = svn_dirent_join(tmp_dir, "lines", pool);
588   SVN_ERR(svn_io_file_create(tmp_file, "first\nsecond\n", pool));
589 
590   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
591                            APR_OS_DEFAULT, pool));
592 
593   buffer_limit = sizeof(buffer);
594   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
595   SVN_ERR(err);
596   SVN_TEST_ASSERT(buffer_limit == 5);
597   SVN_TEST_STRING_ASSERT(buffer, "first");
598 
599   buffer_limit = sizeof(buffer);
600   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
601   SVN_ERR(err);
602   SVN_TEST_ASSERT(buffer_limit == 6);
603   SVN_TEST_STRING_ASSERT(buffer, "second");
604 
605   buffer_limit = sizeof(buffer);
606   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
607   SVN_TEST_ASSERT_ERROR(err, APR_EOF);
608 
609   SVN_ERR(svn_io_file_close(f, pool));
610 
611   /* Test 4: Content without end-of-line.*/
612   tmp_file = svn_dirent_join(tmp_dir, "no-eol", pool);
613   SVN_ERR(svn_io_file_create(tmp_file, "text", pool));
614 
615   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
616                            APR_OS_DEFAULT, pool));
617 
618   buffer_limit = sizeof(buffer);
619   err = svn_io_read_length_line(f, buffer, &buffer_limit, pool);
620   SVN_TEST_ASSERT_ERROR(err, APR_EOF);
621 
622   SVN_ERR(svn_io_file_close(f, pool));
623 
624   return SVN_NO_ERROR;
625 }
626 
627 static svn_error_t *
test_file_readline(apr_pool_t * pool)628 test_file_readline(apr_pool_t *pool)
629 {
630   const char *tmp_dir;
631   const char *tmp_file;
632   svn_stringbuf_t *buf;
633   apr_file_t *f;
634   svn_error_t *err;
635   const char *eol;
636   svn_boolean_t eof;
637   apr_off_t pos;
638 
639   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_file_readline",
640                                     pool));
641 
642   tmp_file = svn_dirent_join(tmp_dir, "foo", pool);
643 
644   SVN_ERR(svn_io_file_create(tmp_file, "CR\rLF\nCRLF\r\nno-eol", pool));
645 
646   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
647                            APR_OS_DEFAULT, pool));
648   err = svn_io_file_readline(f, &buf, &eol, &eof, APR_SIZE_MAX, pool, pool);
649   SVN_ERR(err);
650   SVN_TEST_STRING_ASSERT(buf->data, "CR");
651   SVN_TEST_STRING_ASSERT(eol, "\r");
652   SVN_TEST_ASSERT(!eof);
653 
654   /* Check that APR file reports correct offset. See r1719196 why it's
655      important. */
656   SVN_ERR(svn_io_file_get_offset(&pos, f, pool));
657   SVN_TEST_INT_ASSERT(pos, 3);
658 
659   err = svn_io_file_readline(f, &buf, &eol, &eof, APR_SIZE_MAX, pool, pool);
660   SVN_ERR(err);
661   SVN_TEST_STRING_ASSERT(buf->data, "LF");
662   SVN_TEST_STRING_ASSERT(eol, "\n");
663   SVN_TEST_ASSERT(!eof);
664 
665   /* Check that APR file reports correct offset. See r1719196 why it's
666      important. */
667   SVN_ERR(svn_io_file_get_offset(&pos, f, pool));
668   SVN_TEST_INT_ASSERT(pos, 6);
669 
670   err = svn_io_file_readline(f, &buf, &eol, &eof, APR_SIZE_MAX, pool, pool);
671   SVN_ERR(err);
672   SVN_TEST_STRING_ASSERT(buf->data, "CRLF");
673   SVN_TEST_STRING_ASSERT(eol, "\r\n");
674   SVN_TEST_ASSERT(!eof);
675 
676   /* Check that APR file reports correct offset. See r1719196 why it's
677      important. */
678   SVN_ERR(svn_io_file_get_offset(&pos, f, pool));
679   SVN_TEST_INT_ASSERT(pos, 12);
680 
681   err = svn_io_file_readline(f, &buf, &eol, &eof, APR_SIZE_MAX, pool, pool);
682   SVN_ERR(err);
683   SVN_TEST_STRING_ASSERT(buf->data, "no-eol");
684   SVN_TEST_STRING_ASSERT(eol, NULL);
685   SVN_TEST_ASSERT(eof);
686 
687   /* Check that APR file reports correct offset. See r1719196 why it's
688      important. */
689   SVN_ERR(svn_io_file_get_offset(&pos, f, pool));
690   SVN_TEST_INT_ASSERT(pos, 18);
691 
692   /* Further reads still returns EOF. */
693   err = svn_io_file_readline(f, &buf, &eol, &eof, APR_SIZE_MAX, pool, pool);
694   SVN_ERR(err);
695   SVN_TEST_STRING_ASSERT(buf->data, "");
696   SVN_TEST_STRING_ASSERT(eol, NULL);
697   SVN_TEST_ASSERT(eof);
698 
699   /* Check that APR file reports correct offset. See r1719196 why it's
700      important. */
701   SVN_ERR(svn_io_file_get_offset(&pos, f, pool));
702   SVN_TEST_INT_ASSERT(pos, 18);
703 
704   SVN_ERR(svn_io_file_close(f, pool));
705 
706   return SVN_NO_ERROR;
707 }
708 
709 static svn_error_t *
test_open_uniquely_named(apr_pool_t * pool)710 test_open_uniquely_named(apr_pool_t *pool)
711 {
712   const char *tmp_dir;
713   apr_file_t *file;
714   const char *path;
715   svn_error_t *err;
716 
717   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_open_uniquely_named",
718                                     pool));
719 
720   /* Test #1: File 'foo.tmp' doesn't exist. */
721   SVN_ERR(svn_io_open_uniquely_named(&file, &path, tmp_dir, "foo", ".tmp",
722                                      svn_io_file_del_none, pool, pool));
723   SVN_TEST_STRING_ASSERT(path, svn_dirent_join(tmp_dir, "foo.tmp", pool));
724   SVN_ERR(svn_io_file_close(file, pool));
725 
726   /* Test #2: File 'foo.tmp' is already exist. */
727   SVN_ERR(svn_io_open_uniquely_named(NULL, &path, tmp_dir, "foo", ".tmp",
728                                      svn_io_file_del_none, pool, pool));
729   SVN_TEST_STRING_ASSERT(path, svn_dirent_join(tmp_dir, "foo.2.tmp", pool));
730 
731   /* Test #3: Directory named 'bar.tmp' is already exist. */
732   SVN_ERR(svn_io_dir_make(svn_dirent_join(tmp_dir, "bar.tmp", pool),
733                           APR_OS_DEFAULT, pool));
734   SVN_ERR(svn_io_open_uniquely_named(NULL, &path, tmp_dir, "bar", ".tmp",
735                                      svn_io_file_del_none, pool, pool));
736   SVN_TEST_STRING_ASSERT(path, svn_dirent_join(tmp_dir, "bar.2.tmp", pool));
737 
738 
739   /* Test #4: Attempt create file in non-existing directory. */
740   err = svn_io_open_uniquely_named(NULL, &path,
741                                    svn_dirent_join(tmp_dir, "non-existing", pool),
742                                    NULL, NULL, svn_io_file_del_none, pool, pool);
743   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
744     {
745       svn_error_clear(err);
746     }
747   else if (err)
748     {
749       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
750                                "Expected error APR_STATUS_IS_ENOTDIR() but "
751                                "got %s",
752                                svn_error_symbolic_name(err->apr_err));
753     }
754   else
755     {
756       SVN_TEST_ASSERT_ANY_ERROR(err);
757     }
758 
759   /* Test #5: File 'yota.tmp' is already exist and readonly. */
760   SVN_ERR(svn_io_file_create_empty(svn_dirent_join(tmp_dir, "yota.tmp", pool),
761                                    pool));
762   SVN_ERR(svn_io_set_file_read_only(svn_dirent_join(tmp_dir, "yota.tmp", pool),
763                                     FALSE, pool));
764   SVN_ERR(svn_io_open_uniquely_named(NULL, &path, tmp_dir, "yota", ".tmp",
765                                     svn_io_file_del_none, pool, pool));
766   SVN_TEST_STRING_ASSERT(path, svn_dirent_join(tmp_dir, "yota.2.tmp", pool));
767 
768   return SVN_NO_ERROR;
769 }
770 
771 /* Move the read pointer in FILE to absolute position OFFSET and align
772  * the read buffer to multiples of BLOCK_SIZE.  BUFFERED is set only if
773  * FILE actually uses a read buffer.  Use POOL for allocations.
774  */
775 static svn_error_t *
aligned_seek(apr_file_t * file,apr_size_t block_size,apr_size_t offset,svn_boolean_t buffered,apr_pool_t * pool)776 aligned_seek(apr_file_t *file,
777              apr_size_t block_size,
778              apr_size_t offset,
779              svn_boolean_t buffered,
780              apr_pool_t *pool)
781 {
782   apr_off_t block_start;
783   apr_off_t current;
784 
785   SVN_ERR(svn_io_file_aligned_seek(file, (apr_off_t)block_size,
786                                    &block_start, (apr_off_t)offset, pool));
787 
788   /* block start shall be aligned to multiples of block_size.
789      If it isn't, it must be aligned to APR's default block size(pre-1.3 APR)
790    */
791   if (buffered)
792     {
793       SVN_TEST_ASSERT(block_start % block_size == 0);
794       SVN_TEST_ASSERT(offset - block_start < block_size);
795     }
796 
797   /* we must be at the desired offset */
798   SVN_ERR(svn_io_file_get_offset(&current, file, pool));
799   SVN_TEST_ASSERT(current == (apr_off_t)offset);
800 
801   return SVN_NO_ERROR;
802 }
803 
804 /* Move the read pointer in FILE to absolute position OFFSET, align the
805  * read buffer to multiples of BLOCK_SIZE and read one byte from that
806  * position.  Verify that it matches the CONTENTS for that offset.
807  * BUFFERED is set only if FILE actually uses a read buffer.
808  * Use POOL for allocations.
809  */
810 static svn_error_t *
aligned_read_at(apr_file_t * file,svn_stringbuf_t * contents,apr_size_t block_size,apr_size_t offset,svn_boolean_t buffered,apr_pool_t * pool)811 aligned_read_at(apr_file_t *file,
812                 svn_stringbuf_t *contents,
813                 apr_size_t block_size,
814                 apr_size_t offset,
815                 svn_boolean_t buffered,
816                 apr_pool_t *pool)
817 {
818   char c;
819   SVN_ERR(aligned_seek(file, block_size, offset, buffered, pool));
820 
821   /* the data we read must match whatever we wrote there */
822   SVN_ERR(svn_io_file_getc(&c, file, pool));
823   SVN_TEST_ASSERT(c == contents->data[offset]);
824 
825   return SVN_NO_ERROR;
826 }
827 
828 /* Verify that aligned seek with the given BLOCK_SIZE works for FILE.
829  * CONTENTS is the data expected from FILE.  BUFFERED is set only if FILE
830  * actually uses a read buffer.  Use POOL for allocations.
831  */
832 static svn_error_t *
aligned_read(apr_file_t * file,svn_stringbuf_t * contents,apr_size_t block_size,svn_boolean_t buffered,apr_pool_t * pool)833 aligned_read(apr_file_t *file,
834              svn_stringbuf_t *contents,
835              apr_size_t block_size,
836              svn_boolean_t buffered,
837              apr_pool_t *pool)
838 {
839   apr_size_t i;
840   apr_size_t offset = 0;
841   const apr_size_t prime = 78427;
842 
843   /* "random" access to different offsets */
844   for (i = 0, offset = prime; i < 10; ++i, offset += prime)
845     SVN_ERR(aligned_read_at(file, contents, block_size,
846                             offset % contents->len, buffered, pool));
847 
848   /* we can seek to EOF */
849   SVN_ERR(aligned_seek(file, contents->len, block_size, buffered, pool));
850 
851   /* reversed order access to all bytes */
852   for (i = contents->len; i > 0; --i)
853     SVN_ERR(aligned_read_at(file, contents, block_size, i - 1, buffered,
854                             pool));
855 
856   /* forward order access to all bytes */
857   for (i = 0; i < contents->len; ++i)
858     SVN_ERR(aligned_read_at(file, contents, block_size, i, buffered, pool));
859 
860   return SVN_NO_ERROR;
861 }
862 
863 static svn_error_t *
aligned_seek_test(apr_pool_t * pool)864 aligned_seek_test(apr_pool_t *pool)
865 {
866   apr_size_t i;
867   const char *tmp_dir;
868   const char *tmp_file;
869   apr_file_t *f;
870   svn_stringbuf_t *contents;
871   const apr_size_t file_size = 100000;
872 
873   /* create a temp folder & schedule it for automatic cleanup */
874   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "aligned_seek_tmp", pool));
875 
876   /* create a temp file with know contents */
877 
878   contents = svn_stringbuf_create_ensure(file_size, pool);
879   for (i = 0; i < file_size; ++i)
880     svn_stringbuf_appendbyte(contents, (char)rand());
881 
882   SVN_ERR(svn_io_write_unique(&tmp_file, tmp_dir, contents->data,
883                               contents->len,
884                               svn_io_file_del_on_pool_cleanup, pool));
885 
886   /* now, access read data with varying alignment sizes */
887   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ | APR_BUFFERED,
888                            APR_OS_DEFAULT, pool));
889   SVN_ERR(aligned_read(f, contents,   0x1000, TRUE, pool)); /* APR default */
890   SVN_ERR(aligned_read(f, contents,   0x8000, TRUE, pool)); /* "unusual" 32K */
891   SVN_ERR(aligned_read(f, contents,  0x10000, TRUE, pool)); /* FSX default */
892   SVN_ERR(aligned_read(f, contents, 0x100000, TRUE, pool)); /* larger than file */
893   SVN_ERR(aligned_read(f, contents,    10001, TRUE, pool)); /* odd, larger than
894                                                                APR default */
895   SVN_ERR(aligned_read(f, contents,     1003, TRUE, pool)); /* odd, smaller than
896                                                                APR default */
897   SVN_ERR(svn_io_file_close(f, pool));
898 
899   /* now, try read data with buffering disabled.
900      That is a special case because APR reports a buffer size of 0. */
901   SVN_ERR(svn_io_file_open(&f, tmp_file, APR_READ, APR_OS_DEFAULT, pool));
902   SVN_ERR(aligned_read(f, contents,   0x1000, FALSE, pool));
903   SVN_ERR(aligned_read(f, contents,   0x8000, FALSE, pool));
904   SVN_ERR(aligned_read(f, contents,  0x10000, FALSE, pool));
905   SVN_ERR(aligned_read(f, contents, 0x100000, FALSE, pool));
906   SVN_ERR(aligned_read(f, contents,    10001, FALSE, pool));
907   SVN_ERR(aligned_read(f, contents,     1003, FALSE, pool));
908   SVN_ERR(svn_io_file_close(f, pool));
909 
910   return SVN_NO_ERROR;
911 }
912 
913 static svn_error_t *
ignore_enoent(apr_pool_t * pool)914 ignore_enoent(apr_pool_t *pool)
915 {
916   const char *tmp_dir, *path;
917   const svn_io_dirent2_t *dirent_p;
918   apr_file_t *file;
919 
920   /* Create an empty directory. */
921   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "ignore_enoent", pool));
922 
923   /* Path does not exist. */
924   path = svn_dirent_join(tmp_dir, "not-present", pool);
925   SVN_ERR(svn_io_remove_dir2(path, TRUE, NULL, NULL, pool));
926   SVN_ERR(svn_io_remove_file2(path, TRUE, pool));
927   SVN_ERR(svn_io_set_file_read_only(path, TRUE, pool));
928   SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
929   SVN_ERR(svn_io_set_file_executable(path, TRUE, TRUE, pool));
930   SVN_ERR(svn_io_set_file_executable(path, FALSE, TRUE, pool));
931   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, TRUE, TRUE, pool, pool));
932   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, FALSE, TRUE, pool, pool));
933 
934   /* Neither path nor parent exists. */
935   path = svn_dirent_join(path, "not-present", pool);
936   SVN_ERR(svn_io_remove_dir2(path, TRUE, NULL, NULL, pool));
937   SVN_ERR(svn_io_remove_file2(path, TRUE, pool));
938   SVN_ERR(svn_io_set_file_read_only(path, TRUE, pool));
939   SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
940   SVN_ERR(svn_io_set_file_executable(path, TRUE, TRUE, pool));
941   SVN_ERR(svn_io_set_file_executable(path, FALSE, TRUE, pool));
942   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, TRUE, TRUE, pool, pool));
943   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, FALSE, TRUE, pool, pool));
944 
945   /* File does exist. */
946   path = svn_dirent_join(tmp_dir, "present", pool);
947   SVN_ERR(svn_io_file_open(&file, path,
948                            APR_WRITE | APR_CREATE | APR_TRUNCATE,
949                            APR_OS_DEFAULT,
950                            pool));
951   SVN_ERR(svn_io_file_close(file, pool));
952 
953   /* Path does not exist as child of file. */
954   path = svn_dirent_join(path, "not-present", pool);
955   SVN_ERR(svn_io_remove_dir2(path, TRUE, NULL, NULL, pool));
956   SVN_ERR(svn_io_remove_file2(path, TRUE, pool));
957   SVN_ERR(svn_io_set_file_read_only(path, TRUE, pool));
958   SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
959   SVN_ERR(svn_io_set_file_executable(path, TRUE, TRUE, pool));
960   SVN_ERR(svn_io_set_file_executable(path, FALSE, TRUE, pool));
961   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, TRUE, TRUE, pool, pool));
962   SVN_ERR(svn_io_stat_dirent2(&dirent_p, path, FALSE, TRUE, pool, pool));
963 
964   return SVN_NO_ERROR;
965 }
966 
967 static svn_error_t *
test_install_stream_to_longpath(apr_pool_t * pool)968 test_install_stream_to_longpath(apr_pool_t *pool)
969 {
970   const char *tmp_dir;
971   const char *final_abspath;
972   const char *deep_dir;
973   svn_stream_t *stream;
974   svn_stringbuf_t *actual_content;
975   int i;
976 
977   /* Create an empty directory. */
978   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
979                                     "test_install_stream_to_longpath",
980                                     pool));
981 
982   deep_dir = tmp_dir;
983 
984   /* Generate very long path (> 260 symbols) */
985   for (i = 0; i < 26; i++)
986     {
987       deep_dir = svn_dirent_join(deep_dir, "1234567890", pool);
988       SVN_ERR(svn_io_make_dir_recursively(deep_dir, pool));
989     }
990 
991   final_abspath = svn_dirent_join(deep_dir, "stream1", pool);
992   SVN_ERR(svn_stream__create_for_install(&stream, deep_dir, pool, pool));
993   SVN_ERR(svn_stream_puts(stream, "stream1 content"));
994   SVN_ERR(svn_stream_close(stream));
995   SVN_ERR(svn_stream__install_stream(stream,
996                                      final_abspath,
997                                      TRUE,
998                                      pool));
999 
1000   SVN_ERR(svn_stringbuf_from_file2(&actual_content,
1001                                    final_abspath,
1002                                    pool));
1003 
1004   SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content");
1005 
1006   return SVN_NO_ERROR;
1007 }
1008 
1009 static svn_error_t *
test_install_stream_over_readonly_file(apr_pool_t * pool)1010 test_install_stream_over_readonly_file(apr_pool_t *pool)
1011 {
1012   const char *tmp_dir;
1013   const char *final_abspath;
1014   svn_stream_t *stream;
1015   svn_stringbuf_t *actual_content;
1016 
1017   /* Create an empty directory. */
1018   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
1019                                     "test_install_stream_over_readonly_file",
1020                                     pool));
1021 
1022   final_abspath = svn_dirent_join(tmp_dir, "stream1", pool);
1023 
1024   /* Create empty read-only file. */
1025   SVN_ERR(svn_io_file_create_empty(final_abspath, pool));
1026   SVN_ERR(svn_io_set_file_read_only(final_abspath, FALSE, pool));
1027 
1028   SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool));
1029   SVN_ERR(svn_stream_puts(stream, "stream1 content"));
1030   SVN_ERR(svn_stream_close(stream));
1031   SVN_ERR(svn_stream__install_stream(stream,
1032                                      final_abspath,
1033                                      TRUE,
1034                                      pool));
1035 
1036   SVN_ERR(svn_stringbuf_from_file2(&actual_content,
1037                                    final_abspath,
1038                                    pool));
1039 
1040   SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content");
1041 
1042   return SVN_NO_ERROR;
1043 }
1044 
1045 static svn_error_t *
test_file_size_get(apr_pool_t * pool)1046 test_file_size_get(apr_pool_t *pool)
1047 {
1048   const char *tmp_dir, *path;
1049   apr_file_t *file;
1050   svn_filesize_t filesize;
1051 
1052   /* Create an empty directory. */
1053   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_file_size_get", pool));
1054 
1055   /* Path does not exist. */
1056   path = svn_dirent_join(tmp_dir, "file", pool);
1057 
1058   /* Create a file.*/
1059   SVN_ERR(svn_io_file_open(&file, path,
1060                            APR_WRITE | APR_CREATE | APR_BUFFERED,
1061                            APR_OS_DEFAULT, pool));
1062   SVN_ERR(svn_io_file_size_get(&filesize, file, pool));
1063   SVN_TEST_ASSERT(filesize == 0);
1064 
1065   /* Write 8 bytes and check new size. */
1066   SVN_ERR(svn_io_file_write_full(file, "12345678", 8, NULL, pool));
1067 
1068   SVN_ERR(svn_io_file_size_get(&filesize, file, pool));
1069   SVN_TEST_ASSERT(filesize == 8);
1070 
1071   /* Truncate to 2 bytes. */
1072   SVN_ERR(svn_io_file_trunc(file, 2, pool));
1073 
1074   SVN_ERR(svn_io_file_size_get(&filesize, file, pool));
1075   SVN_TEST_ASSERT(filesize == 2);
1076 
1077   /* Close the file. */
1078   SVN_ERR(svn_io_file_close(file, pool));
1079   return SVN_NO_ERROR;
1080 }
1081 
1082 static svn_error_t *
test_file_rename2(apr_pool_t * pool)1083 test_file_rename2(apr_pool_t *pool)
1084 {
1085   const char *tmp_dir;
1086   const char *foo_path;
1087   const char *bar_path;
1088   svn_stringbuf_t *actual_content;
1089   svn_node_kind_t actual_kind;
1090 
1091   /* Create an empty directory. */
1092   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_file_rename2", pool));
1093 
1094   foo_path = svn_dirent_join(tmp_dir, "foo", pool);
1095   bar_path = svn_dirent_join(tmp_dir, "bar", pool);
1096 
1097   /* Test 1: Simple file rename. */
1098   SVN_ERR(svn_io_file_create(foo_path, "file content", pool));
1099 
1100   SVN_ERR(svn_io_file_rename2(foo_path, bar_path, FALSE, pool));
1101 
1102   SVN_ERR(svn_stringbuf_from_file2(&actual_content, bar_path, pool));
1103   SVN_TEST_STRING_ASSERT(actual_content->data, "file content");
1104 
1105   SVN_ERR(svn_io_check_path(foo_path, &actual_kind, pool));
1106   SVN_TEST_ASSERT(actual_kind == svn_node_none);
1107   SVN_ERR(svn_io_remove_file2(bar_path, FALSE, pool));
1108 
1109   /* Test 2: Rename file with flush_to_disk flag. */
1110   SVN_ERR(svn_io_file_create(foo_path, "file content", pool));
1111 
1112   SVN_ERR(svn_io_file_rename2(foo_path, bar_path, TRUE, pool));
1113 
1114   SVN_ERR(svn_stringbuf_from_file2(&actual_content, bar_path, pool));
1115   SVN_TEST_STRING_ASSERT(actual_content->data, "file content");
1116   SVN_ERR(svn_io_check_path(foo_path, &actual_kind, pool));
1117   SVN_TEST_ASSERT(actual_kind == svn_node_none);
1118 
1119   SVN_ERR(svn_io_remove_file2(bar_path, FALSE, pool));
1120 
1121   /* Test 3: Rename file over existing read-only file. */
1122   SVN_ERR(svn_io_file_create(foo_path, "file content", pool));
1123   SVN_ERR(svn_io_file_create(bar_path, "bar content", pool));
1124   SVN_ERR(svn_io_set_file_read_only(bar_path, FALSE, pool));
1125 
1126   SVN_ERR(svn_io_file_rename2(foo_path, bar_path, FALSE, pool));
1127 
1128   SVN_ERR(svn_stringbuf_from_file2(&actual_content, bar_path, pool));
1129   SVN_TEST_STRING_ASSERT(actual_content->data, "file content");
1130   SVN_ERR(svn_io_check_path(foo_path, &actual_kind, pool));
1131   SVN_TEST_ASSERT(actual_kind == svn_node_none);
1132   SVN_ERR(svn_io_remove_file2(bar_path, FALSE, pool));
1133 
1134   return SVN_NO_ERROR;
1135 }
1136 
1137 static svn_error_t *
test_apr_trunc_workaround(apr_pool_t * pool)1138 test_apr_trunc_workaround(apr_pool_t *pool)
1139 {
1140   const char *tmp_dir;
1141   const char *tmp_file;
1142   apr_file_t *f;
1143   apr_size_t len;
1144   apr_off_t offset;
1145   char dummy;
1146 
1147   /* create a temp folder & schedule it for automatic cleanup */
1148   SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_apr_trunc_workaround",
1149                                   pool));
1150   SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool));
1151   SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool));
1152   svn_test_add_dir_cleanup(tmp_dir);
1153 
1154   /* create an r/w file */
1155   tmp_file = svn_dirent_join(tmp_dir, "file", pool);
1156   SVN_ERR(svn_io_file_open(&f, tmp_file,
1157                            APR_READ | APR_WRITE | APR_BUFFERED | APR_CREATE |
1158                               APR_TRUNCATE,
1159                            APR_OS_DEFAULT, pool));
1160 
1161   /* write some content and put it internally into read mode */
1162   len = 10;
1163   SVN_ERR(svn_io_file_write(f, "0123456789", &len, pool));
1164 
1165   offset = 0;
1166   SVN_ERR(svn_io_file_seek(f, APR_SET, &offset, pool));
1167   SVN_ERR(svn_io_file_getc(&dummy, f, pool));
1168 
1169   /* clear the file and write some new content */
1170   SVN_ERR(svn_io_file_trunc(f, 0, pool));
1171   len = 3;
1172   SVN_ERR(svn_io_file_write(f, "abc", &len, pool));
1173 
1174   /* we should now be positioned at the end of the new content */
1175   offset = 0;
1176   SVN_ERR(svn_io_file_seek(f, APR_CUR, &offset, pool));
1177   SVN_TEST_ASSERT(offset == (int)len);
1178 
1179   return SVN_NO_ERROR;
1180 }
1181 
1182 
1183 /* Issue #4806 */
1184 static svn_error_t *
test_rmtree_all_writable(apr_pool_t * pool)1185 test_rmtree_all_writable(apr_pool_t *pool)
1186 {
1187   const char *dir_path = NULL;
1188 
1189   SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_writable",
1190                           FALSE, FALSE, pool));
1191   SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
1192   return SVN_NO_ERROR;
1193 }
1194 
1195 /* Issue #4806 */
1196 static svn_error_t *
test_rmtree_file_readonly(apr_pool_t * pool)1197 test_rmtree_file_readonly(apr_pool_t *pool)
1198 {
1199   const char *dir_path = NULL;
1200 
1201   SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_file_readonly",
1202                           FALSE, TRUE, pool));
1203   SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
1204   return SVN_NO_ERROR;
1205 }
1206 
1207 /* Issue #4806 */
1208 static svn_error_t *
test_rmtree_dir_readonly(apr_pool_t * pool)1209 test_rmtree_dir_readonly(apr_pool_t *pool)
1210 {
1211   const char *dir_path = NULL;
1212 
1213   SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_dir_readonly",
1214                           TRUE, FALSE, pool));
1215   SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
1216   return SVN_NO_ERROR;
1217 }
1218 
1219 /* Issue #4806 */
1220 static svn_error_t *
test_rmtree_all_readonly(apr_pool_t * pool)1221 test_rmtree_all_readonly(apr_pool_t *pool)
1222 {
1223   const char *dir_path = NULL;
1224 
1225   SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_readonly",
1226                           TRUE, TRUE, pool));
1227   SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
1228   return SVN_NO_ERROR;
1229 }
1230 
1231 
1232 /* The test table.  */
1233 
1234 static int max_threads = 3;
1235 
1236 static struct svn_test_descriptor_t test_funcs[] =
1237   {
1238     SVN_TEST_NULL,
1239     SVN_TEST_PASS2(test_two_file_size_comparison,
1240                    "two file size comparison"),
1241     SVN_TEST_PASS2(test_two_file_content_comparison,
1242                    "two file content comparison"),
1243     SVN_TEST_PASS2(test_three_file_size_comparison,
1244                    "three file size comparison"),
1245     SVN_TEST_PASS2(test_three_file_content_comparison,
1246                    "three file content comparison"),
1247     SVN_TEST_PASS2(read_length_line_shouldnt_loop,
1248                    "svn_io_read_length_line() shouldn't loop"),
1249     SVN_TEST_PASS2(aligned_seek_test,
1250                    "test aligned seek"),
1251     SVN_TEST_PASS2(ignore_enoent,
1252                    "test ignore-enoent"),
1253     SVN_TEST_PASS2(test_install_stream_to_longpath,
1254                    "test svn_stream__install_stream to long path"),
1255     SVN_TEST_PASS2(test_install_stream_over_readonly_file,
1256                    "test svn_stream__install_stream over RO file"),
1257     SVN_TEST_PASS2(test_file_size_get,
1258                    "test svn_io_file_size_get"),
1259     SVN_TEST_PASS2(test_file_rename2,
1260                    "test svn_io_file_rename2"),
1261     SVN_TEST_PASS2(test_read_length_line,
1262                    "test svn_io_read_length_line()"),
1263     SVN_TEST_PASS2(test_file_readline,
1264                    "test svn_io_file_readline()"),
1265     SVN_TEST_PASS2(test_open_uniquely_named,
1266                    "test svn_io_open_uniquely_named()"),
1267     SVN_TEST_PASS2(test_apr_trunc_workaround,
1268                    "test workaround for APR in svn_io_file_trunc"),
1269     SVN_TEST_PASS2(test_rmtree_all_writable,
1270                    "test svn_io_remove_dir2() with writable tree"),
1271     SVN_TEST_PASS2(test_rmtree_file_readonly,
1272                    "test svn_io_remove_dir2() with read-only file"),
1273     SVN_TEST_PASS2(test_rmtree_dir_readonly,
1274                    "test svn_io_remove_dir2() with read-only directory"),
1275     SVN_TEST_PASS2(test_rmtree_all_readonly,
1276                    "test svn_io_remove_dir2() with read-only tree"),
1277     SVN_TEST_NULL
1278   };
1279 
1280 SVN_TEST_MAIN
1281