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
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 * ====================================================================
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"
37 #include "adm_files.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
;
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
));
66 svn_wc_get_adm_dir(apr_pool_t
*pool
)
73 svn_wc_set_adm_dir(const char *name
, apr_pool_t
*pool
)
75 /* This is the canonical list of administrative directory names.
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
[] = {
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
;
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.
119 v_extend_with_adm_name(const char *path
,
120 const char *extension
,
121 svn_boolean_t use_tmp
,
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. */
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
)
140 path
= svn_path_join(path
, this, pool
);
144 path
= apr_pstrcat(pool
, path
, extension
, NULL
);
150 /* See v_extend_with_adm_name() for details. */
152 extend_with_adm_name(const char *path
,
153 const char *extension
,
154 svn_boolean_t use_tmp
,
161 path
= v_extend_with_adm_name(path
, extension
, use_tmp
, pool
, ap
);
169 svn_wc__adm_path(const char *path
,
177 path
= v_extend_with_adm_name(path
, NULL
, tmp
, pool
, ap
);
185 svn_wc__adm_path_exists(const char *path
,
190 svn_node_kind_t kind
;
195 path
= v_extend_with_adm_name(path
, NULL
, tmp
, pool
, ap
);
198 err
= svn_io_check_path(path
, &kind
, pool
);
201 svn_error_clear(err
);
202 /* Return early, since kind is undefined in this case. */
206 if (kind
== svn_node_none
)
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.
224 svn_wc__make_adm_thing(svn_wc_adm_access_t
*adm_access
,
226 svn_node_kind_t type
,
227 apr_fileperms_t perms
,
231 svn_error_t
*err
= SVN_NO_ERROR
;
232 apr_file_t
*f
= NULL
;
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
),
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"));
268 svn_wc__make_killme(svn_wc_adm_access_t
*adm_access
,
269 svn_boolean_t adm_only
,
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
);
283 svn_wc__check_killme(svn_wc_adm_access_t
*adm_access
,
284 svn_boolean_t
*exists
,
285 svn_boolean_t
*kill_adm_only
,
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
);
299 if (APR_STATUS_IS_ENOENT(err
->apr_err
))
301 /* Killme file doesn't exist. */
303 svn_error_clear(err
);
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
);
321 /*** Syncing files in the adm area. ***/
324 sync_adm_file(const char *path
,
325 const char *extension
,
329 /* Some code duplication with close_adm_file() seems unavoidable,
330 given how C va_lists work. */
332 const char *tmp_path
;
335 /* Extend tmp name. */
337 tmp_path
= v_extend_with_adm_name(path
, extension
, 1, pool
, ap
);
340 /* Extend real name. */
342 path
= v_extend_with_adm_name(path
, extension
, 0, pool
, ap
);
346 SVN_ERR(svn_io_file_rename(tmp_path
, path
, pool
));
347 SVN_ERR(svn_io_set_file_read_only(path
, FALSE
, pool
));
353 /* Rename a tmp text-base file to its real text-base name.
354 The file had better already be closed. */
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
,
363 SVN_WC__ADM_TEXT_BASE
,
369 svn_wc__text_base_path(const char *path
,
373 const char *newpath
, *base_name
;
375 svn_path_split(path
, &newpath
, &base_name
, pool
);
376 return extend_with_adm_name(newpath
,
380 SVN_WC__ADM_TEXT_BASE
,
386 svn_wc__text_revert_path(const char *path
,
390 const char *newpath
, *base_name
;
392 svn_path_split(path
, &newpath
, &base_name
, pool
);
393 return extend_with_adm_name(newpath
,
397 SVN_WC__ADM_TEXT_BASE
,
403 svn_wc__prop_path(const char **prop_path
,
405 svn_node_kind_t node_kind
,
406 svn_wc__props_kind_t props_kind
,
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
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
448 extensions
[props_kind
],
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().
473 open_adm_file(apr_file_t
**handle
,
475 const char *extension
,
476 apr_fileperms_t protection
,
481 svn_error_t
*err
= SVN_NO_ERROR
;
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. */
501 path
= v_extend_with_adm_name(path
, extension
, 1, pool
, ap
);
506 /* Extend with regular adm name. */
508 path
= v_extend_with_adm_name(path
, extension
, 0, pool
, 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
);
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
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"));
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().
551 close_adm_file(apr_file_t
*fp
,
553 const char *extension
,
558 const char *tmp_path
;
561 /* Get the full name of the thing we're closing. */
563 tmp_path
= v_extend_with_adm_name(path
, extension
, sync
, pool
, 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. */
571 /* Some code duplication with sync_adm_file() seems unavoidable,
572 given how C va_lists work. */
574 /* Obtain dest name. */
576 path
= v_extend_with_adm_name(path
, extension
, 0, pool
, ap
);
580 SVN_ERR(svn_io_file_rename(tmp_path
, path
, pool
));
581 SVN_ERR(svn_io_set_file_read_only(path
, FALSE
, pool
));
591 svn_wc__open_adm_file(apr_file_t
**handle
,
597 return open_adm_file(handle
, path
, NULL
, APR_OS_DEFAULT
, flags
, pool
,
603 svn_wc__close_adm_file(apr_file_t
*fp
,
609 return close_adm_file(fp
, path
, NULL
, sync
, pool
, fname
, NULL
);
614 svn_wc__remove_adm_file(const char *path
, apr_pool_t
*pool
, ...)
619 path
= v_extend_with_adm_name(path
, NULL
, 0, pool
, ap
);
622 SVN_ERR(svn_io_remove_file(path
, pool
));
630 svn_wc__open_text_base(apr_file_t
**handle
,
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
);
643 svn_wc__open_revert_base(apr_file_t
**handle
,
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
);
657 svn_wc__close_text_base(apr_file_t
*fp
,
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
);
669 svn_wc__close_revert_base(apr_file_t
*fp
,
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
);
683 svn_wc__open_props(apr_file_t
**handle
,
685 svn_node_kind_t kind
,
688 svn_boolean_t wcprops
,
691 const char *parent_dir
, *base_name
;
692 int wc_format_version
;
694 if (kind
== svn_node_dir
)
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. */
711 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND
, NULL
,
712 _("No such thing as 'base' "
713 "working copy properties!"));
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
);
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
);
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
);
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
);
747 SVN_WC__WORK_EXT
, APR_OS_DEFAULT
,
748 flags
, pool
, SVN_WC__ADM_PROPS
, base_name
, NULL
);
756 svn_wc__close_props(apr_file_t
*fp
,
758 svn_node_kind_t kind
,
760 svn_boolean_t wcprops
,
764 const char *parent_dir
, *base_name
;
766 if (kind
== svn_node_dir
)
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. */
778 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND
, NULL
,
779 _("No such thing as 'base' "
780 "working copy properties!"));
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
);
788 return close_adm_file(fp
, parent_dir
, SVN_WC__BASE_EXT
, sync
, pool
,
789 SVN_WC__ADM_PROP_BASE
, base_name
, NULL
);
793 if (kind
== svn_node_dir
)
794 return close_adm_file(fp
, parent_dir
, NULL
, sync
, pool
,
795 SVN_WC__ADM_DIR_WCPROPS
, NULL
);
797 return close_adm_file
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
);
808 return close_adm_file
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.
830 check_adm_exists(svn_boolean_t
*exists
,
833 svn_revnum_t revision
,
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*
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,
846 if (err
&& err
->apr_err
== SVN_ERR_WC_NOT_DIRECTORY
)
848 svn_error_clear(err
);
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
)
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)
875 (SVN_ERR_WC_OBSTRUCTED_UPDATE
, NULL
,
876 _("URL '%s' doesn't match existing URL '%s' in '%s'"),
877 url
, entry
->url
, path
);
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
));
896 init_adm_tmp_area(svn_wc_adm_access_t
*adm_access
,
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
));
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. */
927 init_adm(const char *path
,
931 svn_revnum_t initial_rev
,
935 svn_wc_adm_access_t
*adm_access
;
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
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. */
991 svn_wc_ensure_adm3(const char *path
,
995 svn_revnum_t revision
,
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
));
1007 svn_wc_ensure_adm2(const char *path
,
1011 svn_revnum_t revision
,
1014 return svn_wc_ensure_adm3(path
, uuid
, url
, repos
, revision
,
1015 svn_depth_infinity
, pool
);
1020 svn_wc_ensure_adm(const char *path
,
1023 svn_revnum_t revision
,
1026 return svn_wc_ensure_adm2(path
, uuid
, url
, NULL
, revision
, pool
);
1030 svn_wc__adm_destroy(svn_wc_adm_access_t
*adm_access
,
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
;
1049 svn_wc__adm_cleanup_tmp_area(svn_wc_adm_access_t
*adm_access
,
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
;
1070 svn_wc_create_tmp_file2(apr_file_t
**fp
,
1071 const char **new_name
,
1073 svn_io_file_del_t delete_when
,
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
));
1091 SVN_ERR(svn_io_file_close(file
, pool
));
1093 return SVN_NO_ERROR
;
1098 svn_wc_create_tmp_file(apr_file_t
**fp
,
1100 svn_boolean_t delete_on_close
,
1103 return svn_wc_create_tmp_file2(fp
, NULL
, path
,
1105 ? svn_io_file_del_on_close
1106 : svn_io_file_del_none
,