1 /*
2 * io.c: shared file reading, writing, and probing code.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include <stdio.h>
27
28 #ifndef WIN32
29 #include <unistd.h>
30 #endif
31
32 #ifndef APR_STATUS_IS_EPERM
33 #include <errno.h>
34 #ifdef EPERM
35 #define APR_STATUS_IS_EPERM(s) ((s) == EPERM)
36 #else
37 #define APR_STATUS_IS_EPERM(s) (0)
38 #endif
39 #endif
40
41 #include <apr_lib.h>
42 #include <apr_pools.h>
43 #include <apr_file_io.h>
44 #include <apr_file_info.h>
45 #include <apr_general.h>
46 #include <apr_strings.h>
47 #include <apr_portable.h>
48 #include <apr_md5.h>
49
50 #if APR_HAVE_FCNTL_H
51 #include <fcntl.h>
52 #endif
53
54 #include "svn_hash.h"
55 #include "svn_types.h"
56 #include "svn_dirent_uri.h"
57 #include "svn_path.h"
58 #include "svn_string.h"
59 #include "svn_error.h"
60 #include "svn_io.h"
61 #include "svn_pools.h"
62 #include "svn_utf.h"
63 #include "svn_config.h"
64 #include "svn_private_config.h"
65 #include "svn_ctype.h"
66
67 #include "private/svn_atomic.h"
68 #include "private/svn_io_private.h"
69 #include "private/svn_utf_private.h"
70 #include "private/svn_dep_compat.h"
71
72 #define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS"
73
74 /*
75 Windows is 'aided' by a number of types of applications that
76 follow other applications around and open up files they have
77 changed for various reasons (the most intrusive are virus
78 scanners). So, if one of these other apps has glommed onto
79 our file we may get an 'access denied' error.
80
81 This retry loop does not completely solve the problem (who
82 knows how long the other app is going to hold onto it for), but
83 goes a long way towards minimizing it. It is not an infinite
84 loop because there might really be an error.
85
86 Another reason for retrying delete operations on Windows
87 is that they are asynchronous -- the file or directory is not
88 actually deleted until the last handle to it is closed. The
89 retry loop cannot completely solve this problem either, but can
90 help mitigate it.
91 */
92 #define RETRY_MAX_ATTEMPTS 100
93 #define RETRY_INITIAL_SLEEP 1000
94 #define RETRY_MAX_SLEEP 128000
95
96 #define RETRY_LOOP(err, expr, retry_test, sleep_test) \
97 do \
98 { \
99 apr_status_t os_err = APR_TO_OS_ERROR(err); \
100 int sleep_count = RETRY_INITIAL_SLEEP; \
101 int retries; \
102 for (retries = 0; \
103 retries < RETRY_MAX_ATTEMPTS && (retry_test); \
104 os_err = APR_TO_OS_ERROR(err)) \
105 { \
106 if (sleep_test) \
107 { \
108 ++retries; \
109 apr_sleep(sleep_count); \
110 if (sleep_count < RETRY_MAX_SLEEP) \
111 sleep_count *= 2; \
112 } \
113 (err) = (expr); \
114 } \
115 } \
116 while (0)
117
118 #if defined(EDEADLK) && APR_HAS_THREADS
119 #define FILE_LOCK_RETRY_LOOP(err, expr) \
120 RETRY_LOOP(err, \
121 expr, \
122 (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \
123 (!APR_STATUS_IS_EINTR(err)))
124 #else
125 #define FILE_LOCK_RETRY_LOOP(err, expr) \
126 RETRY_LOOP(err, \
127 expr, \
128 (APR_STATUS_IS_EINTR(err)), \
129 0)
130 #endif
131
132 #ifndef WIN32_RETRY_LOOP
133 #if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP)
134 #define WIN32_RETRY_LOOP(err, expr) \
135 RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \
136 || os_err == ERROR_SHARING_VIOLATION \
137 || os_err == ERROR_DIR_NOT_EMPTY), \
138 1)
139 #else
140 #define WIN32_RETRY_LOOP(err, expr) ((void)0)
141 #endif
142 #endif
143
144 #ifdef WIN32
145
146 #if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */
147 typedef struct _FILE_RENAME_INFO {
148 BOOL ReplaceIfExists;
149 HANDLE RootDirectory;
150 DWORD FileNameLength;
151 WCHAR FileName[1];
152 } FILE_RENAME_INFO, *PFILE_RENAME_INFO;
153
154 typedef struct _FILE_DISPOSITION_INFO {
155 BOOL DeleteFile;
156 } FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO;
157
158 #define FileRenameInfo 3
159 #define FileDispositionInfo 4
160 #endif /* WIN32 < Vista */
161
162 /* One-time initialization of the late bound Windows API functions. */
163 static volatile svn_atomic_t win_dynamic_imports_state = 0;
164
165 /* Pointer to GetFinalPathNameByHandleW function from kernel32.dll. */
166 typedef DWORD (WINAPI *GETFINALPATHNAMEBYHANDLE)(
167 HANDLE hFile,
168 WCHAR *lpszFilePath,
169 DWORD cchFilePath,
170 DWORD dwFlags);
171
172 typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile,
173 int FileInformationClass,
174 LPVOID lpFileInformation,
175 DWORD dwBufferSize);
176
177 static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL;
178 static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL;
179
180 /* Forward declaration. */
181 static svn_error_t * io_win_read_link(svn_string_t **dest,
182 const char *path,
183 apr_pool_t *pool);
184
185 #endif
186
187 /* Forward declaration */
188 static apr_status_t
189 dir_is_empty(const char *dir, apr_pool_t *pool);
190 static APR_INLINE svn_error_t *
191 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
192 const char *msg, const char *msg_no_name,
193 apr_pool_t *pool);
194
195 /* Local wrapper of svn_path_cstring_to_utf8() that does no copying on
196 * operating systems where APR always uses utf-8 as native path format */
197 static svn_error_t *
cstring_to_utf8(const char ** path_utf8,const char * path_apr,apr_pool_t * pool)198 cstring_to_utf8(const char **path_utf8,
199 const char *path_apr,
200 apr_pool_t *pool)
201 {
202 #if defined(WIN32) || defined(DARWIN)
203 *path_utf8 = path_apr;
204 return SVN_NO_ERROR;
205 #else
206 return svn_path_cstring_to_utf8(path_utf8, path_apr, pool);
207 #endif
208 }
209
210 /* Local wrapper of svn_path_cstring_from_utf8() that does no copying on
211 * operating systems where APR always uses utf-8 as native path format */
212 static svn_error_t *
cstring_from_utf8(const char ** path_apr,const char * path_utf8,apr_pool_t * pool)213 cstring_from_utf8(const char **path_apr,
214 const char *path_utf8,
215 apr_pool_t *pool)
216 {
217 #if defined(WIN32) || defined(DARWIN)
218 *path_apr = path_utf8;
219 return SVN_NO_ERROR;
220 #else
221 return svn_path_cstring_from_utf8(path_apr, path_utf8, pool);
222 #endif
223 }
224
225 /* Helper function that allows to convert an APR-level PATH to something
226 * that we can pass the svn_error_wrap_apr. Since we use it in context
227 * of error reporting, having *some* path info may be more useful than
228 * having none. Therefore, we use a best effort approach here.
229 *
230 * This is different from svn_io_file_name_get in that it uses a different
231 * signature style and will never fail.
232 */
233 static const char *
try_utf8_from_internal_style(const char * path,apr_pool_t * pool)234 try_utf8_from_internal_style(const char *path, apr_pool_t *pool)
235 {
236 svn_error_t *error;
237 const char *path_utf8;
238
239 /* Special case. */
240 if (path == NULL)
241 return "(NULL)";
242
243 /* (try to) convert PATH to UTF-8. If that fails, continue with the plain
244 * PATH because it is the best we have. It may actually be UTF-8 already.
245 */
246 error = cstring_to_utf8(&path_utf8, path, pool);
247 if (error)
248 {
249 /* fallback to best representation we have */
250
251 svn_error_clear(error);
252 path_utf8 = path;
253 }
254
255 /* Toggle (back-)slashes etc. as necessary.
256 */
257 return svn_dirent_local_style(path_utf8, pool);
258 }
259
260
261 /* Set *NAME_P to the UTF-8 representation of directory entry NAME.
262 * NAME is in the internal encoding used by APR; PARENT is in
263 * UTF-8 and in internal (not local) style.
264 *
265 * Use PARENT only for generating an error string if the conversion
266 * fails because NAME could not be represented in UTF-8. In that
267 * case, return a two-level error in which the outer error's message
268 * mentions PARENT, but the inner error's message does not mention
269 * NAME (except possibly in hex) since NAME may not be printable.
270 * Such a compound error at least allows the user to go looking in the
271 * right directory for the problem.
272 *
273 * If there is any other error, just return that error directly.
274 *
275 * If there is any error, the effect on *NAME_P is undefined.
276 *
277 * *NAME_P and NAME may refer to the same storage.
278 */
279 static svn_error_t *
entry_name_to_utf8(const char ** name_p,const char * name,const char * parent,apr_pool_t * pool)280 entry_name_to_utf8(const char **name_p,
281 const char *name,
282 const char *parent,
283 apr_pool_t *pool)
284 {
285 #if defined(WIN32) || defined(DARWIN)
286 *name_p = apr_pstrdup(pool, name);
287 return SVN_NO_ERROR;
288 #else
289 svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool);
290 if (err && err->apr_err == APR_EINVAL)
291 {
292 return svn_error_createf(err->apr_err, err,
293 _("Error converting entry "
294 "in directory '%s' to UTF-8"),
295 svn_dirent_local_style(parent, pool));
296 }
297 return err;
298 #endif
299 }
300
301
302
303 static void
map_apr_finfo_to_node_kind(svn_node_kind_t * kind,svn_boolean_t * is_special,apr_finfo_t * finfo)304 map_apr_finfo_to_node_kind(svn_node_kind_t *kind,
305 svn_boolean_t *is_special,
306 apr_finfo_t *finfo)
307 {
308 *is_special = FALSE;
309
310 if (finfo->filetype == APR_REG)
311 *kind = svn_node_file;
312 else if (finfo->filetype == APR_DIR)
313 *kind = svn_node_dir;
314 else if (finfo->filetype == APR_LNK)
315 {
316 *is_special = TRUE;
317 *kind = svn_node_file;
318 }
319 else
320 *kind = svn_node_unknown;
321 }
322
323 /* Helper for svn_io_check_path() and svn_io_check_resolved_path();
324 essentially the same semantics as those two, with the obvious
325 interpretation for RESOLVE_SYMLINKS. */
326 static svn_error_t *
io_check_path(const char * path,svn_boolean_t resolve_symlinks,svn_boolean_t * is_special_p,svn_node_kind_t * kind,apr_pool_t * pool)327 io_check_path(const char *path,
328 svn_boolean_t resolve_symlinks,
329 svn_boolean_t *is_special_p,
330 svn_node_kind_t *kind,
331 apr_pool_t *pool)
332 {
333 apr_int32_t flags;
334 apr_finfo_t finfo;
335 apr_status_t apr_err;
336 const char *path_apr;
337 svn_boolean_t is_special = FALSE;
338
339 if (path[0] == '\0')
340 path = ".";
341
342 /* Not using svn_io_stat() here because we want to check the
343 apr_err return explicitly. */
344 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
345
346 flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
347 apr_err = apr_stat(&finfo, path_apr, flags, pool);
348
349 if (APR_STATUS_IS_ENOENT(apr_err))
350 *kind = svn_node_none;
351 else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err))
352 *kind = svn_node_none;
353 else if (apr_err)
354 return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"),
355 svn_dirent_local_style(path, pool));
356 else
357 map_apr_finfo_to_node_kind(kind, &is_special, &finfo);
358
359 *is_special_p = is_special;
360
361 return SVN_NO_ERROR;
362 }
363
364
365 /* Wrapper for apr_file_open(), taking an APR-encoded filename. */
366 static apr_status_t
file_open(apr_file_t ** f,const char * fname_apr,apr_int32_t flag,apr_fileperms_t perm,svn_boolean_t retry_on_failure,apr_pool_t * pool)367 file_open(apr_file_t **f,
368 const char *fname_apr,
369 apr_int32_t flag,
370 apr_fileperms_t perm,
371 svn_boolean_t retry_on_failure,
372 apr_pool_t *pool)
373 {
374 apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool);
375
376 if (retry_on_failure)
377 {
378 #ifdef WIN32
379 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
380 {
381 if ((flag & (APR_CREATE | APR_EXCL)) == (APR_CREATE | APR_EXCL))
382 return status; /* Can't create if there is something */
383
384 if (flag & (APR_WRITE | APR_CREATE))
385 {
386 apr_finfo_t finfo;
387
388 if (!apr_stat(&finfo, fname_apr, SVN__APR_FINFO_READONLY, pool))
389 {
390 if (finfo.protection & APR_FREADONLY)
391 return status; /* Retrying won't fix this */
392 }
393 }
394 }
395 #endif
396
397 WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool));
398 }
399 return status;
400 }
401
402
403 svn_error_t *
svn_io_check_resolved_path(const char * path,svn_node_kind_t * kind,apr_pool_t * pool)404 svn_io_check_resolved_path(const char *path,
405 svn_node_kind_t *kind,
406 apr_pool_t *pool)
407 {
408 svn_boolean_t ignored;
409 return io_check_path(path, TRUE, &ignored, kind, pool);
410 }
411
412 svn_error_t *
svn_io_check_path(const char * path,svn_node_kind_t * kind,apr_pool_t * pool)413 svn_io_check_path(const char *path,
414 svn_node_kind_t *kind,
415 apr_pool_t *pool)
416 {
417 svn_boolean_t ignored;
418 return io_check_path(path, FALSE, &ignored, kind, pool);
419 }
420
421 svn_error_t *
svn_io_check_special_path(const char * path,svn_node_kind_t * kind,svn_boolean_t * is_special,apr_pool_t * pool)422 svn_io_check_special_path(const char *path,
423 svn_node_kind_t *kind,
424 svn_boolean_t *is_special,
425 apr_pool_t *pool)
426 {
427 return io_check_path(path, FALSE, is_special, kind, pool);
428 }
429
430 struct temp_file_cleanup_s
431 {
432 apr_pool_t *pool;
433 /* The (APR-encoded) full path of the file to be removed, or NULL if
434 * nothing to do. */
435 const char *fname_apr;
436 };
437
438
439 static apr_status_t
temp_file_plain_cleanup_handler(void * baton)440 temp_file_plain_cleanup_handler(void *baton)
441 {
442 struct temp_file_cleanup_s *b = baton;
443 apr_status_t apr_err = APR_SUCCESS;
444
445 if (b->fname_apr)
446 {
447 apr_err = apr_file_remove(b->fname_apr, b->pool);
448 WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool));
449 }
450
451 return apr_err;
452 }
453
454
455 static apr_status_t
temp_file_child_cleanup_handler(void * baton)456 temp_file_child_cleanup_handler(void *baton)
457 {
458 struct temp_file_cleanup_s *b = baton;
459
460 apr_pool_cleanup_kill(b->pool, b,
461 temp_file_plain_cleanup_handler);
462
463 return APR_SUCCESS;
464 }
465
466
467 svn_error_t *
svn_io_open_uniquely_named(apr_file_t ** file,const char ** unique_path,const char * dirpath,const char * filename,const char * suffix,svn_io_file_del_t delete_when,apr_pool_t * result_pool,apr_pool_t * scratch_pool)468 svn_io_open_uniquely_named(apr_file_t **file,
469 const char **unique_path,
470 const char *dirpath,
471 const char *filename,
472 const char *suffix,
473 svn_io_file_del_t delete_when,
474 apr_pool_t *result_pool,
475 apr_pool_t *scratch_pool)
476 {
477 const char *path;
478 unsigned int i;
479 struct temp_file_cleanup_s *baton = NULL;
480
481 /* At the beginning, we don't know whether unique_path will need
482 UTF8 conversion */
483 svn_boolean_t needs_utf8_conversion = TRUE;
484
485 SVN_ERR_ASSERT(file || unique_path);
486
487 if (dirpath == NULL)
488 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
489 if (filename == NULL)
490 filename = "tempfile";
491 if (suffix == NULL)
492 suffix = ".tmp";
493
494 path = svn_dirent_join(dirpath, filename, scratch_pool);
495
496 if (delete_when == svn_io_file_del_on_pool_cleanup)
497 {
498 baton = apr_palloc(result_pool, sizeof(*baton));
499
500 baton->pool = result_pool;
501 baton->fname_apr = NULL;
502
503 /* Because cleanups are run LIFO, we need to make sure to register
504 our cleanup before the apr_file_close cleanup:
505
506 On Windows, you can't remove an open file.
507 */
508 apr_pool_cleanup_register(result_pool, baton,
509 temp_file_plain_cleanup_handler,
510 temp_file_child_cleanup_handler);
511 }
512
513 for (i = 1; i <= 99999; i++)
514 {
515 const char *unique_name;
516 const char *unique_name_apr;
517 apr_file_t *try_file;
518 apr_status_t apr_err;
519 apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL
520 | APR_BUFFERED | APR_BINARY);
521
522 if (delete_when == svn_io_file_del_on_close)
523 flag |= APR_DELONCLOSE;
524
525 /* Special case the first attempt -- if we can avoid having a
526 generated numeric portion at all, that's best. So first we
527 try with just the suffix; then future tries add a number
528 before the suffix. (A do-while loop could avoid the repeated
529 conditional, but it's not worth the clarity loss.)
530
531 If the first attempt fails, the first number will be "2".
532 This is good, since "1" would misleadingly imply that
533 the second attempt was actually the first... and if someone's
534 got conflicts on their conflicts, we probably don't want to
535 add to their confusion :-). */
536 if (i == 1)
537 unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix);
538 else
539 unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix);
540
541 /* Hmmm. Ideally, we would append to a native-encoding buf
542 before starting iteration, then convert back to UTF-8 for
543 return. But I suppose that would make the appending code
544 sensitive to i18n in a way it shouldn't be... Oh well. */
545 if (needs_utf8_conversion)
546 {
547 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
548 scratch_pool));
549 if (i == 1)
550 {
551 /* The variable parts of unique_name will not require UTF8
552 conversion. Therefore, if UTF8 conversion had no effect
553 on it in the first iteration, it won't require conversion
554 in any future iteration. */
555 needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
556 }
557 }
558 else
559 unique_name_apr = unique_name;
560
561 apr_err = file_open(&try_file, unique_name_apr, flag,
562 APR_OS_DEFAULT, FALSE, result_pool);
563
564 if (APR_STATUS_IS_EEXIST(apr_err))
565 continue;
566 else if (apr_err)
567 {
568 /* On Win32, CreateFile fails with an "Access Denied" error
569 code, rather than "File Already Exists", if the colliding
570 name belongs to a directory. */
571 if (APR_STATUS_IS_EACCES(apr_err))
572 {
573 apr_finfo_t finfo;
574 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
575 APR_FINFO_TYPE, scratch_pool);
576
577 if (!apr_err_2 && finfo.filetype == APR_DIR)
578 continue;
579
580 #ifdef WIN32
581 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) ||
582 apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
583 {
584 /* The file is in use by another process or is hidden;
585 create a new name, but don't do this 99999 times in
586 case the folder is not writable */
587 i += 797;
588 continue;
589 }
590 #endif
591
592 /* Else fall through and return the original error. */
593 }
594
595 if (file)
596 *file = NULL;
597 if (unique_path)
598 *unique_path = NULL;
599 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
600 svn_dirent_local_style(unique_name,
601 scratch_pool));
602 }
603 else
604 {
605 if (delete_when == svn_io_file_del_on_pool_cleanup)
606 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr);
607
608 if (file)
609 *file = try_file;
610 else
611 apr_file_close(try_file);
612 if (unique_path)
613 *unique_path = apr_pstrdup(result_pool, unique_name);
614
615 return SVN_NO_ERROR;
616 }
617 }
618
619 if (file)
620 *file = NULL;
621 if (unique_path)
622 *unique_path = NULL;
623 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
624 NULL,
625 _("Unable to make name for '%s'"),
626 svn_dirent_local_style(path, scratch_pool));
627 }
628
629 svn_error_t *
svn_io_create_unique_link(const char ** unique_name_p,const char * path,const char * dest,const char * suffix,apr_pool_t * pool)630 svn_io_create_unique_link(const char **unique_name_p,
631 const char *path,
632 const char *dest,
633 const char *suffix,
634 apr_pool_t *pool)
635 {
636 #ifdef HAVE_SYMLINK
637 unsigned int i;
638 const char *unique_name;
639 const char *unique_name_apr;
640 const char *dest_apr;
641 int rv;
642
643 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool));
644 for (i = 1; i <= 99999; i++)
645 {
646 apr_status_t apr_err;
647
648 /* Special case the first attempt -- if we can avoid having a
649 generated numeric portion at all, that's best. So first we
650 try with just the suffix; then future tries add a number
651 before the suffix. (A do-while loop could avoid the repeated
652 conditional, but it's not worth the clarity loss.)
653
654 If the first attempt fails, the first number will be "2".
655 This is good, since "1" would misleadingly imply that
656 the second attempt was actually the first... and if someone's
657 got conflicts on their conflicts, we probably don't want to
658 add to their confusion :-). */
659 if (i == 1)
660 unique_name = apr_psprintf(pool, "%s%s", path, suffix);
661 else
662 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix);
663
664 /* Hmmm. Ideally, we would append to a native-encoding buf
665 before starting iteration, then convert back to UTF-8 for
666 return. But I suppose that would make the appending code
667 sensitive to i18n in a way it shouldn't be... Oh well. */
668 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
669 do {
670 rv = symlink(dest_apr, unique_name_apr);
671 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
672
673 apr_err = apr_get_os_error();
674
675 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err))
676 continue;
677 else if (rv == -1 && apr_err)
678 {
679 /* On Win32, CreateFile fails with an "Access Denied" error
680 code, rather than "File Already Exists", if the colliding
681 name belongs to a directory. */
682 if (APR_STATUS_IS_EACCES(apr_err))
683 {
684 apr_finfo_t finfo;
685 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
686 APR_FINFO_TYPE, pool);
687
688 if (!apr_err_2
689 && (finfo.filetype == APR_DIR))
690 continue;
691
692 /* Else ignore apr_err_2; better to fall through and
693 return the original error. */
694 }
695
696 *unique_name_p = NULL;
697 return svn_error_wrap_apr(apr_err,
698 _("Can't create symbolic link '%s'"),
699 svn_dirent_local_style(unique_name, pool));
700 }
701 else
702 {
703 *unique_name_p = unique_name;
704 return SVN_NO_ERROR;
705 }
706 }
707
708 *unique_name_p = NULL;
709 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
710 NULL,
711 _("Unable to make name for '%s'"),
712 svn_dirent_local_style(path, pool));
713 #else
714 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
715 _("Symbolic links are not supported on this "
716 "platform"));
717 #endif
718 }
719
720 svn_error_t *
svn_io_read_link(svn_string_t ** dest,const char * path,apr_pool_t * pool)721 svn_io_read_link(svn_string_t **dest,
722 const char *path,
723 apr_pool_t *pool)
724 {
725 #if defined(HAVE_READLINK)
726 svn_string_t dest_apr;
727 const char *path_apr;
728 char buf[1025];
729 ssize_t rv;
730
731 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
732 do {
733 rv = readlink(path_apr, buf, sizeof(buf) - 1);
734 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
735
736 if (rv == -1)
737 return svn_error_wrap_apr(apr_get_os_error(),
738 _("Can't read contents of link"));
739
740 buf[rv] = '\0';
741 dest_apr.data = buf;
742 dest_apr.len = rv;
743
744 /* ### Cast needed, one of these interfaces is wrong */
745 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool);
746 #elif defined(WIN32)
747 return io_win_read_link(dest, path, pool);
748 #else
749 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
750 _("Symbolic links are not supported on this "
751 "platform"));
752 #endif
753 }
754
755
756 svn_error_t *
svn_io_copy_link(const char * src,const char * dst,apr_pool_t * pool)757 svn_io_copy_link(const char *src,
758 const char *dst,
759 apr_pool_t *pool)
760
761 {
762 #ifdef HAVE_READLINK
763 svn_string_t *link_dest;
764 const char *dst_tmp;
765
766 /* Notice what the link is pointing at... */
767 SVN_ERR(svn_io_read_link(&link_dest, src, pool));
768
769 /* Make a tmp-link pointing at the same thing. */
770 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data,
771 ".tmp", pool));
772
773 /* Move the tmp-link to link. */
774 return svn_io_file_rename2(dst_tmp, dst, FALSE, pool);
775
776 #else
777 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
778 _("Symbolic links are not supported on this "
779 "platform"));
780 #endif
781 }
782
783 /* Temporary directory name cache for svn_io_temp_dir() */
784 static volatile svn_atomic_t temp_dir_init_state = 0;
785 static const char *temp_dir;
786
787 /* Helper function to initialize temp dir. Passed to svn_atomic__init_once */
788 static svn_error_t *
init_temp_dir(void * baton,apr_pool_t * scratch_pool)789 init_temp_dir(void *baton, apr_pool_t *scratch_pool)
790 {
791 /* Global pool for the temp path */
792 apr_pool_t *global_pool = svn_pool_create(NULL);
793 const char *dir;
794
795 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool);
796
797 if (apr_err)
798 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory"));
799
800 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool));
801
802 dir = svn_dirent_internal_style(dir, scratch_pool);
803
804 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool));
805
806 return SVN_NO_ERROR;
807 }
808
809
810 svn_error_t *
svn_io_temp_dir(const char ** dir,apr_pool_t * pool)811 svn_io_temp_dir(const char **dir,
812 apr_pool_t *pool)
813 {
814 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state,
815 init_temp_dir, NULL, pool));
816
817 *dir = apr_pstrdup(pool, temp_dir);
818
819 return SVN_NO_ERROR;
820 }
821
822
823
824
825 /*** Creating, copying and appending files. ***/
826
827 /* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary
828 * allocations.
829 *
830 * NOTE: We don't use apr_copy_file() for this, since it takes filenames
831 * as parameters. Since we want to copy to a temporary file
832 * and rename for atomicity (see below), this would require an extra
833 * close/open pair, which can be expensive, especially on
834 * remote file systems.
835 */
836 static apr_status_t
copy_contents(apr_file_t * from_file,apr_file_t * to_file,apr_pool_t * pool)837 copy_contents(apr_file_t *from_file,
838 apr_file_t *to_file,
839 apr_pool_t *pool)
840 {
841 /* Copy bytes till the cows come home. */
842 while (1)
843 {
844 char buf[SVN__STREAM_CHUNK_SIZE];
845 apr_size_t bytes_this_time = sizeof(buf);
846 apr_status_t read_err;
847 apr_status_t write_err;
848
849 /* Read 'em. */
850 read_err = apr_file_read(from_file, buf, &bytes_this_time);
851 if (read_err && !APR_STATUS_IS_EOF(read_err))
852 {
853 return read_err;
854 }
855
856 /* Write 'em. */
857 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL);
858 if (write_err)
859 {
860 return write_err;
861 }
862
863 if (read_err && APR_STATUS_IS_EOF(read_err))
864 {
865 /* Return the results of this close: an error, or success. */
866 return APR_SUCCESS;
867 }
868 }
869 /* NOTREACHED */
870 }
871
872
873 svn_error_t *
svn_io_copy_file(const char * src,const char * dst,svn_boolean_t copy_perms,apr_pool_t * pool)874 svn_io_copy_file(const char *src,
875 const char *dst,
876 svn_boolean_t copy_perms,
877 apr_pool_t *pool)
878 {
879 apr_file_t *from_file, *to_file;
880 apr_status_t apr_err;
881 const char *dst_tmp;
882 svn_error_t *err;
883
884 /* ### NOTE: sometimes src == dst. In this case, because we copy to a
885 ### temporary file, and then rename over the top of the destination,
886 ### the net result is resetting the permissions on src/dst.
887 ###
888 ### Note: specifically, this can happen during a switch when the desired
889 ### permissions for a file change from one branch to another. See
890 ### switch_tests 17.
891 ###
892 ### ... yes, we should avoid copying to the same file, and we should
893 ### make the "reset perms" explicit. The switch *happens* to work
894 ### because of this copy-to-temp-then-rename implementation. If it
895 ### weren't for that, the switch would break.
896 */
897 #ifdef CHECK_FOR_SAME_FILE
898 if (strcmp(src, dst) == 0)
899 return SVN_NO_ERROR;
900 #endif
901
902 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ,
903 APR_OS_DEFAULT, pool));
904
905 /* For atomicity, we copy to a tmp file and then rename the tmp
906 file over the real destination. */
907
908 SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp,
909 svn_dirent_dirname(dst, pool),
910 svn_io_file_del_none, pool, pool));
911
912 apr_err = copy_contents(from_file, to_file, pool);
913
914 if (apr_err)
915 {
916 err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"),
917 svn_dirent_local_style(src, pool),
918 svn_dirent_local_style(dst_tmp, pool));
919 }
920 else
921 err = NULL;
922
923 err = svn_error_compose_create(err,
924 svn_io_file_close(from_file, pool));
925
926 err = svn_error_compose_create(err,
927 svn_io_file_close(to_file, pool));
928
929 if (err)
930 {
931 return svn_error_compose_create(
932 err,
933 svn_io_remove_file2(dst_tmp, TRUE, pool));
934 }
935
936 /* If copying perms, set the perms on dst_tmp now, so they will be
937 atomically inherited in the upcoming rename. But note that we
938 had to wait until now to set perms, because if they say
939 read-only, then we'd have failed filling dst_tmp's contents. */
940 if (copy_perms)
941 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool));
942
943 return svn_error_trace(svn_io_file_rename2(dst_tmp, dst, FALSE, pool));
944 }
945
946 #if !defined(WIN32) && !defined(__OS2__)
947 /* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */
948 static svn_error_t *
file_perms_set(const char * fname,apr_fileperms_t perms,apr_pool_t * pool)949 file_perms_set(const char *fname, apr_fileperms_t perms,
950 apr_pool_t *pool)
951 {
952 const char *fname_apr;
953 apr_status_t status;
954
955 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
956
957 status = apr_file_perms_set(fname_apr, perms);
958 if (status)
959 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
960 fname);
961 else
962 return SVN_NO_ERROR;
963 }
964
965 /* Set permissions PERMS on the FILE. This is a cheaper variant of the
966 * file_perms_set wrapper() function because no locale-dependent string
967 * conversion is required. POOL will be used for allocations.
968 */
969 static svn_error_t *
file_perms_set2(apr_file_t * file,apr_fileperms_t perms,apr_pool_t * pool)970 file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool)
971 {
972 const char *fname_apr;
973 apr_status_t status;
974
975 status = apr_file_name_get(&fname_apr, file);
976 if (status)
977 return svn_error_wrap_apr(status, _("Can't get file name"));
978
979 status = apr_file_perms_set(fname_apr, perms);
980 if (status)
981 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
982 try_utf8_from_internal_style(fname_apr, pool));
983 else
984 return SVN_NO_ERROR;
985 }
986
987 #endif /* !WIN32 && !__OS2__ */
988
989 svn_error_t *
svn_io_copy_perms(const char * src,const char * dst,apr_pool_t * pool)990 svn_io_copy_perms(const char *src,
991 const char *dst,
992 apr_pool_t *pool)
993 {
994 /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL,
995 and the path passed to apr_file_perms_set must be encoded
996 in the platform-specific path encoding; not necessary UTF-8.
997 We need a platform-specific implementation to get the
998 permissions right. */
999
1000 #if !defined(WIN32) && !defined(__OS2__)
1001 {
1002 apr_finfo_t finfo;
1003 svn_node_kind_t kind;
1004 svn_boolean_t is_special;
1005 svn_error_t *err;
1006
1007 /* If DST is a symlink, don't bother copying permissions. */
1008 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool));
1009 if (is_special)
1010 return SVN_NO_ERROR;
1011
1012 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool));
1013 err = file_perms_set(dst, finfo.protection, pool);
1014 if (err)
1015 {
1016 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL
1017 here under normal circumstances, because the perms themselves
1018 came from a call to apr_file_info_get(), and we already know
1019 this is the non-Win32 case. But if it does happen, it's not
1020 an error. */
1021 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
1022 APR_STATUS_IS_ENOTIMPL(err->apr_err))
1023 svn_error_clear(err);
1024 else
1025 {
1026 return svn_error_quick_wrapf(
1027 err, _("Can't set permissions on '%s'"),
1028 svn_dirent_local_style(dst, pool));
1029 }
1030 }
1031 }
1032 #endif /* !WIN32 && !__OS2__ */
1033
1034 return SVN_NO_ERROR;
1035 }
1036
1037
1038 svn_error_t *
svn_io_append_file(const char * src,const char * dst,apr_pool_t * pool)1039 svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool)
1040 {
1041 apr_status_t apr_err;
1042 const char *src_apr, *dst_apr;
1043
1044 SVN_ERR(cstring_from_utf8(&src_apr, src, pool));
1045 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool));
1046
1047 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool);
1048
1049 if (apr_err)
1050 return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"),
1051 svn_dirent_local_style(src, pool),
1052 svn_dirent_local_style(dst, pool));
1053
1054 return SVN_NO_ERROR;
1055 }
1056
1057
svn_io_copy_dir_recursively(const char * src,const char * dst_parent,const char * dst_basename,svn_boolean_t copy_perms,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)1058 svn_error_t *svn_io_copy_dir_recursively(const char *src,
1059 const char *dst_parent,
1060 const char *dst_basename,
1061 svn_boolean_t copy_perms,
1062 svn_cancel_func_t cancel_func,
1063 void *cancel_baton,
1064 apr_pool_t *pool)
1065 {
1066 svn_node_kind_t kind;
1067 apr_status_t status;
1068 const char *dst_path;
1069 apr_dir_t *this_dir;
1070 apr_finfo_t this_entry;
1071 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
1072
1073 /* Make a subpool for recursion */
1074 apr_pool_t *subpool = svn_pool_create(pool);
1075
1076 /* The 'dst_path' is simply dst_parent/dst_basename */
1077 dst_path = svn_dirent_join(dst_parent, dst_basename, pool);
1078
1079 /* Sanity checks: SRC and DST_PARENT are directories, and
1080 DST_BASENAME doesn't already exist in DST_PARENT. */
1081 SVN_ERR(svn_io_check_path(src, &kind, subpool));
1082 if (kind != svn_node_dir)
1083 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1084 _("Source '%s' is not a directory"),
1085 svn_dirent_local_style(src, pool));
1086
1087 SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool));
1088 if (kind != svn_node_dir)
1089 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1090 _("Destination '%s' is not a directory"),
1091 svn_dirent_local_style(dst_parent, pool));
1092
1093 SVN_ERR(svn_io_check_path(dst_path, &kind, subpool));
1094 if (kind != svn_node_none)
1095 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
1096 _("Destination '%s' already exists"),
1097 svn_dirent_local_style(dst_path, pool));
1098
1099 /* Create the new directory. */
1100 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */
1101 SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool));
1102
1103 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */
1104 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool));
1105
1106 for (status = apr_dir_read(&this_entry, flags, this_dir);
1107 status == APR_SUCCESS;
1108 status = apr_dir_read(&this_entry, flags, this_dir))
1109 {
1110 if ((this_entry.name[0] == '.')
1111 && ((this_entry.name[1] == '\0')
1112 || ((this_entry.name[1] == '.')
1113 && (this_entry.name[2] == '\0'))))
1114 {
1115 continue;
1116 }
1117 else
1118 {
1119 const char *src_target, *entryname_utf8;
1120
1121 if (cancel_func)
1122 SVN_ERR(cancel_func(cancel_baton));
1123
1124 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name,
1125 src, subpool));
1126 src_target = svn_dirent_join(src, entryname_utf8, subpool);
1127
1128 if (this_entry.filetype == APR_REG) /* regular file */
1129 {
1130 const char *dst_target = svn_dirent_join(dst_path,
1131 entryname_utf8,
1132 subpool);
1133 SVN_ERR(svn_io_copy_file(src_target, dst_target,
1134 copy_perms, subpool));
1135 }
1136 else if (this_entry.filetype == APR_LNK) /* symlink */
1137 {
1138 const char *dst_target = svn_dirent_join(dst_path,
1139 entryname_utf8,
1140 subpool);
1141 SVN_ERR(svn_io_copy_link(src_target, dst_target,
1142 subpool));
1143 }
1144 else if (this_entry.filetype == APR_DIR) /* recurse */
1145 {
1146 /* Prevent infinite recursion by filtering off our
1147 newly created destination path. */
1148 if (strcmp(src, dst_parent) == 0
1149 && strcmp(entryname_utf8, dst_basename) == 0)
1150 continue;
1151
1152 SVN_ERR(svn_io_copy_dir_recursively
1153 (src_target,
1154 dst_path,
1155 entryname_utf8,
1156 copy_perms,
1157 cancel_func,
1158 cancel_baton,
1159 subpool));
1160 }
1161 /* ### support other APR node types someday?? */
1162
1163 }
1164 }
1165
1166 if (! (APR_STATUS_IS_ENOENT(status)))
1167 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
1168 svn_dirent_local_style(src, pool));
1169
1170 status = apr_dir_close(this_dir);
1171 if (status)
1172 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
1173 svn_dirent_local_style(src, pool));
1174
1175 /* Free any memory used by recursion */
1176 svn_pool_destroy(subpool);
1177
1178 return SVN_NO_ERROR;
1179 }
1180
1181
1182 svn_error_t *
svn_io_make_dir_recursively(const char * path,apr_pool_t * pool)1183 svn_io_make_dir_recursively(const char *path, apr_pool_t *pool)
1184 {
1185 const char *path_apr;
1186 apr_status_t apr_err;
1187
1188 if (svn_path_is_empty(path))
1189 /* Empty path (current dir) is assumed to always exist,
1190 so we do nothing, per docs. */
1191 return SVN_NO_ERROR;
1192
1193 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1194
1195 apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool);
1196 #ifdef WIN32
1197 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
1198 permanent error */
1199 if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
1200 WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr,
1201 APR_OS_DEFAULT, pool));
1202 #endif
1203
1204 if (apr_err)
1205 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"),
1206 svn_dirent_local_style(path, pool));
1207
1208 return SVN_NO_ERROR;
1209 }
1210
1211 svn_error_t *
svn_io_file_create_bytes(const char * file,const void * contents,apr_size_t length,apr_pool_t * scratch_pool)1212 svn_io_file_create_bytes(const char *file,
1213 const void *contents,
1214 apr_size_t length,
1215 apr_pool_t *scratch_pool)
1216 {
1217 apr_file_t *f;
1218 apr_size_t written;
1219 svn_error_t *err = SVN_NO_ERROR;
1220
1221 SVN_ERR(svn_io_file_open(&f, file,
1222 (APR_WRITE | APR_CREATE | APR_EXCL),
1223 APR_OS_DEFAULT,
1224 scratch_pool));
1225 if (length)
1226 err = svn_io_file_write_full(f, contents, length, &written,
1227 scratch_pool);
1228
1229 err = svn_error_compose_create(
1230 err,
1231 svn_io_file_close(f, scratch_pool));
1232
1233 if (err)
1234 {
1235 /* Our caller doesn't know if we left a file or not if we return
1236 an error. Better to cleanup after ourselves if we created the
1237 file. */
1238 return svn_error_trace(
1239 svn_error_compose_create(
1240 err,
1241 svn_io_remove_file2(file, TRUE, scratch_pool)));
1242 }
1243
1244 return SVN_NO_ERROR;
1245 }
1246
1247 svn_error_t *
svn_io_file_create(const char * file,const char * contents,apr_pool_t * pool)1248 svn_io_file_create(const char *file,
1249 const char *contents,
1250 apr_pool_t *pool)
1251 {
1252 return svn_error_trace(svn_io_file_create_bytes(file, contents,
1253 contents ? strlen(contents)
1254 : 0,
1255 pool));
1256 }
1257
1258 svn_error_t *
svn_io_file_create_empty(const char * file,apr_pool_t * scratch_pool)1259 svn_io_file_create_empty(const char *file,
1260 apr_pool_t *scratch_pool)
1261 {
1262 return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0,
1263 scratch_pool));
1264 }
1265
1266 svn_error_t *
svn_io_dir_file_copy(const char * src_path,const char * dest_path,const char * file,apr_pool_t * pool)1267 svn_io_dir_file_copy(const char *src_path,
1268 const char *dest_path,
1269 const char *file,
1270 apr_pool_t *pool)
1271 {
1272 const char *file_dest_path = svn_dirent_join(dest_path, file, pool);
1273 const char *file_src_path = svn_dirent_join(src_path, file, pool);
1274
1275 return svn_error_trace(
1276 svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool));
1277 }
1278
1279
1280 /*** Modtime checking. ***/
1281
1282 svn_error_t *
svn_io_file_affected_time(apr_time_t * apr_time,const char * path,apr_pool_t * pool)1283 svn_io_file_affected_time(apr_time_t *apr_time,
1284 const char *path,
1285 apr_pool_t *pool)
1286 {
1287 apr_finfo_t finfo;
1288
1289 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
1290
1291 *apr_time = finfo.mtime;
1292
1293 return SVN_NO_ERROR;
1294 }
1295
1296
1297 svn_error_t *
svn_io_set_file_affected_time(apr_time_t apr_time,const char * path,apr_pool_t * pool)1298 svn_io_set_file_affected_time(apr_time_t apr_time,
1299 const char *path,
1300 apr_pool_t *pool)
1301 {
1302 apr_status_t status;
1303 const char *native_path;
1304
1305 SVN_ERR(cstring_from_utf8(&native_path, path, pool));
1306 status = apr_file_mtime_set(native_path, apr_time, pool);
1307
1308 if (status)
1309 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"),
1310 svn_dirent_local_style(path, pool));
1311
1312 return SVN_NO_ERROR;
1313 }
1314
1315
1316 void
svn_io_sleep_for_timestamps(const char * path,apr_pool_t * pool)1317 svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool)
1318 {
1319 apr_time_t now, then;
1320 svn_error_t *err;
1321 char *sleep_env_var;
1322
1323 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR);
1324
1325 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0)
1326 return; /* Allow skipping for testing */
1327
1328 now = apr_time_now();
1329
1330 /* Calculate 0.02 seconds after the next second wallclock tick. */
1331 then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50);
1332
1333 /* Worst case is waiting one second, so we can use that time to determine
1334 if we can sleep shorter than that */
1335 if (path)
1336 {
1337 apr_finfo_t finfo;
1338
1339 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool);
1340
1341 if (err)
1342 {
1343 svn_error_clear(err); /* Fall back on original behavior */
1344 }
1345 else if (finfo.mtime % APR_USEC_PER_SEC)
1346 {
1347 /* Very simplistic but safe approach:
1348 If the filesystem has < sec mtime we can be reasonably sure
1349 that the filesystem has some sub-second resolution. On Windows
1350 it is likely to be sub-millisecond; on Linux systems it depends
1351 on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution.
1352
1353 ## Perhaps find a better algorithm here. This will fail once
1354 in every 1000 cases on a millisecond precision filesystem
1355 if the mtime happens to be an exact second.
1356
1357 But better to fail once in every thousand cases than every
1358 time, like we did before.
1359
1360 Note for further research on algorithm:
1361 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime.
1362
1363 Linux/ext4 with CONFIG_HZ=250 has high resolution
1364 apr_time_now and although the filesystem timestamps
1365 have similar high precision they are only updated with
1366 a coarser 4ms resolution. */
1367
1368 /* 10 milliseconds after now. */
1369 #ifndef SVN_HI_RES_SLEEP_MS
1370 #define SVN_HI_RES_SLEEP_MS 10
1371 #endif
1372 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS);
1373 }
1374
1375 /* Remove time taken to do stat() from sleep. */
1376 now = apr_time_now();
1377 }
1378
1379 if (now >= then)
1380 return; /* Passing negative values may suspend indefinitely (Windows) */
1381
1382 /* (t < 1000 will be round to 0 in apr) */
1383 if (then - now < 1000)
1384 apr_sleep(1000);
1385 else
1386 apr_sleep(then - now);
1387 }
1388
1389
1390 svn_error_t *
svn_io_filesizes_different_p(svn_boolean_t * different_p,const char * file1,const char * file2,apr_pool_t * pool)1391 svn_io_filesizes_different_p(svn_boolean_t *different_p,
1392 const char *file1,
1393 const char *file2,
1394 apr_pool_t *pool)
1395 {
1396 apr_finfo_t finfo1;
1397 apr_finfo_t finfo2;
1398 apr_status_t status;
1399 const char *file1_apr, *file2_apr;
1400
1401 /* Not using svn_io_stat() because don't want to generate
1402 svn_error_t objects for non-error conditions. */
1403
1404 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool));
1405 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool));
1406
1407 /* Stat both files */
1408 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool);
1409 if (status)
1410 {
1411 /* If we got an error stat'ing a file, it could be because the
1412 file was removed... or who knows. Whatever the case, we
1413 don't know if the filesizes are definitely different, so
1414 assume that they're not. */
1415 *different_p = FALSE;
1416 return SVN_NO_ERROR;
1417 }
1418
1419 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool);
1420 if (status)
1421 {
1422 /* See previous comment. */
1423 *different_p = FALSE;
1424 return SVN_NO_ERROR;
1425 }
1426
1427 /* Examine file sizes */
1428 if (finfo1.size == finfo2.size)
1429 *different_p = FALSE;
1430 else
1431 *different_p = TRUE;
1432
1433 return SVN_NO_ERROR;
1434 }
1435
1436
1437 svn_error_t *
svn_io_filesizes_three_different_p(svn_boolean_t * different_p12,svn_boolean_t * different_p23,svn_boolean_t * different_p13,const char * file1,const char * file2,const char * file3,apr_pool_t * scratch_pool)1438 svn_io_filesizes_three_different_p(svn_boolean_t *different_p12,
1439 svn_boolean_t *different_p23,
1440 svn_boolean_t *different_p13,
1441 const char *file1,
1442 const char *file2,
1443 const char *file3,
1444 apr_pool_t *scratch_pool)
1445 {
1446 apr_finfo_t finfo1, finfo2, finfo3;
1447 apr_status_t status1, status2, status3;
1448 const char *file1_apr, *file2_apr, *file3_apr;
1449
1450 /* Not using svn_io_stat() because don't want to generate
1451 svn_error_t objects for non-error conditions. */
1452
1453 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool));
1454 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool));
1455 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool));
1456
1457 /* Stat all three files */
1458 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool);
1459 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool);
1460 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool);
1461
1462 /* If we got an error stat'ing a file, it could be because the
1463 file was removed... or who knows. Whatever the case, we
1464 don't know if the filesizes are definitely different, so
1465 assume that they're not. */
1466 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size;
1467 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size;
1468 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size;
1469
1470 return SVN_NO_ERROR;
1471 }
1472
1473
1474 svn_error_t *
svn_io_file_checksum2(svn_checksum_t ** checksum,const char * file,svn_checksum_kind_t kind,apr_pool_t * pool)1475 svn_io_file_checksum2(svn_checksum_t **checksum,
1476 const char *file,
1477 svn_checksum_kind_t kind,
1478 apr_pool_t *pool)
1479 {
1480 svn_stream_t *file_stream;
1481 apr_file_t* f;
1482
1483 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool));
1484 file_stream = svn_stream_from_aprfile2(f, FALSE, pool);
1485 SVN_ERR(svn_stream_contents_checksum(checksum, file_stream, kind,
1486 pool, pool));
1487
1488 return SVN_NO_ERROR;
1489 }
1490
1491
1492 svn_error_t *
svn_io_file_checksum(unsigned char digest[],const char * file,apr_pool_t * pool)1493 svn_io_file_checksum(unsigned char digest[],
1494 const char *file,
1495 apr_pool_t *pool)
1496 {
1497 svn_checksum_t *checksum;
1498
1499 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool));
1500 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE);
1501
1502 return SVN_NO_ERROR;
1503 }
1504
1505
1506
1507 /*** Permissions and modes. ***/
1508
1509 #if !defined(WIN32) && !defined(__OS2__)
1510 /* Given the file specified by PATH, attempt to create an
1511 identical version of it owned by the current user. This is done by
1512 moving it to a temporary location, copying the file back to its old
1513 path, then deleting the temporarily moved version. All temporary
1514 allocations are done in POOL. */
1515 static svn_error_t *
reown_file(const char * path,apr_pool_t * pool)1516 reown_file(const char *path,
1517 apr_pool_t *pool)
1518 {
1519 const char *unique_name;
1520
1521 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name,
1522 svn_dirent_dirname(path, pool),
1523 svn_io_file_del_none, pool, pool));
1524 SVN_ERR(svn_io_file_rename2(path, unique_name, FALSE, pool));
1525 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool));
1526 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool));
1527 }
1528
1529 /* Determine what the PERMS for a new file should be by looking at the
1530 permissions of a temporary file that we create in DIRECTORY.
1531 DIRECTORY can be NULL in which case the system temporary dir is used.
1532 Unfortunately, umask() as defined in POSIX provides no thread-safe way
1533 to get at the current value of the umask, so what we're doing here is
1534 the only way we have to determine which combination of write bits
1535 (User/Group/World) should be set by default.
1536 Make temporary allocations in SCRATCH_POOL. */
1537 static svn_error_t *
get_default_file_perms(apr_fileperms_t * perms,const char * directory,apr_pool_t * scratch_pool)1538 get_default_file_perms(apr_fileperms_t *perms,
1539 const char *directory,
1540 apr_pool_t *scratch_pool)
1541 {
1542 /* the default permissions as read from the temp folder */
1543 static apr_fileperms_t default_perms = 0;
1544
1545 /* Technically, this "racy": Multiple threads may use enter here and
1546 try to figure out the default permission concurrently. That's fine
1547 since they will end up with the same results. Even more technical,
1548 apr_fileperms_t is an atomic type on 32+ bit machines.
1549 */
1550 if (default_perms == 0)
1551 {
1552 apr_finfo_t finfo;
1553 apr_file_t *fd;
1554 const char *fname_base, *fname;
1555 apr_uint32_t randomish;
1556 svn_error_t *err;
1557
1558 /* Get the perms for a newly created file to find out what bits
1559 should be set.
1560
1561 Explicitly delete the file because we want this file to be as
1562 short-lived as possible since its presence means other
1563 processes may have to try multiple names.
1564
1565 Using svn_io_open_uniquely_named() here because other tempfile
1566 creation functions tweak the permission bits of files they create.
1567
1568 Note that APR pool structures are allocated as the first item
1569 in their first memory page (with e.g. 4kB granularity), i.e. the
1570 lower bits tend to be identical between pool instances. That is
1571 particularly true for the MMAPed allocator.
1572 */
1573 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool
1574 + (apr_uint32_t)((apr_uintptr_t)scratch_pool / 4096)
1575 + (apr_uint32_t)apr_time_now());
1576 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish);
1577
1578 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, directory, fname_base,
1579 NULL, svn_io_file_del_none,
1580 scratch_pool, scratch_pool));
1581 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool);
1582 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool));
1583 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE,
1584 scratch_pool));
1585 SVN_ERR(err);
1586 *perms = finfo.protection;
1587 default_perms = finfo.protection;
1588 }
1589 else
1590 *perms = default_perms;
1591
1592 return SVN_NO_ERROR;
1593 }
1594
1595 /* OR together permission bits of the file FD and the default permissions
1596 of a file as determined by get_default_file_perms(). DIRECTORY is used
1597 to create temporary files, DIRECTORY can be NULL. Do temporary
1598 allocations in SCRATCH_POOL. */
1599 static svn_error_t *
merge_default_file_perms(apr_file_t * fd,apr_fileperms_t * perms,const char * directory,apr_pool_t * scratch_pool)1600 merge_default_file_perms(apr_file_t *fd,
1601 apr_fileperms_t *perms,
1602 const char *directory,
1603 apr_pool_t *scratch_pool)
1604 {
1605 apr_finfo_t finfo;
1606 apr_fileperms_t default_perms;
1607
1608 SVN_ERR(get_default_file_perms(&default_perms, directory, scratch_pool));
1609 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool));
1610
1611 /* Glom the perms together. */
1612 *perms = default_perms | finfo.protection;
1613 return SVN_NO_ERROR;
1614 }
1615
1616 /* This is a helper function for the svn_io_set_file_read* functions
1617 that attempts to honor the users umask when dealing with
1618 permission changes. It is a no-op when invoked on a symlink. */
1619 static svn_error_t *
io_set_file_perms(const char * path,svn_boolean_t change_readwrite,svn_boolean_t enable_write,svn_boolean_t change_executable,svn_boolean_t executable,svn_boolean_t ignore_enoent,apr_pool_t * pool)1620 io_set_file_perms(const char *path,
1621 svn_boolean_t change_readwrite,
1622 svn_boolean_t enable_write,
1623 svn_boolean_t change_executable,
1624 svn_boolean_t executable,
1625 svn_boolean_t ignore_enoent,
1626 apr_pool_t *pool)
1627 {
1628 apr_status_t status;
1629 const char *path_apr;
1630 apr_finfo_t finfo;
1631 apr_fileperms_t perms_to_set;
1632
1633 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
1634
1635 /* Try to change only a minimal amount of the perms first
1636 by getting the current perms and adding bits
1637 only on where read perms are granted. If this fails
1638 fall through to just setting file attributes. */
1639 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool);
1640 if (status)
1641 {
1642 if (ignore_enoent && (APR_STATUS_IS_ENOENT(status)
1643 || SVN__APR_STATUS_IS_ENOTDIR(status)))
1644 return SVN_NO_ERROR;
1645 else if (status != APR_ENOTIMPL)
1646 return svn_error_wrap_apr(status,
1647 _("Can't change perms of file '%s'"),
1648 svn_dirent_local_style(path, pool));
1649 return SVN_NO_ERROR;
1650 }
1651
1652 if (finfo.filetype == APR_LNK)
1653 return SVN_NO_ERROR;
1654
1655 perms_to_set = finfo.protection;
1656 if (change_readwrite)
1657 {
1658 if (enable_write) /* Make read-write. */
1659 {
1660 /* Tweak the owner bits only. The group/other bits aren't safe to
1661 * touch because we may end up setting them in undesired ways. */
1662 perms_to_set |= (APR_UREAD|APR_UWRITE);
1663 }
1664 else
1665 {
1666 if (finfo.protection & APR_UREAD)
1667 perms_to_set &= ~APR_UWRITE;
1668 if (finfo.protection & APR_GREAD)
1669 perms_to_set &= ~APR_GWRITE;
1670 if (finfo.protection & APR_WREAD)
1671 perms_to_set &= ~APR_WWRITE;
1672 }
1673 }
1674
1675 if (change_executable)
1676 {
1677 if (executable)
1678 {
1679 if (finfo.protection & APR_UREAD)
1680 perms_to_set |= APR_UEXECUTE;
1681 if (finfo.protection & APR_GREAD)
1682 perms_to_set |= APR_GEXECUTE;
1683 if (finfo.protection & APR_WREAD)
1684 perms_to_set |= APR_WEXECUTE;
1685 }
1686 else
1687 {
1688 if (finfo.protection & APR_UREAD)
1689 perms_to_set &= ~APR_UEXECUTE;
1690 if (finfo.protection & APR_GREAD)
1691 perms_to_set &= ~APR_GEXECUTE;
1692 if (finfo.protection & APR_WREAD)
1693 perms_to_set &= ~APR_WEXECUTE;
1694 }
1695 }
1696
1697 /* If we aren't changing anything then just return, this saves
1698 some system calls and helps with shared working copies */
1699 if (perms_to_set == finfo.protection)
1700 return SVN_NO_ERROR;
1701
1702 status = apr_file_perms_set(path_apr, perms_to_set);
1703 if (!status)
1704 return SVN_NO_ERROR;
1705
1706 if (APR_STATUS_IS_EPERM(status))
1707 {
1708 /* We don't have permissions to change the
1709 permissions! Try a move, copy, and delete
1710 workaround to see if we can get the file owned by
1711 us. If these succeed, try the permissions set
1712 again.
1713
1714 Note that we only attempt this in the
1715 stat-available path. This assumes that the
1716 move-copy workaround will only be helpful on
1717 platforms that implement apr_stat. */
1718 SVN_ERR(reown_file(path, pool));
1719 status = apr_file_perms_set(path_apr, perms_to_set);
1720 }
1721
1722 if (!status)
1723 return SVN_NO_ERROR;
1724
1725 if (ignore_enoent && APR_STATUS_IS_ENOENT(status))
1726 return SVN_NO_ERROR;
1727 else if (status == APR_ENOTIMPL)
1728 {
1729 /* At least try to set the attributes. */
1730 apr_fileattrs_t attrs = 0;
1731 apr_fileattrs_t attrs_values = 0;
1732
1733 if (change_readwrite)
1734 {
1735 attrs = APR_FILE_ATTR_READONLY;
1736 if (!enable_write)
1737 attrs_values = APR_FILE_ATTR_READONLY;
1738 }
1739 if (change_executable)
1740 {
1741 attrs = APR_FILE_ATTR_EXECUTABLE;
1742 if (executable)
1743 attrs_values = APR_FILE_ATTR_EXECUTABLE;
1744 }
1745 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool);
1746 }
1747
1748 return svn_error_wrap_apr(status,
1749 _("Can't change perms of file '%s'"),
1750 svn_dirent_local_style(path, pool));
1751 }
1752 #endif /* !WIN32 && !__OS2__ */
1753
1754 #ifdef WIN32
1755 /* This is semantically the same as the APR utf8_to_unicode_path
1756 function, but reimplemented here because APR does not export it. */
1757 svn_error_t*
svn_io__utf8_to_unicode_longpath(const WCHAR ** result,const char * source,apr_pool_t * result_pool)1758 svn_io__utf8_to_unicode_longpath(const WCHAR **result,
1759 const char *source,
1760 apr_pool_t *result_pool)
1761 {
1762 /* This is correct, we don't twist the filename if it will
1763 * definitely be shorter than 248 characters. It merits some
1764 * performance testing to see if this has any effect, but there
1765 * seem to be applications that get confused by the resulting
1766 * Unicode \\?\ style file names, especially if they use argv[0]
1767 * or call the Win32 API functions such as GetModuleName, etc.
1768 * Not every application is prepared to handle such names.
1769 *
1770 * Note also this is shorter than MAX_PATH, as directory paths
1771 * are actually limited to 248 characters.
1772 *
1773 * Note that a utf-8 name can never result in more wide chars
1774 * than the original number of utf-8 narrow chars.
1775 */
1776 const WCHAR *prefix = NULL;
1777 const size_t srclen = strlen(source);
1778 WCHAR *buffer;
1779
1780 if (srclen > 248)
1781 {
1782 if (svn_ctype_isalpha(source[0]) && source[1] == ':'
1783 && (source[2] == '/' || source[2] == '\\'))
1784 {
1785 /* This is an ordinary absolute path. */
1786 prefix = L"\\\\?\\";
1787 }
1788 else if ((source[0] == '/' || source[0] == '\\')
1789 && (source[1] == '/' || source[1] == '\\')
1790 && source[2] != '?')
1791 {
1792 /* This is a UNC path */
1793 source += 2; /* Skip the leading slashes */
1794 prefix = L"\\\\?\\UNC\\";
1795 }
1796 }
1797
1798 SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source,
1799 prefix, result_pool));
1800
1801 /* Convert slashes to backslashes because the \\?\ path format
1802 does not allow backslashes as path separators. */
1803 *result = buffer;
1804 for (; *buffer; ++buffer)
1805 {
1806 if (*buffer == '/')
1807 *buffer = '\\';
1808 }
1809 return SVN_NO_ERROR;
1810 }
1811
1812 /* This is semantically the same as the APR unicode_to_utf8_path
1813 function, but reimplemented here because APR does not export it. */
1814 static svn_error_t *
io_unicode_to_utf8_path(const char ** result,const WCHAR * source,apr_pool_t * result_pool)1815 io_unicode_to_utf8_path(const char **result,
1816 const WCHAR *source,
1817 apr_pool_t *result_pool)
1818 {
1819 const char *utf8_buffer;
1820 char *buffer;
1821
1822 SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source,
1823 NULL, result_pool));
1824 if (!*utf8_buffer)
1825 {
1826 *result = utf8_buffer;
1827 return SVN_NO_ERROR;
1828 }
1829
1830 /* We know that the non-empty buffer returned from the UTF-16 to
1831 UTF-8 conversion function is in fact writable. */
1832 buffer = (char*)utf8_buffer;
1833
1834 /* Skip the leading 4 characters if the path begins \\?\, or substitute
1835 * // for the \\?\UNC\ path prefix, allocating the maximum string
1836 * length based on the remaining string, plus the trailing null.
1837 * then transform \\'s back into /'s since the \\?\ form never
1838 * allows '/' path separators, and APR always uses '/'s.
1839 */
1840 if (0 == strncmp(buffer, "\\\\?\\", 4))
1841 {
1842 buffer += 4;
1843 if (0 == strncmp(buffer, "UNC\\", 4))
1844 {
1845 buffer += 2;
1846 *buffer = '/';
1847 }
1848 }
1849
1850 *result = buffer;
1851 for (; *buffer; ++buffer)
1852 {
1853 if (*buffer == '\\')
1854 *buffer = '/';
1855 }
1856 return SVN_NO_ERROR;
1857 }
1858
1859 static svn_error_t *
io_win_file_attrs_set(const char * fname,DWORD attributes,DWORD attr_mask,apr_pool_t * pool)1860 io_win_file_attrs_set(const char *fname,
1861 DWORD attributes,
1862 DWORD attr_mask,
1863 apr_pool_t *pool)
1864 {
1865 /* this is an implementation of apr_file_attrs_set() but one
1866 that uses the proper Windows attributes instead of the apr
1867 attributes. This way, we can apply any Windows file and
1868 folder attributes even if apr doesn't implement them */
1869 DWORD flags;
1870 const WCHAR *wfname;
1871
1872 SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool));
1873
1874 flags = GetFileAttributesW(wfname);
1875 if (flags == 0xFFFFFFFF)
1876 return svn_error_wrap_apr(apr_get_os_error(),
1877 _("Can't get attributes of file '%s'"),
1878 svn_dirent_local_style(fname, pool));
1879
1880 flags &= ~attr_mask;
1881 flags |= (attributes & attr_mask);
1882
1883 if (!SetFileAttributesW(wfname, flags))
1884 return svn_error_wrap_apr(apr_get_os_error(),
1885 _("Can't set attributes of file '%s'"),
1886 svn_dirent_local_style(fname, pool));
1887
1888 return SVN_NO_ERROR;
1889 }
1890
win_init_dynamic_imports(void * baton,apr_pool_t * pool)1891 static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool)
1892 {
1893 HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
1894
1895 if (kernel32)
1896 {
1897 get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE)
1898 GetProcAddress(kernel32, "GetFinalPathNameByHandleW");
1899
1900 set_file_information_by_handle_proc = (SetFileInformationByHandle_t)
1901 GetProcAddress(kernel32, "SetFileInformationByHandle");
1902 }
1903
1904 return SVN_NO_ERROR;
1905 }
1906
io_win_read_link(svn_string_t ** dest,const char * path,apr_pool_t * pool)1907 static svn_error_t * io_win_read_link(svn_string_t **dest,
1908 const char *path,
1909 apr_pool_t *pool)
1910 {
1911 SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state,
1912 win_init_dynamic_imports, NULL, pool));
1913
1914 if (get_final_path_name_by_handle_proc)
1915 {
1916 DWORD rv;
1917 apr_status_t status;
1918 apr_file_t *file;
1919 apr_os_file_t filehand;
1920 WCHAR wdest[APR_PATH_MAX];
1921 const char *data;
1922
1923 /* reserve one char for terminating zero. */
1924 DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1;
1925
1926 status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool);
1927
1928 if (status)
1929 return svn_error_wrap_apr(status,
1930 _("Can't read contents of link"));
1931
1932 apr_os_file_get(&filehand, file);
1933
1934 rv = get_final_path_name_by_handle_proc(
1935 filehand, wdest, wdest_len,
1936 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
1937
1938 /* Save error code. */
1939 status = apr_get_os_error();
1940
1941 /* Close file/directory handle in any case. */
1942 apr_file_close(file);
1943
1944 /* GetFinaPathNameByHandleW returns number of characters copied to
1945 * output buffer. Returns zero on error. Returns required buffer size
1946 * if supplied buffer is not enough. */
1947 if (rv > wdest_len || rv == 0)
1948 {
1949 return svn_error_wrap_apr(status,
1950 _("Can't read contents of link"));
1951 }
1952
1953 /* GetFinaPathNameByHandleW doesn't add terminating NUL. */
1954 wdest[rv] = 0;
1955 SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool));
1956
1957 /* The result is already in the correct pool, so avoid copying
1958 it to create the string. */
1959 *dest = svn_string_create_empty(pool);
1960 if (*data)
1961 {
1962 (*dest)->data = data;
1963 (*dest)->len = strlen(data);
1964 }
1965
1966 return SVN_NO_ERROR;
1967 }
1968 else
1969 {
1970 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1971 _("Symbolic links are not supported on this "
1972 "platform"));
1973 }
1974 }
1975
1976 /* Wrapper around Windows API function SetFileInformationByHandle() that
1977 * returns APR status instead of boolean flag. */
1978 static apr_status_t
win32_set_file_information_by_handle(HANDLE hFile,int FileInformationClass,LPVOID lpFileInformation,DWORD dwBufferSize)1979 win32_set_file_information_by_handle(HANDLE hFile,
1980 int FileInformationClass,
1981 LPVOID lpFileInformation,
1982 DWORD dwBufferSize)
1983 {
1984 svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state,
1985 win_init_dynamic_imports,
1986 NULL, NULL));
1987
1988 if (!set_file_information_by_handle_proc)
1989 {
1990 return SVN_ERR_UNSUPPORTED_FEATURE;
1991 }
1992
1993 if (!set_file_information_by_handle_proc(hFile, FileInformationClass,
1994 lpFileInformation,
1995 dwBufferSize))
1996 {
1997 return apr_get_os_error();
1998 }
1999
2000 return APR_SUCCESS;
2001 }
2002
2003 svn_error_t *
svn_io__win_delete_file_on_close(apr_file_t * file,const char * path,apr_pool_t * pool)2004 svn_io__win_delete_file_on_close(apr_file_t *file,
2005 const char *path,
2006 apr_pool_t *pool)
2007 {
2008 FILE_DISPOSITION_INFO disposition_info;
2009 HANDLE hFile;
2010 apr_status_t status;
2011
2012 apr_os_file_get(&hFile, file);
2013
2014 disposition_info.DeleteFile = TRUE;
2015
2016 status = win32_set_file_information_by_handle(hFile, FileDispositionInfo,
2017 &disposition_info,
2018 sizeof(disposition_info));
2019
2020 if (status)
2021 {
2022 return svn_error_wrap_apr(status, _("Can't remove file '%s'"),
2023 svn_dirent_local_style(path, pool));
2024 }
2025
2026 return SVN_NO_ERROR;
2027 }
2028
2029 svn_error_t *
svn_io__win_rename_open_file(apr_file_t * file,const char * from_path,const char * to_path,apr_pool_t * pool)2030 svn_io__win_rename_open_file(apr_file_t *file,
2031 const char *from_path,
2032 const char *to_path,
2033 apr_pool_t *pool)
2034 {
2035 WCHAR *w_final_abspath;
2036 size_t path_len;
2037 size_t rename_size;
2038 FILE_RENAME_INFO *rename_info;
2039 HANDLE hFile;
2040 apr_status_t status;
2041
2042 apr_os_file_get(&hFile, file);
2043
2044 SVN_ERR(svn_io__utf8_to_unicode_longpath(
2045 &w_final_abspath, svn_dirent_local_style(to_path,pool),
2046 pool));
2047
2048 path_len = wcslen(w_final_abspath);
2049 rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len;
2050
2051 /* The rename info struct doesn't need hacks for long paths,
2052 so no ugly escaping calls here */
2053 rename_info = apr_pcalloc(pool, rename_size);
2054 rename_info->ReplaceIfExists = TRUE;
2055 rename_info->FileNameLength = path_len;
2056 memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR));
2057
2058 status = win32_set_file_information_by_handle(hFile, FileRenameInfo,
2059 rename_info,
2060 rename_size);
2061
2062 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
2063 {
2064 /* Set the destination file writable because Windows will not allow
2065 us to rename when final_abspath is read-only. */
2066 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
2067
2068 status = win32_set_file_information_by_handle(hFile,
2069 FileRenameInfo,
2070 rename_info,
2071 rename_size);
2072 }
2073
2074 /* Windows returns Vista+ client accessing network share stored on Windows
2075 Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+
2076 client access Windows Server 2008 with disabled SMBv2 protocol.
2077
2078 So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when
2079 SetFileInformationByHandle() is not available and let caller to
2080 handle it.
2081
2082 See "Access denied error on checkout-commit after updating to 1.9.X"
2083 discussion on [email protected]:
2084 http://svn.haxx.se/dev/archive-2015-09/0054.shtml */
2085 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
2086 {
2087 status = SVN_ERR_UNSUPPORTED_FEATURE;
2088 }
2089
2090 if (status)
2091 {
2092 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
2093 svn_dirent_local_style(from_path, pool),
2094 svn_dirent_local_style(to_path, pool));
2095 }
2096
2097 return SVN_NO_ERROR;
2098 }
2099
2100 #endif /* WIN32 */
2101
2102 svn_error_t *
svn_io_set_file_read_write_carefully(const char * path,svn_boolean_t enable_write,svn_boolean_t ignore_enoent,apr_pool_t * pool)2103 svn_io_set_file_read_write_carefully(const char *path,
2104 svn_boolean_t enable_write,
2105 svn_boolean_t ignore_enoent,
2106 apr_pool_t *pool)
2107 {
2108 if (enable_write)
2109 return svn_io_set_file_read_write(path, ignore_enoent, pool);
2110 return svn_io_set_file_read_only(path, ignore_enoent, pool);
2111 }
2112
2113 svn_error_t *
svn_io_set_file_read_only(const char * path,svn_boolean_t ignore_enoent,apr_pool_t * pool)2114 svn_io_set_file_read_only(const char *path,
2115 svn_boolean_t ignore_enoent,
2116 apr_pool_t *pool)
2117 {
2118 /* On Windows and OS/2, just set the file attributes -- on unix call
2119 our internal function which attempts to honor the umask. */
2120 #if !defined(WIN32) && !defined(__OS2__)
2121 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE,
2122 ignore_enoent, pool);
2123 #else
2124 apr_status_t status;
2125 const char *path_apr;
2126
2127 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2128
2129 status = apr_file_attrs_set(path_apr,
2130 APR_FILE_ATTR_READONLY,
2131 APR_FILE_ATTR_READONLY,
2132 pool);
2133
2134 if (status && status != APR_ENOTIMPL)
2135 if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status)
2136 || SVN__APR_STATUS_IS_ENOTDIR(status))))
2137 return svn_error_wrap_apr(status,
2138 _("Can't set file '%s' read-only"),
2139 svn_dirent_local_style(path, pool));
2140
2141 return SVN_NO_ERROR;
2142 #endif
2143 }
2144
2145
2146 svn_error_t *
svn_io_set_file_read_write(const char * path,svn_boolean_t ignore_enoent,apr_pool_t * pool)2147 svn_io_set_file_read_write(const char *path,
2148 svn_boolean_t ignore_enoent,
2149 apr_pool_t *pool)
2150 {
2151 /* On Windows and OS/2, just set the file attributes -- on unix call
2152 our internal function which attempts to honor the umask. */
2153 #if !defined(WIN32) && !defined(__OS2__)
2154 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE,
2155 ignore_enoent, pool);
2156 #else
2157 apr_status_t status;
2158 const char *path_apr;
2159
2160 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
2161
2162 status = apr_file_attrs_set(path_apr,
2163 0,
2164 APR_FILE_ATTR_READONLY,
2165 pool);
2166
2167 if (status && status != APR_ENOTIMPL)
2168 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status))
2169 return svn_error_wrap_apr(status,
2170 _("Can't set file '%s' read-write"),
2171 svn_dirent_local_style(path, pool));
2172
2173 return SVN_NO_ERROR;
2174 #endif
2175 }
2176
2177 svn_error_t *
svn_io_set_file_executable(const char * path,svn_boolean_t executable,svn_boolean_t ignore_enoent,apr_pool_t * pool)2178 svn_io_set_file_executable(const char *path,
2179 svn_boolean_t executable,
2180 svn_boolean_t ignore_enoent,
2181 apr_pool_t *pool)
2182 {
2183 /* On Windows and OS/2, just exit -- on unix call our internal function
2184 which attempts to honor the umask. */
2185 #if (!defined(WIN32) && !defined(__OS2__))
2186 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable,
2187 ignore_enoent, pool);
2188 #else
2189 return SVN_NO_ERROR;
2190 #endif
2191 }
2192
2193
2194 svn_error_t *
svn_io__is_finfo_read_only(svn_boolean_t * read_only,apr_finfo_t * file_info,apr_pool_t * pool)2195 svn_io__is_finfo_read_only(svn_boolean_t *read_only,
2196 apr_finfo_t *file_info,
2197 apr_pool_t *pool)
2198 {
2199 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2200 apr_status_t apr_err;
2201 apr_uid_t uid;
2202 apr_gid_t gid;
2203
2204 *read_only = FALSE;
2205
2206 apr_err = apr_uid_current(&uid, &gid, pool);
2207
2208 if (apr_err)
2209 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2210
2211 /* Check write bit for current user. */
2212 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2213 *read_only = !(file_info->protection & APR_UWRITE);
2214
2215 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2216 *read_only = !(file_info->protection & APR_GWRITE);
2217
2218 else
2219 *read_only = !(file_info->protection & APR_WWRITE);
2220
2221 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2222 *read_only = (file_info->protection & APR_FREADONLY);
2223 #endif
2224
2225 return SVN_NO_ERROR;
2226 }
2227
2228 svn_error_t *
svn_io__is_finfo_executable(svn_boolean_t * executable,apr_finfo_t * file_info,apr_pool_t * pool)2229 svn_io__is_finfo_executable(svn_boolean_t *executable,
2230 apr_finfo_t *file_info,
2231 apr_pool_t *pool)
2232 {
2233 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2234 apr_status_t apr_err;
2235 apr_uid_t uid;
2236 apr_gid_t gid;
2237
2238 *executable = FALSE;
2239
2240 apr_err = apr_uid_current(&uid, &gid, pool);
2241
2242 if (apr_err)
2243 return svn_error_wrap_apr(apr_err, _("Error getting UID of process"));
2244
2245 /* Check executable bit for current user. */
2246 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS)
2247 *executable = (file_info->protection & APR_UEXECUTE);
2248
2249 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS)
2250 *executable = (file_info->protection & APR_GEXECUTE);
2251
2252 else
2253 *executable = (file_info->protection & APR_WEXECUTE);
2254
2255 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2256 *executable = FALSE;
2257 #endif
2258
2259 return SVN_NO_ERROR;
2260 }
2261
2262 svn_error_t *
svn_io_is_file_executable(svn_boolean_t * executable,const char * path,apr_pool_t * pool)2263 svn_io_is_file_executable(svn_boolean_t *executable,
2264 const char *path,
2265 apr_pool_t *pool)
2266 {
2267 #if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__)
2268 apr_finfo_t file_info;
2269
2270 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER,
2271 pool));
2272 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool));
2273
2274 #else /* WIN32 || __OS2__ || !APR_HAS_USER */
2275 *executable = FALSE;
2276 #endif
2277
2278 return SVN_NO_ERROR;
2279 }
2280
2281
2282 /*** File locking. ***/
2283 /* Clear all outstanding locks on ARG, an open apr_file_t *. */
2284 static apr_status_t
file_clear_locks(void * arg)2285 file_clear_locks(void *arg)
2286 {
2287 apr_status_t apr_err;
2288 apr_file_t *f = arg;
2289
2290 /* Remove locks. */
2291 apr_err = apr_file_unlock(f);
2292 if (apr_err)
2293 return apr_err;
2294
2295 return 0;
2296 }
2297
2298 svn_error_t *
svn_io_lock_open_file(apr_file_t * lockfile_handle,svn_boolean_t exclusive,svn_boolean_t nonblocking,apr_pool_t * pool)2299 svn_io_lock_open_file(apr_file_t *lockfile_handle,
2300 svn_boolean_t exclusive,
2301 svn_boolean_t nonblocking,
2302 apr_pool_t *pool)
2303 {
2304 int locktype = APR_FLOCK_SHARED;
2305 apr_status_t apr_err;
2306 const char *fname;
2307
2308 if (exclusive)
2309 locktype = APR_FLOCK_EXCLUSIVE;
2310 if (nonblocking)
2311 locktype |= APR_FLOCK_NONBLOCK;
2312
2313 /* We need this only in case of an error but this is cheap to get -
2314 * so we do it here for clarity. */
2315 apr_err = apr_file_name_get(&fname, lockfile_handle);
2316 if (apr_err)
2317 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2318
2319 /* Get lock on the filehandle. */
2320 apr_err = apr_file_lock(lockfile_handle, locktype);
2321
2322 /* In deployments with two or more multithreaded servers running on
2323 the same system serving two or more fsfs repositories it is
2324 possible for a deadlock to occur when getting a write lock on
2325 db/txn-current-lock:
2326
2327 Process 1 Process 2
2328 --------- ---------
2329 thread 1: get lock in repos A
2330 thread 1: get lock in repos B
2331 thread 2: block getting lock in repos A
2332 thread 2: try to get lock in B *** deadlock ***
2333
2334 Retry for a while for the deadlock to clear. */
2335 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype));
2336
2337 if (apr_err)
2338 {
2339 switch (locktype & APR_FLOCK_TYPEMASK)
2340 {
2341 case APR_FLOCK_SHARED:
2342 return svn_error_wrap_apr(apr_err,
2343 _("Can't get shared lock on file '%s'"),
2344 try_utf8_from_internal_style(fname, pool));
2345 case APR_FLOCK_EXCLUSIVE:
2346 return svn_error_wrap_apr(apr_err,
2347 _("Can't get exclusive lock on file '%s'"),
2348 try_utf8_from_internal_style(fname, pool));
2349 default:
2350 SVN_ERR_MALFUNCTION();
2351 }
2352 }
2353
2354 /* On Windows, a process may not release file locks before closing the
2355 handle, and in this case the outstanding locks are unlocked by the OS.
2356 However, this is not recommended, because the actual unlocking may be
2357 postponed depending on available system resources. We explicitly unlock
2358 the file as a part of the pool cleanup handler. */
2359 apr_pool_cleanup_register(pool, lockfile_handle,
2360 file_clear_locks,
2361 apr_pool_cleanup_null);
2362
2363 return SVN_NO_ERROR;
2364 }
2365
2366 svn_error_t *
svn_io_unlock_open_file(apr_file_t * lockfile_handle,apr_pool_t * pool)2367 svn_io_unlock_open_file(apr_file_t *lockfile_handle,
2368 apr_pool_t *pool)
2369 {
2370 const char *fname;
2371 apr_status_t apr_err;
2372
2373 /* We need this only in case of an error but this is cheap to get -
2374 * so we do it here for clarity. */
2375 apr_err = apr_file_name_get(&fname, lockfile_handle);
2376 if (apr_err)
2377 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2378
2379 /* The actual unlock attempt. */
2380 apr_err = apr_file_unlock(lockfile_handle);
2381 if (apr_err)
2382 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"),
2383 try_utf8_from_internal_style(fname, pool));
2384
2385 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks);
2386
2387 return SVN_NO_ERROR;
2388 }
2389
2390 svn_error_t *
svn_io_file_lock2(const char * lock_file,svn_boolean_t exclusive,svn_boolean_t nonblocking,apr_pool_t * pool)2391 svn_io_file_lock2(const char *lock_file,
2392 svn_boolean_t exclusive,
2393 svn_boolean_t nonblocking,
2394 apr_pool_t *pool)
2395 {
2396 int locktype = APR_FLOCK_SHARED;
2397 apr_file_t *lockfile_handle;
2398 apr_int32_t flags;
2399
2400 if (exclusive)
2401 locktype = APR_FLOCK_EXCLUSIVE;
2402
2403 flags = APR_READ;
2404 if (locktype == APR_FLOCK_EXCLUSIVE)
2405 flags |= APR_WRITE;
2406
2407 /* locktype is never read after this block, so we don't need to bother
2408 setting it. If that were to ever change, uncomment the following
2409 block.
2410 if (nonblocking)
2411 locktype |= APR_FLOCK_NONBLOCK;
2412 */
2413
2414 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
2415 APR_OS_DEFAULT,
2416 pool));
2417
2418 /* Get lock on the filehandle. */
2419 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool);
2420 }
2421
2422 svn_error_t *
svn_io__file_lock_autocreate(const char * lock_file,apr_pool_t * pool)2423 svn_io__file_lock_autocreate(const char *lock_file,
2424 apr_pool_t *pool)
2425 {
2426 svn_error_t *err
2427 = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2428 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
2429 {
2430 /* No lock file? No big deal; these are just empty files anyway.
2431 Create it and try again. */
2432 svn_error_clear(err);
2433
2434 /* This file creation is racy.
2435 We don't care as long as file gets created at all. */
2436 err = svn_io_file_create_empty(lock_file, pool);
2437 if (err && APR_STATUS_IS_EEXIST(err->apr_err))
2438 {
2439 svn_error_clear(err);
2440 err = NULL;
2441 }
2442
2443 /* Finally, lock the file - if it exists */
2444 if (!err)
2445 err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool);
2446 }
2447
2448 return svn_error_trace(err);
2449 }
2450
2451
2452
2453 /* Data consistency/coherency operations. */
2454
svn_io_file_flush_to_disk(apr_file_t * file,apr_pool_t * pool)2455 svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file,
2456 apr_pool_t *pool)
2457 {
2458 apr_os_file_t filehand;
2459 const char *fname;
2460 apr_status_t apr_err;
2461
2462 /* We need this only in case of an error but this is cheap to get -
2463 * so we do it here for clarity. */
2464 apr_err = apr_file_name_get(&fname, file);
2465 if (apr_err)
2466 return svn_error_wrap_apr(apr_err, _("Can't get file name"));
2467
2468 /* ### In apr 1.4+ we could delegate most of this function to
2469 apr_file_sync(). The only major difference is that this doesn't
2470 contain the retry loop for EINTR on linux. */
2471
2472 /* First make sure that any user-space buffered data is flushed. */
2473 SVN_ERR(svn_io_file_flush(file, pool));
2474
2475 apr_os_file_get(&filehand, file);
2476
2477 /* Call the operating system specific function to actually force the
2478 data to disk. */
2479 {
2480 #ifdef WIN32
2481
2482 if (! FlushFileBuffers(filehand))
2483 return svn_error_wrap_apr(apr_get_os_error(),
2484 _("Can't flush file '%s' to disk"),
2485 try_utf8_from_internal_style(fname, pool));
2486
2487 #else
2488 int rv;
2489
2490 do {
2491 #ifdef F_FULLFSYNC
2492 rv = fcntl(filehand, F_FULLFSYNC, 0);
2493 #else
2494 rv = fsync(filehand);
2495 #endif
2496 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error()));
2497
2498 /* If the file is in a memory filesystem, fsync() may return
2499 EINVAL. Presumably the user knows the risks, and we can just
2500 ignore the error. */
2501 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error()))
2502 return SVN_NO_ERROR;
2503
2504 if (rv == -1)
2505 return svn_error_wrap_apr(apr_get_os_error(),
2506 _("Can't flush file '%s' to disk"),
2507 try_utf8_from_internal_style(fname, pool));
2508
2509 #endif
2510 }
2511 return SVN_NO_ERROR;
2512 }
2513
2514
2515
2516 /* TODO write test for these two functions, then refactor. */
2517
2518 /* Set RESULT to an svn_stringbuf_t containing the contents of FILE.
2519 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name
2520 isn't known. If CHECK_SIZE is TRUE, the function will attempt to
2521 first stat() the file to determine it's size before sucking its
2522 contents into the stringbuf. (Doing so can prevent unnecessary
2523 memory usage, an unwanted side effect of the stringbuf growth and
2524 reallocation mechanism.) */
2525 static svn_error_t *
stringbuf_from_aprfile(svn_stringbuf_t ** result,const char * filename,apr_file_t * file,svn_boolean_t check_size,apr_pool_t * pool)2526 stringbuf_from_aprfile(svn_stringbuf_t **result,
2527 const char *filename,
2528 apr_file_t *file,
2529 svn_boolean_t check_size,
2530 apr_pool_t *pool)
2531 {
2532 apr_size_t len;
2533 svn_error_t *err;
2534 svn_stringbuf_t *res = NULL;
2535 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE;
2536 char *buf;
2537
2538 /* If our caller wants us to check the size of the file for
2539 efficient memory handling, we'll try to do so. */
2540 if (check_size)
2541 {
2542 apr_finfo_t finfo = { 0 };
2543
2544 /* In some cases we get size 0 and no error for non files,
2545 so we also check for the name. (= cached in apr_file_t) */
2546 if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname)
2547 {
2548 /* we've got the file length. Now, read it in one go. */
2549 svn_boolean_t eof;
2550 res_initial_len = (apr_size_t)finfo.size;
2551 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2552 SVN_ERR(svn_io_file_read_full2(file, res->data,
2553 res_initial_len, &res->len,
2554 &eof, pool));
2555 res->data[res->len] = 0;
2556
2557 *result = res;
2558 return SVN_NO_ERROR;
2559 }
2560 }
2561
2562 /* XXX: We should check the incoming data for being of type binary. */
2563 buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
2564 res = svn_stringbuf_create_ensure(res_initial_len, pool);
2565
2566 /* apr_file_read will not return data and eof in the same call. So this loop
2567 * is safe from missing read data. */
2568 len = SVN__STREAM_CHUNK_SIZE;
2569 err = svn_io_file_read(file, buf, &len, pool);
2570 while (! err)
2571 {
2572 svn_stringbuf_appendbytes(res, buf, len);
2573 len = SVN__STREAM_CHUNK_SIZE;
2574 err = svn_io_file_read(file, buf, &len, pool);
2575 }
2576
2577 /* Having read all the data we *expect* EOF */
2578 if (err && !APR_STATUS_IS_EOF(err->apr_err))
2579 return svn_error_trace(err);
2580 svn_error_clear(err);
2581
2582 *result = res;
2583 return SVN_NO_ERROR;
2584 }
2585
2586 svn_error_t *
svn_stringbuf_from_file2(svn_stringbuf_t ** result,const char * filename,apr_pool_t * pool)2587 svn_stringbuf_from_file2(svn_stringbuf_t **result,
2588 const char *filename,
2589 apr_pool_t *pool)
2590 {
2591 apr_file_t *f;
2592
2593 if (filename[0] == '-' && filename[1] == '\0')
2594 {
2595 apr_status_t apr_err;
2596 if ((apr_err = apr_file_open_stdin(&f, pool)))
2597 return svn_error_wrap_apr(apr_err, _("Can't open stdin"));
2598 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool));
2599 }
2600 else
2601 {
2602 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool));
2603 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool));
2604 }
2605 return svn_io_file_close(f, pool);
2606 }
2607
2608
2609 svn_error_t *
svn_stringbuf_from_file(svn_stringbuf_t ** result,const char * filename,apr_pool_t * pool)2610 svn_stringbuf_from_file(svn_stringbuf_t **result,
2611 const char *filename,
2612 apr_pool_t *pool)
2613 {
2614 if (filename[0] == '-' && filename[1] == '\0')
2615 return svn_error_create
2616 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2617 _("Reading from stdin is disallowed"));
2618 return svn_stringbuf_from_file2(result, filename, pool);
2619 }
2620
2621 svn_error_t *
svn_stringbuf_from_aprfile(svn_stringbuf_t ** result,apr_file_t * file,apr_pool_t * pool)2622 svn_stringbuf_from_aprfile(svn_stringbuf_t **result,
2623 apr_file_t *file,
2624 apr_pool_t *pool)
2625 {
2626 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool);
2627 }
2628
2629
2630
2631 /* Deletion. */
2632
2633 svn_error_t *
svn_io_remove_file2(const char * path,svn_boolean_t ignore_enoent,apr_pool_t * scratch_pool)2634 svn_io_remove_file2(const char *path,
2635 svn_boolean_t ignore_enoent,
2636 apr_pool_t *scratch_pool)
2637 {
2638 apr_status_t apr_err;
2639 const char *path_apr;
2640
2641 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool));
2642
2643 apr_err = apr_file_remove(path_apr, scratch_pool);
2644
2645 #ifdef WIN32
2646 /* If the target is read only NTFS reports EACCESS and FAT/FAT32
2647 reports EEXIST */
2648 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err))
2649 {
2650 /* Set the destination file writable because Windows will not
2651 allow us to delete when path is read-only */
2652 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool));
2653 apr_err = apr_file_remove(path_apr, scratch_pool);
2654 }
2655
2656 /* Check to make sure we aren't trying to delete a directory */
2657 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)
2658 || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
2659 {
2660 apr_finfo_t finfo;
2661
2662 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool)
2663 && finfo.filetype == APR_REG)
2664 {
2665 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, scratch_pool));
2666 }
2667 }
2668
2669 /* Just return the delete error */
2670 #endif
2671
2672 if (!apr_err)
2673 {
2674 return SVN_NO_ERROR;
2675 }
2676 else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err)
2677 || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))
2678 {
2679 return SVN_NO_ERROR;
2680 }
2681 else
2682 {
2683 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
2684 svn_dirent_local_style(path, scratch_pool));
2685 }
2686 }
2687
2688
2689 svn_error_t *
svn_io_remove_dir(const char * path,apr_pool_t * pool)2690 svn_io_remove_dir(const char *path, apr_pool_t *pool)
2691 {
2692 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
2693 }
2694
2695 /*
2696 Mac OS X has a bug where if you're reading the contents of a
2697 directory via readdir in a loop, and you remove one of the entries in
2698 the directory and the directory has 338 or more files in it you will
2699 skip over some of the entries in the directory. Needless to say,
2700 this causes problems if you are using this kind of loop inside a
2701 function that is recursively deleting a directory, because when you
2702 get around to removing the directory it will still have something in
2703 it. A similar problem has been observed in other BSDs. This bug has
2704 since been fixed. See http://www.vnode.ch/fixing_seekdir for details.
2705
2706 The workaround is to delete the files only _after_ the initial
2707 directory scan. A previous workaround involving rewinddir is
2708 problematic on Win32 and some NFS clients, notably NetBSD.
2709
2710 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
2711 http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
2712 */
2713
2714 /* Neither windows nor unix allows us to delete a non-empty
2715 directory.
2716
2717 This is a function to perform the equivalent of 'rm -rf'. */
2718 svn_error_t *
svn_io_remove_dir2(const char * path,svn_boolean_t ignore_enoent,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)2719 svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent,
2720 svn_cancel_func_t cancel_func, void *cancel_baton,
2721 apr_pool_t *pool)
2722 {
2723 svn_error_t *err;
2724 apr_pool_t *subpool;
2725 apr_hash_t *dirents;
2726 apr_hash_index_t *hi;
2727
2728 /* Check for pending cancellation request.
2729 If we need to bail out, do so early. */
2730
2731 if (cancel_func)
2732 SVN_ERR(cancel_func(cancel_baton));
2733
2734 subpool = svn_pool_create(pool);
2735
2736 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool);
2737 if (err)
2738 {
2739 /* if the directory doesn't exist, our mission is accomplished */
2740 if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err)
2741 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2742 {
2743 svn_error_clear(err);
2744 return SVN_NO_ERROR;
2745 }
2746 return svn_error_trace(err);
2747 }
2748
2749 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
2750 {
2751 const char *name = apr_hash_this_key(hi);
2752 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
2753 const char *fullpath;
2754
2755 fullpath = svn_dirent_join(path, name, subpool);
2756 if (dirent->kind == svn_node_dir)
2757 {
2758 /* Don't check for cancellation, the callee will immediately do so */
2759 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func,
2760 cancel_baton, subpool));
2761 }
2762 else
2763 {
2764 if (cancel_func)
2765 SVN_ERR(cancel_func(cancel_baton));
2766
2767 err = svn_io_remove_file2(fullpath, FALSE, subpool);
2768 if (err)
2769 return svn_error_createf
2770 (err->apr_err, err, _("Can't remove '%s'"),
2771 svn_dirent_local_style(fullpath, subpool));
2772 }
2773 }
2774
2775 svn_pool_destroy(subpool);
2776
2777 return svn_io_dir_remove_nonrecursive(path, pool);
2778 }
2779
2780 svn_error_t *
svn_io_get_dir_filenames(apr_hash_t ** dirents,const char * path,apr_pool_t * pool)2781 svn_io_get_dir_filenames(apr_hash_t **dirents,
2782 const char *path,
2783 apr_pool_t *pool)
2784 {
2785 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE,
2786 pool, pool));
2787 }
2788
2789 svn_io_dirent2_t *
svn_io_dirent2_create(apr_pool_t * result_pool)2790 svn_io_dirent2_create(apr_pool_t *result_pool)
2791 {
2792 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent));
2793
2794 /*dirent->kind = svn_node_none;
2795 dirent->special = FALSE;*/
2796 dirent->filesize = SVN_INVALID_FILESIZE;
2797 /*dirent->mtime = 0;*/
2798
2799 return dirent;
2800 }
2801
2802 svn_io_dirent2_t *
svn_io_dirent2_dup(const svn_io_dirent2_t * item,apr_pool_t * result_pool)2803 svn_io_dirent2_dup(const svn_io_dirent2_t *item,
2804 apr_pool_t *result_pool)
2805 {
2806 return apr_pmemdup(result_pool,
2807 item,
2808 sizeof(*item));
2809 }
2810
2811 svn_error_t *
svn_io_get_dirents3(apr_hash_t ** dirents,const char * path,svn_boolean_t only_check_type,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2812 svn_io_get_dirents3(apr_hash_t **dirents,
2813 const char *path,
2814 svn_boolean_t only_check_type,
2815 apr_pool_t *result_pool,
2816 apr_pool_t *scratch_pool)
2817 {
2818 apr_status_t status;
2819 apr_dir_t *this_dir;
2820 apr_finfo_t this_entry;
2821 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
2822
2823 if (!only_check_type)
2824 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME;
2825
2826 *dirents = apr_hash_make(result_pool);
2827
2828 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool));
2829
2830 for (status = apr_dir_read(&this_entry, flags, this_dir);
2831 status == APR_SUCCESS;
2832 status = apr_dir_read(&this_entry, flags, this_dir))
2833 {
2834 if ((this_entry.name[0] == '.')
2835 && ((this_entry.name[1] == '\0')
2836 || ((this_entry.name[1] == '.')
2837 && (this_entry.name[2] == '\0'))))
2838 {
2839 continue;
2840 }
2841 else
2842 {
2843 const char *name;
2844 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool);
2845
2846 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool));
2847
2848 map_apr_finfo_to_node_kind(&(dirent->kind),
2849 &(dirent->special),
2850 &this_entry);
2851
2852 if (!only_check_type)
2853 {
2854 dirent->filesize = this_entry.size;
2855 dirent->mtime = this_entry.mtime;
2856 }
2857
2858 svn_hash_sets(*dirents, name, dirent);
2859 }
2860 }
2861
2862 if (! (APR_STATUS_IS_ENOENT(status)))
2863 return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
2864 svn_dirent_local_style(path, scratch_pool));
2865
2866 status = apr_dir_close(this_dir);
2867 if (status)
2868 return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
2869 svn_dirent_local_style(path, scratch_pool));
2870
2871 return SVN_NO_ERROR;
2872 }
2873
2874 svn_error_t *
svn_io_stat_dirent2(const svn_io_dirent2_t ** dirent_p,const char * path,svn_boolean_t verify_truename,svn_boolean_t ignore_enoent,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2875 svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p,
2876 const char *path,
2877 svn_boolean_t verify_truename,
2878 svn_boolean_t ignore_enoent,
2879 apr_pool_t *result_pool,
2880 apr_pool_t *scratch_pool)
2881 {
2882 apr_finfo_t finfo;
2883 svn_io_dirent2_t *dirent;
2884 svn_error_t *err;
2885 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK
2886 | APR_FINFO_SIZE | APR_FINFO_MTIME;
2887
2888 #if defined(WIN32) || defined(__OS2__)
2889 if (verify_truename)
2890 wanted |= APR_FINFO_NAME;
2891 #endif
2892
2893 err = svn_io_stat(&finfo, path, wanted, scratch_pool);
2894
2895 if (err && ignore_enoent &&
2896 (APR_STATUS_IS_ENOENT(err->apr_err)
2897 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2898 {
2899 svn_error_clear(err);
2900 dirent = svn_io_dirent2_create(result_pool);
2901 SVN_ERR_ASSERT(dirent->kind == svn_node_none);
2902
2903 *dirent_p = dirent;
2904 return SVN_NO_ERROR;
2905 }
2906 SVN_ERR(err);
2907
2908 #if defined(WIN32) || defined(__OS2__) || defined(DARWIN)
2909 if (verify_truename)
2910 {
2911 const char *requested_name = svn_dirent_basename(path, NULL);
2912
2913 if (requested_name[0] == '\0')
2914 {
2915 /* No parent directory. No need to stat/verify */
2916 }
2917 #if defined(WIN32) || defined(__OS2__)
2918 else if (finfo.name)
2919 {
2920 const char *name_on_disk;
2921 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path,
2922 scratch_pool));
2923
2924 if (strcmp(name_on_disk, requested_name) /* != 0 */)
2925 {
2926 if (ignore_enoent)
2927 {
2928 *dirent_p = svn_io_dirent2_create(result_pool);
2929 return SVN_NO_ERROR;
2930 }
2931 else
2932 return svn_error_createf(APR_ENOENT, NULL,
2933 _("Path '%s' not found, case obstructed by '%s'"),
2934 svn_dirent_local_style(path, scratch_pool),
2935 name_on_disk);
2936 }
2937 }
2938 #elif defined(DARWIN)
2939 /* Currently apr doesn't set finfo.name on DARWIN, returning
2940 APR_INCOMPLETE.
2941 ### Can we optimize this in another way? */
2942 else
2943 {
2944 apr_hash_t *dirents;
2945
2946 err = svn_io_get_dirents3(&dirents,
2947 svn_dirent_dirname(path, scratch_pool),
2948 TRUE /* only_check_type */,
2949 scratch_pool, scratch_pool);
2950
2951 if (err && ignore_enoent
2952 && (APR_STATUS_IS_ENOENT(err->apr_err)
2953 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
2954 {
2955 svn_error_clear(err);
2956
2957 *dirent_p = svn_io_dirent2_create(result_pool);
2958 return SVN_NO_ERROR;
2959 }
2960 else
2961 SVN_ERR(err);
2962
2963 if (! svn_hash_gets(dirents, requested_name))
2964 {
2965 if (ignore_enoent)
2966 {
2967 *dirent_p = svn_io_dirent2_create(result_pool);
2968 return SVN_NO_ERROR;
2969 }
2970 else
2971 return svn_error_createf(APR_ENOENT, NULL,
2972 _("Path '%s' not found"),
2973 svn_dirent_local_style(path, scratch_pool));
2974 }
2975 }
2976 #endif
2977 }
2978 #endif
2979
2980 dirent = svn_io_dirent2_create(result_pool);
2981 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);
2982
2983 dirent->filesize = finfo.size;
2984 dirent->mtime = finfo.mtime;
2985
2986 *dirent_p = dirent;
2987
2988 return SVN_NO_ERROR;
2989 }
2990
2991 /* Pool userdata key for the error file passed to svn_io_start_cmd(). */
2992 #define ERRFILE_KEY "svn-io-start-cmd-errfile"
2993
2994 /* Handle an error from the child process (before command execution) by
2995 printing DESC and the error string corresponding to STATUS to stderr. */
2996 static void
handle_child_process_error(apr_pool_t * pool,apr_status_t status,const char * desc)2997 handle_child_process_error(apr_pool_t *pool, apr_status_t status,
2998 const char *desc)
2999 {
3000 char errbuf[256];
3001 apr_file_t *errfile;
3002 void *p;
3003
3004 /* We can't do anything if we get an error here, so just return. */
3005 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool))
3006 return;
3007 errfile = p;
3008
3009 if (errfile)
3010 /* What we get from APR is in native encoding. */
3011 apr_file_printf(errfile, "%s: %s",
3012 desc, apr_strerror(status, errbuf,
3013 sizeof(errbuf)));
3014 }
3015
3016
3017 svn_error_t *
svn_io_start_cmd3(apr_proc_t * cmd_proc,const char * path,const char * cmd,const char * const * args,const char * const * env,svn_boolean_t inherit,svn_boolean_t infile_pipe,apr_file_t * infile,svn_boolean_t outfile_pipe,apr_file_t * outfile,svn_boolean_t errfile_pipe,apr_file_t * errfile,apr_pool_t * pool)3018 svn_io_start_cmd3(apr_proc_t *cmd_proc,
3019 const char *path,
3020 const char *cmd,
3021 const char *const *args,
3022 const char *const *env,
3023 svn_boolean_t inherit,
3024 svn_boolean_t infile_pipe,
3025 apr_file_t *infile,
3026 svn_boolean_t outfile_pipe,
3027 apr_file_t *outfile,
3028 svn_boolean_t errfile_pipe,
3029 apr_file_t *errfile,
3030 apr_pool_t *pool)
3031 {
3032 apr_status_t apr_err;
3033 apr_procattr_t *cmdproc_attr;
3034 int num_args;
3035 const char **args_native;
3036 const char *cmd_apr;
3037
3038 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe));
3039 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe));
3040 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe));
3041
3042 /* Create the process attributes. */
3043 apr_err = apr_procattr_create(&cmdproc_attr, pool);
3044 if (apr_err)
3045 return svn_error_wrap_apr(apr_err,
3046 _("Can't create process '%s' attributes"),
3047 cmd);
3048
3049 /* Make sure we invoke cmd directly, not through a shell. */
3050 apr_err = apr_procattr_cmdtype_set(cmdproc_attr,
3051 inherit ? APR_PROGRAM_PATH : APR_PROGRAM);
3052 if (apr_err)
3053 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"),
3054 cmd);
3055
3056 /* Set the process's working directory. */
3057 if (path)
3058 {
3059 const char *path_apr;
3060
3061 /* APR doesn't like our canonical path format for current directory */
3062 if (path[0] == '\0')
3063 path = ".";
3064
3065 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
3066 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr);
3067 if (apr_err)
3068 return svn_error_wrap_apr(apr_err,
3069 _("Can't set process '%s' directory"),
3070 cmd);
3071 }
3072
3073 /* Use requested inputs and outputs.
3074
3075 ### Unfortunately each of these apr functions creates a pipe and then
3076 overwrites the pipe file descriptor with the descriptor we pass
3077 in. The pipes can then never be closed. This is an APR bug. */
3078 if (infile)
3079 {
3080 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL);
3081 if (apr_err)
3082 return svn_error_wrap_apr(apr_err,
3083 _("Can't set process '%s' child input"),
3084 cmd);
3085 }
3086 if (outfile)
3087 {
3088 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL);
3089 if (apr_err)
3090 return svn_error_wrap_apr(apr_err,
3091 _("Can't set process '%s' child outfile"),
3092 cmd);
3093 }
3094 if (errfile)
3095 {
3096 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL);
3097 if (apr_err)
3098 return svn_error_wrap_apr(apr_err,
3099 _("Can't set process '%s' child errfile"),
3100 cmd);
3101 }
3102
3103 /* Forward request for pipes to APR. */
3104 if (infile_pipe || outfile_pipe || errfile_pipe)
3105 {
3106 apr_err = apr_procattr_io_set(cmdproc_attr,
3107 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3108 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE,
3109 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE);
3110
3111 if (apr_err)
3112 return svn_error_wrap_apr(apr_err,
3113 _("Can't set process '%s' stdio pipes"),
3114 cmd);
3115 }
3116
3117 /* Have the child print any problems executing its program to errfile. */
3118 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool);
3119 if (apr_err)
3120 return svn_error_wrap_apr(apr_err,
3121 _("Can't set process '%s' child errfile for "
3122 "error handler"),
3123 cmd);
3124 apr_err = apr_procattr_child_errfn_set(cmdproc_attr,
3125 handle_child_process_error);
3126 if (apr_err)
3127 return svn_error_wrap_apr(apr_err,
3128 _("Can't set process '%s' error handler"),
3129 cmd);
3130
3131 /* Convert cmd and args from UTF-8 */
3132 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool));
3133 for (num_args = 0; args[num_args]; num_args++)
3134 ;
3135 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *));
3136 args_native[num_args] = NULL;
3137 while (num_args--)
3138 {
3139 /* ### Well, it turns out that on APR on Windows expects all
3140 program args to be in UTF-8. Callers of svn_io_run_cmd
3141 should be aware of that. */
3142 SVN_ERR(cstring_from_utf8(&args_native[num_args],
3143 args[num_args], pool));
3144 }
3145
3146
3147 /* Start the cmd command. */
3148 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native,
3149 inherit ? NULL : env, cmdproc_attr, pool);
3150 if (apr_err)
3151 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd);
3152
3153 return SVN_NO_ERROR;
3154 }
3155
3156 #undef ERRFILE_KEY
3157
3158 svn_error_t *
svn_io_wait_for_cmd(apr_proc_t * cmd_proc,const char * cmd,int * exitcode,apr_exit_why_e * exitwhy,apr_pool_t * pool)3159 svn_io_wait_for_cmd(apr_proc_t *cmd_proc,
3160 const char *cmd,
3161 int *exitcode,
3162 apr_exit_why_e *exitwhy,
3163 apr_pool_t *pool)
3164 {
3165 apr_status_t apr_err;
3166 apr_exit_why_e exitwhy_val;
3167 int exitcode_val;
3168
3169 /* The Win32 apr_proc_wait doesn't set this... */
3170 exitwhy_val = APR_PROC_EXIT;
3171
3172 /* Wait for the cmd command to finish. */
3173 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT);
3174 if (!APR_STATUS_IS_CHILD_DONE(apr_err))
3175 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"),
3176 cmd);
3177
3178 if (exitwhy)
3179 *exitwhy = exitwhy_val;
3180 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)
3181 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val))
3182 return svn_error_createf
3183 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3184 _("Process '%s' failed (signal %d, core dumped)"),
3185 cmd, exitcode_val);
3186 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val))
3187 return svn_error_createf
3188 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3189 _("Process '%s' failed (signal %d)"),
3190 cmd, exitcode_val);
3191 else if (! APR_PROC_CHECK_EXIT(exitwhy_val))
3192 /* Don't really know what happened here. */
3193 return svn_error_createf
3194 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3195 _("Process '%s' failed (exitwhy %d, exitcode %d)"),
3196 cmd, exitwhy_val, exitcode_val);
3197
3198 if (exitcode)
3199 *exitcode = exitcode_val;
3200 else if (exitcode_val != 0)
3201 return svn_error_createf
3202 (SVN_ERR_EXTERNAL_PROGRAM, NULL,
3203 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val);
3204
3205 return SVN_NO_ERROR;
3206 }
3207
3208
3209 svn_error_t *
svn_io_run_cmd(const char * path,const char * cmd,const char * const * args,int * exitcode,apr_exit_why_e * exitwhy,svn_boolean_t inherit,apr_file_t * infile,apr_file_t * outfile,apr_file_t * errfile,apr_pool_t * pool)3210 svn_io_run_cmd(const char *path,
3211 const char *cmd,
3212 const char *const *args,
3213 int *exitcode,
3214 apr_exit_why_e *exitwhy,
3215 svn_boolean_t inherit,
3216 apr_file_t *infile,
3217 apr_file_t *outfile,
3218 apr_file_t *errfile,
3219 apr_pool_t *pool)
3220 {
3221 apr_proc_t cmd_proc;
3222
3223 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit,
3224 FALSE, infile, FALSE, outfile, FALSE, errfile,
3225 pool));
3226
3227 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
3228 }
3229
3230
3231 svn_error_t *
svn_io_run_diff2(const char * dir,const char * const * user_args,int num_user_args,const char * label1,const char * label2,const char * from,const char * to,int * pexitcode,apr_file_t * outfile,apr_file_t * errfile,const char * diff_cmd,apr_pool_t * pool)3232 svn_io_run_diff2(const char *dir,
3233 const char *const *user_args,
3234 int num_user_args,
3235 const char *label1,
3236 const char *label2,
3237 const char *from,
3238 const char *to,
3239 int *pexitcode,
3240 apr_file_t *outfile,
3241 apr_file_t *errfile,
3242 const char *diff_cmd,
3243 apr_pool_t *pool)
3244 {
3245 const char **args;
3246 int i;
3247 int exitcode;
3248 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */
3249 apr_pool_t *subpool = svn_pool_create(pool);
3250
3251 if (pexitcode == NULL)
3252 pexitcode = &exitcode;
3253
3254 if (user_args != NULL)
3255 nargs += num_user_args;
3256 else
3257 nargs += 1; /* -u */
3258
3259 if (label1 != NULL)
3260 nargs += 2; /* the -L and the label itself */
3261 if (label2 != NULL)
3262 nargs += 2; /* the -L and the label itself */
3263
3264 args = apr_palloc(subpool, nargs * sizeof(char *));
3265
3266 i = 0;
3267 args[i++] = diff_cmd;
3268
3269 if (user_args != NULL)
3270 {
3271 int j;
3272 for (j = 0; j < num_user_args; ++j)
3273 args[i++] = user_args[j];
3274 }
3275 else
3276 args[i++] = "-u"; /* assume -u if the user didn't give us any args */
3277
3278 if (label1 != NULL)
3279 {
3280 args[i++] = "-L";
3281 args[i++] = label1;
3282 }
3283 if (label2 != NULL)
3284 {
3285 args[i++] = "-L";
3286 args[i++] = label2;
3287 }
3288
3289 args[i++] = svn_dirent_local_style(from, subpool);
3290 args[i++] = svn_dirent_local_style(to, subpool);
3291 args[i++] = NULL;
3292
3293 SVN_ERR_ASSERT(i == nargs);
3294
3295 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE,
3296 NULL, outfile, errfile, subpool));
3297
3298 /* The man page for (GNU) diff describes the return value as:
3299
3300 "An exit status of 0 means no differences were found, 1 means
3301 some differences were found, and 2 means trouble."
3302
3303 A return value of 2 typically occurs when diff cannot read its input
3304 or write to its output, but in any case we probably ought to return an
3305 error for anything other than 0 or 1 as the output is likely to be
3306 corrupt.
3307 */
3308 if (*pexitcode != 0 && *pexitcode != 1)
3309 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3310 _("'%s' returned %d"),
3311 svn_dirent_local_style(diff_cmd, pool),
3312 *pexitcode);
3313
3314 svn_pool_destroy(subpool);
3315
3316 return SVN_NO_ERROR;
3317 }
3318
3319
3320 svn_error_t *
svn_io_run_diff3_3(int * exitcode,const char * dir,const char * mine,const char * older,const char * yours,const char * mine_label,const char * older_label,const char * yours_label,apr_file_t * merged,const char * diff3_cmd,const apr_array_header_t * user_args,apr_pool_t * pool)3321 svn_io_run_diff3_3(int *exitcode,
3322 const char *dir,
3323 const char *mine,
3324 const char *older,
3325 const char *yours,
3326 const char *mine_label,
3327 const char *older_label,
3328 const char *yours_label,
3329 apr_file_t *merged,
3330 const char *diff3_cmd,
3331 const apr_array_header_t *user_args,
3332 apr_pool_t *pool)
3333 {
3334 const char **args = apr_palloc(pool,
3335 sizeof(char*) * (13
3336 + (user_args
3337 ? user_args->nelts
3338 : 1)));
3339 #ifndef NDEBUG
3340 int nargs = 12;
3341 #endif
3342 int i = 0;
3343
3344 /* Labels fall back to sensible defaults if not specified. */
3345 if (mine_label == NULL)
3346 mine_label = ".working";
3347 if (older_label == NULL)
3348 older_label = ".old";
3349 if (yours_label == NULL)
3350 yours_label = ".new";
3351
3352 /* Set up diff3 command line. */
3353 args[i++] = diff3_cmd;
3354 if (user_args)
3355 {
3356 int j;
3357 for (j = 0; j < user_args->nelts; ++j)
3358 args[i++] = APR_ARRAY_IDX(user_args, j, const char *);
3359 #ifndef NDEBUG
3360 nargs += user_args->nelts;
3361 #endif
3362 }
3363 else
3364 {
3365 args[i++] = "-E"; /* We tried "-A" here, but that caused
3366 overlapping identical changes to
3367 conflict. See issue #682. */
3368 #ifndef NDEBUG
3369 ++nargs;
3370 #endif
3371 }
3372 args[i++] = "-m";
3373 args[i++] = "-L";
3374 args[i++] = mine_label;
3375 args[i++] = "-L";
3376 args[i++] = older_label; /* note: this label is ignored if
3377 using 2-part markers, which is the
3378 case with "-E". */
3379 args[i++] = "-L";
3380 args[i++] = yours_label;
3381 #ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG
3382 {
3383 svn_boolean_t has_arg;
3384
3385 /* ### FIXME: we really shouldn't be reading the config here;
3386 instead, the necessary bits should be passed in by the caller.
3387 But should we add another parameter to this function, when the
3388 whole external diff3 thing might eventually go away? */
3389 apr_hash_t *config;
3390 svn_config_t *cfg;
3391
3392 SVN_ERR(svn_config_get_config(&config, pool));
3393 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
3394 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS,
3395 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG,
3396 TRUE));
3397 if (has_arg)
3398 {
3399 const char *diff_cmd, *diff_utf8;
3400 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
3401 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF);
3402 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool));
3403 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8,
3404 SVN_VA_NULL);
3405 #ifndef NDEBUG
3406 ++nargs;
3407 #endif
3408 }
3409 }
3410 #endif
3411 args[i++] = svn_dirent_local_style(mine, pool);
3412 args[i++] = svn_dirent_local_style(older, pool);
3413 args[i++] = svn_dirent_local_style(yours, pool);
3414 args[i++] = NULL;
3415 #ifndef NDEBUG
3416 SVN_ERR_ASSERT(i == nargs);
3417 #endif
3418
3419 /* Run diff3, output the merged text into the scratch file. */
3420 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args,
3421 exitcode, NULL,
3422 TRUE, /* keep environment */
3423 NULL, merged, NULL,
3424 pool));
3425
3426 /* According to the diff3 docs, a '0' means the merge was clean, and
3427 '1' means conflict markers were found. Anything else is real
3428 error. */
3429 if ((*exitcode != 0) && (*exitcode != 1))
3430 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
3431 _("Error running '%s': exitcode was %d, "
3432 "args were:"
3433 "\nin directory '%s', basenames:\n%s\n%s\n%s"),
3434 svn_dirent_local_style(diff3_cmd, pool),
3435 *exitcode,
3436 svn_dirent_local_style(dir, pool),
3437 /* Don't call svn_path_local_style() on
3438 the basenames. We don't want them to
3439 be absolute, and we don't need the
3440 separator conversion. */
3441 mine, older, yours);
3442
3443 return SVN_NO_ERROR;
3444 }
3445
3446
3447 /* Canonicalize a string for hashing. Modifies KEY in place. */
3448 static APR_INLINE char *
fileext_tolower(char * key)3449 fileext_tolower(char *key)
3450 {
3451 register char *p;
3452 for (p = key; *p != 0; ++p)
3453 *p = (char)apr_tolower(*p);
3454 return key;
3455 }
3456
3457
3458 svn_error_t *
svn_io_parse_mimetypes_file(apr_hash_t ** type_map,const char * mimetypes_file,apr_pool_t * pool)3459 svn_io_parse_mimetypes_file(apr_hash_t **type_map,
3460 const char *mimetypes_file,
3461 apr_pool_t *pool)
3462 {
3463 svn_error_t *err = SVN_NO_ERROR;
3464 apr_hash_t *types = apr_hash_make(pool);
3465 svn_boolean_t eof = FALSE;
3466 svn_stringbuf_t *buf;
3467 apr_pool_t *subpool = svn_pool_create(pool);
3468 apr_file_t *types_file;
3469 svn_stream_t *mimetypes_stream;
3470
3471 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file,
3472 APR_READ, APR_OS_DEFAULT, pool));
3473 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool);
3474
3475 while (1)
3476 {
3477 apr_array_header_t *tokens;
3478 const char *type;
3479
3480 svn_pool_clear(subpool);
3481
3482 /* Read a line. */
3483 if ((err = svn_stream_readline(mimetypes_stream, &buf,
3484 APR_EOL_STR, &eof, subpool)))
3485 break;
3486
3487 /* Only pay attention to non-empty, non-comment lines. */
3488 if (buf->len)
3489 {
3490 int i;
3491
3492 if (buf->data[0] == '#')
3493 continue;
3494
3495 /* Tokenize (into our return pool). */
3496 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool);
3497 if (tokens->nelts < 2)
3498 continue;
3499
3500 /* The first token in a multi-token line is the media type.
3501 Subsequent tokens are filename extensions associated with
3502 that media type. */
3503 type = APR_ARRAY_IDX(tokens, 0, const char *);
3504 for (i = 1; i < tokens->nelts; i++)
3505 {
3506 /* We can safely address 'ext' as a non-const string because
3507 * we know svn_cstring_split() allocated it in 'pool' for us. */
3508 char *ext = APR_ARRAY_IDX(tokens, i, char *);
3509 fileext_tolower(ext);
3510 svn_hash_sets(types, ext, type);
3511 }
3512 }
3513 if (eof)
3514 break;
3515 }
3516 svn_pool_destroy(subpool);
3517
3518 /* If there was an error above, close the file (ignoring any error
3519 from *that*) and return the originally error. */
3520 if (err)
3521 {
3522 svn_error_clear(svn_stream_close(mimetypes_stream));
3523 return err;
3524 }
3525
3526 /* Close the stream (which closes the underlying file, too). */
3527 SVN_ERR(svn_stream_close(mimetypes_stream));
3528
3529 *type_map = types;
3530 return SVN_NO_ERROR;
3531 }
3532
3533
3534 svn_error_t *
svn_io_detect_mimetype2(const char ** mimetype,const char * file,apr_hash_t * mimetype_map,apr_pool_t * pool)3535 svn_io_detect_mimetype2(const char **mimetype,
3536 const char *file,
3537 apr_hash_t *mimetype_map,
3538 apr_pool_t *pool)
3539 {
3540 static const char * const generic_binary = "application/octet-stream";
3541
3542 svn_node_kind_t kind;
3543 apr_file_t *fh;
3544 svn_error_t *err;
3545 unsigned char block[1024];
3546 apr_size_t amt_read = sizeof(block);
3547
3548 /* Default return value is NULL. */
3549 *mimetype = NULL;
3550
3551 /* If there is a mimetype_map provided, we'll first try to look up
3552 our file's extension in the map. Failing that, we'll run the
3553 heuristic. */
3554 if (mimetype_map)
3555 {
3556 const char *type_from_map;
3557 char *path_ext; /* Can point to physical const memory but only when
3558 svn_path_splitext sets it to "". */
3559 svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
3560 fileext_tolower(path_ext);
3561 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext)))
3562 {
3563 *mimetype = type_from_map;
3564 return SVN_NO_ERROR;
3565 }
3566 }
3567
3568 /* See if this file even exists, and make sure it really is a file. */
3569 SVN_ERR(svn_io_check_path(file, &kind, pool));
3570 if (kind != svn_node_file)
3571 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
3572 _("Can't detect MIME type of non-file '%s'"),
3573 svn_dirent_local_style(file, pool));
3574
3575 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool));
3576
3577 /* Read a block of data from FILE. */
3578 err = svn_io_file_read(fh, block, &amt_read, pool);
3579 if (err && ! APR_STATUS_IS_EOF(err->apr_err))
3580 return err;
3581 svn_error_clear(err);
3582
3583 /* Now close the file. No use keeping it open any more. */
3584 SVN_ERR(svn_io_file_close(fh, pool));
3585
3586 if (svn_io_is_binary_data(block, amt_read))
3587 *mimetype = generic_binary;
3588
3589 return SVN_NO_ERROR;
3590 }
3591
3592
3593 svn_boolean_t
svn_io_is_binary_data(const void * data,apr_size_t len)3594 svn_io_is_binary_data(const void *data, apr_size_t len)
3595 {
3596 const unsigned char *buf = data;
3597
3598 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
3599 {
3600 /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
3601 * Treat it as plain text. */
3602 return FALSE;
3603 }
3604
3605 /* Right now, this function is going to be really stupid. It's
3606 going to examine the block of data, and make sure that 15%
3607 of the bytes are such that their value is in the ranges 0x07-0x0D
3608 or 0x20-0x7F, and that none of those bytes is 0x00. If those
3609 criteria are not met, we're calling it binary.
3610
3611 NOTE: Originally, I intended to target 85% of the bytes being in
3612 the specified ranges, but I flubbed the condition. At any rate,
3613 folks aren't complaining, so I'm not sure that it's worth
3614 adjusting this retroactively now. --cmpilato */
3615 if (len > 0)
3616 {
3617 apr_size_t i;
3618 apr_size_t binary_count = 0;
3619
3620 /* Run through the data we've read, counting the 'binary-ish'
3621 bytes. HINT: If we see a 0x00 byte, we'll set our count to its
3622 max and stop reading the file. */
3623 for (i = 0; i < len; i++)
3624 {
3625 if (buf[i] == 0)
3626 {
3627 binary_count = len;
3628 break;
3629 }
3630 if ((buf[i] < 0x07)
3631 || ((buf[i] > 0x0D) && (buf[i] < 0x20))
3632 || (buf[i] > 0x7F))
3633 {
3634 binary_count++;
3635 }
3636 }
3637
3638 return (((binary_count * 1000) / len) > 850);
3639 }
3640
3641 return FALSE;
3642 }
3643
3644
3645 svn_error_t *
svn_io_detect_mimetype(const char ** mimetype,const char * file,apr_pool_t * pool)3646 svn_io_detect_mimetype(const char **mimetype,
3647 const char *file,
3648 apr_pool_t *pool)
3649 {
3650 return svn_io_detect_mimetype2(mimetype, file, NULL, pool);
3651 }
3652
3653
3654 svn_error_t *
svn_io_file_open(apr_file_t ** new_file,const char * fname,apr_int32_t flag,apr_fileperms_t perm,apr_pool_t * pool)3655 svn_io_file_open(apr_file_t **new_file, const char *fname,
3656 apr_int32_t flag, apr_fileperms_t perm,
3657 apr_pool_t *pool)
3658 {
3659 const char *fname_apr;
3660 apr_status_t status;
3661
3662 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
3663 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE,
3664 pool);
3665
3666 if (status)
3667 return svn_error_wrap_apr(status, _("Can't open file '%s'"),
3668 svn_dirent_local_style(fname, pool));
3669 else
3670 return SVN_NO_ERROR;
3671 }
3672
3673
3674 static APR_INLINE svn_error_t *
do_io_file_wrapper_cleanup(apr_file_t * file,apr_status_t status,const char * msg,const char * msg_no_name,apr_pool_t * pool)3675 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
3676 const char *msg, const char *msg_no_name,
3677 apr_pool_t *pool)
3678 {
3679 const char *name;
3680 svn_error_t *err;
3681
3682 if (! status)
3683 return SVN_NO_ERROR;
3684
3685 err = svn_io_file_name_get(&name, file, pool);
3686 if (err)
3687 name = NULL;
3688 svn_error_clear(err);
3689
3690 /* ### Issue #3014: Return a specific error for broken pipes,
3691 * ### with a single element in the error chain. */
3692 if (SVN__APR_STATUS_IS_EPIPE(status))
3693 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
3694
3695 if (name)
3696 return svn_error_wrap_apr(status, _(msg),
3697 try_utf8_from_internal_style(name, pool));
3698 else
3699 return svn_error_wrap_apr(status, "%s", _(msg_no_name));
3700 }
3701
3702
3703 svn_error_t *
svn_io_file_close(apr_file_t * file,apr_pool_t * pool)3704 svn_io_file_close(apr_file_t *file, apr_pool_t *pool)
3705 {
3706 return do_io_file_wrapper_cleanup(file, apr_file_close(file),
3707 N_("Can't close file '%s'"),
3708 N_("Can't close stream"),
3709 pool);
3710 }
3711
3712
3713 svn_error_t *
svn_io_file_getc(char * ch,apr_file_t * file,apr_pool_t * pool)3714 svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool)
3715 {
3716 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file),
3717 N_("Can't read file '%s'"),
3718 N_("Can't read stream"),
3719 pool);
3720 }
3721
3722
3723 svn_error_t *
svn_io_file_putc(char ch,apr_file_t * file,apr_pool_t * pool)3724 svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool)
3725 {
3726 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file),
3727 N_("Can't write file '%s'"),
3728 N_("Can't write stream"),
3729 pool);
3730 }
3731
3732
3733 svn_error_t *
svn_io_file_info_get(apr_finfo_t * finfo,apr_int32_t wanted,apr_file_t * file,apr_pool_t * pool)3734 svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
3735 apr_file_t *file, apr_pool_t *pool)
3736 {
3737 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
3738 wanted &= ~SVN__APR_FINFO_MASK_OUT;
3739
3740 return do_io_file_wrapper_cleanup(
3741 file, apr_file_info_get(finfo, wanted, file),
3742 N_("Can't get attribute information from file '%s'"),
3743 N_("Can't get attribute information from stream"),
3744 pool);
3745 }
3746
3747 svn_error_t *
svn_io_file_size_get(svn_filesize_t * filesize_p,apr_file_t * file,apr_pool_t * pool)3748 svn_io_file_size_get(svn_filesize_t *filesize_p, apr_file_t *file,
3749 apr_pool_t *pool)
3750 {
3751 apr_finfo_t finfo;
3752 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, file, pool));
3753
3754 *filesize_p = finfo.size;
3755 return SVN_NO_ERROR;
3756 }
3757
3758 svn_error_t *
svn_io_file_get_offset(apr_off_t * offset_p,apr_file_t * file,apr_pool_t * pool)3759 svn_io_file_get_offset(apr_off_t *offset_p,
3760 apr_file_t *file,
3761 apr_pool_t *pool)
3762 {
3763 apr_off_t offset;
3764
3765 /* Note that, for buffered files, one (possibly surprising) side-effect
3766 of this call is to flush any unwritten data to disk. */
3767 offset = 0;
3768 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
3769 *offset_p = offset;
3770
3771 return SVN_NO_ERROR;
3772 }
3773
3774 svn_error_t *
svn_io_file_read(apr_file_t * file,void * buf,apr_size_t * nbytes,apr_pool_t * pool)3775 svn_io_file_read(apr_file_t *file, void *buf,
3776 apr_size_t *nbytes, apr_pool_t *pool)
3777 {
3778 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes),
3779 N_("Can't read file '%s'"),
3780 N_("Can't read stream"),
3781 pool);
3782 }
3783
3784
3785 svn_error_t *
svn_io_file_read_full2(apr_file_t * file,void * buf,apr_size_t nbytes,apr_size_t * bytes_read,svn_boolean_t * hit_eof,apr_pool_t * pool)3786 svn_io_file_read_full2(apr_file_t *file, void *buf,
3787 apr_size_t nbytes, apr_size_t *bytes_read,
3788 svn_boolean_t *hit_eof,
3789 apr_pool_t *pool)
3790 {
3791 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read);
3792 if (hit_eof)
3793 {
3794 if (APR_STATUS_IS_EOF(status))
3795 {
3796 *hit_eof = TRUE;
3797 return SVN_NO_ERROR;
3798 }
3799 else
3800 *hit_eof = FALSE;
3801 }
3802
3803 return do_io_file_wrapper_cleanup(file, status,
3804 N_("Can't read file '%s'"),
3805 N_("Can't read stream"),
3806 pool);
3807 }
3808
3809
3810 svn_error_t *
svn_io_file_seek(apr_file_t * file,apr_seek_where_t where,apr_off_t * offset,apr_pool_t * pool)3811 svn_io_file_seek(apr_file_t *file, apr_seek_where_t where,
3812 apr_off_t *offset, apr_pool_t *pool)
3813 {
3814 return do_io_file_wrapper_cleanup(
3815 file, apr_file_seek(file, where, offset),
3816 N_("Can't set position pointer in file '%s'"),
3817 N_("Can't set position pointer in stream"),
3818 pool);
3819 }
3820
3821 svn_error_t *
svn_io_file_aligned_seek(apr_file_t * file,apr_off_t block_size,apr_off_t * buffer_start,apr_off_t offset,apr_pool_t * scratch_pool)3822 svn_io_file_aligned_seek(apr_file_t *file,
3823 apr_off_t block_size,
3824 apr_off_t *buffer_start,
3825 apr_off_t offset,
3826 apr_pool_t *scratch_pool)
3827 {
3828 const apr_size_t apr_default_buffer_size = 4096;
3829 apr_size_t file_buffer_size = apr_default_buffer_size;
3830 apr_off_t desired_offset = 0;
3831 apr_off_t current = 0;
3832 apr_off_t aligned_offset = 0;
3833 svn_boolean_t fill_buffer = FALSE;
3834
3835 /* paranoia check: huge blocks on 32 bit machines may cause overflows */
3836 SVN_ERR_ASSERT(block_size == (apr_size_t)block_size);
3837
3838 /* default for invalid block sizes */
3839 if (block_size == 0)
3840 block_size = apr_default_buffer_size;
3841
3842 file_buffer_size = apr_file_buffer_size_get(file);
3843
3844 /* don't try to set a buffer size for non-buffered files! */
3845 if (file_buffer_size == 0)
3846 {
3847 aligned_offset = offset;
3848 }
3849 else if (file_buffer_size != (apr_size_t)block_size)
3850 {
3851 /* FILE has the wrong buffer size. correct it */
3852 char *buffer;
3853 file_buffer_size = (apr_size_t)block_size;
3854 buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size);
3855 apr_file_buffer_set(file, buffer, file_buffer_size);
3856
3857 /* seek to the start of the block and cause APR to read 1 block */
3858 aligned_offset = offset - (offset % block_size);
3859 fill_buffer = TRUE;
3860 }
3861 else
3862 {
3863 aligned_offset = offset - (offset % file_buffer_size);
3864
3865 /* We have no way to determine the block start of an APR file.
3866 Furthermore, we don't want to throw away the current buffer
3867 contents. Thus, we re-align the buffer only if the CURRENT
3868 offset definitely lies outside the desired, aligned buffer.
3869 This covers the typical case of linear reads getting very
3870 close to OFFSET but reading the previous / following block.
3871
3872 Note that ALIGNED_OFFSET may still be within the current
3873 buffer and no I/O will actually happen in the FILL_BUFFER
3874 section below.
3875 */
3876 SVN_ERR(svn_io_file_seek(file, APR_CUR, ¤t, scratch_pool));
3877 fill_buffer = aligned_offset + file_buffer_size <= current
3878 || current <= aligned_offset;
3879 }
3880
3881 if (fill_buffer)
3882 {
3883 char dummy;
3884 apr_status_t status;
3885
3886 /* seek to the start of the block and cause APR to read 1 block */
3887 SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset,
3888 scratch_pool));
3889 status = apr_file_getc(&dummy, file);
3890
3891 /* read may fail if we seek to or behind EOF. That's ok then. */
3892 if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status))
3893 return do_io_file_wrapper_cleanup(file, status,
3894 N_("Can't read file '%s'"),
3895 N_("Can't read stream"),
3896 scratch_pool);
3897 }
3898
3899 /* finally, seek to the OFFSET the caller wants */
3900 desired_offset = offset;
3901 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool));
3902 if (desired_offset != offset)
3903 return do_io_file_wrapper_cleanup(file, APR_EOF,
3904 N_("Can't seek in file '%s'"),
3905 N_("Can't seek in stream"),
3906 scratch_pool);
3907
3908 /* return the buffer start that we (probably) enforced */
3909 if (buffer_start)
3910 *buffer_start = aligned_offset;
3911
3912 return SVN_NO_ERROR;
3913 }
3914
3915
3916 svn_error_t *
svn_io_file_write(apr_file_t * file,const void * buf,apr_size_t * nbytes,apr_pool_t * pool)3917 svn_io_file_write(apr_file_t *file, const void *buf,
3918 apr_size_t *nbytes, apr_pool_t *pool)
3919 {
3920 return svn_error_trace(do_io_file_wrapper_cleanup(
3921 file, apr_file_write(file, buf, nbytes),
3922 N_("Can't write to file '%s'"),
3923 N_("Can't write to stream"),
3924 pool));
3925 }
3926
3927 svn_error_t *
svn_io_file_flush(apr_file_t * file,apr_pool_t * scratch_pool)3928 svn_io_file_flush(apr_file_t *file,
3929 apr_pool_t *scratch_pool)
3930 {
3931 return svn_error_trace(do_io_file_wrapper_cleanup(
3932 file, apr_file_flush(file),
3933 N_("Can't flush file '%s'"),
3934 N_("Can't flush stream"),
3935 scratch_pool));
3936 }
3937
3938 svn_error_t *
svn_io_file_write_full(apr_file_t * file,const void * buf,apr_size_t nbytes,apr_size_t * bytes_written,apr_pool_t * pool)3939 svn_io_file_write_full(apr_file_t *file, const void *buf,
3940 apr_size_t nbytes, apr_size_t *bytes_written,
3941 apr_pool_t *pool)
3942 {
3943 #ifdef WIN32
3944 #define MAXBUFSIZE 30*1024
3945 apr_size_t bw = nbytes;
3946 apr_size_t to_write = nbytes;
3947 apr_status_t rv;
3948
3949 rv = apr_file_write_full(file, buf, nbytes, &bw);
3950 buf = (char *)buf + bw;
3951 to_write -= bw;
3952
3953 /* Issue #1789: On Windows, writing may fail for large values of NBYTES.
3954 If that is the case, keep track of how many bytes have been written
3955 by the apr_file_write_full() call, and attempt to write the remaining
3956 part in smaller chunks. */
3957 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
3958 && nbytes > MAXBUFSIZE)
3959 {
3960 do {
3961 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write;
3962 rv = apr_file_write(file, buf, &bw);
3963 buf = (char *)buf + bw;
3964 to_write -= bw;
3965 } while (rv == APR_SUCCESS && to_write > 0);
3966 }
3967
3968 /* bytes_written may actually be NULL */
3969 if (bytes_written)
3970 *bytes_written = nbytes - to_write;
3971 #undef MAXBUFSIZE
3972 #else
3973 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written);
3974 #endif
3975
3976 return svn_error_trace(do_io_file_wrapper_cleanup(
3977 file, rv,
3978 N_("Can't write to file '%s'"),
3979 N_("Can't write to stream"),
3980 pool));
3981 }
3982
3983
3984 svn_error_t *
svn_io_write_unique(const char ** tmp_path,const char * dirpath,const void * buf,apr_size_t nbytes,svn_io_file_del_t delete_when,apr_pool_t * pool)3985 svn_io_write_unique(const char **tmp_path,
3986 const char *dirpath,
3987 const void *buf,
3988 apr_size_t nbytes,
3989 svn_io_file_del_t delete_when,
3990 apr_pool_t *pool)
3991 {
3992 apr_file_t *new_file;
3993 svn_error_t *err;
3994
3995 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath,
3996 delete_when, pool, pool));
3997
3998 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool);
3999
4000 if (!err)
4001 {
4002 /* svn_io_file_flush_to_disk() can be very expensive, so use the
4003 cheaper standard flush if the file is created as temporary file
4004 anyway */
4005 if (delete_when == svn_io_file_del_none)
4006 err = svn_io_file_flush_to_disk(new_file, pool);
4007 else
4008 err = svn_io_file_flush(new_file, pool);
4009 }
4010
4011 return svn_error_trace(
4012 svn_error_compose_create(err,
4013 svn_io_file_close(new_file, pool)));
4014 }
4015
4016 svn_error_t *
svn_io_write_atomic2(const char * final_path,const void * buf,apr_size_t nbytes,const char * copy_perms_path,svn_boolean_t flush_to_disk,apr_pool_t * scratch_pool)4017 svn_io_write_atomic2(const char *final_path,
4018 const void *buf,
4019 apr_size_t nbytes,
4020 const char *copy_perms_path,
4021 svn_boolean_t flush_to_disk,
4022 apr_pool_t *scratch_pool)
4023 {
4024 apr_file_t *tmp_file;
4025 const char *tmp_path;
4026 svn_error_t *err;
4027 const char *dirname = svn_dirent_dirname(final_path, scratch_pool);
4028
4029 SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname,
4030 svn_io_file_del_none,
4031 scratch_pool, scratch_pool));
4032
4033 err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool);
4034
4035 if (!err && flush_to_disk)
4036 err = svn_io_file_flush_to_disk(tmp_file, scratch_pool);
4037
4038 err = svn_error_compose_create(err,
4039 svn_io_file_close(tmp_file, scratch_pool));
4040
4041 if (!err && copy_perms_path)
4042 err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool);
4043
4044 if (!err)
4045 err = svn_io_file_rename2(tmp_path, final_path, flush_to_disk,
4046 scratch_pool);
4047
4048 if (err)
4049 {
4050 err = svn_error_compose_create(err,
4051 svn_io_remove_file2(tmp_path, TRUE,
4052 scratch_pool));
4053
4054 return svn_error_createf(err->apr_err, err,
4055 _("Can't write '%s' atomically"),
4056 svn_dirent_local_style(final_path,
4057 scratch_pool));
4058 }
4059
4060 return SVN_NO_ERROR;
4061 }
4062
4063 svn_error_t *
svn_io_file_trunc(apr_file_t * file,apr_off_t offset,apr_pool_t * pool)4064 svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool)
4065 {
4066 /* Workaround for yet another APR issue with trunc.
4067
4068 If the APR file internally is in read mode, the current buffer pointer
4069 will not be clipped to the valid data range. get_file_offset may then
4070 return an invalid position *after* new data was written to it.
4071
4072 To prevent this, write 1 dummy byte just after the OFFSET at which we
4073 will trunc it. That will force the APR file into write mode
4074 internally and the flush() work-around below becomes effective. */
4075 apr_off_t position = 0;
4076
4077 /* A frequent usage is OFFSET==0, in which case we don't need to preserve
4078 any file content or file pointer. */
4079 if (offset)
4080 {
4081 SVN_ERR(svn_io_file_seek(file, APR_CUR, &position, pool));
4082 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
4083 }
4084 SVN_ERR(svn_io_file_putc(0, file, pool));
4085
4086 /* This is a work-around. APR would flush the write buffer
4087 _after_ truncating the file causing now invalid buffered
4088 data to be written behind OFFSET. */
4089 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file),
4090 N_("Can't flush file '%s'"),
4091 N_("Can't flush stream"),
4092 pool));
4093
4094 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset),
4095 N_("Can't truncate file '%s'"),
4096 N_("Can't truncate stream"),
4097 pool));
4098
4099 /* Restore original file pointer, if necessary.
4100 It's currently at OFFSET. */
4101 if (position < offset)
4102 SVN_ERR(svn_io_file_seek(file, APR_SET, &position, pool));
4103
4104 return SVN_NO_ERROR;
4105 }
4106
4107
4108 svn_error_t *
svn_io_read_length_line(apr_file_t * file,char * buf,apr_size_t * limit,apr_pool_t * pool)4109 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
4110 apr_pool_t *pool)
4111 {
4112 /* variables */
4113 apr_size_t total_read = 0;
4114 svn_boolean_t eof = FALSE;
4115 const char *name;
4116 svn_error_t *err;
4117 apr_size_t buf_size = *limit;
4118
4119 while (buf_size > 0)
4120 {
4121 /* read a fair chunk of data at once. But don't get too ambitious
4122 * as that would result in too much waste. Also make sure we can
4123 * put a NUL after the last byte read.
4124 */
4125 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
4126 apr_size_t bytes_read = 0;
4127 char *eol;
4128
4129 if (to_read == 0)
4130 break;
4131
4132 /* read data block (or just a part of it) */
4133 SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
4134 &bytes_read, &eof, pool));
4135
4136 /* look or a newline char */
4137 buf[bytes_read] = 0;
4138 eol = strchr(buf, '\n');
4139 if (eol)
4140 {
4141 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
4142
4143 *eol = 0;
4144 *limit = total_read + (eol - buf);
4145
4146 /* correct the file pointer:
4147 * appear as though we just had read the newline char
4148 */
4149 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
4150
4151 return SVN_NO_ERROR;
4152 }
4153 else if (eof)
4154 {
4155 /* no EOL found but we hit the end of the file.
4156 * Generate a nice EOF error object and return it.
4157 */
4158 char dummy;
4159 SVN_ERR(svn_io_file_getc(&dummy, file, pool));
4160 }
4161
4162 /* next data chunk */
4163 buf_size -= bytes_read;
4164 buf += bytes_read;
4165 total_read += bytes_read;
4166 }
4167
4168 /* buffer limit has been exceeded without finding the EOL */
4169 err = svn_io_file_name_get(&name, file, pool);
4170 if (err)
4171 name = NULL;
4172 svn_error_clear(err);
4173
4174 if (name)
4175 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
4176 _("Can't read length line in file '%s'"),
4177 svn_dirent_local_style(name, pool));
4178 else
4179 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL,
4180 _("Can't read length line in stream"));
4181 }
4182
4183
4184 svn_error_t *
svn_io_stat(apr_finfo_t * finfo,const char * fname,apr_int32_t wanted,apr_pool_t * pool)4185 svn_io_stat(apr_finfo_t *finfo, const char *fname,
4186 apr_int32_t wanted, apr_pool_t *pool)
4187 {
4188 apr_status_t status;
4189 const char *fname_apr;
4190
4191 /* APR doesn't like "" directories */
4192 if (fname[0] == '\0')
4193 fname = ".";
4194
4195 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool));
4196
4197 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4198 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4199
4200 status = apr_stat(finfo, fname_apr, wanted, pool);
4201 if (status)
4202 return svn_error_wrap_apr(status, _("Can't stat '%s'"),
4203 svn_dirent_local_style(fname, pool));
4204
4205 return SVN_NO_ERROR;
4206 }
4207
4208 #if defined(WIN32)
4209 /* Platform specific implementation of apr_file_rename() to workaround
4210 APR problems on Windows. */
4211 static apr_status_t
win32_file_rename(const WCHAR * from_path_w,const WCHAR * to_path_w,svn_boolean_t flush_to_disk)4212 win32_file_rename(const WCHAR *from_path_w,
4213 const WCHAR *to_path_w,
4214 svn_boolean_t flush_to_disk)
4215 {
4216 /* APR calls MoveFileExW() with MOVEFILE_COPY_ALLOWED, while we rely
4217 * that rename is atomic operation. Call MoveFileEx directly on Windows
4218 * without MOVEFILE_COPY_ALLOWED flag to workaround it.
4219 */
4220
4221 DWORD flags = MOVEFILE_REPLACE_EXISTING;
4222
4223 if (flush_to_disk)
4224 {
4225 /* Do not return until the file has actually been moved on the disk. */
4226 flags |= MOVEFILE_WRITE_THROUGH;
4227 }
4228
4229 if (!MoveFileExW(from_path_w, to_path_w, flags))
4230 return apr_get_os_error();
4231
4232 return APR_SUCCESS;
4233 }
4234 #endif
4235
4236 svn_error_t *
svn_io_file_rename2(const char * from_path,const char * to_path,svn_boolean_t flush_to_disk,apr_pool_t * pool)4237 svn_io_file_rename2(const char *from_path, const char *to_path,
4238 svn_boolean_t flush_to_disk, apr_pool_t *pool)
4239 {
4240 apr_status_t status = APR_SUCCESS;
4241 const char *from_path_apr, *to_path_apr;
4242 #if defined(WIN32)
4243 WCHAR *from_path_w;
4244 WCHAR *to_path_w;
4245 #endif
4246
4247 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool));
4248 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool));
4249
4250 #if defined(WIN32)
4251 SVN_ERR(svn_io__utf8_to_unicode_longpath(&from_path_w, from_path_apr, pool));
4252 SVN_ERR(svn_io__utf8_to_unicode_longpath(&to_path_w, to_path_apr, pool));
4253 status = win32_file_rename(from_path_w, to_path_w, flush_to_disk);
4254
4255 /* If the target file is read only NTFS reports EACCESS and
4256 FAT/FAT32 reports EEXIST */
4257 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4258 {
4259 /* Set the destination file writable because Windows will not
4260 allow us to rename when to_path is read-only, but will
4261 allow renaming when from_path is read only. */
4262 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4263
4264 status = win32_file_rename(from_path_w, to_path_w, flush_to_disk);
4265 }
4266 WIN32_RETRY_LOOP(status, win32_file_rename(from_path_w, to_path_w,
4267 flush_to_disk));
4268 #elif defined(__OS2__)
4269 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4270 /* If the target file is read only NTFS reports EACCESS and
4271 FAT/FAT32 reports EEXIST */
4272 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status))
4273 {
4274 /* Set the destination file writable because OS/2 will not
4275 allow us to rename when to_path is read-only, but will
4276 allow renaming when from_path is read only. */
4277 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool));
4278
4279 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4280 }
4281 #else
4282 status = apr_file_rename(from_path_apr, to_path_apr, pool);
4283 #endif /* WIN32 || __OS2__ */
4284
4285 if (status)
4286 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"),
4287 svn_dirent_local_style(from_path, pool),
4288 svn_dirent_local_style(to_path, pool));
4289
4290 #if defined(SVN_ON_POSIX)
4291 if (flush_to_disk)
4292 {
4293 /* On POSIX, the file name is stored in the file's directory entry.
4294 Hence, we need to fsync() that directory as well.
4295 On other operating systems, we'd only be asking for trouble
4296 by trying to open and fsync a directory. */
4297 const char *dirname;
4298 apr_file_t *file;
4299
4300 dirname = svn_dirent_dirname(to_path, pool);
4301 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
4302 pool));
4303 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
4304 SVN_ERR(svn_io_file_close(file, pool));
4305 }
4306 #elif !defined(WIN32)
4307 /* Flush the target of the rename to disk. */
4308 if (flush_to_disk)
4309 {
4310 apr_file_t *file;
4311 SVN_ERR(svn_io_file_open(&file, to_path, APR_WRITE,
4312 APR_OS_DEFAULT, pool));
4313 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
4314 SVN_ERR(svn_io_file_close(file, pool));
4315 }
4316 #endif
4317
4318 return SVN_NO_ERROR;
4319 }
4320
4321
4322 svn_error_t *
svn_io_file_move(const char * from_path,const char * to_path,apr_pool_t * pool)4323 svn_io_file_move(const char *from_path, const char *to_path,
4324 apr_pool_t *pool)
4325 {
4326 svn_error_t *err = svn_error_trace(svn_io_file_rename2(from_path, to_path,
4327 FALSE, pool));
4328
4329 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
4330 {
4331 svn_error_clear(err);
4332
4333 /* svn_io_copy_file() performs atomic copy via temporary file. */
4334 err = svn_error_trace(svn_io_copy_file(from_path, to_path, TRUE,
4335 pool));
4336 }
4337
4338 return err;
4339 }
4340
4341 /* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden.
4342 HIDDEN determines if the hidden attribute
4343 should be set on the newly created directory. */
4344 static svn_error_t *
dir_make(const char * path,apr_fileperms_t perm,svn_boolean_t hidden,svn_boolean_t sgid,apr_pool_t * pool)4345 dir_make(const char *path, apr_fileperms_t perm,
4346 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool)
4347 {
4348 apr_status_t status;
4349 const char *path_apr;
4350
4351 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4352
4353 /* APR doesn't like "" directories */
4354 if (path_apr[0] == '\0')
4355 path_apr = ".";
4356
4357 #if (APR_OS_DEFAULT & APR_WSTICKY)
4358 /* The APR shipped with httpd 2.0.50 contains a bug where
4359 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits.
4360 There is a special case for file creation, but not directory
4361 creation, so directories wind up getting created with the sticky
4362 bit set. (There is no such thing as a setuid directory, and the
4363 setgid bit is apparently ignored at mkdir() time.) If we detect
4364 this problem, work around it by unsetting those bits if we are
4365 passed APR_OS_DEFAULT. */
4366 if (perm == APR_OS_DEFAULT)
4367 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY);
4368 #endif
4369
4370 status = apr_dir_make(path_apr, perm, pool);
4371
4372 #ifdef WIN32
4373 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a
4374 permanent error */
4375 if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
4376 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool));
4377 #endif
4378
4379 if (status)
4380 return svn_error_wrap_apr(status, _("Can't create directory '%s'"),
4381 svn_dirent_local_style(path, pool));
4382
4383 #ifdef APR_FILE_ATTR_HIDDEN
4384 if (hidden)
4385 {
4386 #ifndef WIN32
4387 status = apr_file_attrs_set(path_apr,
4388 APR_FILE_ATTR_HIDDEN,
4389 APR_FILE_ATTR_HIDDEN,
4390 pool);
4391 if (status)
4392 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"),
4393 svn_dirent_local_style(path, pool));
4394 #else
4395 /* on Windows, use our wrapper so we can also set the
4396 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */
4397 svn_error_t *err =
4398 io_win_file_attrs_set(path_apr,
4399 FILE_ATTRIBUTE_HIDDEN |
4400 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4401 FILE_ATTRIBUTE_HIDDEN |
4402 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
4403 pool);
4404 if (err)
4405 return svn_error_createf(err->apr_err, err,
4406 _("Can't hide directory '%s'"),
4407 svn_dirent_local_style(path, pool));
4408 #endif /* WIN32 */
4409 }
4410 #endif /* APR_FILE_ATTR_HIDDEN */
4411
4412 /* Windows does not implement sgid. Skip here because retrieving
4413 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented
4414 to be 'incredibly expensive'. */
4415 #ifndef WIN32
4416 if (sgid)
4417 {
4418 apr_finfo_t finfo;
4419
4420 /* Per our contract, don't do error-checking. Some filesystems
4421 * don't support the sgid bit, and that's okay. */
4422 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool);
4423
4424 if (!status)
4425 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID);
4426 }
4427 #endif
4428
4429 return SVN_NO_ERROR;
4430 }
4431
4432 svn_error_t *
svn_io_dir_make(const char * path,apr_fileperms_t perm,apr_pool_t * pool)4433 svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
4434 {
4435 return dir_make(path, perm, FALSE, FALSE, pool);
4436 }
4437
4438 svn_error_t *
svn_io_dir_make_hidden(const char * path,apr_fileperms_t perm,apr_pool_t * pool)4439 svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm,
4440 apr_pool_t *pool)
4441 {
4442 return dir_make(path, perm, TRUE, FALSE, pool);
4443 }
4444
4445 svn_error_t *
svn_io_dir_make_sgid(const char * path,apr_fileperms_t perm,apr_pool_t * pool)4446 svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm,
4447 apr_pool_t *pool)
4448 {
4449 return dir_make(path, perm, FALSE, TRUE, pool);
4450 }
4451
4452
4453 svn_error_t *
svn_io_dir_open(apr_dir_t ** new_dir,const char * dirname,apr_pool_t * pool)4454 svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool)
4455 {
4456 apr_status_t status;
4457 const char *dirname_apr;
4458
4459 /* APR doesn't like "" directories */
4460 if (dirname[0] == '\0')
4461 dirname = ".";
4462
4463 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4464
4465 status = apr_dir_open(new_dir, dirname_apr, pool);
4466 if (status)
4467 return svn_error_wrap_apr(status, _("Can't open directory '%s'"),
4468 svn_dirent_local_style(dirname, pool));
4469
4470 return SVN_NO_ERROR;
4471 }
4472
4473 svn_error_t *
svn_io_dir_remove_nonrecursive(const char * dirname,apr_pool_t * pool)4474 svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool)
4475 {
4476 apr_status_t status;
4477 const char *dirname_apr;
4478
4479 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4480
4481 status = apr_dir_remove(dirname_apr, pool);
4482
4483 #ifdef WIN32
4484 {
4485 svn_boolean_t retry = TRUE;
4486
4487 if (status == APR_FROM_OS_ERROR(ERROR_DIR_NOT_EMPTY))
4488 {
4489 apr_status_t empty_status = dir_is_empty(dirname_apr, pool);
4490
4491 if (APR_STATUS_IS_ENOTEMPTY(empty_status))
4492 retry = FALSE;
4493 }
4494
4495 if (retry)
4496 {
4497 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool));
4498 }
4499 }
4500 #endif
4501 if (status)
4502 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"),
4503 svn_dirent_local_style(dirname, pool));
4504
4505 return SVN_NO_ERROR;
4506 }
4507
4508
4509 svn_error_t *
svn_io_dir_read(apr_finfo_t * finfo,apr_int32_t wanted,apr_dir_t * thedir,apr_pool_t * pool)4510 svn_io_dir_read(apr_finfo_t *finfo,
4511 apr_int32_t wanted,
4512 apr_dir_t *thedir,
4513 apr_pool_t *pool)
4514 {
4515 apr_status_t status;
4516
4517 status = apr_dir_read(finfo, wanted, thedir);
4518
4519 if (status)
4520 return svn_error_wrap_apr(status, _("Can't read directory"));
4521
4522 /* It would be nice to use entry_name_to_utf8() below, but can we
4523 get the dir's path out of an apr_dir_t? I don't see a reliable
4524 way to do it. */
4525
4526 if (finfo->fname)
4527 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool));
4528
4529 if (finfo->name)
4530 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool));
4531
4532 return SVN_NO_ERROR;
4533 }
4534
4535 svn_error_t *
svn_io_dir_close(apr_dir_t * thedir)4536 svn_io_dir_close(apr_dir_t *thedir)
4537 {
4538 apr_status_t apr_err = apr_dir_close(thedir);
4539 if (apr_err)
4540 return svn_error_wrap_apr(apr_err, _("Error closing directory"));
4541
4542 return SVN_NO_ERROR;
4543 }
4544
4545 svn_error_t *
svn_io_dir_walk2(const char * dirname,apr_int32_t wanted,svn_io_walk_func_t walk_func,void * walk_baton,apr_pool_t * pool)4546 svn_io_dir_walk2(const char *dirname,
4547 apr_int32_t wanted,
4548 svn_io_walk_func_t walk_func,
4549 void *walk_baton,
4550 apr_pool_t *pool)
4551 {
4552 apr_status_t apr_err;
4553 apr_dir_t *handle;
4554 apr_pool_t *subpool;
4555 const char *dirname_apr;
4556 apr_finfo_t finfo;
4557
4558 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME;
4559
4560 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */
4561 wanted &= ~SVN__APR_FINFO_MASK_OUT;
4562
4563 /* The documentation for apr_dir_read used to state that "." and ".."
4564 will be returned as the first two files, but it doesn't
4565 work that way in practice, in particular ext3 on Linux-2.6 doesn't
4566 follow the rules. For details see
4567 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666
4568
4569 If APR ever does implement "dot-first" then it would be possible to
4570 remove the svn_io_stat and walk_func calls and use the walk_func
4571 inside the loop.
4572
4573 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is
4574 documented to provide it, so we have to do a bit extra. */
4575 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool));
4576 SVN_ERR(cstring_from_utf8(&finfo.name,
4577 svn_dirent_basename(dirname, pool),
4578 pool));
4579 finfo.valid |= APR_FINFO_NAME;
4580 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool));
4581
4582 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
4583
4584 /* APR doesn't like "" directories */
4585 if (dirname_apr[0] == '\0')
4586 dirname_apr = ".";
4587
4588 apr_err = apr_dir_open(&handle, dirname_apr, pool);
4589 if (apr_err)
4590 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
4591 svn_dirent_local_style(dirname, pool));
4592
4593 /* iteration subpool */
4594 subpool = svn_pool_create(pool);
4595
4596 while (1)
4597 {
4598 const char *name_utf8;
4599 const char *full_path;
4600
4601 svn_pool_clear(subpool);
4602
4603 apr_err = apr_dir_read(&finfo, wanted, handle);
4604 if (APR_STATUS_IS_ENOENT(apr_err))
4605 break;
4606 else if (apr_err)
4607 {
4608 return svn_error_wrap_apr(apr_err,
4609 _("Can't read directory entry in '%s'"),
4610 svn_dirent_local_style(dirname, pool));
4611 }
4612
4613 if (finfo.filetype == APR_DIR)
4614 {
4615 if (finfo.name[0] == '.'
4616 && (finfo.name[1] == '\0'
4617 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))
4618 /* skip "." and ".." */
4619 continue;
4620
4621 /* some other directory. recurse. it will be passed to the
4622 callback inside the recursion. */
4623 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4624 subpool));
4625 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4626 SVN_ERR(svn_io_dir_walk2(full_path,
4627 wanted,
4628 walk_func,
4629 walk_baton,
4630 subpool));
4631 }
4632 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
4633 {
4634 /* some other directory. pass it to the callback. */
4635 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
4636 subpool));
4637 full_path = svn_dirent_join(dirname, name_utf8, subpool);
4638 SVN_ERR((*walk_func)(walk_baton,
4639 full_path,
4640 &finfo,
4641 subpool));
4642 }
4643 /* else:
4644 Some other type of file; skip it for now. We've reserved the
4645 right to expand our coverage here in the future, though,
4646 without revving this API.
4647 */
4648 }
4649
4650 svn_pool_destroy(subpool);
4651
4652 apr_err = apr_dir_close(handle);
4653 if (apr_err)
4654 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"),
4655 svn_dirent_local_style(dirname, pool));
4656
4657 return SVN_NO_ERROR;
4658 }
4659
4660
4661
4662 /**
4663 * Determine if a directory is empty or not.
4664 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not.
4665 * @param path The directory.
4666 * @param pool Used for temporary allocation.
4667 * @remark If path is not a directory, or some other error occurs,
4668 * then return the appropriate apr status code.
4669 *
4670 * (This function is written in APR style, in anticipation of
4671 * perhaps someday being moved to APR as 'apr_dir_is_empty'.)
4672 */
4673 static apr_status_t
dir_is_empty(const char * dir,apr_pool_t * pool)4674 dir_is_empty(const char *dir, apr_pool_t *pool)
4675 {
4676 apr_status_t apr_err;
4677 apr_dir_t *dir_handle;
4678 apr_finfo_t finfo;
4679 apr_status_t retval = APR_SUCCESS;
4680
4681 /* APR doesn't like "" directories */
4682 if (dir[0] == '\0')
4683 dir = ".";
4684
4685 apr_err = apr_dir_open(&dir_handle, dir, pool);
4686 if (apr_err != APR_SUCCESS)
4687 return apr_err;
4688
4689 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle);
4690 apr_err == APR_SUCCESS;
4691 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle))
4692 {
4693 /* Ignore entries for this dir and its parent, robustly.
4694 (APR promises that they'll come first, so technically
4695 this guard could be moved outside the loop. But Ryan Bloom
4696 says he doesn't believe it, and I believe him. */
4697 if (! (finfo.name[0] == '.'
4698 && (finfo.name[1] == '\0'
4699 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))))
4700 {
4701 retval = APR_ENOTEMPTY;
4702 break;
4703 }
4704 }
4705
4706 /* Make sure we broke out of the loop for the right reason. */
4707 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err))
4708 return apr_err;
4709
4710 apr_err = apr_dir_close(dir_handle);
4711 if (apr_err != APR_SUCCESS)
4712 return apr_err;
4713
4714 return retval;
4715 }
4716
4717
4718 svn_error_t *
svn_io_dir_empty(svn_boolean_t * is_empty_p,const char * path,apr_pool_t * pool)4719 svn_io_dir_empty(svn_boolean_t *is_empty_p,
4720 const char *path,
4721 apr_pool_t *pool)
4722 {
4723 apr_status_t status;
4724 const char *path_apr;
4725
4726 SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
4727
4728 status = dir_is_empty(path_apr, pool);
4729
4730 if (!status)
4731 *is_empty_p = TRUE;
4732 else if (APR_STATUS_IS_ENOTEMPTY(status))
4733 *is_empty_p = FALSE;
4734 else
4735 return svn_error_wrap_apr(status, _("Can't check directory '%s'"),
4736 svn_dirent_local_style(path, pool));
4737
4738 return SVN_NO_ERROR;
4739 }
4740
4741
4742
4743 /*** Version/format files ***/
4744
4745 svn_error_t *
svn_io_write_version_file(const char * path,int version,apr_pool_t * pool)4746 svn_io_write_version_file(const char *path,
4747 int version,
4748 apr_pool_t *pool)
4749 {
4750 const char *path_tmp;
4751 const char *format_contents = apr_psprintf(pool, "%d\n", version);
4752
4753 SVN_ERR_ASSERT(version >= 0);
4754
4755 SVN_ERR(svn_io_write_unique(&path_tmp,
4756 svn_dirent_dirname(path, pool),
4757 format_contents, strlen(format_contents),
4758 svn_io_file_del_none, pool));
4759
4760 #if defined(WIN32) || defined(__OS2__)
4761 /* make the destination writable, but only on Windows, because
4762 Windows does not let us replace read-only files. */
4763 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
4764 #endif /* WIN32 || __OS2__ */
4765
4766 /* rename the temp file as the real destination */
4767 SVN_ERR(svn_io_file_rename2(path_tmp, path, FALSE, pool));
4768
4769 /* And finally remove the perms to make it read only */
4770 return svn_io_set_file_read_only(path, FALSE, pool);
4771 }
4772
4773
4774 svn_error_t *
svn_io_read_version_file(int * version,const char * path,apr_pool_t * pool)4775 svn_io_read_version_file(int *version,
4776 const char *path,
4777 apr_pool_t *pool)
4778 {
4779 apr_file_t *format_file;
4780 char buf[80];
4781 apr_size_t len;
4782 svn_error_t *err;
4783
4784 /* Read a chunk of data from PATH */
4785 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ,
4786 APR_OS_DEFAULT, pool));
4787 len = sizeof(buf);
4788 err = svn_io_file_read(format_file, buf, &len, pool);
4789
4790 /* Close the file. */
4791 SVN_ERR(svn_error_compose_create(err,
4792 svn_io_file_close(format_file, pool)));
4793
4794 /* If there was no data in PATH, return an error. */
4795 if (len == 0)
4796 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
4797 _("Reading '%s'"),
4798 svn_dirent_local_style(path, pool));
4799
4800 /* Check that the first line contains only digits. */
4801 {
4802 apr_size_t i;
4803
4804 for (i = 0; i < len; ++i)
4805 {
4806 char c = buf[i];
4807
4808 if (i > 0 && (c == '\r' || c == '\n'))
4809 {
4810 buf[i] = '\0';
4811 break;
4812 }
4813 if (! svn_ctype_isdigit(c))
4814 return svn_error_createf
4815 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
4816 _("First line of '%s' contains non-digit"),
4817 svn_dirent_local_style(path, pool));
4818 }
4819 }
4820
4821 /* Convert to integer. */
4822 SVN_ERR(svn_cstring_atoi(version, buf));
4823
4824 return SVN_NO_ERROR;
4825 }
4826
4827
4828 /* Do a byte-for-byte comparison of FILE1 and FILE2. */
4829 static svn_error_t *
contents_identical_p(svn_boolean_t * identical_p,const char * file1,const char * file2,apr_pool_t * pool)4830 contents_identical_p(svn_boolean_t *identical_p,
4831 const char *file1,
4832 const char *file2,
4833 apr_pool_t *pool)
4834 {
4835 svn_error_t *err;
4836 apr_size_t bytes_read1, bytes_read2;
4837 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4838 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
4839 apr_file_t *file1_h;
4840 apr_file_t *file2_h;
4841 svn_boolean_t eof1 = FALSE;
4842 svn_boolean_t eof2 = FALSE;
4843
4844 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4845 pool));
4846
4847 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4848 pool);
4849
4850 if (err)
4851 return svn_error_trace(
4852 svn_error_compose_create(err,
4853 svn_io_file_close(file1_h, pool)));
4854
4855 *identical_p = TRUE; /* assume TRUE, until disproved below */
4856 while (!err && !eof1 && !eof2)
4857 {
4858 err = svn_io_file_read_full2(file1_h, buf1,
4859 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4860 &eof1, pool);
4861 if (err)
4862 break;
4863
4864 err = svn_io_file_read_full2(file2_h, buf2,
4865 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4866 &eof2, pool);
4867 if (err)
4868 break;
4869
4870 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1))
4871 {
4872 *identical_p = FALSE;
4873 break;
4874 }
4875 }
4876
4877 /* Special case: one file being a prefix of the other and the shorter
4878 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */
4879 if (!err && (eof1 != eof2))
4880 *identical_p = FALSE;
4881
4882 return svn_error_trace(
4883 svn_error_compose_create(
4884 err,
4885 svn_error_compose_create(svn_io_file_close(file1_h, pool),
4886 svn_io_file_close(file2_h, pool))));
4887 }
4888
4889
4890
4891 /* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */
4892 static svn_error_t *
contents_three_identical_p(svn_boolean_t * identical_p12,svn_boolean_t * identical_p23,svn_boolean_t * identical_p13,const char * file1,const char * file2,const char * file3,apr_pool_t * scratch_pool)4893 contents_three_identical_p(svn_boolean_t *identical_p12,
4894 svn_boolean_t *identical_p23,
4895 svn_boolean_t *identical_p13,
4896 const char *file1,
4897 const char *file2,
4898 const char *file3,
4899 apr_pool_t *scratch_pool)
4900 {
4901 svn_error_t *err;
4902 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4903 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4904 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
4905 apr_file_t *file1_h;
4906 apr_file_t *file2_h;
4907 apr_file_t *file3_h;
4908 svn_boolean_t eof1 = FALSE;
4909 svn_boolean_t eof2 = FALSE;
4910 svn_boolean_t eof3 = FALSE;
4911
4912 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT,
4913 scratch_pool));
4914
4915 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT,
4916 scratch_pool);
4917
4918 if (err)
4919 return svn_error_trace(
4920 svn_error_compose_create(err,
4921 svn_io_file_close(file1_h, scratch_pool)));
4922
4923 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT,
4924 scratch_pool);
4925
4926 if (err)
4927 return svn_error_trace(
4928 svn_error_compose_create(
4929 err,
4930 svn_error_compose_create(svn_io_file_close(file1_h,
4931 scratch_pool),
4932 svn_io_file_close(file2_h,
4933 scratch_pool))));
4934
4935 /* assume TRUE, until disproved below */
4936 *identical_p12 = *identical_p23 = *identical_p13 = TRUE;
4937 /* We need to read as long as no error occurs, and as long as one of the
4938 * flags could still change due to a read operation */
4939 while (!err
4940 && ((*identical_p12 && !eof1 && !eof2)
4941 || (*identical_p23 && !eof2 && !eof3)
4942 || (*identical_p13 && !eof1 && !eof3)))
4943 {
4944 apr_size_t bytes_read1, bytes_read2, bytes_read3;
4945 svn_boolean_t read_1, read_2, read_3;
4946
4947 read_1 = read_2 = read_3 = FALSE;
4948
4949 /* As long as a file is not at the end yet, and it is still
4950 * potentially identical to another file, we read the next chunk.*/
4951 if (!eof1 && (*identical_p12 || *identical_p13))
4952 {
4953 err = svn_io_file_read_full2(file1_h, buf1,
4954 SVN__STREAM_CHUNK_SIZE, &bytes_read1,
4955 &eof1, scratch_pool);
4956 if (err)
4957 break;
4958 read_1 = TRUE;
4959 }
4960
4961 if (!eof2 && (*identical_p12 || *identical_p23))
4962 {
4963 err = svn_io_file_read_full2(file2_h, buf2,
4964 SVN__STREAM_CHUNK_SIZE, &bytes_read2,
4965 &eof2, scratch_pool);
4966 if (err)
4967 break;
4968 read_2 = TRUE;
4969 }
4970
4971 if (!eof3 && (*identical_p13 || *identical_p23))
4972 {
4973 err = svn_io_file_read_full2(file3_h, buf3,
4974 SVN__STREAM_CHUNK_SIZE, &bytes_read3,
4975 &eof3, scratch_pool);
4976 if (err)
4977 break;
4978 read_3 = TRUE;
4979 }
4980
4981 /* If the files are still marked identical, and at least one of them
4982 * is not at the end of file, we check whether they differ, and set
4983 * their flag to false then. */
4984 if (*identical_p12
4985 && (read_1 || read_2)
4986 && ((eof1 != eof2)
4987 || (bytes_read1 != bytes_read2)
4988 || memcmp(buf1, buf2, bytes_read1)))
4989 {
4990 *identical_p12 = FALSE;
4991 }
4992
4993 if (*identical_p23
4994 && (read_2 || read_3)
4995 && ((eof2 != eof3)
4996 || (bytes_read2 != bytes_read3)
4997 || memcmp(buf2, buf3, bytes_read2)))
4998 {
4999 *identical_p23 = FALSE;
5000 }
5001
5002 if (*identical_p13
5003 && (read_1 || read_3)
5004 && ((eof1 != eof3)
5005 || (bytes_read1 != bytes_read3)
5006 || memcmp(buf1, buf3, bytes_read3)))
5007 {
5008 *identical_p13 = FALSE;
5009 }
5010 }
5011
5012 return svn_error_trace(
5013 svn_error_compose_create(
5014 err,
5015 svn_error_compose_create(
5016 svn_io_file_close(file1_h, scratch_pool),
5017 svn_error_compose_create(
5018 svn_io_file_close(file2_h, scratch_pool),
5019 svn_io_file_close(file3_h, scratch_pool)))));
5020 }
5021
5022
5023
5024 svn_error_t *
svn_io_files_contents_same_p(svn_boolean_t * same,const char * file1,const char * file2,apr_pool_t * pool)5025 svn_io_files_contents_same_p(svn_boolean_t *same,
5026 const char *file1,
5027 const char *file2,
5028 apr_pool_t *pool)
5029 {
5030 svn_boolean_t q;
5031
5032 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool));
5033
5034 if (q)
5035 {
5036 *same = FALSE;
5037 return SVN_NO_ERROR;
5038 }
5039
5040 SVN_ERR(contents_identical_p(&q, file1, file2, pool));
5041
5042 if (q)
5043 *same = TRUE;
5044 else
5045 *same = FALSE;
5046
5047 return SVN_NO_ERROR;
5048 }
5049
5050 svn_error_t *
svn_io_files_contents_three_same_p(svn_boolean_t * same12,svn_boolean_t * same23,svn_boolean_t * same13,const char * file1,const char * file2,const char * file3,apr_pool_t * scratch_pool)5051 svn_io_files_contents_three_same_p(svn_boolean_t *same12,
5052 svn_boolean_t *same23,
5053 svn_boolean_t *same13,
5054 const char *file1,
5055 const char *file2,
5056 const char *file3,
5057 apr_pool_t *scratch_pool)
5058 {
5059 svn_boolean_t diff_size12, diff_size23, diff_size13;
5060
5061 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12,
5062 &diff_size23,
5063 &diff_size13,
5064 file1,
5065 file2,
5066 file3,
5067 scratch_pool));
5068
5069 if (diff_size12 && diff_size23 && diff_size13)
5070 {
5071 *same12 = *same23 = *same13 = FALSE;
5072 }
5073 else if (diff_size12 && diff_size23)
5074 {
5075 *same12 = *same23 = FALSE;
5076 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool));
5077 }
5078 else if (diff_size23 && diff_size13)
5079 {
5080 *same23 = *same13 = FALSE;
5081 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool));
5082 }
5083 else if (diff_size12 && diff_size13)
5084 {
5085 *same12 = *same13 = FALSE;
5086 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool));
5087 }
5088 else
5089 {
5090 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13);
5091 SVN_ERR(contents_three_identical_p(same12, same23, same13,
5092 file1, file2, file3,
5093 scratch_pool));
5094 }
5095
5096 return SVN_NO_ERROR;
5097 }
5098
5099 #ifdef WIN32
5100 /* Counter value of file_mktemp request (used in a threadsafe way), to make
5101 sure that a single process normally never generates the same tempname
5102 twice */
5103 static volatile apr_uint32_t tempname_counter = 0;
5104 #endif
5105
5106 /* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
5107 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name.
5108 Perform temporary allocations in SCRATCH_POOL and the result in
5109 RESULT_POOL. */
5110 static svn_error_t *
temp_file_create(apr_file_t ** new_file,const char ** new_file_name,const char * directory,apr_int32_t flags,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5111 temp_file_create(apr_file_t **new_file,
5112 const char **new_file_name,
5113 const char *directory,
5114 apr_int32_t flags,
5115 apr_pool_t *result_pool,
5116 apr_pool_t *scratch_pool)
5117 {
5118 #ifndef WIN32
5119 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool);
5120 const char *templ_apr;
5121 apr_status_t status;
5122
5123 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool));
5124
5125 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
5126 data available in POOL and we need a non-const pointer here,
5127 as apr changes the template to return the new filename. */
5128 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool);
5129
5130 if (status)
5131 return svn_error_wrap_apr(status, _("Can't create temporary file from "
5132 "template '%s'"), templ);
5133
5134 /* Translate the returned path back to utf-8 before returning it */
5135 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name,
5136 templ_apr,
5137 result_pool));
5138 #else
5139 /* The Windows implementation of apr_file_mktemp doesn't handle access
5140 denied errors correctly. Therefore we implement our own temp file
5141 creation function here. */
5142
5143 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
5144 ### the function we used before. But we try to guess a more unique
5145 ### name before trying if it exists. */
5146
5147 /* Offset by some time value and a unique request nr to make the number
5148 +- unique for both this process and on the computer */
5149 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter)
5150 + GetCurrentProcessId();
5151 int i;
5152
5153 /* ### Maybe use an iterpool? */
5154 for (i = 0; i <= 99999; i++)
5155 {
5156 apr_uint32_t unique_nr;
5157 const char *unique_name;
5158 const char *unique_name_apr;
5159 apr_file_t *try_file;
5160 apr_status_t apr_err;
5161
5162 /* Generate a number that should be unique for this application and
5163 usually for the entire computer to reduce the number of cycles
5164 through this loop. (A bit of calculation is much cheaper than
5165 disk io) */
5166 unique_nr = baseNr + 3 * i;
5167
5168 unique_name = svn_dirent_join(directory,
5169 apr_psprintf(scratch_pool, "svn-%X",
5170 unique_nr),
5171 scratch_pool);
5172
5173 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
5174
5175 apr_err = file_open(&try_file, unique_name_apr, flags,
5176 APR_OS_DEFAULT, FALSE, scratch_pool);
5177
5178 if (APR_STATUS_IS_EEXIST(apr_err))
5179 continue;
5180 else if (apr_err)
5181 {
5182 /* On Win32, CreateFile fails with an "Access Denied" error
5183 code, rather than "File Already Exists", if the colliding
5184 name belongs to a directory. */
5185
5186 if (APR_STATUS_IS_EACCES(apr_err))
5187 {
5188 apr_finfo_t finfo;
5189 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
5190 APR_FINFO_TYPE, scratch_pool);
5191
5192 if (!apr_err_2 && finfo.filetype == APR_DIR)
5193 continue;
5194
5195 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) ||
5196 apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION))
5197 {
5198 /* The file is in use by another process or is hidden;
5199 create a new name, but don't do this 99999 times in
5200 case the folder is not writable */
5201 i += 797;
5202 continue;
5203 }
5204
5205 /* Else fall through and return the original error. */
5206 }
5207
5208 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
5209 svn_dirent_local_style(unique_name,
5210 scratch_pool));
5211 }
5212 else
5213 {
5214 /* Move file to the right pool */
5215 apr_err = apr_file_setaside(new_file, try_file, result_pool);
5216
5217 if (apr_err)
5218 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"),
5219 svn_dirent_local_style(unique_name,
5220 scratch_pool));
5221
5222 *new_file_name = apr_pstrdup(result_pool, unique_name);
5223
5224 return SVN_NO_ERROR;
5225 }
5226 }
5227
5228 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
5229 NULL,
5230 _("Unable to make name in '%s'"),
5231 svn_dirent_local_style(directory, scratch_pool));
5232 #endif
5233 }
5234
5235 /* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */
5236 svn_error_t *
svn_io_file_name_get(const char ** filename,apr_file_t * file,apr_pool_t * pool)5237 svn_io_file_name_get(const char **filename,
5238 apr_file_t *file,
5239 apr_pool_t *pool)
5240 {
5241 const char *fname_apr;
5242 apr_status_t status;
5243
5244 status = apr_file_name_get(&fname_apr, file);
5245 if (status)
5246 return svn_error_wrap_apr(status, _("Can't get file name"));
5247
5248 if (fname_apr)
5249 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool));
5250 else
5251 *filename = NULL;
5252
5253 return SVN_NO_ERROR;
5254 }
5255
5256
5257 svn_error_t *
svn_io_open_unique_file3(apr_file_t ** file,const char ** unique_path,const char * dirpath,svn_io_file_del_t delete_when,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5258 svn_io_open_unique_file3(apr_file_t **file,
5259 const char **unique_path,
5260 const char *dirpath,
5261 svn_io_file_del_t delete_when,
5262 apr_pool_t *result_pool,
5263 apr_pool_t *scratch_pool)
5264 {
5265 apr_file_t *tempfile;
5266 const char *tempname;
5267 struct temp_file_cleanup_s *baton = NULL;
5268 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
5269 APR_BUFFERED | APR_BINARY);
5270 #if !defined(WIN32) && !defined(__OS2__)
5271 apr_fileperms_t perms;
5272 svn_boolean_t using_system_temp_dir = FALSE;
5273 #endif
5274
5275 SVN_ERR_ASSERT(file || unique_path);
5276 if (file)
5277 *file = NULL;
5278 if (unique_path)
5279 *unique_path = NULL;
5280
5281 if (dirpath == NULL)
5282 {
5283 #if !defined(WIN32) && !defined(__OS2__)
5284 using_system_temp_dir = TRUE;
5285 #endif
5286 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
5287 }
5288
5289 switch (delete_when)
5290 {
5291 case svn_io_file_del_on_pool_cleanup:
5292 baton = apr_palloc(result_pool, sizeof(*baton));
5293 baton->pool = result_pool;
5294 baton->fname_apr = NULL;
5295
5296 /* Because cleanups are run LIFO, we need to make sure to register
5297 our cleanup before the apr_file_close cleanup:
5298
5299 On Windows, you can't remove an open file.
5300 */
5301 apr_pool_cleanup_register(result_pool, baton,
5302 temp_file_plain_cleanup_handler,
5303 temp_file_child_cleanup_handler);
5304
5305 break;
5306 case svn_io_file_del_on_close:
5307 flags |= APR_DELONCLOSE;
5308 break;
5309 default:
5310 break;
5311 }
5312
5313 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags,
5314 result_pool, scratch_pool));
5315
5316 #if !defined(WIN32) && !defined(__OS2__)
5317 /* apr_file_mktemp() creates files with mode 0600.
5318 * This is appropriate if we're using a system temp dir since we don't
5319 * want to leak sensitive data into temp files other users can read.
5320 * If we're not using a system temp dir we're probably using the
5321 * .svn/tmp area and it's likely that the tempfile will end up being
5322 * copied or renamed into the working copy.
5323 * This would cause working files having mode 0600 while users might
5324 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this
5325 * case, but only if the umask allows it. */
5326 if (!using_system_temp_dir)
5327 {
5328 svn_error_t *err;
5329
5330 SVN_ERR(merge_default_file_perms(tempfile, &perms, dirpath,
5331 scratch_pool));
5332 err = file_perms_set2(tempfile, perms, scratch_pool);
5333 if (err)
5334 {
5335 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
5336 APR_STATUS_IS_ENOTIMPL(err->apr_err))
5337 svn_error_clear(err);
5338 else
5339 {
5340 return svn_error_quick_wrapf(
5341 err, _("Can't set permissions on '%s'"),
5342 svn_dirent_local_style(tempname, scratch_pool));
5343 }
5344 }
5345 }
5346 #endif
5347
5348 if (file)
5349 *file = tempfile;
5350 else
5351 SVN_ERR(svn_io_file_close(tempfile, scratch_pool));
5352
5353 if (unique_path)
5354 *unique_path = tempname; /* Was allocated in result_pool */
5355
5356 if (baton)
5357 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool));
5358
5359 return SVN_NO_ERROR;
5360 }
5361
5362 svn_error_t *
svn_io_file_readline(apr_file_t * file,svn_stringbuf_t ** stringbuf,const char ** eol,svn_boolean_t * eof,apr_size_t max_len,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5363 svn_io_file_readline(apr_file_t *file,
5364 svn_stringbuf_t **stringbuf,
5365 const char **eol,
5366 svn_boolean_t *eof,
5367 apr_size_t max_len,
5368 apr_pool_t *result_pool,
5369 apr_pool_t *scratch_pool)
5370 {
5371 svn_stringbuf_t *str;
5372 const char *eol_str;
5373 apr_size_t numbytes;
5374 char c;
5375 apr_size_t len;
5376 svn_boolean_t found_eof;
5377
5378 str = svn_stringbuf_create_ensure(80, result_pool);
5379
5380 /* Read bytes into STR up to and including, but not storing,
5381 * the next EOL sequence. */
5382 eol_str = NULL;
5383 numbytes = 1;
5384 len = 0;
5385 found_eof = FALSE;
5386 while (!found_eof)
5387 {
5388 if (len < max_len)
5389 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5390 &found_eof, scratch_pool));
5391 len++;
5392 if (numbytes != 1 || len > max_len)
5393 {
5394 found_eof = TRUE;
5395 break;
5396 }
5397
5398 if (c == '\n')
5399 {
5400 eol_str = "\n";
5401 }
5402 else if (c == '\r')
5403 {
5404 eol_str = "\r";
5405
5406 if (!found_eof && len < max_len)
5407 {
5408 apr_off_t pos;
5409
5410 /* Check for "\r\n" by peeking at the next byte. */
5411 SVN_ERR(svn_io_file_get_offset(&pos, file, scratch_pool));
5412 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes,
5413 &found_eof, scratch_pool));
5414 if (numbytes == 1 && c == '\n')
5415 {
5416 eol_str = "\r\n";
5417 len++;
5418 }
5419 else
5420 {
5421 /* Pretend we never peeked. */
5422 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool));
5423 found_eof = FALSE;
5424 numbytes = 1;
5425 }
5426 }
5427 }
5428 else
5429 svn_stringbuf_appendbyte(str, c);
5430
5431 if (eol_str)
5432 break;
5433 }
5434
5435 if (eol)
5436 *eol = eol_str;
5437 if (eof)
5438 *eof = found_eof;
5439 *stringbuf = str;
5440
5441 return SVN_NO_ERROR;
5442 }
5443