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