#include <assert.h>
#include <string.h>
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_sorts.h"
#include "private/svn_temp_serializer.h"
#include "private/svn_sorts_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_string_private.h"
#include "private/svn_io_private.h"
#include "fs_fs.h"
#include "pack.h"
#include "util.h"
#include "id.h"
#include "index.h"
#include "low_level.h"
#include "revprops.h"
#include "transaction.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_private_config.h"
#include "temp_serializer.h"
#define DEFAULT_MAX_MEM (64 * 1024 * 1024)
typedef struct path_order_t
{
svn_prefix_string__t *path;
svn_fs_fs__id_part_t node_id;
svn_revnum_t revision;
int predecessor_count;
svn_boolean_t is_head;
apr_int64_t expanded_size;
svn_fs_fs__id_part_t noderev_id;
svn_fs_fs__id_part_t rep_id;
} path_order_t;
typedef struct reference_t
{
svn_fs_fs__id_part_t to;
svn_fs_fs__id_part_t from;
} reference_t;
typedef struct pack_context_t
{
svn_fs_t *fs;
svn_cancel_func_t cancel_func;
void *cancel_baton;
svn_revnum_t shard_rev;
svn_revnum_t start_rev;
svn_revnum_t end_rev;
svn_revnum_t shard_end_rev;
apr_file_t *proto_l2p_index;
apr_file_t *proto_p2l_index;
const char *shard_dir;
const char *pack_file_dir;
const char *pack_file_path;
apr_off_t pack_offset;
apr_file_t *pack_file;
apr_array_header_t *changes;
apr_file_t *changes_file;
apr_array_header_t *file_props;
apr_file_t *file_props_file;
apr_array_header_t *dir_props;
apr_file_t *dir_props_file;
svn_prefix_tree__t *paths;
apr_array_header_t *path_order;
apr_array_header_t *references;
apr_array_header_t *reps;
apr_array_header_t *rev_offsets;
apr_file_t *reps_file;
apr_pool_t *info_pool;
svn_boolean_t flush_to_disk;
} pack_context_t;
static svn_error_t *
initialize_pack_context(pack_context_t *context,
svn_fs_t *fs,
const char *pack_file_dir,
const char *shard_dir,
svn_revnum_t shard_rev,
int max_items,
svn_boolean_t flush_to_disk,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
const char *temp_dir;
int max_revs = MIN(ffd->max_files_per_dir, max_items);
SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT);
SVN_ERR_ASSERT(shard_rev % ffd->max_files_per_dir == 0);
SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
context->fs = fs;
context->cancel_func = cancel_func;
context->cancel_baton = cancel_baton;
context->shard_rev = shard_rev;
context->start_rev = shard_rev;
context->end_rev = shard_rev;
context->shard_end_rev = shard_rev + ffd->max_files_per_dir;
context->info_pool = svn_pool_create(pool);
context->paths = svn_prefix_tree__create(context->info_pool);
context->flush_to_disk = flush_to_disk;
context->shard_dir = shard_dir;
context->pack_file_dir = pack_file_dir;
context->pack_file_path
= svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
SVN_ERR(svn_io_file_open(&context->pack_file, context->pack_file_path,
APR_WRITE | APR_BUFFERED | APR_BINARY | APR_EXCL
| APR_CREATE, APR_OS_DEFAULT, pool));
SVN_ERR(svn_fs_fs__l2p_proto_index_open(
&context->proto_l2p_index,
svn_dirent_join(pack_file_dir,
PATH_INDEX PATH_EXT_L2P_INDEX,
pool),
pool));
SVN_ERR(svn_fs_fs__p2l_proto_index_open(
&context->proto_p2l_index,
svn_dirent_join(pack_file_dir,
PATH_INDEX PATH_EXT_P2L_INDEX,
pool),
pool));
context->changes = apr_array_make(pool, max_items,
sizeof(svn_fs_fs__p2l_entry_t *));
SVN_ERR(svn_io_open_unique_file3(&context->changes_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
context->file_props = apr_array_make(pool, max_items,
sizeof(svn_fs_fs__p2l_entry_t *));
SVN_ERR(svn_io_open_unique_file3(&context->file_props_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
context->dir_props = apr_array_make(pool, max_items,
sizeof(svn_fs_fs__p2l_entry_t *));
SVN_ERR(svn_io_open_unique_file3(&context->dir_props_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
context->rev_offsets = apr_array_make(pool, max_revs, sizeof(int));
context->path_order = apr_array_make(pool, max_items,
sizeof(path_order_t *));
context->references = apr_array_make(pool, max_items,
sizeof(reference_t *));
context->reps = apr_array_make(pool, max_items,
sizeof(svn_fs_fs__p2l_entry_t *));
SVN_ERR(svn_io_open_unique_file3(&context->reps_file, NULL, temp_dir,
svn_io_file_del_on_close, pool, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
reset_pack_context(pack_context_t *context,
apr_pool_t *pool)
{
const char *temp_dir;
apr_array_clear(context->changes);
SVN_ERR(svn_io_file_close(context->changes_file, pool));
apr_array_clear(context->file_props);
SVN_ERR(svn_io_file_close(context->file_props_file, pool));
apr_array_clear(context->dir_props);
SVN_ERR(svn_io_file_close(context->dir_props_file, pool));
apr_array_clear(context->rev_offsets);
apr_array_clear(context->path_order);
apr_array_clear(context->references);
apr_array_clear(context->reps);
SVN_ERR(svn_io_file_close(context->reps_file, pool));
svn_pool_clear(context->info_pool);
SVN_ERR(svn_io_temp_dir(&temp_dir, pool));
SVN_ERR(svn_io_open_unique_file3(&context->changes_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
SVN_ERR(svn_io_open_unique_file3(&context->file_props_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
SVN_ERR(svn_io_open_unique_file3(&context->dir_props_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
SVN_ERR(svn_io_open_unique_file3(&context->reps_file, NULL, temp_dir,
svn_io_file_del_on_close,
context->info_pool, pool));
context->paths = svn_prefix_tree__create(context->info_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
close_pack_context(pack_context_t *context,
apr_pool_t *pool)
{
const char *proto_l2p_index_path;
const char *proto_p2l_index_path;
SVN_ERR(svn_io_file_name_get(&proto_l2p_index_path,
context->proto_l2p_index, pool));
SVN_ERR(svn_io_file_name_get(&proto_p2l_index_path,
context->proto_p2l_index, pool));
SVN_ERR(svn_io_file_close(context->proto_l2p_index, pool));
SVN_ERR(svn_io_file_close(context->proto_p2l_index, pool));
SVN_ERR(svn_fs_fs__add_index_data(context->fs, context->pack_file,
proto_l2p_index_path,
proto_p2l_index_path,
context->shard_rev,
pool));
SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool));
SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, pool));
if (context->flush_to_disk)
SVN_ERR(svn_io_file_flush_to_disk(context->pack_file, pool));
SVN_ERR(svn_io_file_close(context->pack_file, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
copy_file_data(pack_context_t *context,
apr_file_t *dest,
apr_file_t *source,
apr_off_t size,
apr_pool_t *pool)
{
enum { STACK_BUFFER_SIZE = 1024 };
if (size < STACK_BUFFER_SIZE)
{
char buffer[STACK_BUFFER_SIZE];
SVN_ERR(svn_io_file_read_full2(source, buffer, (apr_size_t)size,
NULL, NULL, pool));
SVN_ERR(svn_io_file_write_full(dest, buffer, (apr_size_t)size,
NULL, pool));
}
else
{
fs_fs_data_t *ffd = context->fs->fsap_data;
apr_pool_t *copypool = svn_pool_create(pool);
char *buffer = apr_palloc(copypool, ffd->block_size);
while (size)
{
apr_size_t to_copy = (apr_size_t)(MIN(size, ffd->block_size));
if (context->cancel_func)
SVN_ERR(context->cancel_func(context->cancel_baton));
SVN_ERR(svn_io_file_read_full2(source, buffer, to_copy,
NULL, NULL, pool));
SVN_ERR(svn_io_file_write_full(dest, buffer, to_copy,
NULL, pool));
size -= to_copy;
}
svn_pool_destroy(copypool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
write_null_bytes(apr_file_t *dest,
apr_off_t size,
apr_pool_t *pool)
{
enum { BUFFER_SIZE = 1024 };
static const char buffer[BUFFER_SIZE] = { 0 };
while (size)
{
apr_size_t to_write = MIN(size, BUFFER_SIZE);
SVN_ERR(svn_io_file_write_full(dest, buffer, to_write, NULL, pool));
size -= to_write;
}
return SVN_NO_ERROR;
}
static svn_error_t *
copy_item_to_temp(pack_context_t *context,
apr_array_header_t *entries,
apr_file_t *temp_file,
apr_file_t *rev_file,
svn_fs_fs__p2l_entry_t *entry,
apr_pool_t *pool)
{
svn_fs_fs__p2l_entry_t *new_entry
= apr_pmemdup(context->info_pool, entry, sizeof(*entry));
SVN_ERR(svn_io_file_get_offset(&new_entry->offset, temp_file, pool));
APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = new_entry;
SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool));
return SVN_NO_ERROR;
}
static int
get_item_array_index(pack_context_t *context,
svn_revnum_t revision,
apr_int64_t item_index)
{
assert(revision >= context->start_rev);
return (int)item_index + APR_ARRAY_IDX(context->rev_offsets,
revision - context->start_rev,
int);
}
static void
add_item_rep_mapping(pack_context_t *context,
svn_fs_fs__p2l_entry_t *entry)
{
int idx;
idx = get_item_array_index(context,
entry->item.revision,
entry->item.number);
while (context->reps->nelts <= idx)
APR_ARRAY_PUSH(context->reps, void *) = NULL;
assert(!APR_ARRAY_IDX(context->reps, idx, void *));
APR_ARRAY_IDX(context->reps, idx, void *) = entry;
}
static svn_fs_fs__p2l_entry_t *
get_item(pack_context_t *context,
const svn_fs_fs__id_part_t *id,
svn_boolean_t reset)
{
svn_fs_fs__p2l_entry_t *result = NULL;
if (id->number && id->revision >= context->start_rev)
{
int idx = get_item_array_index(context, id->revision, id->number);
if (context->reps->nelts > idx)
{
result = APR_ARRAY_IDX(context->reps, idx, void *);
if (result && reset)
APR_ARRAY_IDX(context->reps, idx, void *) = NULL;
}
}
return result;
}
static svn_error_t *
copy_rep_to_temp(pack_context_t *context,
apr_file_t *rev_file,
svn_fs_fs__p2l_entry_t *entry,
apr_pool_t *pool)
{
svn_fs_fs__rep_header_t *rep_header;
svn_stream_t *stream;
apr_off_t source_offset = entry->offset;
entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry));
SVN_ERR(svn_io_file_get_offset(&entry->offset, context->reps_file, pool));
add_item_rep_mapping(context, entry);
stream = svn_stream_from_aprfile2(rev_file, TRUE, pool);
SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, stream, pool, pool));
SVN_ERR(svn_stream_close(stream));
if ( rep_header->type == svn_fs_fs__rep_delta
&& rep_header->base_revision >= context->start_rev)
{
reference_t *reference = apr_pcalloc(context->info_pool,
sizeof(*reference));
reference->from = entry->item;
reference->to.revision = rep_header->base_revision;
reference->to.number = rep_header->base_item_index;
APR_ARRAY_PUSH(context->references, reference_t *) = reference;
}
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool));
SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size,
pool));
return SVN_NO_ERROR;
}
static int
compare_dir_entries_format7(const svn_sort__item_t *a,
const svn_sort__item_t *b)
{
const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value;
const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value;
return strcmp(lhs->name, rhs->name);
}
static int
compare_dir_entries_format6(const svn_sort__item_t *a,
const svn_sort__item_t *b)
{
const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value;
const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value;
const svn_fs_fs__id_part_t *lhs_rev_item
= svn_fs_fs__id_rev_item(lhs->id);
const svn_fs_fs__id_part_t *rhs_rev_item
= svn_fs_fs__id_rev_item(rhs->id);
if (lhs_rev_item->revision != rhs_rev_item->revision)
return lhs_rev_item->revision > rhs_rev_item->revision ? -1 : 1;
if (lhs_rev_item->number != rhs_rev_item->number)
return lhs_rev_item->number > rhs_rev_item->number ? 1 : -1;
return 0;
}
apr_array_header_t *
svn_fs_fs__order_dir_entries(svn_fs_t *fs,
apr_hash_t *directory,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *ordered
= svn_sort__hash(directory,
svn_fs_fs__use_log_addressing(fs)
? compare_dir_entries_format7
: compare_dir_entries_format6,
scratch_pool);
apr_array_header_t *result
= apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *));
int i;
for (i = 0; i < ordered->nelts; ++i)
APR_ARRAY_PUSH(result, svn_fs_dirent_t *)
= APR_ARRAY_IDX(ordered, i, svn_sort__item_t).value;
return result;
}
static const char *
tweak_path_for_ordering(const char *original,
apr_pool_t *pool)
{
enum {SPECIAL_COUNT = 2};
static const char *special[SPECIAL_COUNT] = {"trunk", "branch"};
char *pos;
char *path = apr_pstrdup(pool, original);
int i;
for (i = 0; i < SPECIAL_COUNT; ++i)
for (pos = strstr(path, special[i]);
pos;
pos = strstr(pos + 1, special[i]))
{
*pos = (char)(i + '\1');
}
return path;
}
static svn_error_t *
copy_node_to_temp(pack_context_t *context,
svn_fs_fs__revision_file_t *rev_file,
svn_fs_fs__p2l_entry_t *entry,
apr_pool_t *pool)
{
path_order_t *path_order = apr_pcalloc(context->info_pool,
sizeof(*path_order));
node_revision_t *noderev;
const char *sort_path;
apr_off_t source_offset = entry->offset;
SVN_ERR(svn_fs_fs__read_noderev(&noderev, rev_file->stream, pool, pool));
entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry));
SVN_ERR(svn_io_file_get_offset(&entry->offset, context->reps_file,
pool));
add_item_rep_mapping(context, entry);
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &source_offset, pool));
SVN_ERR(copy_file_data(context, context->reps_file, rev_file->file,
entry->size, pool));
if (noderev->data_rep && noderev->data_rep->revision >= context->start_rev)
{
path_order->rep_id.revision = noderev->data_rep->revision;
path_order->rep_id.number = noderev->data_rep->item_index;
path_order->expanded_size = noderev->data_rep->expanded_size;
}
sort_path = tweak_path_for_ordering(noderev->created_path, pool);
path_order->path = svn_prefix_string__create(context->paths, sort_path);
path_order->node_id = *svn_fs_fs__id_node_id(noderev->id);
path_order->revision = svn_fs_fs__id_rev(noderev->id);
path_order->predecessor_count = noderev->predecessor_count;
path_order->noderev_id = *svn_fs_fs__id_rev_item(noderev->id);
APR_ARRAY_PUSH(context->path_order, path_order_t *) = path_order;
return SVN_NO_ERROR;
}
static int
compare_path_order(const path_order_t * const * lhs_p,
const path_order_t * const * rhs_p)
{
const path_order_t * lhs = *lhs_p;
const path_order_t * rhs = *rhs_p;
int diff = svn_prefix_string__compare(lhs->path, rhs->path);
if (diff)
return diff;
diff = svn_fs_fs__id_part_compare(&rhs->node_id, &lhs->node_id);
if (diff)
return diff;
if (lhs->revision != rhs->revision)
return lhs->revision < rhs->revision ? 1 : -1;
return 0;
}
static int
compare_references(const reference_t * const * lhs_p,
const reference_t * const * rhs_p)
{
const reference_t * lhs = *lhs_p;
const reference_t * rhs = *rhs_p;
int diff = svn_fs_fs__id_part_compare(&lhs->from, &rhs->from);
return diff ? diff : svn_fs_fs__id_part_compare(&lhs->to, &rhs->to);
}
static int
compare_ref_to_item(const reference_t * const * lhs_p,
const svn_fs_fs__id_part_t * rhs_p)
{
return svn_fs_fs__id_part_compare(&(*lhs_p)->from, rhs_p);
}
static int
roundness(int value)
{
return value - (value & (value - 1));
}
static void
classify_nodes(path_order_t **path_order,
int count)
{
const svn_prefix_string__t *path;
int i;
if (count == 0)
return;
path = path_order[0]->path;
path_order[0]->is_head = TRUE;
for (i = 1; i < count; ++i)
{
if (svn_prefix_string__compare(path, path_order[i]->path))
{
path = path_order[i]->path;
path_order[i]->is_head = TRUE;
}
}
}
static void
sort_reps_range(pack_context_t *context,
path_order_t **path_order,
path_order_t **temp,
int first,
int last)
{
const svn_prefix_string__t *path;
int i, dest;
svn_fs_fs__id_part_t rep_id;
fs_fs_data_t *ffd = context->fs->fsap_data;
if (first == last)
return;
dest = first;
for (i = first; i < last; ++i)
{
int round = roundness(path_order[i]->predecessor_count);
svn_boolean_t likely_target
= (round >= ffd->max_linear_deltification)
&& (round >= path_order[i]->predecessor_count / 4);
svn_boolean_t likely_head
= path_order[i]->predecessor_count
< ffd->max_linear_deltification;
if (likely_target || likely_head)
{
temp[dest++] = path_order[i];
path_order[i] = NULL;
}
}
for (i = first; i < last; ++i)
if (path_order[i])
{
path = path_order[i]->path;
rep_id = path_order[i]->rep_id;
break;
}
for (i = first; i < last; ++i)
if (path_order[i])
{
if (svn_prefix_string__compare(path, path_order[i]->path))
{
path = path_order[i]->path;
rep_id = path_order[i]->rep_id;
}
if (svn_fs_fs__id_part_eq(&path_order[i]->rep_id, &rep_id))
{
reference_t **reference;
temp[dest++] = path_order[i];
path_order[i] = NULL;
reference = svn_sort__array_lookup(context->references,
&rep_id, NULL,
(int (*)(const void *, const void *))compare_ref_to_item);
if (reference)
rep_id = (*reference)->to;
}
}
for (i = first; i < last; ++i)
if (path_order[i])
temp[dest++] = path_order[i];
assert(dest == last);
}
static void
sort_reps(pack_context_t *context)
{
apr_pool_t *temp_pool;
path_order_t **temp, **path_order;
int i, count;
if (context->path_order->nelts == 0)
{
assert(context->references->nelts == 0);
return;
}
svn_sort__array(context->path_order,
(int (*)(const void *, const void *))compare_path_order);
svn_sort__array(context->references,
(int (*)(const void *, const void *))compare_references);
temp_pool = svn_pool_create(context->info_pool);
count = context->path_order->nelts;
temp = apr_pcalloc(temp_pool, count * sizeof(*temp));
path_order = (void *)context->path_order->elts;
classify_nodes(path_order, count);
sort_reps_range(context, path_order, temp, 0, count);
for (i = 0; i < count; ++i)
path_order[i] = temp[i];
svn_pool_destroy(temp_pool);
}
static int
compare_p2l_info(const svn_fs_fs__p2l_entry_t * const * lhs,
const svn_fs_fs__p2l_entry_t * const * rhs)
{
assert(*lhs != *rhs);
if ((*lhs)->item.revision == (*rhs)->item.revision)
return (*lhs)->item.number > (*rhs)->item.number ? -1 : 1;
return (*lhs)->item.revision > (*rhs)->item.revision ? -1 : 1;
}
static void
sort_items(apr_array_header_t *entries)
{
svn_sort__array(entries,
(int (*)(const void *, const void *))compare_p2l_info);
}
static apr_off_t
get_block_left(pack_context_t *context)
{
fs_fs_data_t *ffd = context->fs->fsap_data;
return ffd->block_size - (context->pack_offset % ffd->block_size);
}
static svn_error_t *
auto_pad_block(pack_context_t *context,
apr_off_t to_add,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = context->fs->fsap_data;
const apr_off_t max_padding = MAX(ffd->block_size / 50, 512);
apr_off_t padding = get_block_left(context);
if (padding < to_add && padding < max_padding)
{
svn_fs_fs__p2l_entry_t null_entry;
null_entry.offset = context->pack_offset;
null_entry.size = padding;
null_entry.type = SVN_FS_FS__ITEM_TYPE_UNUSED;
null_entry.item.revision = SVN_INVALID_REVNUM;
null_entry.item.number = SVN_FS_FS__ITEM_INDEX_UNUSED;
null_entry.fnv1_checksum = 0;
SVN_ERR(write_null_bytes(context->pack_file, padding, pool));
SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(
context->proto_p2l_index, &null_entry, pool));
context->pack_offset += padding;
}
return SVN_NO_ERROR;
}
static svn_error_t *
store_item(pack_context_t *context,
apr_file_t *temp_file,
svn_fs_fs__p2l_entry_t *item,
apr_pool_t *pool)
{
apr_off_t safety_margin;
if (item->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
return SVN_NO_ERROR;
safety_margin = item->type == SVN_FS_FS__ITEM_TYPE_NODEREV
? SVN__LINE_CHUNK_SIZE
: 0;
SVN_ERR(auto_pad_block(context, item->size + safety_margin, pool));
SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &item->offset, pool));
SVN_ERR(copy_file_data(context, context->pack_file, temp_file,
item->size, pool));
item->offset = context->pack_offset;
context->pack_offset += item->size;
SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(context->proto_p2l_index,
item, pool));
APR_ARRAY_PUSH(context->reps, svn_fs_fs__p2l_entry_t *) = item;
return SVN_NO_ERROR;
}
static svn_error_t *
store_items(pack_context_t *context,
apr_file_t *temp_file,
apr_array_header_t *items,
apr_pool_t *pool)
{
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
for (i = 0; i < items->nelts; ++i)
{
svn_pool_clear(iterpool);
SVN_ERR(store_item(context, temp_file,
APR_ARRAY_IDX(items, i, svn_fs_fs__p2l_entry_t *),
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
copy_reps_from_temp(pack_context_t *context,
apr_file_t *temp_file,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
apr_array_header_t *path_order = context->path_order;
int i;
for (i = 0; i < path_order->nelts; ++i)
{
path_order_t *current_path;
svn_fs_fs__p2l_entry_t *node_part;
svn_fs_fs__p2l_entry_t *rep_part;
svn_pool_clear(iterpool);
current_path = APR_ARRAY_IDX(path_order, i, path_order_t *);
if (current_path->is_head)
{
node_part = get_item(context, ¤t_path->noderev_id, TRUE);
if (node_part)
SVN_ERR(store_item(context, temp_file, node_part, iterpool));
}
rep_part = get_item(context, ¤t_path->rep_id, TRUE);
if (rep_part)
SVN_ERR(store_item(context, temp_file, rep_part, iterpool));
}
for (i = 0; i < path_order->nelts; ++i)
{
path_order_t *current_path;
svn_fs_fs__p2l_entry_t *node_part;
svn_pool_clear(iterpool);
current_path = APR_ARRAY_IDX(path_order, i, path_order_t *);
node_part = get_item(context, ¤t_path->noderev_id, TRUE);
if (node_part)
SVN_ERR(store_item(context, temp_file, node_part, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static int
compare_p2l_info_rev(const svn_fs_fs__p2l_entry_t * const * lhs_p,
const svn_fs_fs__p2l_entry_t * const * rhs_p)
{
const svn_fs_fs__p2l_entry_t * lhs = *lhs_p;
const svn_fs_fs__p2l_entry_t * rhs = *rhs_p;
if (lhs->item.revision == rhs->item.revision)
return 0;
return lhs->item.revision < rhs->item.revision ? -1 : 1;
}
static svn_error_t *
write_l2p_index(pack_context_t *context,
apr_pool_t *pool)
{
apr_pool_t *iterpool = svn_pool_create(pool);
svn_revnum_t prev_rev = SVN_INVALID_REVNUM;
int i, dest;
for (i = 0, dest = 0; i < context->reps->nelts; ++i)
{
svn_fs_fs__p2l_entry_t *entry
= APR_ARRAY_IDX(context->reps, i, svn_fs_fs__p2l_entry_t *);
if (entry)
APR_ARRAY_IDX(context->reps, dest++, svn_fs_fs__p2l_entry_t *)
= entry;
}
context->reps->nelts = dest;
svn_sort__array(context->reps,
(int (*)(const void *, const void *))compare_p2l_info_rev);
for (i = 0; i < context->reps->nelts; ++i)
{
svn_fs_fs__p2l_entry_t *p2l_entry
= APR_ARRAY_IDX(context->reps, i, svn_fs_fs__p2l_entry_t *);
if (p2l_entry == NULL)
continue;
if (prev_rev != p2l_entry->item.revision)
{
prev_rev = p2l_entry->item.revision;
SVN_ERR(svn_fs_fs__l2p_proto_index_add_revision(
context->proto_l2p_index, iterpool));
}
SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry(context->proto_l2p_index,
p2l_entry->offset,
p2l_entry->item.number,
iterpool));
if (i % 256 == 0)
svn_pool_clear(iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
pack_range(pack_context_t *context,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = context->fs->fsap_data;
apr_pool_t *revpool = svn_pool_create(pool);
apr_pool_t *iterpool = svn_pool_create(pool);
apr_pool_t *iterpool2 = svn_pool_create(pool);
svn_revnum_t revision;
for (revision = context->start_rev; revision < context->end_rev; ++revision)
{
apr_off_t offset = 0;
svn_fs_fs__revision_file_t *rev_file;
svn_pool_clear(revpool);
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, context->fs,
revision, revpool, iterpool));
SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts;
while (offset < rev_file->l2p_offset)
{
int i;
apr_array_header_t *entries;
svn_pool_clear(iterpool);
SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs,
rev_file, revision, offset,
ffd->p2l_page_size, iterpool,
iterpool));
for (i = 0; i < entries->nelts; ++i)
{
svn_fs_fs__p2l_entry_t *entry
= &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
if (offset > entry->offset)
continue;
svn_pool_clear(iterpool2);
offset = entry->offset;
if (offset < rev_file->l2p_offset)
{
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset,
iterpool2));
if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES)
SVN_ERR(copy_item_to_temp(context,
context->changes,
context->changes_file,
rev_file->file, entry,
iterpool2));
else if (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS)
SVN_ERR(copy_item_to_temp(context,
context->file_props,
context->file_props_file,
rev_file->file, entry,
iterpool2));
else if (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_PROPS)
SVN_ERR(copy_item_to_temp(context,
context->dir_props,
context->dir_props_file,
rev_file->file, entry,
iterpool2));
else if ( entry->type == SVN_FS_FS__ITEM_TYPE_FILE_REP
|| entry->type == SVN_FS_FS__ITEM_TYPE_DIR_REP)
SVN_ERR(copy_rep_to_temp(context, rev_file->file, entry,
iterpool2));
else if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV)
SVN_ERR(copy_node_to_temp(context, rev_file, entry,
iterpool2));
else
SVN_ERR_ASSERT(entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED);
offset += entry->size;
}
}
if (context->cancel_func)
SVN_ERR(context->cancel_func(context->cancel_baton));
}
}
svn_pool_destroy(iterpool2);
svn_pool_destroy(iterpool);
sort_items(context->changes);
sort_items(context->file_props);
sort_items(context->dir_props);
sort_reps(context);
SVN_ERR(store_items(context, context->changes_file, context->changes,
revpool));
svn_pool_clear(revpool);
SVN_ERR(store_items(context, context->file_props_file, context->file_props,
revpool));
svn_pool_clear(revpool);
SVN_ERR(store_items(context, context->dir_props_file, context->dir_props,
revpool));
svn_pool_clear(revpool);
SVN_ERR(copy_reps_from_temp(context, context->reps_file, revpool));
svn_pool_clear(revpool);
SVN_ERR(write_l2p_index(context, revpool));
svn_pool_destroy(revpool);
return SVN_NO_ERROR;
}
static svn_error_t *
append_revision(pack_context_t *context,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = context->fs->fsap_data;
apr_off_t offset = 0;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_fs_fs__revision_file_t *rev_file;
svn_filesize_t revdata_size;
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, context->fs,
context->start_rev, pool,
iterpool));
SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
revdata_size = rev_file->l2p_offset;
SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, NULL, 0,
iterpool));
SVN_ERR(copy_file_data(context, context->pack_file, rev_file->file,
revdata_size, iterpool));
SVN_ERR(svn_fs_fs__l2p_proto_index_add_revision(context->proto_l2p_index,
pool));
while (offset < revdata_size)
{
int i;
apr_array_header_t *entries;
svn_pool_clear(iterpool);
SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs, rev_file,
context->start_rev, offset,
ffd->p2l_page_size, iterpool,
iterpool));
for (i = 0; i < entries->nelts; ++i)
{
svn_fs_fs__p2l_entry_t *entry
= &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
if (offset > entry->offset)
continue;
offset = entry->offset;
if (offset < revdata_size)
{
entry->offset += context->pack_offset;
offset += entry->size;
SVN_ERR(svn_fs_fs__l2p_proto_index_add_entry(
context->proto_l2p_index, entry->offset,
entry->item.number, iterpool));
SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(
context->proto_p2l_index, entry, iterpool));
}
}
}
svn_pool_destroy(iterpool);
context->pack_offset += revdata_size;
SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
return SVN_NO_ERROR;
}
static svn_error_t *
pack_log_addressed(svn_fs_t *fs,
const char *pack_file_dir,
const char *shard_dir,
svn_revnum_t shard_rev,
apr_size_t max_mem,
svn_boolean_t flush_to_disk,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
enum
{
PER_ITEM_MEM = APR_ALIGN_DEFAULT(sizeof(path_order_t))
+ APR_ALIGN_DEFAULT(2 *sizeof(void*))
+ APR_ALIGN_DEFAULT(sizeof(reference_t))
+ APR_ALIGN_DEFAULT(sizeof(svn_fs_fs__p2l_entry_t))
+ 6 * sizeof(void*)
};
int max_items;
apr_array_header_t *max_ids;
pack_context_t context = { 0 };
int i;
apr_size_t item_count = 0;
apr_pool_t *iterpool = svn_pool_create(pool);
{
apr_size_t temp = max_mem / PER_ITEM_MEM;
SVN_ERR_ASSERT(temp <= INT_MAX);
max_items = (int)temp;
}
SVN_ERR(initialize_pack_context(&context, fs, pack_file_dir, shard_dir,
shard_rev, max_items, flush_to_disk,
cancel_func, cancel_baton, pool));
SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, shard_rev,
context.shard_end_rev - shard_rev,
pool, pool));
for (i = 0; i < max_ids->nelts; ++i)
if ( APR_ARRAY_IDX(max_ids, i, apr_uint64_t)
<= (apr_uint64_t)max_items - item_count)
{
item_count += APR_ARRAY_IDX(max_ids, i, apr_uint64_t);
context.end_rev++;
}
else
{
svn_pool_clear(iterpool);
if (context.start_rev < context.end_rev)
{
SVN_ERR(pack_range(&context, iterpool));
SVN_ERR(reset_pack_context(&context, iterpool));
item_count = 0;
}
context.start_rev = i + context.shard_rev;
context.end_rev = context.start_rev + 1;
if (APR_ARRAY_IDX(max_ids, i, apr_uint64_t) > max_items)
{
SVN_ERR(append_revision(&context, iterpool));
context.start_rev++;
}
else
item_count += (apr_size_t)APR_ARRAY_IDX(max_ids, i, apr_uint64_t);
}
if (context.start_rev < context.end_rev)
SVN_ERR(pack_range(&context, iterpool));
SVN_ERR(reset_pack_context(&context, iterpool));
SVN_ERR(close_pack_context(&context, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__get_packed_offset(apr_off_t *rev_offset,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_stream_t *manifest_stream;
svn_boolean_t is_cached;
svn_revnum_t shard;
apr_int64_t shard_pos;
apr_array_header_t *manifest;
apr_pool_t *iterpool;
shard = rev / ffd->max_files_per_dir;
shard_pos = rev % ffd->max_files_per_dir;
SVN_ERR(svn_cache__get_partial((void **) rev_offset, &is_cached,
ffd->packed_offset_cache, &shard,
svn_fs_fs__get_sharded_offset, &shard_pos,
pool));
if (is_cached)
return SVN_NO_ERROR;
SVN_ERR(svn_stream_open_readonly(&manifest_stream,
svn_fs_fs__path_rev_packed(fs, rev,
PATH_MANIFEST,
pool),
pool, pool));
iterpool = svn_pool_create(pool);
manifest = apr_array_make(pool, ffd->max_files_per_dir, sizeof(apr_off_t));
while (1)
{
svn_boolean_t eof;
apr_int64_t val;
svn_pool_clear(iterpool);
SVN_ERR(svn_fs_fs__read_number_from_stream(&val, &eof, manifest_stream,
iterpool));
if (eof)
break;
APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val;
}
svn_pool_destroy(iterpool);
*rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
apr_off_t);
SVN_ERR(svn_stream_close(manifest_stream));
return svn_cache__set(ffd->packed_offset_cache, &shard, manifest, pool);
}
static svn_error_t *
pack_phys_addressed(const char *pack_file_dir,
const char *shard_path,
svn_revnum_t start_rev,
int max_files_per_dir,
svn_boolean_t flush_to_disk,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *pack_file_path, *manifest_file_path;
apr_file_t *pack_file;
apr_file_t *manifest_file;
svn_stream_t *manifest_stream;
svn_revnum_t end_rev, rev;
apr_pool_t *iterpool;
pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST, pool);
SVN_ERR(svn_io_file_open(&pack_file, pack_file_path,
APR_WRITE | APR_CREATE | APR_EXCL,
APR_OS_DEFAULT, pool));
SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path,
APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL,
APR_OS_DEFAULT, pool));
manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, pool);
end_rev = start_rev + max_files_per_dir - 1;
iterpool = svn_pool_create(pool);
for (rev = start_rev; rev <= end_rev; rev++)
{
svn_stream_t *rev_stream;
const char *path;
apr_off_t offset;
apr_file_t *rev_file;
svn_pool_clear(iterpool);
path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
iterpool);
SVN_ERR(svn_io_file_get_offset(&offset, pack_file, iterpool));
SVN_ERR(svn_stream_printf(manifest_stream, iterpool,
"%" APR_OFF_T_FMT "\n", offset));
SVN_ERR(svn_io_file_open(&rev_file, path, APR_READ, APR_OS_DEFAULT,
iterpool));
rev_stream = svn_stream_from_aprfile2(rev_file, FALSE, iterpool);
SVN_ERR(svn_stream_copy3(rev_stream,
svn_stream_from_aprfile2(pack_file, TRUE,
iterpool),
cancel_func, cancel_baton, iterpool));
}
SVN_ERR(svn_stream_close(manifest_stream));
if (flush_to_disk)
SVN_ERR(svn_io_file_flush_to_disk(manifest_file, pool));
SVN_ERR(svn_io_file_close(manifest_file, pool));
SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool));
if (flush_to_disk)
SVN_ERR(svn_io_file_flush_to_disk(pack_file, pool));
SVN_ERR(svn_io_file_close(pack_file, pool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
pack_rev_shard(svn_fs_t *fs,
const char *pack_file_dir,
const char *shard_path,
apr_int64_t shard,
int max_files_per_dir,
apr_size_t max_mem,
svn_boolean_t flush_to_disk,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *pack_file_path;
svn_revnum_t shard_rev = (svn_revnum_t) (shard * max_files_per_dir);
pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
SVN_ERR(svn_io_remove_dir2(pack_file_dir, TRUE, cancel_func, cancel_baton,
pool));
SVN_ERR(svn_io_dir_make(pack_file_dir, APR_OS_DEFAULT, pool));
if (svn_fs_fs__use_log_addressing(fs))
SVN_ERR(pack_log_addressed(fs, pack_file_dir, shard_path,
shard_rev, max_mem, flush_to_disk,
cancel_func, cancel_baton, pool));
else
SVN_ERR(pack_phys_addressed(pack_file_dir, shard_path, shard_rev,
max_files_per_dir, flush_to_disk,
cancel_func, cancel_baton, pool));
SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool));
SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool));
return SVN_NO_ERROR;
}
struct pack_baton
{
svn_fs_t *fs;
svn_fs_pack_notify_t notify_func;
void *notify_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
size_t max_mem;
const char *revs_dir;
const char *revsprops_dir;
apr_int64_t shard;
const char *rev_shard_path;
};
static svn_error_t *
synced_pack_shard(void *baton,
apr_pool_t *pool)
{
struct pack_baton *pb = baton;
fs_fs_data_t *ffd = pb->fs->fsap_data;
const char *revprops_shard_path, *revprops_pack_file_dir;
if (pb->revsprops_dir)
{
apr_int64_t pack_size_limit = 0.9 * ffd->revprop_pack_size;
revprops_pack_file_dir = svn_dirent_join(pb->revsprops_dir,
apr_psprintf(pool,
"%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
pb->shard),
pool);
revprops_shard_path = svn_dirent_join(pb->revsprops_dir,
apr_psprintf(pool, "%" APR_INT64_T_FMT, pb->shard),
pool);
SVN_ERR(svn_fs_fs__pack_revprops_shard(revprops_pack_file_dir,
revprops_shard_path,
pb->shard,
ffd->max_files_per_dir,
pack_size_limit,
ffd->compress_packed_revprops
? SVN__COMPRESSION_ZLIB_DEFAULT
: SVN__COMPRESSION_NONE,
ffd->flush_to_disk,
pb->cancel_func,
pb->cancel_baton,
pool));
}
SVN_ERR(svn_fs_fs__write_min_unpacked_rev(pb->fs,
(svn_revnum_t)((pb->shard + 1) * ffd->max_files_per_dir),
pool));
ffd->min_unpacked_rev
= (svn_revnum_t)((pb->shard + 1) * ffd->max_files_per_dir);
SVN_ERR(svn_io_remove_dir2(pb->rev_shard_path, TRUE,
pb->cancel_func, pb->cancel_baton, pool));
if (pb->revsprops_dir)
{
svn_node_kind_t kind = svn_node_dir;
apr_int64_t to_cleanup = pb->shard;
do
{
SVN_ERR(svn_fs_fs__delete_revprops_shard(revprops_shard_path,
to_cleanup,
ffd->max_files_per_dir,
pb->cancel_func,
pb->cancel_baton,
pool));
revprops_shard_path = svn_dirent_join(pb->revsprops_dir,
apr_psprintf(pool, "%" APR_INT64_T_FMT, --to_cleanup),
pool);
SVN_ERR(svn_io_check_path(revprops_shard_path, &kind, pool));
}
while (kind == svn_node_dir && to_cleanup > 0);
}
return SVN_NO_ERROR;
}
static svn_error_t *
pack_shard(struct pack_baton *baton,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = baton->fs->fsap_data;
const char *rev_pack_file_dir;
if (baton->notify_func)
SVN_ERR(baton->notify_func(baton->notify_baton, baton->shard,
svn_fs_pack_notify_start, pool));
rev_pack_file_dir = svn_dirent_join(baton->revs_dir,
apr_psprintf(pool,
"%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
baton->shard),
pool);
baton->rev_shard_path = svn_dirent_join(baton->revs_dir,
apr_psprintf(pool,
"%" APR_INT64_T_FMT,
baton->shard),
pool);
SVN_ERR(pack_rev_shard(baton->fs, rev_pack_file_dir, baton->rev_shard_path,
baton->shard, ffd->max_files_per_dir,
baton->max_mem, ffd->flush_to_disk,
baton->cancel_func, baton->cancel_baton, pool));
if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
SVN_ERR(svn_fs_fs__with_write_lock(baton->fs, synced_pack_shard, baton,
pool));
else
SVN_ERR(synced_pack_shard(baton, pool));
if (baton->notify_func)
SVN_ERR(baton->notify_func(baton->notify_baton, baton->shard,
svn_fs_pack_notify_end, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_pack_status(svn_boolean_t *fully_packed,
svn_fs_t *fs,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
apr_int64_t completed_shards;
svn_revnum_t youngest;
SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs,
scratch_pool));
SVN_ERR(svn_fs_fs__youngest_rev(&youngest, fs, scratch_pool));
completed_shards = (youngest + 1) / ffd->max_files_per_dir;
if (ffd->min_unpacked_rev == (completed_shards * ffd->max_files_per_dir))
*fully_packed = TRUE;
else
*fully_packed = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
pack_body(void *baton,
apr_pool_t *pool)
{
struct pack_baton *pb = baton;
fs_fs_data_t *ffd = pb->fs->fsap_data;
apr_int64_t completed_shards;
apr_pool_t *iterpool;
svn_boolean_t fully_packed;
SVN_ERR(get_pack_status(&fully_packed, pb->fs, pool));
if (fully_packed)
{
if (pb->notify_func)
(*pb->notify_func)(pb->notify_baton,
ffd->min_unpacked_rev / ffd->max_files_per_dir,
svn_fs_pack_notify_noop, pool);
return SVN_NO_ERROR;
}
completed_shards = (ffd->youngest_rev_cache + 1) / ffd->max_files_per_dir;
pb->revs_dir = svn_dirent_join(pb->fs->path, PATH_REVS_DIR, pool);
if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
pb->revsprops_dir = svn_dirent_join(pb->fs->path, PATH_REVPROPS_DIR,
pool);
iterpool = svn_pool_create(pool);
for (pb->shard = ffd->min_unpacked_rev / ffd->max_files_per_dir;
pb->shard < completed_shards;
pb->shard++)
{
svn_pool_clear(iterpool);
if (pb->cancel_func)
SVN_ERR(pb->cancel_func(pb->cancel_baton));
SVN_ERR(pack_shard(pb, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__pack(svn_fs_t *fs,
apr_size_t max_mem,
svn_fs_pack_notify_t notify_func,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
struct pack_baton pb = { 0 };
fs_fs_data_t *ffd = fs->fsap_data;
svn_error_t *err;
svn_boolean_t fully_packed;
if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("FSFS format (%d) too old to pack; please upgrade the filesystem."),
ffd->format);
if (!ffd->max_files_per_dir)
{
if (notify_func)
(*notify_func)(notify_baton, -1, svn_fs_pack_notify_noop, pool);
return SVN_NO_ERROR;
}
SVN_ERR(get_pack_status(&fully_packed, fs, pool));
if (fully_packed)
{
if (notify_func)
(*notify_func)(notify_baton,
ffd->min_unpacked_rev / ffd->max_files_per_dir,
svn_fs_pack_notify_noop, pool);
return SVN_NO_ERROR;
}
pb.fs = fs;
pb.notify_func = notify_func;
pb.notify_baton = notify_baton;
pb.cancel_func = cancel_func;
pb.cancel_baton = cancel_baton;
pb.max_mem = max_mem ? max_mem : DEFAULT_MAX_MEM;
if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
{
err = svn_fs_fs__with_pack_lock(fs, pack_body, &pb, pool);
}
else
{
err = svn_fs_fs__with_write_lock(fs, pack_body, &pb, pool);
}
return svn_error_trace(err);
}