summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_wc/adm_files.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_wc/adm_files.c')
-rw-r--r--subversion/libsvn_wc/adm_files.c584
1 files changed, 584 insertions, 0 deletions
diff --git a/subversion/libsvn_wc/adm_files.c b/subversion/libsvn_wc/adm_files.c
new file mode 100644
index 0000000..11ad277
--- /dev/null
+++ b/subversion/libsvn_wc/adm_files.c
@@ -0,0 +1,584 @@
+/*
+ * adm_files.c: helper routines for handling files & dirs in the
+ * working copy administrative area (creating,
+ * deleting, opening, and closing). This is the only
+ * code that actually knows where administrative
+ * information is kept.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#include <stdarg.h>
+#include <apr_pools.h>
+#include <apr_file_io.h>
+#include <apr_strings.h>
+
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_hash.h"
+
+#include "wc.h"
+#include "adm_files.h"
+#include "entries.h"
+#include "lock.h"
+
+#include "svn_private_config.h"
+#include "private/svn_wc_private.h"
+
+
+/*** File names in the adm area. ***/
+
+/* The default name of the WC admin directory. This name is always
+ checked by svn_wc_is_adm_dir. */
+static const char default_adm_dir_name[] = ".svn";
+
+/* The name that is actually used for the WC admin directory. The
+ commonest case where this won't be the default is in Windows
+ ASP.NET development environments, which used to choke on ".svn". */
+static const char *adm_dir_name = default_adm_dir_name;
+
+
+svn_boolean_t
+svn_wc_is_adm_dir(const char *name, apr_pool_t *pool)
+{
+ return (0 == strcmp(name, adm_dir_name)
+ || 0 == strcmp(name, default_adm_dir_name));
+}
+
+
+const char *
+svn_wc_get_adm_dir(apr_pool_t *pool)
+{
+ return adm_dir_name;
+}
+
+
+svn_error_t *
+svn_wc_set_adm_dir(const char *name, apr_pool_t *pool)
+{
+ /* This is the canonical list of administrative directory names.
+
+ FIXME:
+ An identical list is used in
+ libsvn_subr/opt.c:svn_opt__args_to_target_array(),
+ but that function can't use this list, because that use would
+ create a circular dependency between libsvn_wc and libsvn_subr.
+ Make sure changes to the lists are always synchronized! */
+ static const char *valid_dir_names[] = {
+ default_adm_dir_name,
+ "_svn",
+ NULL
+ };
+
+ const char **dir_name;
+ for (dir_name = valid_dir_names; *dir_name; ++dir_name)
+ if (0 == strcmp(name, *dir_name))
+ {
+ /* Use the pointer to the statically allocated string
+ constant, to avoid potential pool lifetime issues. */
+ adm_dir_name = *dir_name;
+ return SVN_NO_ERROR;
+ }
+ return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
+ _("'%s' is not a valid administrative "
+ "directory name"),
+ svn_dirent_local_style(name, pool));
+}
+
+
+const char *
+svn_wc__adm_child(const char *path,
+ const char *child,
+ apr_pool_t *result_pool)
+{
+ return svn_dirent_join_many(result_pool,
+ path,
+ adm_dir_name,
+ child,
+ NULL);
+}
+
+
+svn_boolean_t
+svn_wc__adm_area_exists(const char *adm_abspath,
+ apr_pool_t *pool)
+{
+ const char *path = svn_wc__adm_child(adm_abspath, NULL, pool);
+ svn_node_kind_t kind;
+ svn_error_t *err;
+
+ err = svn_io_check_path(path, &kind, pool);
+ if (err)
+ {
+ svn_error_clear(err);
+ /* Return early, since kind is undefined in this case. */
+ return FALSE;
+ }
+
+ return kind != svn_node_none;
+}
+
+
+
+/*** Making and using files in the adm area. ***/
+
+
+/* */
+static svn_error_t *
+make_adm_subdir(const char *path,
+ const char *subdir,
+ apr_pool_t *pool)
+{
+ const char *fullpath;
+
+ fullpath = svn_wc__adm_child(path, subdir, pool);
+
+ return svn_io_dir_make(fullpath, APR_OS_DEFAULT, pool);
+}
+
+
+
+/*** Syncing files in the adm area. ***/
+
+
+svn_error_t *
+svn_wc__text_base_path_to_read(const char **result_abspath,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_status_t status;
+ svn_node_kind_t kind;
+ const svn_checksum_t *checksum;
+
+ SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL,
+ &checksum, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Sanity */
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Can only get the pristine contents of files; "
+ "'%s' is not a file"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ if (status == svn_wc__db_status_not_present)
+ /* We know that the delete of this node has been committed.
+ This should be the same as if called on an unknown path. */
+ return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+ _("Cannot get the pristine contents of '%s' "
+ "because its delete is already committed"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ else if (status == svn_wc__db_status_server_excluded
+ || status == svn_wc__db_status_excluded
+ || status == svn_wc__db_status_incomplete)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+ _("Cannot get the pristine contents of '%s' "
+ "because it has an unexpected status"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ if (checksum == NULL)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+ _("Node '%s' has no pristine text"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath,
+ checksum,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__get_pristine_contents(svn_stream_t **contents,
+ svn_filesize_t *size,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_status_t status;
+ svn_node_kind_t kind;
+ const svn_checksum_t *sha1_checksum;
+
+ if (size)
+ *size = SVN_INVALID_FILESIZE;
+
+ SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL,
+ &sha1_checksum, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Sanity */
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Can only get the pristine contents of files; "
+ "'%s' is not a file"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ if (status == svn_wc__db_status_added && !sha1_checksum)
+ {
+ /* Simply added. The pristine base does not exist. */
+ *contents = NULL;
+ return SVN_NO_ERROR;
+ }
+ else if (status == svn_wc__db_status_not_present)
+ /* We know that the delete of this node has been committed.
+ This should be the same as if called on an unknown path. */
+ return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+ _("Cannot get the pristine contents of '%s' "
+ "because its delete is already committed"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ else if (status == svn_wc__db_status_server_excluded
+ || status == svn_wc__db_status_excluded
+ || status == svn_wc__db_status_incomplete)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+ _("Cannot get the pristine contents of '%s' "
+ "because it has an unexpected status"),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (sha1_checksum)
+ SVN_ERR(svn_wc__db_pristine_read(contents, size, db, local_abspath,
+ sha1_checksum,
+ result_pool, scratch_pool));
+ else
+ *contents = NULL;
+
+ return SVN_NO_ERROR;
+}
+
+
+/*** Opening and closing files in the adm area. ***/
+
+svn_error_t *
+svn_wc__open_adm_stream(svn_stream_t **stream,
+ const char *dir_abspath,
+ const char *fname,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_abspath;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
+
+ local_abspath = svn_wc__adm_child(dir_abspath, fname, scratch_pool);
+ return svn_error_trace(svn_stream_open_readonly(stream, local_abspath,
+ result_pool, scratch_pool));
+}
+
+
+svn_error_t *
+svn_wc__open_writable_base(svn_stream_t **stream,
+ const char **temp_base_abspath,
+ svn_checksum_t **md5_checksum,
+ svn_checksum_t **sha1_checksum,
+ svn_wc__db_t *db,
+ const char *wri_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *temp_dir_abspath;
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+
+ SVN_ERR(svn_wc__db_pristine_get_tempdir(&temp_dir_abspath, db, wri_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_open_unique(stream,
+ temp_base_abspath,
+ temp_dir_abspath,
+ svn_io_file_del_none,
+ result_pool, scratch_pool));
+ if (md5_checksum)
+ *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
+ svn_checksum_md5, FALSE, result_pool);
+ if (sha1_checksum)
+ *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
+ svn_checksum_sha1, FALSE, result_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+
+/*** Checking for and creating administrative subdirs. ***/
+
+
+/* */
+static svn_error_t *
+init_adm_tmp_area(const char *path, apr_pool_t *pool)
+{
+ /* SVN_WC__ADM_TMP */
+ SVN_ERR(make_adm_subdir(path, SVN_WC__ADM_TMP, pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Set up a new adm area for PATH, with REPOS_* as the repos info, and
+ INITIAL_REV as the starting revision. The entries file starts out
+ marked as 'incomplete. The adm area starts out locked; remember to
+ unlock it when done. */
+static svn_error_t *
+init_adm(svn_wc__db_t *db,
+ const char *local_abspath,
+ const char *repos_relpath,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_revnum_t initial_rev,
+ svn_depth_t depth,
+ apr_pool_t *pool)
+{
+ /* First, make an empty administrative area. */
+ SVN_ERR(svn_io_dir_make_hidden(svn_wc__adm_child(local_abspath, NULL, pool),
+ APR_OS_DEFAULT, pool));
+
+ /** Make subdirectories. ***/
+
+ /* SVN_WC__ADM_PRISTINE */
+ SVN_ERR(make_adm_subdir(local_abspath, SVN_WC__ADM_PRISTINE, pool));
+
+ /* ### want to add another directory? do a format bump to ensure that
+ ### all existing working copies get the new directories. or maybe
+ ### create-on-demand (more expensive) */
+
+ /** Init the tmp area. ***/
+ SVN_ERR(init_adm_tmp_area(local_abspath, pool));
+
+ /* Create the SDB. */
+ SVN_ERR(svn_wc__db_init(db, local_abspath,
+ repos_relpath, repos_root_url, repos_uuid,
+ initial_rev, depth,
+ pool));
+
+ /* Stamp ENTRIES and FORMAT files for old clients. */
+ SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
+ SVN_WC__ADM_ENTRIES,
+ pool),
+ SVN_WC__NON_ENTRIES_STRING,
+ pool));
+ SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
+ SVN_WC__ADM_FORMAT,
+ pool),
+ SVN_WC__NON_ENTRIES_STRING,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__internal_ensure_adm(svn_wc__db_t *db,
+ const char *local_abspath,
+ const char *url,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ apr_pool_t *scratch_pool)
+{
+ int format;
+ const char *original_repos_relpath;
+ const char *original_root_url;
+ svn_boolean_t is_op_root;
+ const char *repos_relpath = svn_uri_skip_ancestor(repos_root_url, url,
+ scratch_pool);
+ svn_wc__db_status_t status;
+ const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid;
+ svn_revnum_t db_revision;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ SVN_ERR_ASSERT(url != NULL);
+ SVN_ERR_ASSERT(repos_root_url != NULL);
+ SVN_ERR_ASSERT(repos_uuid != NULL);
+ SVN_ERR_ASSERT(repos_relpath != NULL);
+
+ SVN_ERR(svn_wc__internal_check_wc(&format, db, local_abspath, TRUE,
+ scratch_pool));
+
+ /* Early out: we know we're not dealing with an existing wc, so
+ just create one. */
+ if (format == 0)
+ return svn_error_trace(init_adm(db, local_abspath,
+ repos_relpath, repos_root_url, repos_uuid,
+ revision, depth, scratch_pool));
+
+ SVN_ERR(svn_wc__db_read_info(&status, NULL,
+ &db_revision, &db_repos_relpath,
+ &db_repos_root_url, &db_repos_uuid,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &original_repos_relpath, &original_root_url,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, &is_op_root, NULL, NULL,
+ NULL, NULL, NULL,
+ db, local_abspath, scratch_pool, scratch_pool));
+
+ /* When the directory exists and is scheduled for deletion or is not-present
+ * do not check the revision or the URL. The revision can be any
+ * arbitrary revision and the URL may differ if the add is
+ * being driven from a merge which will have a different URL. */
+ if (status != svn_wc__db_status_deleted
+ && status != svn_wc__db_status_not_present)
+ {
+ /* ### Should we match copyfrom_revision? */
+ if (db_revision != revision)
+ return
+ svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+ _("Revision %ld doesn't match existing "
+ "revision %ld in '%s'"),
+ revision, db_revision, local_abspath);
+
+ if (!db_repos_root_url)
+ {
+ if (status == svn_wc__db_status_added)
+ SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
+ &db_repos_relpath,
+ &db_repos_root_url,
+ &db_repos_uuid,
+ NULL, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ else
+ SVN_ERR(svn_wc__db_scan_base_repos(&db_repos_relpath,
+ &db_repos_root_url,
+ &db_repos_uuid,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+ }
+
+ /* The caller gives us a URL which should match the entry. However,
+ some callers compensate for an old problem in entry->url and pass
+ the copyfrom_url instead. See ^/notes/api-errata/1.7/wc002.txt. As
+ a result, we allow the passed URL to match copyfrom_url if it
+ does not match the entry's primary URL. */
+ if (strcmp(db_repos_uuid, repos_uuid)
+ || strcmp(db_repos_root_url, repos_root_url)
+ || !svn_relpath_skip_ancestor(db_repos_relpath, repos_relpath))
+ {
+ if (!is_op_root /* copy_from was set on op-roots only */
+ || original_root_url == NULL
+ || strcmp(original_root_url, repos_root_url)
+ || strcmp(original_repos_relpath, repos_relpath))
+ return
+ svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+ _("URL '%s' (uuid: '%s') doesn't match existing "
+ "URL '%s' (uuid: '%s') in '%s'"),
+ url,
+ db_repos_uuid,
+ svn_path_url_add_component2(db_repos_root_url,
+ db_repos_relpath,
+ scratch_pool),
+ repos_uuid,
+ local_abspath);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc_ensure_adm4(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *url,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_revnum_t revision,
+ svn_depth_t depth,
+ apr_pool_t *scratch_pool)
+{
+ return svn_error_trace(
+ svn_wc__internal_ensure_adm(wc_ctx->db, local_abspath, url, repos_root_url,
+ repos_uuid, revision, depth, scratch_pool));
+}
+
+svn_error_t *
+svn_wc__adm_destroy(svn_wc__db_t *db,
+ const char *dir_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t is_wcroot;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
+
+ SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
+
+ SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, dir_abspath, scratch_pool));
+
+ /* Well, the coast is clear for blowing away the administrative
+ directory, which also removes remaining locks */
+
+ /* Now close the DB, and we can delete the working copy */
+ if (is_wcroot)
+ {
+ SVN_ERR(svn_wc__db_drop_root(db, dir_abspath, scratch_pool));
+ SVN_ERR(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, NULL,
+ scratch_pool),
+ FALSE,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__adm_cleanup_tmp_area(svn_wc__db_t *db,
+ const char *adm_abspath,
+ apr_pool_t *scratch_pool)
+{
+ const char *tmp_path;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(adm_abspath));
+
+ SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
+
+ /* Get the path to the tmp area, and blow it away. */
+ tmp_path = svn_wc__adm_child(adm_abspath, SVN_WC__ADM_TMP, scratch_pool);
+
+ SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool));
+
+ /* Now, rebuild the tmp area. */
+ return svn_error_trace(init_adm_tmp_area(adm_abspath, scratch_pool));
+}
+
+
+svn_error_t *
+svn_wc__get_tmpdir(const char **tmpdir_abspath,
+ svn_wc_context_t *wc_ctx,
+ const char *wri_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_wc__db_temp_wcroot_tempdir(tmpdir_abspath,
+ wc_ctx->db, wri_abspath,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud