Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_wc / adm_files.c
blob09ac3fb6f76783b38197a091b698713c7b51685f
1 /*
2 * adm_files.c: helper routines for handling files & dirs in the
3 * working copy administrative area (creating,
4 * deleting, opening, and closing). This is the only
5 * code that actually knows where administrative
6 * information is kept.
8 * ====================================================================
9 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at http://subversion.tigris.org/license-1.html.
14 * If newer versions of this license are posted there, you may use a
15 * newer version instead, at your option.
17 * This software consists of voluntary contributions made by many
18 * individuals. For exact contribution history, see the revision
19 * history and logs, available at http://subversion.tigris.org/.
20 * ====================================================================
25 #include <stdarg.h>
26 #include <assert.h>
27 #include <apr_pools.h>
28 #include <apr_file_io.h>
29 #include <apr_strings.h>
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_io.h"
34 #include "svn_path.h"
36 #include "wc.h"
37 #include "adm_files.h"
38 #include "entries.h"
39 #include "lock.h"
41 #include "svn_private_config.h"
42 #include "private/svn_wc_private.h"
45 /*** File names in the adm area. ***/
47 /* The default name of the WC admin directory. This name is always
48 checked by svn_wc_is_adm_dir. */
49 static const char default_adm_dir_name[] = ".svn";
51 /* The name that is actually used for the WC admin directory. The
52 commonest case where this won't be the default is in Windows
53 ASP.NET development environments, which choke on ".svn". */
54 static const char *adm_dir_name = default_adm_dir_name;
57 svn_boolean_t
58 svn_wc_is_adm_dir(const char *name, apr_pool_t *pool)
60 return (0 == strcmp(name, adm_dir_name)
61 || 0 == strcmp(name, default_adm_dir_name));
65 const char *
66 svn_wc_get_adm_dir(apr_pool_t *pool)
68 return adm_dir_name;
72 svn_error_t *
73 svn_wc_set_adm_dir(const char *name, apr_pool_t *pool)
75 /* This is the canonical list of administrative directory names.
77 FIXME:
78 An identical list is used in
79 libsvn_subr/opt.c:svn_opt_args_to_target_array3(),
80 but that function can't use this list, because that use would
81 create a circular dependency between libsvn_wc and libsvn_subr.
82 Make sure changes to the lists are always synchronized! */
83 static const char *valid_dir_names[] = {
84 default_adm_dir_name,
85 "_svn",
86 NULL
89 const char **dir_name;
90 for (dir_name = valid_dir_names; *dir_name; ++dir_name)
91 if (0 == strcmp(name, *dir_name))
93 /* Use the pointer to the statically allocated string
94 constant, to avoid potential pool lifetime issues. */
95 adm_dir_name = *dir_name;
96 return SVN_NO_ERROR;
98 return svn_error_createf
99 (SVN_ERR_BAD_FILENAME, NULL,
100 _("'%s' is not a valid administrative directory name"),
101 svn_path_local_style(name, pool));
105 /* Return the path to something in PATH's administrative area.
107 * First, the adm subdir is appended to PATH as a component, then the
108 * "tmp" directory is added iff USE_TMP is set, then each of the
109 * varargs in AP (char *'s) is appended as a path component. The list
110 * must be terminated with a NULL argument.
112 * Adding an empty component results in no effect (i.e., the separator
113 * char is not doubled).
115 * If EXTENSION is non-null, it will be appended to the final string
116 * without a separator character.
118 static const char *
119 v_extend_with_adm_name(const char *path,
120 const char *extension,
121 svn_boolean_t use_tmp,
122 apr_pool_t *pool,
123 va_list ap)
125 const char *this;
127 /* Tack on the administrative subdirectory. */
128 path = svn_path_join(path, adm_dir_name, pool);
130 /* If this is a tmp file, name it into the tmp area. */
131 if (use_tmp)
132 path = svn_path_join(path, SVN_WC__ADM_TMP, pool);
134 /* Tack on everything else. */
135 while ((this = va_arg(ap, const char *)) != NULL)
137 if (this[0] == '\0')
138 continue;
140 path = svn_path_join(path, this, pool);
143 if (extension)
144 path = apr_pstrcat(pool, path, extension, NULL);
146 return path;
150 /* See v_extend_with_adm_name() for details. */
151 static const char *
152 extend_with_adm_name(const char *path,
153 const char *extension,
154 svn_boolean_t use_tmp,
155 apr_pool_t *pool,
156 ...)
158 va_list ap;
160 va_start(ap, pool);
161 path = v_extend_with_adm_name(path, extension, use_tmp, pool, ap);
162 va_end(ap);
164 return path;
168 const char *
169 svn_wc__adm_path(const char *path,
170 svn_boolean_t tmp,
171 apr_pool_t *pool,
172 ...)
174 va_list ap;
176 va_start(ap, pool);
177 path = v_extend_with_adm_name(path, NULL, tmp, pool, ap);
178 va_end(ap);
180 return path;
184 svn_boolean_t
185 svn_wc__adm_path_exists(const char *path,
186 svn_boolean_t tmp,
187 apr_pool_t *pool,
188 ...)
190 svn_node_kind_t kind;
191 svn_error_t *err;
192 va_list ap;
194 va_start(ap, pool);
195 path = v_extend_with_adm_name(path, NULL, tmp, pool, ap);
196 va_end(ap);
198 err = svn_io_check_path(path, &kind, pool);
199 if (err)
201 svn_error_clear(err);
202 /* Return early, since kind is undefined in this case. */
203 return FALSE;
206 if (kind == svn_node_none)
207 return FALSE;
208 else
209 return TRUE;
214 /*** Making and using files in the adm area. ***/
217 /* Create an empty THING in the adm area with permissions set to PERMS.
218 * If TMP is non-zero, then create THING in the tmp dir.
220 * Does not check if THING already exists, so be careful -- THING will
221 * be empty after this no matter what.
223 svn_error_t *
224 svn_wc__make_adm_thing(svn_wc_adm_access_t *adm_access,
225 const char *thing,
226 svn_node_kind_t type,
227 apr_fileperms_t perms,
228 svn_boolean_t tmp,
229 apr_pool_t *pool)
231 svn_error_t *err = SVN_NO_ERROR;
232 apr_file_t *f = NULL;
233 const char *path;
235 SVN_ERR(svn_wc__adm_write_check(adm_access));
237 path = extend_with_adm_name(svn_wc_adm_access_path(adm_access),
238 NULL, tmp, pool, thing, NULL);
240 if (type == svn_node_file)
242 SVN_ERR(svn_io_file_open(&f, path,
243 (APR_WRITE | APR_CREATE | APR_EXCL),
244 perms,
245 pool));
247 /* Creation succeeded, so close immediately. */
248 SVN_ERR(svn_io_file_close(f, pool));
250 else if (type == svn_node_dir)
252 SVN_ERR(svn_io_dir_make(path, perms, pool));
254 else /* unknown type argument, wrongness */
256 /* We're only capturing this here because there wouldn't be a
257 segfault or other obvious indicator that something went
258 wrong. Even so, not sure if it's appropriate. Thoughts? */
259 err = svn_error_create
260 (0, NULL, _("Bad type indicator"));
263 return err;
267 svn_error_t *
268 svn_wc__make_killme(svn_wc_adm_access_t *adm_access,
269 svn_boolean_t adm_only,
270 apr_pool_t *pool)
272 const char *path;
274 SVN_ERR(svn_wc__adm_write_check(adm_access));
276 path = extend_with_adm_name(svn_wc_adm_access_path(adm_access),
277 NULL, FALSE, pool, SVN_WC__ADM_KILLME, NULL);
279 return svn_io_file_create(path, adm_only ? SVN_WC__KILL_ADM_ONLY : "", pool);
282 svn_error_t *
283 svn_wc__check_killme(svn_wc_adm_access_t *adm_access,
284 svn_boolean_t *exists,
285 svn_boolean_t *kill_adm_only,
286 apr_pool_t *pool)
288 const char *path;
289 svn_error_t *err;
290 svn_stringbuf_t *contents;
292 path = extend_with_adm_name(svn_wc_adm_access_path(adm_access),
293 NULL, FALSE, pool, SVN_WC__ADM_KILLME, NULL);
295 err = svn_stringbuf_from_file(&contents, path, pool);
297 if (err)
299 if (APR_STATUS_IS_ENOENT(err->apr_err))
301 /* Killme file doesn't exist. */
302 *exists = FALSE;
303 svn_error_clear(err);
304 err = SVN_NO_ERROR;
307 return err;
310 *exists = TRUE;
312 /* If the killme file contains the string 'adm-only' then only the
313 administrative area should be removed. */
314 *kill_adm_only = svn_string_compare_stringbuf
315 (svn_string_create(SVN_WC__KILL_ADM_ONLY, pool), contents);
317 return SVN_NO_ERROR;
321 /*** Syncing files in the adm area. ***/
323 static svn_error_t *
324 sync_adm_file(const char *path,
325 const char *extension,
326 apr_pool_t *pool,
327 ...)
329 /* Some code duplication with close_adm_file() seems unavoidable,
330 given how C va_lists work. */
332 const char *tmp_path;
333 va_list ap;
335 /* Extend tmp name. */
336 va_start(ap, pool);
337 tmp_path = v_extend_with_adm_name(path, extension, 1, pool, ap);
338 va_end(ap);
340 /* Extend real name. */
341 va_start(ap, pool);
342 path = v_extend_with_adm_name(path, extension, 0, pool, ap);
343 va_end(ap);
345 /* Rename. */
346 SVN_ERR(svn_io_file_rename(tmp_path, path, pool));
347 SVN_ERR(svn_io_set_file_read_only(path, FALSE, pool));
349 return SVN_NO_ERROR;
353 /* Rename a tmp text-base file to its real text-base name.
354 The file had better already be closed. */
355 svn_error_t *
356 svn_wc__sync_text_base(const char *path, apr_pool_t *pool)
358 const char *parent_path, *base_name;
359 svn_path_split(path, &parent_path, &base_name, pool);
360 return sync_adm_file(parent_path,
361 SVN_WC__BASE_EXT,
362 pool,
363 SVN_WC__ADM_TEXT_BASE,
364 base_name,
365 NULL);
368 const char *
369 svn_wc__text_base_path(const char *path,
370 svn_boolean_t tmp,
371 apr_pool_t *pool)
373 const char *newpath, *base_name;
375 svn_path_split(path, &newpath, &base_name, pool);
376 return extend_with_adm_name(newpath,
377 SVN_WC__BASE_EXT,
378 tmp,
379 pool,
380 SVN_WC__ADM_TEXT_BASE,
381 base_name,
382 NULL);
385 const char *
386 svn_wc__text_revert_path(const char *path,
387 svn_boolean_t tmp,
388 apr_pool_t *pool)
390 const char *newpath, *base_name;
392 svn_path_split(path, &newpath, &base_name, pool);
393 return extend_with_adm_name(newpath,
394 SVN_WC__REVERT_EXT,
395 tmp,
396 pool,
397 SVN_WC__ADM_TEXT_BASE,
398 base_name,
399 NULL);
402 svn_error_t *
403 svn_wc__prop_path(const char **prop_path,
404 const char *path,
405 svn_node_kind_t node_kind,
406 svn_wc__props_kind_t props_kind,
407 svn_boolean_t tmp,
408 apr_pool_t *pool)
410 if (node_kind == svn_node_dir) /* It's a working copy dir */
412 static const char * names[] = {
413 SVN_WC__ADM_DIR_PROP_BASE, /* prop_path_kind_base */
414 SVN_WC__ADM_DIR_PROP_REVERT, /* prop_path_kind_revert */
415 SVN_WC__ADM_DIR_WCPROPS, /* prop_path_kind_wcprop */
416 SVN_WC__ADM_DIR_PROPS /* prop_path_kind_working */
419 *prop_path = extend_with_adm_name
420 (path,
421 NULL,
422 tmp,
423 pool,
424 names[props_kind],
425 NULL);
427 else /* It's a file */
429 static const char * extensions[] = {
430 SVN_WC__BASE_EXT, /* prop_path_kind_base */
431 SVN_WC__REVERT_EXT, /* prop_path_kind_revert */
432 SVN_WC__WORK_EXT, /* prop_path_kind_wcprop */
433 SVN_WC__WORK_EXT /* prop_path_kind_working */
436 static const char * dirs[] = {
437 SVN_WC__ADM_PROP_BASE, /* prop_path_kind_base */
438 SVN_WC__ADM_PROP_BASE, /* prop_path_kind_revert */
439 SVN_WC__ADM_WCPROPS, /* prop_path_kind_wcprop */
440 SVN_WC__ADM_PROPS /* prop_path_kind_working */
443 const char *base_name;
445 svn_path_split(path, prop_path, &base_name, pool);
446 *prop_path = extend_with_adm_name
447 (*prop_path,
448 extensions[props_kind],
449 tmp,
450 pool,
451 dirs[props_kind],
452 base_name,
453 NULL);
456 return SVN_NO_ERROR;
460 /*** Opening and closing files in the adm area. ***/
462 /* Open a file somewhere in the adm area for directory PATH.
463 * First, add the adm subdir as the next component of PATH, then add
464 * each of the varargs (they are char *'s), then add EXTENSION if it
465 * is non-null, then open the resulting file as *HANDLE.
467 * If FLAGS indicates writing, open the file in the adm tmp area.
468 * This means the file will probably need to be renamed from there,
469 * either by passing the sync flag to close_adm_file() later, or with
470 * an explicit call to sync_adm_file().
472 static svn_error_t *
473 open_adm_file(apr_file_t **handle,
474 const char *path,
475 const char *extension,
476 apr_fileperms_t protection,
477 apr_int32_t flags,
478 apr_pool_t *pool,
479 ...)
481 svn_error_t *err = SVN_NO_ERROR;
482 va_list ap;
484 /* If we're writing, always do it to a tmp file. */
485 if (flags & APR_WRITE)
487 if (flags & APR_APPEND)
489 /* We don't handle append. To do so we would need to copy the
490 contents into the apr_file_t once it has been opened. */
491 return svn_error_create
492 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
493 _("APR_APPEND not supported for adm files"));
496 /* Need to own the temporary file, so don't reuse an existing one. */
497 flags |= APR_EXCL | APR_CREATE;
499 /* Extend with tmp name. */
500 va_start(ap, pool);
501 path = v_extend_with_adm_name(path, extension, 1, pool, ap);
502 va_end(ap);
504 else
506 /* Extend with regular adm name. */
507 va_start(ap, pool);
508 path = v_extend_with_adm_name(path, extension, 0, pool, ap);
509 va_end(ap);
512 err = svn_io_file_open(handle, path, flags, protection, pool);
513 if ((flags & APR_WRITE) && err && APR_STATUS_IS_EEXIST(err->apr_err))
515 /* Exclusive open failed, delete and retry */
516 svn_error_clear(err);
517 SVN_ERR(svn_io_remove_file(path, pool));
518 err = svn_io_file_open(handle, path, flags, protection, pool);
521 if (err)
523 /* Oddly enough, APR will set *HANDLE even if the open failed.
524 You'll get a filehandle whose descriptor is -1. There must
525 be a reason this is useful... Anyway, we don't want the
526 handle. */
527 *handle = NULL;
528 /* If we receive a failure to open a file in our temporary directory,
529 * it may be because our temporary directories aren't created.
530 * Older SVN clients did not create these directories.
531 * 'svn cleanup' will fix this problem.
533 if (APR_STATUS_IS_ENOENT(err->apr_err) && (flags & APR_WRITE))
535 err = svn_error_quick_wrap(err,
536 _("Your .svn/tmp directory may be missing or "
537 "corrupt; run 'svn cleanup' and try again"));
541 return err;
545 /* Close the file indicated by FP (PATH is passed to make error
546 * reporting better). If SYNC is non-zero, then the file will be
547 * sync'd from the adm tmp area to its permanent location, otherwise
548 * it will remain in the tmp area. See open_adm_file().
550 static svn_error_t *
551 close_adm_file(apr_file_t *fp,
552 const char *path,
553 const char *extension,
554 svn_boolean_t sync,
555 apr_pool_t *pool,
556 ...)
558 const char *tmp_path;
559 va_list ap;
561 /* Get the full name of the thing we're closing. */
562 va_start(ap, pool);
563 tmp_path = v_extend_with_adm_name(path, extension, sync, pool, ap);
564 va_end(ap);
566 SVN_ERR(svn_io_file_close(fp, pool));
568 /* If we're syncing a tmp file, it needs to be renamed after closing. */
569 if (sync)
571 /* Some code duplication with sync_adm_file() seems unavoidable,
572 given how C va_lists work. */
574 /* Obtain dest name. */
575 va_start(ap, pool);
576 path = v_extend_with_adm_name(path, extension, 0, pool, ap);
577 va_end(ap);
579 /* Rename. */
580 SVN_ERR(svn_io_file_rename(tmp_path, path, pool));
581 SVN_ERR(svn_io_set_file_read_only(path, FALSE, pool));
583 return SVN_NO_ERROR;
586 return SVN_NO_ERROR;
590 svn_error_t *
591 svn_wc__open_adm_file(apr_file_t **handle,
592 const char *path,
593 const char *fname,
594 apr_int32_t flags,
595 apr_pool_t *pool)
597 return open_adm_file(handle, path, NULL, APR_OS_DEFAULT, flags, pool,
598 fname, NULL);
602 svn_error_t *
603 svn_wc__close_adm_file(apr_file_t *fp,
604 const char *path,
605 const char *fname,
606 int sync,
607 apr_pool_t *pool)
609 return close_adm_file(fp, path, NULL, sync, pool, fname, NULL);
613 svn_error_t *
614 svn_wc__remove_adm_file(const char *path, apr_pool_t *pool, ...)
616 va_list ap;
618 va_start(ap, pool);
619 path = v_extend_with_adm_name(path, NULL, 0, pool, ap);
620 va_end(ap);
622 SVN_ERR(svn_io_remove_file(path, pool));
624 return SVN_NO_ERROR;
629 svn_error_t *
630 svn_wc__open_text_base(apr_file_t **handle,
631 const char *path,
632 apr_int32_t flags,
633 apr_pool_t *pool)
635 const char *parent_path, *base_name;
636 svn_path_split(path, &parent_path, &base_name, pool);
637 return open_adm_file(handle, parent_path, SVN_WC__BASE_EXT, APR_OS_DEFAULT,
638 flags, pool, SVN_WC__ADM_TEXT_BASE, base_name, NULL);
642 svn_error_t *
643 svn_wc__open_revert_base(apr_file_t **handle,
644 const char *path,
645 apr_int32_t flags,
646 apr_pool_t *pool)
648 const char *parent_path, *base_name;
649 svn_path_split(path, &parent_path, &base_name, pool);
650 return open_adm_file(handle, parent_path, SVN_WC__REVERT_EXT, APR_OS_DEFAULT,
651 flags, pool, SVN_WC__ADM_TEXT_BASE, base_name, NULL);
656 svn_error_t *
657 svn_wc__close_text_base(apr_file_t *fp,
658 const char *path,
659 int write,
660 apr_pool_t *pool)
662 const char *parent_path, *base_name;
663 svn_path_split(path, &parent_path, &base_name, pool);
664 return close_adm_file(fp, parent_path, SVN_WC__BASE_EXT, write, pool,
665 SVN_WC__ADM_TEXT_BASE, base_name, NULL);
668 svn_error_t *
669 svn_wc__close_revert_base(apr_file_t *fp,
670 const char *path,
671 int write,
672 apr_pool_t *pool)
674 const char *parent_path, *base_name;
675 svn_path_split(path, &parent_path, &base_name, pool);
676 return close_adm_file(fp, parent_path, SVN_WC__REVERT_EXT, write, pool,
677 SVN_WC__ADM_TEXT_BASE, base_name, NULL);
682 svn_error_t *
683 svn_wc__open_props(apr_file_t **handle,
684 const char *path,
685 svn_node_kind_t kind,
686 apr_int32_t flags,
687 svn_boolean_t base,
688 svn_boolean_t wcprops,
689 apr_pool_t *pool)
691 const char *parent_dir, *base_name;
692 int wc_format_version;
694 if (kind == svn_node_dir)
695 parent_dir = path;
696 else
697 svn_path_split(path, &parent_dir, &base_name, pool);
699 /* At this point, we know we need to open a file in the admin area
700 of parent_dir. First check that parent_dir is a working copy: */
701 SVN_ERR(svn_wc_check_wc(parent_dir, &wc_format_version, pool));
702 if (wc_format_version == 0)
703 return svn_error_createf
704 (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
705 _("'%s' is not a working copy"),
706 svn_path_local_style(parent_dir, pool));
708 /* Then examine the flags to know -which- kind of prop file to get. */
710 if (base && wcprops)
711 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
712 _("No such thing as 'base' "
713 "working copy properties!"));
715 else if (base)
717 if (kind == svn_node_dir)
718 return open_adm_file(handle, parent_dir, NULL, APR_OS_DEFAULT, flags,
719 pool, SVN_WC__ADM_DIR_PROP_BASE, NULL);
720 else
721 return open_adm_file(handle, parent_dir, SVN_WC__BASE_EXT,
722 APR_OS_DEFAULT, flags, pool,
723 SVN_WC__ADM_PROP_BASE, base_name, NULL);
725 else if (wcprops)
727 if (kind == svn_node_dir)
728 return open_adm_file(handle, parent_dir, NULL, APR_OS_DEFAULT, flags,
729 pool, SVN_WC__ADM_DIR_WCPROPS, NULL);
730 else
732 return open_adm_file
733 (handle, parent_dir,
734 SVN_WC__WORK_EXT, APR_OS_DEFAULT,
735 flags, pool, SVN_WC__ADM_WCPROPS, base_name, NULL);
738 else /* plain old property file */
740 if (kind == svn_node_dir)
741 return open_adm_file(handle, parent_dir, NULL, APR_OS_DEFAULT, flags,
742 pool, SVN_WC__ADM_DIR_PROPS, NULL);
743 else
745 return open_adm_file
746 (handle, parent_dir,
747 SVN_WC__WORK_EXT, APR_OS_DEFAULT,
748 flags, pool, SVN_WC__ADM_PROPS, base_name, NULL);
755 svn_error_t *
756 svn_wc__close_props(apr_file_t *fp,
757 const char *path,
758 svn_node_kind_t kind,
759 svn_boolean_t base,
760 svn_boolean_t wcprops,
761 int sync,
762 apr_pool_t *pool)
764 const char *parent_dir, *base_name;
766 if (kind == svn_node_dir)
767 parent_dir = path;
768 else
769 svn_path_split(path, &parent_dir, &base_name, pool);
771 /* At this point, we know we need to close a file in the admin area
772 of parent_dir. Since the file must be open already, we know that
773 parent_dir is a working copy. */
775 /* Then examine the flags to know -which- kind of prop file to get. */
777 if (base && wcprops)
778 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
779 _("No such thing as 'base' "
780 "working copy properties!"));
782 else if (base)
784 if (kind == svn_node_dir)
785 return close_adm_file(fp, parent_dir, NULL, sync, pool,
786 SVN_WC__ADM_DIR_PROP_BASE, NULL);
787 else
788 return close_adm_file(fp, parent_dir, SVN_WC__BASE_EXT, sync, pool,
789 SVN_WC__ADM_PROP_BASE, base_name, NULL);
791 else if (wcprops)
793 if (kind == svn_node_dir)
794 return close_adm_file(fp, parent_dir, NULL, sync, pool,
795 SVN_WC__ADM_DIR_WCPROPS, NULL);
796 else
797 return close_adm_file
798 (fp, parent_dir,
799 SVN_WC__WORK_EXT,
800 sync, pool, SVN_WC__ADM_WCPROPS, base_name, NULL);
802 else /* plain old property file */
804 if (kind == svn_node_dir)
805 return close_adm_file(fp, parent_dir, NULL, sync, pool,
806 SVN_WC__ADM_DIR_PROPS, NULL);
807 else
808 return close_adm_file
809 (fp, parent_dir,
810 SVN_WC__WORK_EXT,
811 sync, pool, SVN_WC__ADM_PROPS, base_name, NULL);
818 /*** Checking for and creating administrative subdirs. ***/
820 /* Set *EXISTS to true iff there's an adm area for PATH, and it matches URL
821 * and REVISION. If there's no adm area, set *EXISTS to false; if
822 * there's an adm area but it doesn't match URL and REVISION, then
823 * return error and don't touch *EXISTS.
825 * ### These semantics are totally bizarre. One wonders what the
826 * ### callers' real needs are. In the long term, this function
827 * ### should probably be unified with svn_wc_check_wc.
829 static svn_error_t *
830 check_adm_exists(svn_boolean_t *exists,
831 const char *path,
832 const char *url,
833 svn_revnum_t revision,
834 apr_pool_t *pool)
836 svn_error_t *err = SVN_NO_ERROR;
838 /* This is a bit odd. We have to open an access baton, which relies
839 on this being a working copy, so use an error as a signal this *isn't*
840 a working copy! */
841 svn_wc_adm_access_t *adm_access;
842 const svn_wc_entry_t *entry;
844 err = svn_wc_adm_open3(&adm_access, NULL, path, FALSE, 0,
845 NULL, NULL, pool);
846 if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
848 svn_error_clear(err);
849 *exists = FALSE;
850 return SVN_NO_ERROR;
852 else if (err)
853 return SVN_NO_ERROR;
855 SVN_ERR(svn_wc__entry_versioned(&entry, path, adm_access, FALSE, pool));
856 SVN_ERR(svn_wc_adm_close(adm_access));
858 /* When the directory exists and is scheduled for deletion do not
859 * check the revision or the URL. The revision can be any
860 * arbitrary revision and the URL may differ if the add is
861 * being driven from a merge which will have a different URL. */
862 if (entry->schedule != svn_wc_schedule_delete)
864 if (entry->revision != revision)
865 return
866 svn_error_createf
867 (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
868 _("Revision %ld doesn't match existing revision %ld in '%s'"),
869 revision, entry->revision, path);
871 /** ### comparing URLs, should they be canonicalized first? */
872 if (strcmp(entry->url, url) != 0)
873 return
874 svn_error_createf
875 (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
876 _("URL '%s' doesn't match existing URL '%s' in '%s'"),
877 url, entry->url, path);
880 *exists = TRUE;
882 return SVN_NO_ERROR;
886 static svn_error_t *
887 make_empty_adm(const char *path, apr_pool_t *pool)
889 path = extend_with_adm_name(path, NULL, 0, pool, NULL);
890 SVN_ERR(svn_io_dir_make_hidden(path, APR_OS_DEFAULT, pool));
891 return SVN_NO_ERROR;
895 static svn_error_t *
896 init_adm_tmp_area(svn_wc_adm_access_t *adm_access,
897 apr_pool_t *pool)
899 /* Default perms */
900 apr_fileperms_t perms = APR_OS_DEFAULT;
902 /* SVN_WC__ADM_TMP */
903 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_TMP,
904 svn_node_dir, perms, 0, pool));
906 /* SVN_WC__ADM_TMP/SVN_WC__ADM_TEXT_BASE */
907 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_TEXT_BASE,
908 svn_node_dir, perms, 1, pool));
910 /* SVN_WC__ADM_TMP/SVN_WC__ADM_PROP_BASE */
911 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_PROP_BASE,
912 svn_node_dir, perms, 1, pool));
914 /* SVN_WC__ADM_TMP/SVN_WC__ADM_PROPS */
915 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_PROPS,
916 svn_node_dir, perms, 1, pool));
918 return SVN_NO_ERROR;
922 /* Set up a new adm area for PATH, with URL as the ancestor url, and
923 INITIAL_REV as the starting revision. The entries file starts out
924 marked as 'incomplete. The adm area starts out locked; remember to
925 unlock it when done. */
926 static svn_error_t *
927 init_adm(const char *path,
928 const char *uuid,
929 const char *url,
930 const char *repos,
931 svn_revnum_t initial_rev,
932 svn_depth_t depth,
933 apr_pool_t *pool)
935 svn_wc_adm_access_t *adm_access;
937 /* Default perms */
938 apr_fileperms_t perms = APR_OS_DEFAULT;
940 /* First, make an empty administrative area. */
941 SVN_ERR(make_empty_adm(path, pool));
943 /* Lock it immediately. Theoretically, no compliant wc library
944 would ever consider this an adm area until a README file were
945 present... but locking it is still appropriately paranoid. */
946 SVN_ERR(svn_wc__adm_pre_open(&adm_access, path, pool));
948 /** Make subdirectories. ***/
950 /* SVN_WC__ADM_TEXT_BASE */
951 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_TEXT_BASE,
952 svn_node_dir, perms, 0, pool));
954 /* SVN_WC__ADM_PROP_BASE */
955 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_PROP_BASE,
956 svn_node_dir, perms, 0, pool));
958 /* SVN_WC__ADM_PROPS */
959 SVN_ERR(svn_wc__make_adm_thing(adm_access, SVN_WC__ADM_PROPS,
960 svn_node_dir, perms, 0, pool));
962 /** Init the tmp area. ***/
963 SVN_ERR(init_adm_tmp_area(adm_access, pool));
965 /** Initialize each administrative file. */
967 /* SVN_WC__ADM_ENTRIES */
968 /* THIS FILE MUST BE CREATED LAST:
969 After this exists, the dir is considered complete. */
970 SVN_ERR(svn_wc__entries_init(path, uuid, url, repos,
971 initial_rev, depth, pool));
973 /* We provide this for backwards compatibilty. Clients that don't understand
974 format version 7 or higher will display a nicer error message if this
975 file exists.
976 ### Consider removing this in svn 1.5 or 1.6. */
977 SVN_ERR(svn_io_write_version_file
978 (extend_with_adm_name(path, NULL, FALSE, pool,
979 SVN_WC__ADM_FORMAT, NULL),
980 SVN_WC__VERSION, pool));
982 /* Now unlock it. It's now a valid working copy directory, that
983 just happens to be at revision 0. */
984 SVN_ERR(svn_wc_adm_close(adm_access));
986 /* Else no problems, we're outta here. */
987 return SVN_NO_ERROR;
990 svn_error_t *
991 svn_wc_ensure_adm3(const char *path,
992 const char *uuid,
993 const char *url,
994 const char *repos,
995 svn_revnum_t revision,
996 svn_depth_t depth,
997 apr_pool_t *pool)
999 svn_boolean_t exists_already;
1001 SVN_ERR(check_adm_exists(&exists_already, path, url, revision, pool));
1002 return (exists_already ? SVN_NO_ERROR :
1003 init_adm(path, uuid, url, repos, revision, depth, pool));
1006 svn_error_t *
1007 svn_wc_ensure_adm2(const char *path,
1008 const char *uuid,
1009 const char *url,
1010 const char *repos,
1011 svn_revnum_t revision,
1012 apr_pool_t *pool)
1014 return svn_wc_ensure_adm3(path, uuid, url, repos, revision,
1015 svn_depth_infinity, pool);
1019 svn_error_t *
1020 svn_wc_ensure_adm(const char *path,
1021 const char *uuid,
1022 const char *url,
1023 svn_revnum_t revision,
1024 apr_pool_t *pool)
1026 return svn_wc_ensure_adm2(path, uuid, url, NULL, revision, pool);
1029 svn_error_t *
1030 svn_wc__adm_destroy(svn_wc_adm_access_t *adm_access,
1031 apr_pool_t *pool)
1033 const char *path;
1035 SVN_ERR(svn_wc__adm_write_check(adm_access));
1037 /* Well, the coast is clear for blowing away the administrative
1038 directory, which also removes the lock file */
1039 path = extend_with_adm_name(svn_wc_adm_access_path(adm_access),
1040 NULL, FALSE, pool, NULL);
1041 SVN_ERR(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
1042 SVN_ERR(svn_wc_adm_close(adm_access));
1044 return SVN_NO_ERROR;
1048 svn_error_t *
1049 svn_wc__adm_cleanup_tmp_area(svn_wc_adm_access_t *adm_access,
1050 apr_pool_t *pool)
1052 const char *tmp_path;
1054 SVN_ERR(svn_wc__adm_write_check(adm_access));
1056 /* Get the path to the tmp area, and blow it away. */
1057 tmp_path = extend_with_adm_name(svn_wc_adm_access_path(adm_access),
1058 NULL, 0, pool, SVN_WC__ADM_TMP, NULL);
1060 SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, pool));
1061 /* Now, rebuild the tmp area. */
1062 SVN_ERR(init_adm_tmp_area(adm_access, pool));
1064 return SVN_NO_ERROR;
1069 svn_error_t *
1070 svn_wc_create_tmp_file2(apr_file_t **fp,
1071 const char **new_name,
1072 const char *path,
1073 svn_io_file_del_t delete_when,
1074 apr_pool_t *pool)
1076 apr_file_t *file;
1078 assert(fp || new_name);
1080 /* Use a self-explanatory name for the file :-) . */
1081 path = svn_wc__adm_path(path, TRUE, pool, "tempfile", NULL);
1083 /* Open a unique file; use APR_DELONCLOSE. */
1084 SVN_ERR(svn_io_open_unique_file2(&file, new_name,
1085 path, ".tmp", delete_when, pool));
1088 if (fp)
1089 *fp = file;
1090 else
1091 SVN_ERR(svn_io_file_close(file, pool));
1093 return SVN_NO_ERROR;
1097 svn_error_t *
1098 svn_wc_create_tmp_file(apr_file_t **fp,
1099 const char *path,
1100 svn_boolean_t delete_on_close,
1101 apr_pool_t *pool)
1103 return svn_wc_create_tmp_file2(fp, NULL, path,
1104 delete_on_close
1105 ? svn_io_file_del_on_close
1106 : svn_io_file_del_none,
1107 pool);