blob: d13b3a2170efb6a87d84db94474229585583c6eb [file] [log] [blame]
/*
* working_file_writer.c : utility to prepare and install working files
*
* ====================================================================
* 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 "svn_props.h"
#include "svn_subst.h"
#include "svn_time.h"
#include "svn_path.h"
#include "private/svn_io_private.h"
#include "private/svn_wc_private.h"
struct svn_wc__working_file_writer_t
{
apr_pool_t *pool;
const char *tmp_abspath;
svn_boolean_t is_special;
svn_stream_t *install_stream;
svn_stream_t *write_stream;
};
static apr_status_t
cleanup_file_writer(void *baton)
{
svn_wc__working_file_writer_t *writer = baton;
if (writer->install_stream)
{
svn_error_clear(svn_stream__install_delete(writer->install_stream,
writer->pool));
writer->install_stream = NULL;
}
return APR_SUCCESS;
}
svn_error_t *
svn_wc__working_file_writer_open(svn_wc__working_file_writer_t **writer_p,
const char *tmp_abspath,
apr_time_t final_mtime,
svn_subst_eol_style_t eol_style,
const char *eol,
svn_boolean_t repair_eol,
apr_hash_t *keywords,
svn_boolean_t is_special,
svn_boolean_t is_executable,
svn_boolean_t needs_lock,
svn_boolean_t has_lock,
svn_boolean_t is_added,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__working_file_writer_t *writer;
svn_stream_t *install_stream;
svn_stream_t *write_stream;
if (keywords && apr_hash_count(keywords) <= 0)
keywords = NULL;
SVN_ERR(svn_stream__create_for_install(&install_stream, tmp_abspath,
result_pool, scratch_pool));
if (needs_lock && !is_added && !has_lock)
svn_stream__install_set_read_only(install_stream, TRUE);
if (is_executable)
svn_stream__install_set_executable(install_stream, TRUE);
if (final_mtime >= 0)
svn_stream__install_set_affected_time(install_stream, final_mtime);
write_stream = install_stream;
if (svn_subst_translation_required(eol_style, eol, keywords,
FALSE /* special */,
repair_eol))
{
write_stream = svn_subst_stream_translated(write_stream,
eol,
repair_eol,
keywords,
TRUE /* expand */,
result_pool);
}
writer = apr_pcalloc(result_pool, sizeof(*writer));
writer->pool = result_pool;
writer->tmp_abspath = apr_pstrdup(result_pool, tmp_abspath);
writer->is_special = is_special;
writer->install_stream = install_stream;
writer->write_stream = write_stream;
apr_pool_cleanup_register(result_pool, writer, cleanup_file_writer,
apr_pool_cleanup_null);
*writer_p = writer;
return SVN_NO_ERROR;
}
svn_stream_t *
svn_wc__working_file_writer_get_stream(svn_wc__working_file_writer_t *writer)
{
return writer->write_stream;
}
svn_error_t *
svn_wc__working_file_writer_finalize(apr_time_t *mtime_p,
apr_off_t *size_p,
svn_wc__working_file_writer_t *writer,
apr_pool_t *scratch_pool)
{
SVN_ERR(svn_stream__install_finalize(mtime_p, size_p, writer->install_stream,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__working_file_writer_install(svn_wc__working_file_writer_t *writer,
const char *target_abspath,
apr_pool_t *scratch_pool)
{
if (writer->is_special)
{
const char *temp_path;
svn_stream_t *src_stream;
svn_stream_t *dst_stream;
/* Install the current contents to a temporary file, and use it to
create the resulting special file. */
SVN_ERR(svn_io_open_unique_file3(NULL, &temp_path, writer->tmp_abspath,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream__install_stream(writer->install_stream, temp_path,
TRUE, scratch_pool));
writer->install_stream = NULL;
/* When this stream is closed, the resulting special file will
atomically be created/moved into place at LOCAL_ABSPATH. */
SVN_ERR(svn_subst_create_specialfile(&dst_stream, target_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_readonly(&src_stream, temp_path, scratch_pool,
scratch_pool));
SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL,
scratch_pool));
SVN_ERR(svn_io_remove_file2(temp_path, TRUE, scratch_pool));
return SVN_NO_ERROR;
}
else
{
/* With a single db we might want to install files in a missing directory.
Simply trying this scenario on error won't do any harm and at least
one user reported this problem on IRC. */
SVN_ERR(svn_stream__install_stream(writer->install_stream,
target_abspath, TRUE,
scratch_pool));
writer->install_stream = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__working_file_writer_close(svn_wc__working_file_writer_t *writer)
{
if (writer->install_stream)
{
svn_stream_t *stream = writer->install_stream;
/* Do not retry deleting if it fails, as the stream may already
be in an invalid state. */
writer->install_stream = NULL;
apr_pool_cleanup_kill(writer->pool, writer, cleanup_file_writer);
SVN_ERR(svn_stream__install_delete(stream, writer->pool));
}
return SVN_NO_ERROR;
}