summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_fs_fs/fs_fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_fs/fs_fs.c')
-rw-r--r--subversion/libsvn_fs_fs/fs_fs.c254
1 files changed, 182 insertions, 72 deletions
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index c6074ac..20145d0 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -982,6 +982,31 @@ check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
return check_file_buffer_numeric(buf, offset, path, "Format", pool);
}
+/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
+ number is not the same as a format number supported by this
+ Subversion. */
+static svn_error_t *
+check_format(int format)
+{
+ /* Blacklist. These formats may be either younger or older than
+ SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */
+ if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT)
+ return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
+ _("Found format '%d', only created by "
+ "unreleased dev builds; see "
+ "http://subversion.apache.org"
+ "/docs/release-notes/1.7#revprop-packing"),
+ format);
+
+ /* We support all formats from 1-current simultaneously */
+ if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
+ return SVN_NO_ERROR;
+
+ return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
+ _("Expected FS format between '1' and '%d'; found format '%d'"),
+ SVN_FS_FS__FORMAT_NUMBER, format);
+}
+
/* Read the format number and maximum number of files per directory
from PATH and return them in *PFORMAT and *MAX_FILES_PER_DIR
respectively.
@@ -1032,6 +1057,9 @@ read_format(int *pformat, int *max_files_per_dir,
SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, pool));
SVN_ERR(svn_cstring_atoi(pformat, buf->data));
+ /* Check that we support this format at all */
+ SVN_ERR(check_format(*pformat));
+
/* Set the default values for anything that can be set via an option. */
*max_files_per_dir = 0;
@@ -1117,31 +1145,6 @@ write_format(const char *path, int format, int max_files_per_dir,
return svn_io_set_file_read_only(path, FALSE, pool);
}
-/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
- number is not the same as a format number supported by this
- Subversion. */
-static svn_error_t *
-check_format(int format)
-{
- /* Blacklist. These formats may be either younger or older than
- SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */
- if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT)
- return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
- _("Found format '%d', only created by "
- "unreleased dev builds; see "
- "http://subversion.apache.org"
- "/docs/release-notes/1.7#revprop-packing"),
- format);
-
- /* We support all formats from 1-current simultaneously */
- if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
- return SVN_NO_ERROR;
-
- return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
- _("Expected FS format between '1' and '%d'; found format '%d'"),
- SVN_FS_FS__FORMAT_NUMBER, format);
-}
-
svn_boolean_t
svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs)
{
@@ -1404,7 +1407,6 @@ svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
/* Read the FS format number. */
SVN_ERR(read_format(&format, &max_files_per_dir,
path_format(fs, pool), pool));
- SVN_ERR(check_format(format));
/* Now we've got a format number no matter what. */
ffd->format = format;
@@ -1564,7 +1566,6 @@ upgrade_body(void *baton, apr_pool_t *pool)
/* Read the FS format number and max-files-per-dir setting. */
SVN_ERR(read_format(&format, &max_files_per_dir, format_path, pool));
- SVN_ERR(check_format(format));
/* If the config file does not exist, create one. */
SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
@@ -3536,7 +3537,7 @@ typedef struct packed_revprops_t
/* sum of values in SIZES */
apr_size_t total_size;
- /* first revision in the pack */
+ /* first revision in the pack (>= MANIFEST_START) */
svn_revnum_t start_revision;
/* size of the revprops in PACKED_REVPROPS */
@@ -3550,8 +3551,12 @@ typedef struct packed_revprops_t
* in the pack, i.e. the pack content without header and compression */
svn_stringbuf_t *packed_revprops;
+ /* First revision covered by MANIFEST.
+ * Will equal the shard start revision or 1, for the 1st shard. */
+ svn_revnum_t manifest_start;
+
/* content of the manifest.
- * Maps long(rev - START_REVISION) to const char* pack file name */
+ * Maps long(rev - MANIFEST_START) to const char* pack file name */
apr_array_header_t *manifest;
} packed_revprops_t;
@@ -3655,7 +3660,10 @@ get_revprop_packname(svn_fs_t *fs,
/* parse the manifest. Every line is a file name */
revprops->manifest = apr_array_make(pool, ffd->max_files_per_dir,
sizeof(const char*));
- while (content->data)
+
+ /* Read all lines. Since the last line ends with a newline, we will
+ end up with a valid but empty string after the last entry. */
+ while (content->data && *content->data)
{
APR_ARRAY_PUSH(revprops->manifest, const char*) = content->data;
content->data = strchr(content->data, '\n');
@@ -3667,13 +3675,15 @@ get_revprop_packname(svn_fs_t *fs,
}
/* Index for our revision. Rev 0 is excluded from the first shard. */
- idx = (int)(revprops->revision % ffd->max_files_per_dir);
- if (revprops->revision < ffd->max_files_per_dir)
- --idx;
+ revprops->manifest_start = revprops->revision
+ - (revprops->revision % ffd->max_files_per_dir);
+ if (revprops->manifest_start == 0)
+ ++revprops->manifest_start;
+ idx = (int)(revprops->revision - revprops->manifest_start);
if (revprops->manifest->nelts <= idx)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Packed revprop manifest for rev %ld too "
+ _("Packed revprop manifest for r%ld too "
"small"), revprops->revision);
/* Now get the file name */
@@ -3682,6 +3692,17 @@ get_revprop_packname(svn_fs_t *fs,
return SVN_NO_ERROR;
}
+/* Return TRUE, if revision R1 and R2 refer to the same shard in FS.
+ */
+static svn_boolean_t
+same_shard(svn_fs_t *fs,
+ svn_revnum_t r1,
+ svn_revnum_t r2)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ return (r1 / ffd->max_files_per_dir) == (r2 / ffd->max_files_per_dir);
+}
+
/* Given FS and the full packed file content in REVPROPS->PACKED_REVPROPS,
* fill the START_REVISION, SIZES, OFFSETS members. Also, make
* PACKED_REVPROPS point to the first serialized revprop.
@@ -3714,6 +3735,26 @@ parse_packed_revprops(svn_fs_t *fs,
SVN_ERR(read_number_from_stream(&first_rev, NULL, stream, iterpool));
SVN_ERR(read_number_from_stream(&count, NULL, stream, iterpool));
+ /* Check revision range for validity. */
+ if ( !same_shard(fs, revprops->revision, first_rev)
+ || !same_shard(fs, revprops->revision, first_rev + count - 1)
+ || count < 1)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Revprop pack for revision r%ld"
+ " contains revprops for r%ld .. r%ld"),
+ revprops->revision,
+ (svn_revnum_t)first_rev,
+ (svn_revnum_t)(first_rev + count -1));
+
+ /* Since start & end are in the same shard, it is enough to just test
+ * the FIRST_REV for being actually packed. That will also cover the
+ * special case of rev 0 never being packed. */
+ if (!is_packed_revprop(fs, first_rev))
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Revprop pack for revision r%ld"
+ " starts at non-packed revisions r%ld"),
+ revprops->revision, (svn_revnum_t)first_rev);
+
/* make PACKED_REVPROPS point to the first char after the header.
* This is where the serialized revprops are. */
header_end = strstr(uncompressed->data, "\n\n");
@@ -3846,14 +3887,14 @@ read_pack_revprop(packed_revprops_t **revprops,
/* the file content should be available now */
if (!result->packed_revprops)
return svn_error_createf(SVN_ERR_FS_PACKED_REVPROP_READ_FAILURE, NULL,
- _("Failed to read revprop pack file for rev %ld"), rev);
+ _("Failed to read revprop pack file for r%ld"), rev);
/* parse it. RESULT will be complete afterwards. */
err = parse_packed_revprops(fs, result, pool, iterpool);
svn_pool_destroy(iterpool);
if (err)
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
- _("Revprop pack file for rev %ld is corrupt"), rev);
+ _("Revprop pack file for r%ld is corrupt"), rev);
*revprops = result;
@@ -4117,7 +4158,8 @@ repack_revprops(svn_fs_t *fs,
return SVN_NO_ERROR;
}
-/* Allocate a new pack file name for the revisions at index [START,END)
+/* Allocate a new pack file name for revisions
+ * [REVPROPS->START_REVISION + START, REVPROPS->START_REVISION + END - 1]
* of REVPROPS->MANIFEST. Add the name of old file to FILES_TO_DELETE,
* auto-create that array if necessary. Return an open file stream to
* the new file in *STREAM allocated in POOL.
@@ -4136,10 +4178,13 @@ repack_stream_open(svn_stream_t **stream,
svn_string_t *new_filename;
int i;
apr_file_t *file;
+ int manifest_offset
+ = (int)(revprops->start_revision - revprops->manifest_start);
/* get the old (= current) file name and enlist it for later deletion */
- const char *old_filename
- = APR_ARRAY_IDX(revprops->manifest, start, const char*);
+ const char *old_filename = APR_ARRAY_IDX(revprops->manifest,
+ start + manifest_offset,
+ const char*);
if (*files_to_delete == NULL)
*files_to_delete = apr_array_make(pool, 3, sizeof(const char*));
@@ -4161,7 +4206,8 @@ repack_stream_open(svn_stream_t **stream,
/* update the manifest to point to the new file */
for (i = start; i < end; ++i)
- APR_ARRAY_IDX(revprops->manifest, i, const char*) = new_filename->data;
+ APR_ARRAY_IDX(revprops->manifest, i + manifest_offset, const char*)
+ = new_filename->data;
/* create a file stream for the new file */
SVN_ERR(svn_io_file_open(&file, svn_dirent_join(revprops->folder,
@@ -10883,26 +10929,23 @@ hotcopy_update_current(svn_revnum_t *dst_youngest,
}
-/* Remove revisions between START_REV (inclusive) and END_REV (non-inclusive)
- * from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
+/* Remove revision or revprop files between START_REV (inclusive) and
+ * END_REV (non-inclusive) from folder DST_SUBDIR in DST_FS. Assume
+ * sharding as per MAX_FILES_PER_DIR.
* Use SCRATCH_POOL for temporary allocations. */
static svn_error_t *
-hotcopy_remove_rev_files(svn_fs_t *dst_fs,
- svn_revnum_t start_rev,
- svn_revnum_t end_rev,
- int max_files_per_dir,
- apr_pool_t *scratch_pool)
+hotcopy_remove_files(svn_fs_t *dst_fs,
+ const char *dst_subdir,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
{
- const char *dst_subdir;
const char *shard;
const char *dst_subdir_shard;
svn_revnum_t rev;
apr_pool_t *iterpool;
- SVN_ERR_ASSERT(start_rev <= end_rev);
-
- dst_subdir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, scratch_pool);
-
/* Pre-compute paths for initial shard. */
shard = apr_psprintf(scratch_pool, "%ld", start_rev / max_files_per_dir);
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
@@ -10910,8 +10953,7 @@ hotcopy_remove_rev_files(svn_fs_t *dst_fs,
iterpool = svn_pool_create(scratch_pool);
for (rev = start_rev; rev < end_rev; rev++)
{
- const char *rev_path;
-
+ const char *path;
svn_pool_clear(iterpool);
/* If necessary, update paths for shard. */
@@ -10921,19 +10963,66 @@ hotcopy_remove_rev_files(svn_fs_t *dst_fs,
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool);
}
- rev_path = svn_dirent_join(dst_subdir_shard,
- apr_psprintf(iterpool, "%ld", rev),
- iterpool);
+ /* remove files for REV */
+ path = svn_dirent_join(dst_subdir_shard,
+ apr_psprintf(iterpool, "%ld", rev),
+ iterpool);
/* Make the rev file writable and remove it. */
- SVN_ERR(svn_io_set_file_read_write(rev_path, TRUE, iterpool));
- SVN_ERR(svn_io_remove_file2(rev_path, TRUE, iterpool));
+ SVN_ERR(svn_io_set_file_read_write(path, TRUE, iterpool));
+ SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
}
+
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
+/* Remove revisions between START_REV (inclusive) and END_REV (non-inclusive)
+ * from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+hotcopy_remove_rev_files(svn_fs_t *dst_fs,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(start_rev <= end_rev);
+ SVN_ERR(hotcopy_remove_files(dst_fs,
+ svn_dirent_join(dst_fs->path,
+ PATH_REVS_DIR,
+ scratch_pool),
+ start_rev, end_rev,
+ max_files_per_dir, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Remove revision properties between START_REV (inclusive) and END_REV
+ * (non-inclusive) from DST_FS. Assume sharding as per MAX_FILES_PER_DIR.
+ * Use SCRATCH_POOL for temporary allocations. Revision 0 revprops will
+ * not be deleted. */
+static svn_error_t *
+hotcopy_remove_revprop_files(svn_fs_t *dst_fs,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ int max_files_per_dir,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(start_rev <= end_rev);
+
+ /* don't delete rev 0 props */
+ SVN_ERR(hotcopy_remove_files(dst_fs,
+ svn_dirent_join(dst_fs->path,
+ PATH_REVPROPS_DIR,
+ scratch_pool),
+ start_rev ? start_rev : 1, end_rev,
+ max_files_per_dir, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
/* Verify that DST_FS is a suitable destination for an incremental
* hotcopy from SRC_FS. */
static svn_error_t *
@@ -10970,6 +11059,27 @@ hotcopy_incremental_check_preconditions(svn_fs_t *src_fs,
return SVN_NO_ERROR;
}
+/* Remove folder PATH. Ignore errors due to the sub-tree not being empty.
+ * CANCEL_FUNC and CANCEL_BATON do the usual thing.
+ * Use POOL for temporary allocations.
+ */
+static svn_error_t *
+remove_folder(const char *path,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool)
+{
+ svn_error_t *err = svn_io_remove_dir2(path, TRUE,
+ cancel_func, cancel_baton, pool);
+
+ if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err))
+ {
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(err);
+}
/* Baton for hotcopy_body(). */
struct hotcopy_body_baton {
@@ -11160,8 +11270,6 @@ hotcopy_body(void *baton, apr_pool_t *pool)
/* First, copy packed shards. */
for (rev = 0; rev < src_min_unpacked_rev; rev += max_files_per_dir)
{
- svn_error_t *err;
-
svn_pool_clear(iterpool);
if (cancel_func)
@@ -11181,20 +11289,22 @@ hotcopy_body(void *baton, apr_pool_t *pool)
/* Remove revision files which are now packed. */
if (incremental)
- SVN_ERR(hotcopy_remove_rev_files(dst_fs, rev, rev + max_files_per_dir,
- max_files_per_dir, iterpool));
+ {
+ SVN_ERR(hotcopy_remove_rev_files(dst_fs, rev,
+ rev + max_files_per_dir,
+ max_files_per_dir, iterpool));
+ SVN_ERR(hotcopy_remove_revprop_files(dst_fs, rev,
+ rev + max_files_per_dir,
+ max_files_per_dir, iterpool));
+ }
/* Now that all revisions have moved into the pack, the original
* rev dir can be removed. */
- err = svn_io_remove_dir2(path_rev_shard(dst_fs, rev, iterpool),
- TRUE, cancel_func, cancel_baton, iterpool);
- if (err)
- {
- if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
- svn_error_clear(err);
- else
- return svn_error_trace(err);
- }
+ SVN_ERR(remove_folder(path_rev_shard(dst_fs, rev, iterpool),
+ cancel_func, cancel_baton, iterpool));
+ if (rev > 0)
+ SVN_ERR(remove_folder(path_revprops_shard(dst_fs, rev, iterpool),
+ cancel_func, cancel_baton, iterpool));
}
if (cancel_func)
OpenPOWER on IntegriCloud