1
1
/* fs_fs.c --- filesystem operations specific to fs_fs
3
3
* ====================================================================
4
* Copyright (c) 2000-2009 CollabNet. All rights reserved.
6
* This software is licensed as described in the file COPYING, which
7
* you should have received as part of this distribution. The terms
8
* are also available at http://subversion.tigris.org/license-1.html.
9
* If newer versions of this license are posted there, you may use a
10
* newer version instead, at your option.
12
* This software consists of voluntary contributions made by many
13
* individuals. For exact contribution history, see the revision
14
* history and logs, available at http://subversion.tigris.org/.
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
15
20
* ====================================================================
149
158
return (rev < ffd->min_unpacked_rev);
161
/* Return TRUE is REV is packed in FS, FALSE otherwise. */
163
is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev)
166
fs_fs_data_t *ffd = fs->fsap_data;
168
return (rev < ffd->min_unpacked_revprop);
152
174
static const char *
153
175
path_format(svn_fs_t *fs, apr_pool_t *pool)
155
return svn_path_join(fs->path, PATH_FORMAT, pool);
177
return svn_dirent_join(fs->path, PATH_FORMAT, pool);
158
180
static APR_INLINE const char *
159
181
path_uuid(svn_fs_t *fs, apr_pool_t *pool)
161
return svn_path_join(fs->path, PATH_UUID, pool);
183
return svn_dirent_join(fs->path, PATH_UUID, pool);
165
187
svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
167
return svn_path_join(fs->path, PATH_CURRENT, pool);
189
return svn_dirent_join(fs->path, PATH_CURRENT, pool);
170
192
static APR_INLINE const char *
171
193
path_txn_current(svn_fs_t *fs, apr_pool_t *pool)
173
return svn_path_join(fs->path, PATH_TXN_CURRENT, pool);
195
return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
176
198
static APR_INLINE const char *
177
199
path_txn_current_lock(svn_fs_t *fs, apr_pool_t *pool)
179
return svn_path_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
201
return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
182
204
static APR_INLINE const char *
183
205
path_lock(svn_fs_t *fs, apr_pool_t *pool)
185
return svn_path_join(fs->path, PATH_LOCK_FILE, pool);
207
return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
188
210
static const char *
222
244
if (ffd->max_files_per_dir)
224
return svn_path_join(path_rev_shard(fs, rev, pool),
225
apr_psprintf(pool, "%ld", rev),
246
return svn_dirent_join(path_rev_shard(fs, rev, pool),
247
apr_psprintf(pool, "%ld", rev),
229
return svn_path_join_many(pool, fs->path, PATH_REVS_DIR,
230
apr_psprintf(pool, "%ld", rev), NULL);
251
return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
252
apr_psprintf(pool, "%ld", rev), NULL);
233
/* Returns the path of REV in FS, whether in a pack file or not.
236
256
svn_fs_fs__path_rev_absolute(const char **path,
238
258
svn_revnum_t rev,
239
259
apr_pool_t *pool)
241
if (! is_packed_rev(fs, rev))
261
fs_fs_data_t *ffd = fs->fsap_data;
263
if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT
264
|| ! is_packed_rev(fs, rev))
243
svn_node_kind_t kind;
245
/* Initialize the return variable. */
246
266
*path = path_rev(fs, rev, pool);
248
SVN_ERR(svn_io_check_path(*path, &kind, pool));
249
if (kind == svn_node_file)
251
/* *path is already set correctly. */
256
/* Someone must have run 'svnadmin pack' while this fs object
259
SVN_ERR(update_min_unpacked_rev(fs, pool));
261
/* The rev really should be present now. */
262
if (! is_packed_rev(fs, rev))
263
return svn_error_createf(APR_ENOENT, NULL,
264
_("Revision file '%s' does not exist, "
265
"and r%ld is not packed"),
266
svn_path_local_style(*path, pool),
272
*path = path_rev_packed(fs, rev, "pack", pool);
270
*path = path_rev_packed(fs, rev, "pack", pool);
274
273
return SVN_NO_ERROR;
294
293
if (ffd->max_files_per_dir)
296
return svn_path_join(path_revprops_shard(fs, rev, pool),
297
apr_psprintf(pool, "%ld", rev),
295
return svn_dirent_join(path_revprops_shard(fs, rev, pool),
296
apr_psprintf(pool, "%ld", rev),
301
return svn_path_join_many(pool, fs->path, PATH_REVPROPS_DIR,
302
apr_psprintf(pool, "%ld", rev), NULL);
300
return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
301
apr_psprintf(pool, "%ld", rev), NULL);
305
304
static APR_INLINE const char *
306
305
path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
308
return svn_path_join_many(pool, fs->path, PATH_TXNS_DIR,
309
apr_pstrcat(pool, txn_id, PATH_EXT_TXN, NULL),
307
SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
308
return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
309
apr_pstrcat(pool, txn_id, PATH_EXT_TXN,
313
314
static APR_INLINE const char *
314
315
path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
316
return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
317
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
319
320
static APR_INLINE const char *
320
321
path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
322
return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
323
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
325
326
static APR_INLINE const char *
326
327
path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
328
return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
329
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
331
332
static APR_INLINE const char *
332
333
path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
334
return svn_path_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
335
return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
337
339
static APR_INLINE const char *
338
340
path_txn_proto_rev(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
340
342
fs_fs_data_t *ffd = fs->fsap_data;
341
343
if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
342
return svn_path_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
343
apr_pstrcat(pool, txn_id, PATH_EXT_REV, NULL),
344
return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
345
apr_pstrcat(pool, txn_id, PATH_EXT_REV,
346
return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
349
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
349
352
static APR_INLINE const char *
352
355
fs_fs_data_t *ffd = fs->fsap_data;
353
356
if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
354
return svn_path_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
355
apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
357
return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
358
apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
359
return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK, pool);
362
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK,
362
366
static const char *
368
372
const char *name = apr_psprintf(pool, PATH_PREFIX_NODE "%s.%s",
369
373
node_id, copy_id);
371
return svn_path_join(path_txn_dir(fs, txn_id, pool), name, pool);
375
return svn_dirent_join(path_txn_dir(fs, txn_id, pool), name, pool);
374
378
static APR_INLINE const char *
375
379
path_txn_node_props(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
377
381
return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_PROPS,
381
385
static APR_INLINE const char *
382
386
path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
384
388
return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool),
385
PATH_EXT_CHILDREN, NULL);
389
PATH_EXT_CHILDREN, (char *)NULL);
388
392
static APR_INLINE const char *
389
393
path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool)
391
int len = strlen(node_id);
395
size_t len = strlen(node_id);
392
396
const char *node_id_minus_last_char =
393
397
(len == 1) ? "0" : apr_pstrmemdup(pool, node_id, len - 1);
394
return svn_path_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
395
node_id_minus_last_char, NULL);
398
return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
399
node_id_minus_last_char, NULL);
402
static APR_INLINE const char *
403
path_and_offset_of(apr_file_t *file, apr_pool_t *pool)
406
apr_off_t offset = 0;
408
if (apr_file_name_get(&path, file) != APR_SUCCESS)
411
if (apr_file_seek(file, APR_CUR, &offset) != APR_SUCCESS)
414
return apr_psprintf(pool, "%s:%" APR_OFF_T_FMT, path, offset);
399
419
/* Functions for working with shared transaction data. */
868
/* Check that BUF, a buffer of text from format file PATH, contains
869
only digits, raising error SVN_ERR_BAD_VERSION_FILE_FORMAT if not.
902
/* Check that BUF, a nul-terminated buffer of text from format file PATH,
903
contains only digits at OFFSET and beyond, raising an error if not.
871
905
Uses POOL for temporary allocation. */
872
906
static svn_error_t *
873
check_format_file_buffer_numeric(const char *buf, const char *path,
907
check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
908
const char *path, apr_pool_t *pool)
878
for (p = buf; *p; p++)
879
if (!apr_isdigit(*p))
912
for (p = buf + offset; *p; p++)
913
if (!svn_ctype_isdigit(*p))
880
914
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
881
_("Format file '%s' contains an unexpected non-digit"),
882
svn_path_local_style(path, pool));
915
_("Format file '%s' contains unexpected non-digit '%c' within '%s'"),
916
svn_dirent_local_style(path, pool), *p, buf);
884
918
return SVN_NO_ERROR;
964
998
if (strncmp(buf+7, "sharded ", 8) == 0)
966
1000
/* Check that the argument is numeric. */
967
SVN_ERR(check_format_file_buffer_numeric(buf+15, path, pool));
968
*max_files_per_dir = atoi(buf+15);
1001
SVN_ERR(check_format_file_buffer_numeric(buf, 15, path, pool));
1002
SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf + 15));
973
1007
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
974
1008
_("'%s' contains invalid filesystem format option '%s'"),
975
svn_path_local_style(path, pool), buf);
1009
svn_dirent_local_style(path, pool), buf);
978
1012
return svn_io_file_close(file, pool);
1003
1036
svn_stringbuf_appendcstr(sb, "layout linear\n");
1006
contents = svn_string_create_from_buf(sb, pool);
1008
1039
/* svn_io_write_version_file() does a load of magic to allow it to
1009
1040
replace version files that already exist. We only need to do
1010
1041
that when we're allowed to overwrite an existing file. */
1011
1042
if (! overwrite)
1013
1044
/* Create the file */
1014
SVN_ERR(svn_io_file_create(path, contents->data, pool));
1045
SVN_ERR(svn_io_file_create(path, sb->data, pool));
1018
1049
const char *path_tmp;
1020
1051
SVN_ERR(svn_io_write_unique(&path_tmp,
1021
svn_path_dirname(path, pool),
1022
contents->data, contents->len,
1052
svn_dirent_dirname(path, pool),
1023
1054
svn_io_file_del_none, pool));
1026
/* make the destination writable, but only on Windows, because
1027
Windows does not let us replace read-only files. */
1028
SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
1031
1056
/* rename the temp file as the real destination */
1032
1057
SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
1112
1147
"[" CONFIG_SECTION_REP_SHARING "]" NL
1113
1148
"### To conserve space, the filesystem can optionally avoid storing" NL
1114
"### duplicate representations. This comes at a slight cost in performace," NL
1115
"### as maintaining a database of shared representations can increase" NL
1116
"### commit times. The space savings are dependent upon the size of the" NL
1117
"### repository, the number of objects it contains and the amount of" NL
1149
"### duplicate representations. This comes at a slight cost in" NL
1150
"### performance, as maintaining a database of shared representations can" NL
1151
"### increase commit times. The space savings are dependent upon the size" NL
1152
"### of the repository, the number of objects it contains and the amount of" NL
1118
1153
"### duplication between them, usually a function of the branching and" NL
1119
1154
"### merging process." NL
1121
1156
"### The following parameter enables rep-sharing in the repository. It can" NL
1122
1157
"### be switched on and off at will, but for best space-saving results" NL
1123
1158
"### should be enabled consistently over the life of the repository." NL
1124
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = false" NL
1159
"### rep-sharing is enabled by default." NL
1160
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true" NL
1128
return svn_io_file_create(svn_path_join(fs->path, PATH_CONFIG, pool),
1164
return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1129
1165
fsfs_conf_contents, pool);
1476
1517
config_relpath = svn_dirent_join(src_path, PATH_CONFIG, pool);
1477
1518
err2 = svn_dirent_get_absolute(&src_abspath, src_path, pool);
1479
return svn_error_compose_create(err, err2);
1520
return svn_error_trace(svn_error_compose_create(err, err2));
1480
1521
err2 = svn_dirent_get_absolute(&dst_abspath, dst_path, pool);
1482
return svn_error_compose_create(err, err2);
1523
return svn_error_trace(svn_error_compose_create(err, err2));
1484
1525
/* ### hack: strip off the 'db/' directory from paths so
1485
1526
* ### they make sense to the user */
1486
1527
src_abspath = svn_dirent_dirname(src_abspath, pool);
1495
1536
return svn_error_quick_wrap(err, msg);
1539
return svn_error_trace(err);
1502
/* Copy the current file. */
1543
/* Copy the 'current' file. */
1503
1544
SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool));
1505
1546
/* Copy the uuid. */
1506
1547
SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool));
1549
/* Copy the rep cache before copying the rev files to make sure all
1550
cached references will be present in the copy. */
1551
src_subdir = svn_dirent_join(src_path, REP_CACHE_DB_NAME, pool);
1552
dst_subdir = svn_dirent_join(dst_path, REP_CACHE_DB_NAME, pool);
1553
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1554
if (kind == svn_node_file)
1555
SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
1508
1557
/* Copy the min unpacked rev, and read its value. */
1509
1558
if (format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1511
1560
const char *min_unpacked_rev_path;
1512
min_unpacked_rev_path = svn_path_join(src_path, PATH_MIN_UNPACKED_REV,
1561
min_unpacked_rev_path = svn_dirent_join(src_path, PATH_MIN_UNPACKED_REV,
1515
1564
SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_MIN_UNPACKED_REV,
1560
1610
const char *shard = apr_psprintf(iterpool, "%ld",
1561
1611
rev / max_files_per_dir);
1562
src_subdir_shard = svn_path_join(src_subdir, shard, iterpool);
1563
dst_subdir_shard = svn_path_join(dst_subdir, shard, iterpool);
1612
src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
1613
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
1565
1615
if (rev % max_files_per_dir == 0)
1567
1617
SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
1569
SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
1619
SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
1595
1645
const char *shard = apr_psprintf(iterpool, "%ld",
1596
1646
rev / max_files_per_dir);
1597
src_subdir_shard = svn_path_join(src_subdir, shard, iterpool);
1598
dst_subdir_shard = svn_path_join(dst_subdir, shard, iterpool);
1647
src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
1648
dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
1600
1650
if (rev % max_files_per_dir == 0)
1602
1652
SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
1604
SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
1654
SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
1616
1666
/* Make an empty transactions directory for now. Eventually some
1617
1667
method of copying in progress transactions will need to be
1619
dst_subdir = svn_path_join(dst_path, PATH_TXNS_DIR, pool);
1669
dst_subdir = svn_dirent_join(dst_path, PATH_TXNS_DIR, pool);
1620
1670
SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1621
1671
if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1623
dst_subdir = svn_path_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
1673
dst_subdir = svn_dirent_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
1624
1674
SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1627
1677
/* Now copy the locks tree. */
1628
src_subdir = svn_path_join(src_path, PATH_LOCKS_DIR, pool);
1678
src_subdir = svn_dirent_join(src_path, PATH_LOCKS_DIR, pool);
1629
1679
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1630
1680
if (kind == svn_node_dir)
1631
1681
SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
1635
1685
/* Now copy the node-origins cache tree. */
1636
src_subdir = svn_path_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
1686
src_subdir = svn_dirent_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
1637
1687
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1638
1688
if (kind == svn_node_dir)
1639
1689
SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
1640
1690
PATH_NODE_ORIGINS_DIR, TRUE, NULL,
1643
/* Now copy the rep cache. */
1644
src_subdir = svn_path_join(src_path, REP_CACHE_DB_NAME, pool);
1645
SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1646
if (kind == svn_node_file)
1647
SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, REP_CACHE_DB_NAME, pool));
1649
1693
/* Copy the txn-current file. */
1650
1694
if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1651
1695
SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_TXN_CURRENT, pool));
1653
1697
/* Hotcopied FS is complete. Stamp it with a format file. */
1654
return write_format(svn_path_join(dst_path, PATH_FORMAT, pool),
1698
return write_format(svn_dirent_join(dst_path, PATH_FORMAT, pool),
1655
1699
format, max_files_per_dir, FALSE, pool);
1763
1814
/* Open the correct revision file for REV. If the filesystem FS has
1764
1815
been packed, *FILE will be set to the packed file; otherwise, set *FILE
1765
1816
to the revision file for REV. Return SVN_ERR_FS_NO_SUCH_REVISION if the
1766
file doesn't exist. Use POOL for allocations. */
1819
TODO: Consider returning an indication of whether this is a packed rev
1820
file, so the caller need not rely on is_packed_rev() which in turn
1821
relies on the cached FFD->min_unpacked_rev value not having changed
1822
since the rev file was opened.
1824
Use POOL for allocations. */
1767
1825
static svn_error_t *
1768
1826
open_pack_or_rev_file(apr_file_t **file,
1770
1828
svn_revnum_t rev,
1771
1829
apr_pool_t *pool)
1831
fs_fs_data_t *ffd = fs->fsap_data;
1773
1832
svn_error_t *err;
1774
1833
const char *path;
1776
err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
1779
err = svn_io_file_open(file, path,
1780
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
1782
if (err && APR_STATUS_IS_ENOENT(err->apr_err))
1834
svn_boolean_t retry = FALSE;
1784
svn_error_clear(err);
1785
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1786
_("No such revision %ld"), rev);
1838
err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
1840
/* open the revision file in buffered r/o mode */
1842
err = svn_io_file_open(file, path,
1843
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
1845
if (err && APR_STATUS_IS_ENOENT(err->apr_err)
1846
&& ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1848
/* Could not open the file. This may happen if the
1849
* file once existed but got packed later. */
1850
svn_error_clear(err);
1852
/* if that was our 2nd attempt, leave it at that. */
1854
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1855
_("No such revision %ld"), rev);
1857
/* We failed for the first time. Refresh cache & retry. */
1858
SVN_ERR(update_min_unpacked_rev(fs, pool));
1869
return svn_error_trace(err);
1792
1872
/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file.
1801
1881
svn_stream_t *manifest_stream;
1802
1882
svn_boolean_t is_cached;
1803
1883
svn_revnum_t shard;
1884
apr_int64_t shard_pos;
1804
1885
apr_array_header_t *manifest;
1805
1886
apr_pool_t *iterpool;
1807
1888
shard = rev / ffd->max_files_per_dir;
1808
SVN_ERR(svn_cache__get((void **) &manifest, &is_cached,
1809
ffd->packed_offset_cache, &shard, pool));
1890
/* position of the shard within the manifest */
1891
shard_pos = rev % ffd->max_files_per_dir;
1893
/* fetch exactly that element into *rev_offset, if the manifest is found
1895
SVN_ERR(svn_cache__get_partial((void **) rev_offset, &is_cached,
1896
ffd->packed_offset_cache, &shard,
1897
svn_fs_fs__get_sharded_offset, &shard_pos,
1813
*rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
1815
1901
return SVN_NO_ERROR;
1818
1903
/* Open the manifest file. */
1819
1904
SVN_ERR(svn_stream_open_readonly(&manifest_stream,
1829
1914
svn_stringbuf_t *sb;
1830
1915
svn_boolean_t eof;
1832
1919
svn_pool_clear(iterpool);
1833
1920
SVN_ERR(svn_stream_readline(manifest_stream, &sb, "\n", &eof, iterpool));
1837
APR_ARRAY_PUSH(manifest, apr_off_t) =
1838
apr_atoi64(svn_string_create_from_buf(sb, iterpool)->data);
1839
if (errno == ERANGE)
1840
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1841
"Manifest offset too large");
1924
err = svn_cstring_atoi64(&val, sb->data);
1926
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
1927
_("Manifest offset '%s' too large"),
1929
APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val;
1843
1931
svn_pool_destroy(iterpool);
1962
2051
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1963
2052
_("Malformed text representation offset line in node-rev"));
1965
rep->offset = apr_atoi64(str);
1967
str = apr_strtok(NULL, " ", &last_str);
1969
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1970
_("Malformed text representation offset line in node-rev"));
1972
rep->size = apr_atoi64(str);
1974
str = apr_strtok(NULL, " ", &last_str);
1976
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1977
_("Malformed text representation offset line in node-rev"));
1979
rep->expanded_size = apr_atoi64(str);
2054
SVN_ERR(svn_cstring_atoi64(&val, str));
2055
rep->offset = (apr_off_t)val;
2057
str = apr_strtok(NULL, " ", &last_str);
2059
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2060
_("Malformed text representation offset line in node-rev"));
2062
SVN_ERR(svn_cstring_atoi64(&val, str));
2063
rep->size = (svn_filesize_t)val;
2065
str = apr_strtok(NULL, " ", &last_str);
2067
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2068
_("Malformed text representation offset line in node-rev"));
2070
SVN_ERR(svn_cstring_atoi64(&val, str));
2071
rep->expanded_size = (svn_filesize_t)val;
1981
2073
/* Read in the MD5 hash. */
1982
2074
str = apr_strtok(NULL, " ", &last_str);
2011
2103
return SVN_NO_ERROR;
2014
/* See svn_fs_fs__get_node_revision, which wraps this and adds another
2106
/* Wrap read_rep_offsets_body(), extracting its TXN_ID from our NODEREV_ID,
2107
and adding an error message. */
2108
static svn_error_t *
2109
read_rep_offsets(representation_t **rep_p,
2111
const svn_fs_id_t *noderev_id,
2112
svn_boolean_t mutable_rep_truncated,
2119
txn_id = svn_fs_fs__id_txn_id(noderev_id);
2123
err = read_rep_offsets_body(rep_p, string, txn_id, mutable_rep_truncated,
2127
const svn_string_t *id_unparsed = svn_fs_fs__id_unparse(noderev_id, pool);
2129
where = apr_psprintf(pool,
2130
_("While reading representation offsets "
2131
"for node-revision '%s':"),
2132
noderev_id ? id_unparsed->data : "(null)");
2134
return svn_error_quick_wrap(err, where);
2137
return SVN_NO_ERROR;
2140
static svn_error_t *
2141
err_dangling_id(svn_fs_t *fs, const svn_fs_id_t *id)
2143
svn_string_t *id_str = svn_fs_fs__id_unparse(id, fs->pool);
2144
return svn_error_createf
2145
(SVN_ERR_FS_ID_NOT_FOUND, 0,
2146
_("Reference to non-existent node '%s' in filesystem '%s'"),
2147
id_str->data, fs->path);
2150
/* Return a string that uniquely identifies the noderev with the
2151
* given ID, for use as a cache key.
2154
get_noderev_cache_key(const svn_fs_id_t *id, apr_pool_t *pool)
2156
const svn_string_t *id_unparsed = svn_fs_fs__id_unparse(id, pool);
2157
return id_unparsed->data;
2160
/* Look up the NODEREV_P for ID in FS' node revsion cache. If noderev
2161
* caching has been enabled and the data can be found, IS_CACHED will
2162
* be set to TRUE. The noderev will be allocated from POOL.
2164
* Non-permanent ids (e.g. ids within a TXN) will not be cached.
2166
static svn_error_t *
2167
get_cached_node_revision_body(node_revision_t **noderev_p,
2169
const svn_fs_id_t *id,
2170
svn_boolean_t *is_cached,
2173
fs_fs_data_t *ffd = fs->fsap_data;
2174
if (! ffd->node_revision_cache || svn_fs_fs__id_txn_id(id))
2177
SVN_ERR(svn_cache__get((void **) noderev_p,
2179
ffd->node_revision_cache,
2180
get_noderev_cache_key(id, pool),
2183
return SVN_NO_ERROR;
2186
/* If noderev caching has been enabled, store the NODEREV_P for the given ID
2187
* in FS' node revsion cache. SCRATCH_POOL is used for temporary allcations.
2189
* Non-permanent ids (e.g. ids within a TXN) will not be cached.
2191
static svn_error_t *
2192
set_cached_node_revision_body(node_revision_t *noderev_p,
2194
const svn_fs_id_t *id,
2195
apr_pool_t *scratch_pool)
2197
fs_fs_data_t *ffd = fs->fsap_data;
2199
if (ffd->node_revision_cache && !svn_fs_fs__id_txn_id(id))
2200
return svn_cache__set(ffd->node_revision_cache,
2201
get_noderev_cache_key(id, scratch_pool),
2205
return SVN_NO_ERROR;
2208
/* Get the node-revision for the node ID in FS.
2209
Set *NODEREV_P to the new node-revision structure, allocated in POOL.
2210
See svn_fs_fs__get_node_revision, which wraps this and adds another
2016
2212
static svn_error_t *
2017
2213
get_node_revision_body(node_revision_t **noderev_p,
2042
2244
if (APR_STATUS_IS_ENOENT(err->apr_err))
2044
2246
svn_error_clear(err);
2045
return svn_fs_fs__err_dangling_id(fs, id);
2247
return svn_error_trace(err_dangling_id(fs, id));
2250
return svn_error_trace(err);
2051
return svn_fs_fs__read_noderev(noderev_p,
2052
svn_stream_from_aprfile2(revision_file, FALSE,
2253
SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
2254
svn_stream_from_aprfile2(revision_file, FALSE,
2258
/* The noderev is not in cache, yet. Add it, if caching has been enabled. */
2259
return set_cached_node_revision_body(*noderev_p, fs, id, pool);
2070
2276
/* Read the node-rev id. */
2071
2277
value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
2072
2278
if (value == NULL)
2279
/* ### More information: filename/offset coordinates */
2073
2280
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2074
2281
_("Missing id field in node-rev"));
2076
2283
SVN_ERR(svn_stream_close(stream));
2078
2285
noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool);
2286
noderev_id = value; /* for error messages later */
2080
2288
/* Read the type. */
2081
2289
value = apr_hash_get(headers, HEADER_TYPE, APR_HASH_KEY_STRING);
2083
2291
if ((value == NULL) ||
2084
2292
(strcmp(value, KIND_FILE) != 0 && strcmp(value, KIND_DIR)))
2085
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2086
_("Missing kind field in node-rev"));
2293
/* ### s/kind/type/ */
2294
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2295
_("Missing kind field in node-rev '%s'"),
2088
2298
noderev->kind = (strcmp(value, KIND_FILE) == 0) ? svn_node_file
2089
2299
: svn_node_dir;
2091
2301
/* Read the 'count' field. */
2092
2302
value = apr_hash_get(headers, HEADER_COUNT, APR_HASH_KEY_STRING);
2093
noderev->predecessor_count = (value == NULL) ? 0 : atoi(value);
2304
SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, value));
2306
noderev->predecessor_count = 0;
2095
2308
/* Get the properties location. */
2096
2309
value = apr_hash_get(headers, HEADER_PROPS, APR_HASH_KEY_STRING);
2099
2312
SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
2100
svn_fs_fs__id_txn_id(noderev->id), TRUE, pool));
2313
noderev->id, TRUE, pool));
2103
2316
/* Get the data location. */
2141
2355
str = apr_strtok(value, " ", &last_str);
2142
2356
if (str == NULL)
2143
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2144
_("Malformed copyroot line in node-rev"));
2357
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2358
_("Malformed copyroot line in node-rev '%s'"),
2146
noderev->copyroot_rev = atoi(str);
2361
noderev->copyroot_rev = SVN_STR_TO_REV(str);
2148
2363
if (last_str == NULL)
2149
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2150
_("Malformed copyroot line in node-rev"));
2364
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2365
_("Malformed copyroot line in node-rev '%s'"),
2151
2367
noderev->copyroot_path = apr_pstrdup(pool, last_str);
2165
2381
str = apr_strtok(value, " ", &last_str);
2166
2382
if (str == NULL)
2167
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2168
_("Malformed copyfrom line in node-rev"));
2383
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2384
_("Malformed copyfrom line in node-rev '%s'"),
2170
noderev->copyfrom_rev = atoi(str);
2387
noderev->copyfrom_rev = SVN_STR_TO_REV(str);
2172
2389
if (last_str == NULL)
2173
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2174
_("Malformed copyfrom line in node-rev"));
2390
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2391
_("Malformed copyfrom line in node-rev '%s'"),
2175
2393
noderev->copyfrom_path = apr_pstrdup(pool, last_str);
2396
2619
/* We have hopefully a DELTA vs. a non-empty base revision. */
2397
2620
str = apr_strtok(buffer, " ", &last_str);
2398
if (! str || (strcmp(str, REP_DELTA) != 0)) goto err;
2400
str = apr_strtok(NULL, " ", &last_str);
2401
if (! str) goto err;
2402
rep_args->base_revision = atol(str);
2404
str = apr_strtok(NULL, " ", &last_str);
2405
if (! str) goto err;
2406
rep_args->base_offset = (apr_off_t) apr_atoi64(str);
2408
str = apr_strtok(NULL, " ", &last_str);
2409
if (! str) goto err;
2410
rep_args->base_length = (apr_size_t) apr_atoi64(str);
2621
if (! str || (strcmp(str, REP_DELTA) != 0))
2624
str = apr_strtok(NULL, " ", &last_str);
2627
rep_args->base_revision = SVN_STR_TO_REV(str);
2629
str = apr_strtok(NULL, " ", &last_str);
2632
SVN_ERR(svn_cstring_atoi64(&val, str));
2633
rep_args->base_offset = (apr_off_t)val;
2635
str = apr_strtok(NULL, " ", &last_str);
2638
SVN_ERR(svn_cstring_atoi64(&val, str));
2639
rep_args->base_length = (svn_filesize_t)val;
2412
2641
*rep_args_p = rep_args;
2413
2642
return SVN_NO_ERROR;
2416
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2417
_("Malformed representation header"));
2645
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2646
_("Malformed representation header at %s"),
2647
path_and_offset_of(file, pool));
2420
2650
/* Given a revision file REV_FILE, opened to REV in FS, find the Node-ID
2438
2668
svn_stream_from_aprfile2(rev_file, TRUE, pool),
2671
/* In error messages, the offset is relative to the pack file,
2672
not to the rev file. */
2441
2674
node_id_str = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
2443
2676
if (node_id_str == NULL)
2444
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2445
_("Missing node-id in node-rev"));
2677
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2678
_("Missing node-id in node-rev at r%ld "
2681
apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
2447
2683
id = svn_fs_fs__id_parse(node_id_str, strlen(node_id_str), pool);
2449
2685
if (id == NULL)
2450
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2451
_("Corrupt node-id in node-rev"));
2686
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2687
_("Corrupt node-id '%s' in node-rev at r%ld "
2690
apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
2694
/* ### assert that the txn_id is REV/OFFSET ? */
2455
2696
return SVN_NO_ERROR;
2460
2701
specifies the offset to the root node-id and to the changed path
2461
2702
information. Store the root node offset in *ROOT_OFFSET and the
2462
2703
changed path offset in *CHANGES_OFFSET. If either of these
2463
pointers is NULL, do nothing with it. If PACKED is true, REV_FILE
2464
should be a packed shard file. Allocate temporary variables from POOL. */
2704
pointers is NULL, do nothing with it.
2706
If PACKED is true, REV_FILE should be a packed shard file.
2707
### There is currently no such parameter. This function assumes that
2708
is_packed_rev(FS, REV) will indicate whether REV_FILE is a packed
2709
file. Therefore FS->fsap_data->min_unpacked_rev must not have been
2710
refreshed since REV_FILE was opened if there is a possibility that
2711
revision REV may have become packed since then.
2712
TODO: Take an IS_PACKED parameter instead, in order to remove this
2715
Allocate temporary variables from POOL. */
2465
2716
static svn_error_t *
2466
2717
get_root_changes_offset(apr_off_t *root_offset,
2467
2718
apr_off_t *changes_offset,
2523
2775
if (buf[num_bytes - 1] != '\n')
2525
2777
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2526
_("Revision file lacks trailing newline"));
2778
_("Revision file (r%ld) lacks trailing newline"),
2529
2782
/* Look for the next previous newline. */
2530
2783
for (i = num_bytes - 2; i >= 0; i--)
2532
if (buf[i] == '\n') break;
2537
2791
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2538
_("Final line in revision file longer than 64 "
2792
_("Final line in revision file (r%ld) longer "
2793
"than 64 characters"),
2545
*root_offset = rev_offset + apr_atoi64(&buf[i]);
2547
2800
/* find the next space */
2548
2801
for ( ; i < (num_bytes - 2) ; i++)
2549
if (buf[i] == ' ') break;
2551
2805
if (i == (num_bytes - 2))
2552
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2553
_("Final line in revision file missing space"));
2806
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2807
_("Final line in revision file r%ld missing space"),
2815
SVN_ERR(svn_cstring_atoi64(&val, str));
2816
*root_offset = rev_offset + (apr_off_t)val;
2557
/* note that apr_atoi64() will stop reading as soon as it encounters
2558
the final newline. */
2822
/* find the next newline */
2823
for ( ; i < num_bytes; i++)
2559
2827
if (changes_offset)
2560
*changes_offset = rev_offset + apr_atoi64(&buf[i]);
2832
SVN_ERR(svn_cstring_atoi64(&val, str));
2833
*changes_offset = rev_offset + (apr_off_t)val;
2562
2836
return SVN_NO_ERROR;
2652
2934
return SVN_NO_ERROR;
2656
svn_fs_fs__set_revision_proplist(svn_fs_t *fs,
2658
apr_hash_t *proplist,
2661
const char *final_path = path_revprops(fs, rev, pool);
2662
const char *tmp_path;
2663
const char *perms_reference;
2664
svn_stream_t *stream;
2666
SVN_ERR(ensure_revision_exists(fs, rev, pool));
2668
/* ### do we have a directory sitting around already? we really shouldn't
2669
### have to get the dirname here. */
2670
SVN_ERR(svn_stream_open_unique(&stream, &tmp_path,
2671
svn_path_dirname(final_path, pool),
2672
svn_io_file_del_none, pool, pool));
2673
SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
2674
SVN_ERR(svn_stream_close(stream));
2676
/* We use the rev file of this revision as the perms reference,
2677
because when setting revprops for the first time, the revprop
2678
file won't exist and therefore can't serve as its own reference.
2679
(Whereas the rev file should already exist at this point.) */
2680
SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
2681
SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
2937
/* Set the revision property list of revision REV in filesystem FS to
2938
PROPLIST. Use POOL for temporary allocations. */
2939
static svn_error_t *
2940
set_revision_proplist(svn_fs_t *fs,
2942
apr_hash_t *proplist,
2945
SVN_ERR(ensure_revision_exists(fs, rev, pool));
2949
const char *final_path = path_revprops(fs, rev, pool);
2950
const char *tmp_path;
2951
const char *perms_reference;
2952
svn_stream_t *stream;
2954
/* ### do we have a directory sitting around already? we really shouldn't
2955
### have to get the dirname here. */
2956
SVN_ERR(svn_stream_open_unique(&stream, &tmp_path,
2957
svn_dirent_dirname(final_path, pool),
2958
svn_io_file_del_none, pool, pool));
2959
SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
2960
SVN_ERR(svn_stream_close(stream));
2962
/* We use the rev file of this revision as the perms reference,
2963
because when setting revprops for the first time, the revprop
2964
file won't exist and therefore can't serve as its own reference.
2965
(Whereas the rev file should already exist at this point.) */
2966
SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
2967
SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
2969
return SVN_NO_ERROR;
2972
return SVN_NO_ERROR;
2975
static svn_error_t *
2976
revision_proplist(apr_hash_t **proplist_p,
2981
apr_hash_t *proplist;
2983
SVN_ERR(ensure_revision_exists(fs, rev, pool));
2987
apr_file_t *revprop_file = NULL;
2988
svn_error_t *err = SVN_NO_ERROR;
2990
apr_pool_t *iterpool;
2992
proplist = apr_hash_make(pool);
2993
iterpool = svn_pool_create(pool);
2994
for (i = 0; i < RECOVERABLE_RETRY_COUNT; i++)
2996
svn_pool_clear(iterpool);
2998
/* Clear err here rather than after finding a recoverable error so
2999
* we can return that error on the last iteration of the loop. */
3000
svn_error_clear(err);
3001
err = svn_io_file_open(&revprop_file, path_revprops(fs, rev,
3003
APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
3007
if (APR_STATUS_IS_ENOENT(err->apr_err))
3009
svn_error_clear(err);
3010
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
3011
_("No such revision %ld"), rev);
3014
else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
3015
|| APR_TO_OS_ERROR(err->apr_err) == EIO
3016
|| APR_TO_OS_ERROR(err->apr_err) == ENOENT)
3019
return svn_error_trace(err);
3022
SVN_ERR(svn_hash__clear(proplist, iterpool));
3023
RETRY_RECOVERABLE(err, revprop_file,
3024
svn_hash_read2(proplist,
3025
svn_stream_from_aprfile2(
3026
revprop_file, TRUE, iterpool),
3027
SVN_HASH_TERMINATOR, pool));
3029
IGNORE_RECOVERABLE(err, svn_io_file_close(revprop_file, iterpool));
3035
return svn_error_trace(err);
3036
svn_pool_destroy(iterpool);
3039
*proplist_p = proplist;
2683
3041
return SVN_NO_ERROR;
2689
3047
svn_revnum_t rev,
2690
3048
apr_pool_t *pool)
2692
apr_file_t *revprop_file = NULL;
2693
apr_hash_t *proplist;
2694
svn_error_t *err = SVN_NO_ERROR;
2696
apr_pool_t *iterpool;
2698
SVN_ERR(ensure_revision_exists(fs, rev, pool));
2700
proplist = apr_hash_make(pool);
2701
iterpool = svn_pool_create(pool);
2702
for (i = 0; i < RECOVERABLE_RETRY_COUNT; i++)
2704
svn_pool_clear(iterpool);
2706
/* Clear err here rather than after finding a recoverable error so
2707
* we can return that error on the last iteration of the loop. */
2708
svn_error_clear(err);
2709
err = svn_io_file_open(&revprop_file, path_revprops(fs, rev, iterpool),
2710
APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
2714
if (APR_STATUS_IS_ENOENT(err->apr_err))
2716
svn_error_clear(err);
2717
return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
2718
_("No such revision %ld"), rev);
2721
else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
2722
|| APR_TO_OS_ERROR(err->apr_err) == EIO
2723
|| APR_TO_OS_ERROR(err->apr_err) == ENOENT)
2729
SVN_ERR(svn_hash__clear(proplist));
2730
RETRY_RECOVERABLE(err, revprop_file,
2731
svn_hash_read2(proplist,
2732
svn_stream_from_aprfile2(revprop_file,
2735
SVN_HASH_TERMINATOR, pool));
2737
IGNORE_RECOVERABLE(err, svn_io_file_close(revprop_file, iterpool));
2743
svn_pool_destroy(iterpool);
2745
*proplist_p = proplist;
3050
SVN_ERR(revision_proplist(proplist_p, fs, rev, pool));
2747
3052
return SVN_NO_ERROR;
2963
3280
return SVN_NO_ERROR;
3283
/* Combine the name of the rev file in RS with the given OFFSET to form
3284
* a cache lookup key. Allocations will be made from POOL. */
3286
get_window_key(struct rep_state *rs, apr_off_t offset, apr_pool_t *pool)
3289
const char *last_part;
3290
const char *name_last;
3292
/* the rev file name containing the txdelta window.
3293
* If this fails we are in serious trouble anyways.
3294
* And if nobody else detects the problems, the file content checksum
3295
* comparison _will_ find them.
3297
if (apr_file_name_get(&name, rs->file))
3300
/* Handle packed files as well by scanning backwards until we find the
3301
* revision or pack number. */
3302
name_last = name + strlen(name) - 1;
3303
while (! svn_ctype_isdigit(*name_last))
3306
last_part = name_last;
3307
while (svn_ctype_isdigit(*last_part))
3310
/* We must differentiate between packed files (as of today, the number
3311
* is being followed by a dot) and non-packed files (followed by \0).
3312
* Otherwise, there might be overlaps in the numbering range if the
3313
* repo gets packed after caching the txdeltas of non-packed revs.
3314
* => add the first non-digit char to the packed number. */
3315
if (name_last[1] != '\0')
3318
/* copy one char MORE than the actual number to mark packed files,
3319
* i.e. packed revision file content uses different key space then
3320
* non-packed ones: keys for packed rev file content ends with a dot
3321
* for non-packed rev files they end with a digit. */
3322
name = apr_pstrndup(pool, last_part + 1, name_last - last_part);
3323
return svn_fs_fs__combine_number_and_string(offset, name, pool);
3326
/* Read the WINDOW_P for the rep state RS from the current FSFS session's
3327
* cache. This will be a no-op and IS_CACHED will be set to FALSE if no
3328
* cache has been given. If a cache is available IS_CACHED will inform
3329
* the caller about the success of the lookup. Allocations (of the window
3330
* in particualar) will be made from POOL.
3332
* If the information could be found, put RS and the position within the
3333
* rev file into the same state as if the data had just been read from it.
3335
static svn_error_t *
3336
get_cached_window(svn_txdelta_window_t **window_p,
3337
struct rep_state *rs,
3338
svn_boolean_t *is_cached,
3341
if (! rs->window_cache)
3343
/* txdelta window has not been enabled */
3348
/* ask the cache for the desired txdelta window */
3349
svn_fs_fs__txdelta_cached_window_t *cached_window;
3350
SVN_ERR(svn_cache__get((void **) &cached_window,
3353
get_window_key(rs, rs->off, pool),
3358
/* found it. Pass it back to the caller. */
3359
*window_p = cached_window->window;
3361
/* manipulate the RS as if we just read the data */
3363
rs->off = cached_window->end_offset;
3365
/* manipulate the rev file as if we just read from it */
3366
SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
3370
return SVN_NO_ERROR;
3373
/* Store the WINDOW read at OFFSET for the rep state RS in the current
3374
* FSFS session's cache. This will be a no-op if no cache has been given.
3375
* Temporary allocations will be made from SCRATCH_POOL. */
3376
static svn_error_t *
3377
set_cached_window(svn_txdelta_window_t *window,
3378
struct rep_state *rs,
3380
apr_pool_t *scratch_pool)
3382
if (rs->window_cache)
3384
/* store the window and the first offset _past_ it */
3385
svn_fs_fs__txdelta_cached_window_t cached_window = { window, rs->off };
3387
/* but key it with the start offset because that is the known state
3388
* when we will look it up */
3389
return svn_cache__set(rs->window_cache,
3390
get_window_key(rs, offset, scratch_pool),
3395
return SVN_NO_ERROR;
2966
3398
/* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta
2967
3399
window into *NWIN. */
2968
3400
static svn_error_t *
3227
3681
svn_checksum_t *md5_checksum;
3229
3683
rb->checksum_finalized = TRUE;
3230
svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx, rb->pool);
3684
SVN_ERR(svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx,
3231
3686
if (!svn_checksum_match(md5_checksum, rb->md5_checksum))
3232
return svn_error_createf
3233
(SVN_ERR_FS_CORRUPT, NULL,
3234
_("Checksum mismatch while reading representation:\n"
3237
svn_checksum_to_cstring_display(rb->md5_checksum, rb->pool),
3238
svn_checksum_to_cstring_display(md5_checksum, rb->pool));
3687
return svn_error_create(SVN_ERR_FS_CORRUPT,
3688
svn_checksum_mismatch_err(rb->md5_checksum, md5_checksum,
3690
_("Checksum mismatch while reading representation")),
3519
3940
/* Translate the string dir entries into real entries. */
3520
3941
for (hi = apr_hash_first(pool, str_entries); hi; hi = apr_hash_next(hi))
3524
svn_string_t *str_val;
3943
const char *name = svn__apr_hash_index_key(hi);
3944
svn_string_t *str_val = svn__apr_hash_index_val(hi);
3525
3945
char *str, *last_str;
3526
3946
svn_fs_dirent_t *dirent = apr_pcalloc(pool, sizeof(*dirent));
3528
apr_hash_this(hi, &key, NULL, &val);
3530
3948
str = apr_pstrdup(pool, str_val->data);
3531
dirent->name = apr_pstrdup(pool, key);
3949
dirent->name = apr_pstrdup(pool, name);
3533
3951
str = apr_strtok(str, " ", &last_str);
3534
3952
if (str == NULL)
3535
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3536
_("Directory entry corrupt"));
3953
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
3954
_("Directory entry corrupt in '%s'"),
3538
3957
if (strcmp(str, KIND_FILE) == 0)
3548
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3549
_("Directory entry corrupt"));
3967
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
3968
_("Directory entry corrupt in '%s'"),
3552
3972
str = apr_strtok(NULL, " ", &last_str);
3553
3973
if (str == NULL)
3554
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3555
_("Directory entry corrupt"));
3974
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
3975
_("Directory entry corrupt in '%s'"),
3557
3978
dirent->id = svn_fs_fs__id_parse(str, strlen(str), pool);
3562
3983
return SVN_NO_ERROR;
3566
svn_fs_fs__dir_entries_deserialize(void **out,
3568
apr_size_t data_len,
3986
/* Return the cache object in FS responsible to storing the directory
3987
* the NODEREV. If none exists, return NULL. */
3988
static svn_cache__t *
3989
locate_dir_cache(svn_fs_t *fs,
3990
node_revision_t *noderev)
3571
apr_hash_t *entries = apr_hash_make(pool);
3572
svn_stringbuf_t *buf = svn_stringbuf_ncreate(data, data_len, pool);
3573
svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
3575
SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool));
3576
SVN_ERR(parse_dir_entries(&entries, entries, pool));
3579
return SVN_NO_ERROR;
3992
fs_fs_data_t *ffd = fs->fsap_data;
3993
return svn_fs_fs__id_txn_id(noderev->id)
3994
? ffd->txn_dir_cache
3584
3999
svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
3586
4001
node_revision_t *noderev,
3587
4002
apr_pool_t *pool)
3589
fs_fs_data_t *ffd = fs->fsap_data;
3590
const char *unparsed_id;
4004
const char *unparsed_id = NULL;
3591
4005
apr_hash_t *unparsed_entries, *parsed_entries;
3593
/* Are we looking for an immutable directory? We could try the
3595
if (! svn_fs_fs__id_txn_id(noderev->id))
4007
/* find the cache we may use */
4008
svn_cache__t *cache = locate_dir_cache(fs, noderev);
3597
4011
svn_boolean_t found;
3599
4013
unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
3600
SVN_ERR(svn_cache__get((void **) entries_p, &found, ffd->dir_cache,
4014
SVN_ERR(svn_cache__get((void **) entries_p, &found, cache,
3601
4015
unparsed_id, pool));
3603
4017
return SVN_NO_ERROR;
3606
4020
/* Read in the directory hash. */
3607
4021
unparsed_entries = apr_hash_make(pool);
3608
4022
SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
3609
SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries, pool));
4023
SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries,
4024
unparsed_id, pool));
3611
/* If this is an immutable directory, let's cache the contents. */
3612
if (! svn_fs_fs__id_txn_id(noderev->id))
3613
SVN_ERR(svn_cache__set(ffd->dir_cache, unparsed_id, parsed_entries, pool));
4026
/* Update the cache, if we are to use one. */
4028
SVN_ERR(svn_cache__set(cache, unparsed_id, parsed_entries, pool));
3615
4030
*entries_p = parsed_entries;
3616
4031
return SVN_NO_ERROR;
4035
svn_fs_fs__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
4037
node_revision_t *noderev,
4041
svn_boolean_t found = FALSE;
4043
/* find the cache we may use */
4044
svn_cache__t *cache = locate_dir_cache(fs, noderev);
4047
const char *unparsed_id =
4048
svn_fs_fs__id_unparse(noderev->id, pool)->data;
4051
SVN_ERR(svn_cache__get_partial((void **)dirent,
4055
svn_fs_fs__extract_dir_entry,
4060
/* fetch data from disk if we did not find it in the cache */
4063
apr_hash_t *entries;
4064
svn_fs_dirent_t *entry;
4065
svn_fs_dirent_t *entry_copy = NULL;
4067
/* since we don't need the directory content later on, put it into
4068
some sub-pool that will be reclaimed immedeately after exiting
4069
this function successfully. Opon failure, it will live as long
4072
apr_pool_t *sub_pool = svn_pool_create(pool);
4074
/* read the dir from the file system. It will probably be put it
4075
into the cache for faster lookup in future calls. */
4076
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, sub_pool));
4078
/* find desired entry and return a copy in POOL, if found */
4079
entry = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
4082
entry_copy = apr_palloc(pool, sizeof(*entry_copy));
4083
entry_copy->name = apr_pstrdup(pool, entry->name);
4084
entry_copy->id = svn_fs_fs__id_copy(entry->id, pool);
4085
entry_copy->kind = entry->kind;
4088
*dirent = entry_copy;
4089
apr_pool_destroy(sub_pool);
4092
return SVN_NO_ERROR;
3620
4096
svn_fs_fs__get_proplist(apr_hash_t **proplist_p,
3622
4098
node_revision_t *noderev,
3875
4348
new_change->copyfrom_rev = SVN_INVALID_REVNUM;
3876
4349
new_change->copyfrom_path = NULL;
3878
path = apr_pstrdup(pool, change->path);
3881
4353
if (new_change)
3882
4354
new_change->node_kind = change->node_kind;
3884
/* Add (or update) this path. */
4356
/* Add (or update) this path.
4358
Note: this key might already be present, and it would be nice to
4359
re-use its value, but there is no way to fetch it. The API makes no
4360
guarantees that this (new) key will not be retained. Thus, we (again)
4361
copy the key into the target pool to ensure a proper lifetime. */
4362
path = apr_pstrdup(pool, change->path);
3885
4363
apr_hash_set(changes, path, APR_HASH_KEY_STRING, new_change);
3887
4365
/* Update the copyfrom cache, if any. */
4128
4606
hi = apr_hash_next(hi))
4130
4608
/* KEY is the path. */
4131
const void *hashkey;
4133
apr_hash_this(hi, &hashkey, &klen, NULL);
4609
const char *path = svn__apr_hash_index_key(hi);
4610
apr_ssize_t klen = svn__apr_hash_index_klen(hi);
4135
4612
/* If we come across our own path, ignore it. */
4136
if (strcmp(change->path, hashkey) == 0)
4613
if (strcmp(change->path, path) == 0)
4139
4616
/* If we come across a child of our path, remove it. */
4140
if (svn_path_is_child(change->path, hashkey, iterpool))
4141
apr_hash_set(changed_paths, hashkey, klen, NULL);
4617
if (svn_dirent_is_child(change->path, path, iterpool))
4618
apr_hash_set(changed_paths, path, klen, NULL);
4773
5246
out = svn_stream_from_aprfile2(file, TRUE, pool);
5249
/* if we have a directory cache for this transaction, update it */
5250
if (ffd->txn_dir_cache)
5252
apr_pool_t *subpool = svn_pool_create(pool);
5254
/* build parameters: (name, new entry) pair */
5256
svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
5257
replace_baton_t baton = {name, NULL};
5261
baton.new_entry = apr_pcalloc(subpool, sizeof(*baton.new_entry));
5262
baton.new_entry->name = name;
5263
baton.new_entry->kind = kind;
5264
baton.new_entry->id = id;
5267
/* actually update the cached directory (if cached) */
5268
SVN_ERR(svn_cache__set_partial(ffd->txn_dir_cache, key, svn_fs_fs__replace_dir_entry, &baton, subpool));
5270
svn_pool_destroy(subpool);
4776
5273
/* Append an incremental hash entry for the entry change. */
5112
5613
/* Check and see if we already have a representation somewhere that's
5113
5614
identical to the one we just wrote out. */
5114
5615
if (ffd->rep_sharing_allowed)
5115
/* ### TODO: ignore errors opening the DB (issue #3506) * */
5116
SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
5618
err = svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
5620
/* ### Other error codes that we shouldn't mask out? */
5621
if (err == SVN_NO_ERROR
5622
|| err->apr_err == SVN_ERR_FS_CORRUPT
5623
|| SVN_ERROR_IN_CATEGORY(err->apr_err,
5624
SVN_ERR_MALFUNC_CATEGORY_START))
5626
/* Fatal error; don't mask it.
5628
In particular, this block is triggered when the rep-cache refers
5629
to revisions in the future. We signal that as a corruption situation
5630
since, once those revisions are less than youngest (because of more
5631
commits), the rep-cache would be invalid.
5637
/* Something's wrong with the rep-sharing index. We can continue
5638
without rep-sharing, but warn.
5640
(b->fs->warning)(b->fs->warning_baton, err);
5641
svn_error_clear(err);
5119
5646
old_rep = NULL;
5265
5793
str = apr_strtok(buf, " ", &last_str);
5267
5795
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5268
_("Corrupt current file"));
5796
_("Corrupt 'current' file"));
5270
5798
str = apr_strtok(NULL, " ", &last_str);
5272
5800
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5273
_("Corrupt current file"));
5801
_("Corrupt 'current' file"));
5275
5803
*node_id = apr_pstrdup(pool, str);
5277
5805
str = apr_strtok(NULL, " ", &last_str);
5279
5807
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5280
_("Corrupt current file"));
5808
_("Corrupt 'current' file"));
5282
5810
*copy_id = apr_pstrdup(pool, str);
5339
5867
SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
5341
5869
/* Store the results. */
5342
svn_checksum_final(checksum, whb->checksum_ctx, pool);
5870
SVN_ERR(svn_checksum_final(checksum, whb->checksum_ctx, pool));
5343
5871
*size = whb->size;
5345
5873
return svn_stream_printf(whb->stream, pool, "ENDREP\n");
5876
/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
5877
of (not yet committed) revision REV in FS. Use POOL for temporary
5880
static svn_error_t *
5881
validate_root_noderev(svn_fs_t *fs,
5882
node_revision_t *root_noderev,
5886
svn_revnum_t head_revnum = rev-1;
5887
int head_predecessor_count;
5889
SVN_ERR_ASSERT(rev > 0);
5891
/* Compute HEAD_PREDECESSOR_COUNT. */
5893
svn_fs_root_t *head_revision;
5894
const svn_fs_id_t *head_root_id;
5895
node_revision_t *head_root_noderev;
5897
/* Get /@HEAD's noderev. */
5898
SVN_ERR(svn_fs_fs__revision_root(&head_revision, fs, head_revnum, pool));
5899
SVN_ERR(svn_fs_fs__node_id(&head_root_id, head_revision, "/", pool));
5900
SVN_ERR(svn_fs_fs__get_node_revision(&head_root_noderev, fs, head_root_id,
5903
head_predecessor_count = head_root_noderev->predecessor_count;
5906
/* Check that the root noderev's predecessor count equals REV.
5908
This kind of corruption was seen on svn.apache.org (both on
5909
the root noderev and on other fspaths' noderevs); see
5910
http://mid.gmane.org/20111002202833.GA12373@daniel3.local
5912
Normally (rev == root_noderev->predecessor_count), but here we
5913
use a more roundabout check that should only trigger on new instances
5914
of the corruption, rather then trigger on each and every new commit
5915
to a repository that has triggered the bug somewhere in its root
5918
if (root_noderev->predecessor_count != -1
5919
&& (root_noderev->predecessor_count - head_predecessor_count)
5920
!= (rev - head_revnum))
5922
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5923
_("predecessor count for "
5924
"the root node-revision is wrong: "
5925
"found (%d+%ld != %d), committing r%ld"),
5926
head_predecessor_count,
5927
rev - head_revnum, /* This is equal to 1. */
5928
root_noderev->predecessor_count,
5932
return SVN_NO_ERROR;
5348
5935
/* Copy a node-revision specified by id ID in fileystem FS from a
5349
transaction into the proto-rev-file FILE. Return the offset of
5350
the new node-revision in *OFFSET. If this is a directory, all
5351
children are copied as well. START_NODE_ID and START_COPY_ID are
5936
transaction into the proto-rev-file FILE. Set *NEW_ID_P to a
5937
pointer to the new node-id which will be allocated in POOL.
5938
If this is a directory, copy all children as well.
5940
START_NODE_ID and START_COPY_ID are
5352
5941
the first available node and copy ids for this filesystem, for older
5406
5998
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
6000
svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
5408
6002
svn_pool_clear(subpool);
5409
apr_hash_this(hi, NULL, NULL, &val);
5411
6003
SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
5412
6004
start_node_id, start_copy_id, initial_offset,
5413
reps_to_cache, reps_pool,
6005
reps_to_cache, reps_pool, FALSE,
5415
6007
if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
5416
6008
dirent->id = svn_fs_fs__id_copy(new_id, pool);
5594
6187
return SVN_NO_ERROR;
5599
svn_fs_fs__dup_perms(const char *filename,
5600
const char *perms_reference,
5604
apr_status_t status;
5606
const char *filename_apr, *perms_reference_apr;
5608
SVN_ERR(svn_path_cstring_from_utf8(&filename_apr, filename, pool));
5609
SVN_ERR(svn_path_cstring_from_utf8(&perms_reference_apr, perms_reference,
5612
status = apr_stat(&finfo, perms_reference_apr, APR_FINFO_PROT, pool);
5614
return svn_error_wrap_apr(status, _("Can't stat '%s'"),
5615
svn_path_local_style(perms_reference, pool));
5616
status = apr_file_perms_set(filename_apr, finfo.protection);
5618
return svn_error_wrap_apr(status, _("Can't chmod '%s'"),
5619
svn_path_local_style(filename, pool));
5621
return SVN_NO_ERROR;
5625
/* Atomically update the current file to hold the specifed REV,
6190
/* Atomically update the 'current' file to hold the specifed REV,
5626
6191
NEXT_NODE_ID, and NEXT_COPY_ID. (The two next-ID parameters are
5627
6192
ignored and may be NULL if the FS format does not use them.)
5628
6193
Perform temporary allocations in POOL. */
5841
6404
/* Remove any temporary txn props representing 'flags'. */
5842
6405
SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, cb->txn, pool));
5845
apr_array_header_t *props = apr_array_make(pool, 3, sizeof(svn_prop_t));
5849
if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD,
5850
APR_HASH_KEY_STRING))
5852
prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
5853
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
5856
if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
5857
APR_HASH_KEY_STRING))
5859
prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
5860
APR_ARRAY_PUSH(props, svn_prop_t) = prop;
5863
if (! apr_is_empty_array(props))
5864
SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, props, pool));
6406
txnprop_list = apr_array_make(pool, 3, sizeof(svn_prop_t));
6409
if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, APR_HASH_KEY_STRING))
6411
prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
6412
APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
6415
if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
6416
APR_HASH_KEY_STRING))
6418
prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
6419
APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
6422
if (! apr_is_empty_array(txnprop_list))
6423
SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, txnprop_list, pool));
5867
6425
/* Create the shard for the rev and revprop file, if we're sharding and
5868
6426
this is the first revision of a new shard. We don't care if this
5869
6427
fails because the shard already existed for some reason. */
5870
6428
if (ffd->max_files_per_dir && new_rev % ffd->max_files_per_dir == 0)
5873
const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
5874
err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
5875
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
5877
svn_error_clear(err);
5878
SVN_ERR(svn_fs_fs__dup_perms(new_dir,
5879
svn_path_join(cb->fs->path,
6432
const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
6433
svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
6434
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
6435
return svn_error_trace(err);
6436
svn_error_clear(err);
6437
SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
5884
new_dir = path_revprops_shard(cb->fs, new_rev, pool);
5885
err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
5886
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
5888
svn_error_clear(err);
5889
SVN_ERR(svn_fs_fs__dup_perms(new_dir,
5890
svn_path_join(cb->fs->path,
6443
/* Create the revprops shard. */
6444
SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
6446
const char *new_dir = path_revprops_shard(cb->fs, new_rev, pool);
6447
svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
6448
if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
6449
return svn_error_trace(err);
6450
svn_error_clear(err);
6451
SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
5896
6458
/* Move the finished rev file into place. */
5917
6479
/* Move the revprops file into place. */
6480
SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
5918
6481
revprop_filename = path_txn_props(cb->fs, cb->txn->id, pool);
5919
6482
final_revprop = path_revprops(cb->fs, new_rev, pool);
5920
SVN_ERR(move_into_place(revprop_filename, final_revprop, old_rev_filename,
6483
SVN_ERR(move_into_place(revprop_filename, final_revprop,
6484
old_rev_filename, pool));
5923
6486
/* Update the 'current' file. */
5924
6487
SVN_ERR(write_final_current(cb->fs, cb->txn->id, new_rev, start_node_id,
5970
6526
/* Implements svn_sqlite__transaction_callback_t. */
5971
6527
static svn_error_t *
5972
commit_sqlite_txn_callback(void *baton, svn_sqlite__db_t *db)
6528
commit_sqlite_txn_callback(void *baton, svn_sqlite__db_t *db,
6529
apr_pool_t *scratch_pool)
5974
struct commit_sqlite_txn_baton *cstb = baton;
5975
struct commit_baton *cb = cstb->cb;
6531
struct commit_baton *cb = baton;
5977
6533
/* Write new entries to the rep-sharing database.
5979
6535
* We use an sqlite transcation to speed things up;
5980
6536
* see <http://www.sqlite.org/faq.html#q19>.
5982
SVN_ERR(write_reps_to_cache(cb->fs, cb->reps_to_cache, cstb->pool));
6538
SVN_ERR(write_reps_to_cache(cb->fs, cb->reps_to_cache, scratch_pool));
5984
6540
return SVN_NO_ERROR;
6107
6661
if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
6108
6662
ffd->max_files_per_dir = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
6664
/* Create the revision data directories. */
6110
6665
if (ffd->max_files_per_dir)
6112
SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(fs, 0, pool),
6114
SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(fs, 0, pool),
6666
SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(fs, 0, pool), pool));
6119
SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_REVS_DIR,
6668
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR,
6122
SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path,
6672
/* Create the revprops directory. */
6673
if (ffd->max_files_per_dir)
6674
SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(fs, 0, pool),
6677
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path,
6123
6678
PATH_REVPROPS_DIR,
6127
SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_TXNS_DIR,
6682
/* Create the transaction directory. */
6683
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXNS_DIR,
6687
/* Create the protorevs directory. */
6131
6688
if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
6132
SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_TXN_PROTOS_DIR,
6689
SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXN_PROTOS_DIR,
6693
/* Create the 'current' file. */
6136
6694
SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
6137
6695
(format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT
6138
6696
? "0\n" : "0 1 1\n"),
6312
6865
value = apr_hash_get(headers, HEADER_TEXT, APR_HASH_KEY_STRING);
6314
6867
return SVN_NO_ERROR;
6315
SVN_ERR(read_rep_offsets(&noderev.data_rep, value, NULL, FALSE, pool));
6868
SVN_ERR(read_rep_offsets(&data_rep, value, NULL, FALSE, pool));
6317
6870
/* If the directory's data representation wasn't changed in this revision,
6318
6871
we've already scanned the directory's contents for noderevs, so we don't
6319
6872
need to again. This will occur if a property is changed on a directory
6320
6873
without changing the directory's contents. */
6321
if (noderev.data_rep->revision != rev)
6874
if (data_rep->revision != rev)
6322
6875
return SVN_NO_ERROR;
6324
6877
/* We could use get_dir_contents(), but this is much cheaper. It does
6325
6878
rely on directory entries being stored as PLAIN reps, though. */
6326
offset = noderev.data_rep->offset;
6879
offset = data_rep->offset;
6327
6880
SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
6328
6881
SVN_ERR(read_rep_line(&ra, rev_file, pool));
6329
6882
if (ra->is_delta)
6347
6900
/* Now check each of the entries in our directory to find new node and
6348
6901
copy ids, and recurse into new subdirectories. */
6349
6902
iterpool = svn_pool_create(pool);
6350
for (hi = apr_hash_first(NULL, entries); hi; hi = apr_hash_next(hi))
6903
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
6354
6906
char *str, *last_str;
6355
6907
svn_node_kind_t kind;
6356
6908
svn_fs_id_t *id;
6357
6909
const char *node_id, *copy_id;
6358
6910
apr_off_t child_dir_offset;
6911
const svn_string_t *path = svn__apr_hash_index_val(hi);
6360
6913
svn_pool_clear(iterpool);
6362
apr_hash_this(hi, NULL, NULL, &val);
6363
str_val = apr_pstrdup(iterpool, *((char **)val));
6915
str_val = apr_pstrdup(iterpool, path->data);
6365
6917
str = apr_strtok(str_val, " ", &last_str);
6366
6918
if (str == NULL)
6523
7081
SVN_ERR(svn_io_check_path(path_revprops(fs, max_rev, pool),
6524
7082
&youngest_revprops_kind, pool));
6525
7083
if (youngest_revprops_kind == svn_node_none)
6526
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
6527
_("Revision %ld has a revs file but no "
7087
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
7088
_("Revision %ld has a revs file but no "
6530
7093
else if (youngest_revprops_kind != svn_node_file)
6531
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
6532
_("Revision %ld has a non-file where its "
6533
"revprops file should be"),
7095
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
7096
_("Revision %ld has a non-file where its "
7097
"revprops file should be"),
7101
/* Prune younger-than-(newfound-youngest) revisions from the rep cache. */
7102
if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
7103
SVN_ERR(svn_fs_fs__del_rep_reference(fs, max_rev, pool));
6536
7105
/* Now store the discovered youngest revision, and the next IDs if
6537
relevant, in a new current file. */
7106
relevant, in a new 'current' file. */
6538
7107
return write_current(fs, max_rev, next_node_id, next_copy_id, pool);
6769
7338
names = apr_array_make(pool, 1, sizeof(const char *));
6771
7340
/* Get the transactions directory. */
6772
txn_dir = svn_path_join(fs->path, PATH_TXNS_DIR, pool);
7341
txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
6774
7343
/* Now find a listing of this directory. */
6775
SVN_ERR(svn_io_get_dirents2(&dirents, txn_dir, pool));
7344
SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));
6777
7346
/* Loop through all the entries and return anything that ends with '.txn'. */
6778
7347
for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
6781
const char *name, *id;
6784
apr_hash_this(hi, &key, &klen, NULL);
7349
const char *name = svn__apr_hash_index_key(hi);
7350
apr_ssize_t klen = svn__apr_hash_index_klen(hi);
6787
7353
/* The name must end with ".txn" to be considered a transaction. */
6788
7354
if ((apr_size_t) klen <= ext_len
6857
7424
/* Delete any mutable property representation. */
6858
7425
if (noderev->prop_rep && noderev->prop_rep->txn_id)
6859
SVN_ERR(svn_io_remove_file(path_txn_node_props(fs, id, pool), pool));
7426
SVN_ERR(svn_io_remove_file2(path_txn_node_props(fs, id, pool), FALSE,
6861
7429
/* Delete any mutable data representation. */
6862
7430
if (noderev->data_rep && noderev->data_rep->txn_id
6863
7431
&& noderev->kind == svn_node_dir)
6864
SVN_ERR(svn_io_remove_file(path_txn_node_children(fs, id, pool), pool));
6866
return svn_io_remove_file(path_txn_node_rev(fs, id, pool), pool);
7433
fs_fs_data_t *ffd = fs->fsap_data;
7434
SVN_ERR(svn_io_remove_file2(path_txn_node_children(fs, id, pool), FALSE,
7437
/* remove the corresponding entry from the cache, if such exists */
7438
if (ffd->txn_dir_cache)
7440
const char *key = svn_fs_fs__id_unparse(id, pool)->data;
7441
SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, NULL, pool));
7445
return svn_io_remove_file2(path_txn_node_rev(fs, id, pool), FALSE, pool);
6909
7489
SVN_ERR(svn_fs_fs__revision_proplist(&table, cb->fs, cb->rev, pool));
7491
if (cb->old_value_p)
7493
const svn_string_t *wanted_value = *cb->old_value_p;
7494
const svn_string_t *present_value = apr_hash_get(table, cb->name,
7495
APR_HASH_KEY_STRING);
7496
if ((!wanted_value != !present_value)
7497
|| (wanted_value && present_value
7498
&& !svn_string_compare(wanted_value, present_value)))
7500
/* What we expected isn't what we found. */
7501
return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
7502
_("revprop '%s' has unexpected value in "
6911
7508
apr_hash_set(table, cb->name, APR_HASH_KEY_STRING, cb->value);
6913
return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool);
7510
return set_revision_proplist(cb->fs, cb->rev, table, pool);
6917
7514
svn_fs_fs__change_rev_prop(svn_fs_t *fs,
6918
7515
svn_revnum_t rev,
6919
7516
const char *name,
7517
const svn_string_t *const *old_value_p,
6920
7518
const svn_string_t *value,
6921
7519
apr_pool_t *pool)
7020
7619
/****** Packing FSFS shards *********/
7621
/* Write a file FILENAME in directory FS_PATH, containing a single line
7622
* with the number REVNUM in ASCII decimal. Move the file into place
7623
* atomically, overwriting any existing file.
7625
* Similar to write_current(). */
7626
static svn_error_t *
7627
write_revnum_file(const char *fs_path,
7628
const char *filename,
7629
svn_revnum_t revnum,
7630
apr_pool_t *scratch_pool)
7632
const char *final_path, *tmp_path;
7633
svn_stream_t *tmp_stream;
7635
final_path = svn_dirent_join(fs_path, filename, scratch_pool);
7636
SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path, fs_path,
7637
svn_io_file_del_none,
7638
scratch_pool, scratch_pool));
7639
SVN_ERR(svn_stream_printf(tmp_stream, scratch_pool, "%ld\n", revnum));
7640
SVN_ERR(svn_stream_close(tmp_stream));
7641
SVN_ERR(move_into_place(tmp_path, final_path, final_path, scratch_pool));
7642
return SVN_NO_ERROR;
7021
7645
/* Pack a single shard SHARD in REVS_DIR, using POOL for allocations.
7022
7646
CANCEL_FUNC and CANCEL_BATON are what you think they are.
7034
7658
void *cancel_baton,
7035
7659
apr_pool_t *pool)
7037
const char *tmp_path, *final_path;
7038
7661
const char *pack_file_path, *manifest_file_path, *shard_path;
7039
7662
const char *pack_file_dir;
7040
7663
svn_stream_t *pack_stream, *manifest_stream;
7041
7664
svn_revnum_t start_rev, end_rev, rev;
7042
svn_stream_t *tmp_stream;
7043
7665
apr_off_t next_offset;
7044
7666
apr_pool_t *iterpool;
7046
7668
/* Some useful paths. */
7047
pack_file_dir = svn_path_join(revs_dir,
7669
pack_file_dir = svn_dirent_join(revs_dir,
7048
7670
apr_psprintf(pool, "%" APR_INT64_T_FMT ".pack", shard),
7050
pack_file_path = svn_path_join(pack_file_dir, "pack", pool);
7051
manifest_file_path = svn_path_join(pack_file_dir, "manifest", pool);
7052
shard_path = svn_path_join(revs_dir,
7053
apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
7672
pack_file_path = svn_dirent_join(pack_file_dir, "pack", pool);
7673
manifest_file_path = svn_dirent_join(pack_file_dir, "manifest", pool);
7674
shard_path = svn_dirent_join(revs_dir,
7675
apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
7056
7678
/* Notify caller we're starting to pack this shard. */
7057
7679
if (notify_func)
7084
7706
svn_pool_clear(iterpool);
7086
7708
/* Get the size of the file. */
7087
path = svn_path_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
7709
path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
7089
7711
SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, iterpool));
7091
7713
/* Update the manifest. */
7092
svn_stream_printf(manifest_stream, iterpool, "%" APR_OFF_T_FMT "\n",
7714
SVN_ERR(svn_stream_printf(manifest_stream, iterpool, "%" APR_OFF_T_FMT
7715
"\n", next_offset));
7094
7716
next_offset += finfo.size;
7096
7718
/* Copy all the bits from the rev file to the end of the pack file. */
7103
7725
SVN_ERR(svn_stream_close(manifest_stream));
7104
7726
SVN_ERR(svn_stream_close(pack_stream));
7105
SVN_ERR(svn_fs_fs__dup_perms(pack_file_dir, shard_path, pool));
7727
SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool));
7106
7728
SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool));
7107
7729
SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, pool));
7109
7731
/* Update the min-unpacked-rev file to reflect our newly packed shard.
7110
* (ffd->min_unpacked_rev will be updated by open_pack_or_rev_file().)
7112
final_path = svn_path_join(fs_path, PATH_MIN_UNPACKED_REV, iterpool);
7113
SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path, fs_path,
7114
svn_io_file_del_none, iterpool, iterpool));
7115
SVN_ERR(svn_stream_printf(tmp_stream, iterpool, "%ld\n",
7116
(svn_revnum_t) ((shard + 1) * max_files_per_dir)));
7117
SVN_ERR(svn_stream_close(tmp_stream));
7118
SVN_ERR(move_into_place(tmp_path, final_path, final_path, iterpool));
7732
* (This doesn't update ffd->min_unpacked_rev. That will be updated by
7733
* update_min_unpacked_rev() when necessary.) */
7734
SVN_ERR(write_revnum_file(fs_path, PATH_MIN_UNPACKED_REV,
7735
(svn_revnum_t)((shard + 1) * max_files_per_dir),
7119
7737
svn_pool_destroy(iterpool);
7121
7739
/* Finally, remove the existing shard directory. */
7139
7757
void *cancel_baton;
7761
/* The work-horse for svn_fs_fs__pack, called with the FS write lock.
7762
This implements the svn_fs_fs__with_write_lock() 'body' callback
7763
type. BATON is a 'struct pack_baton *'.
7765
WARNING: if you add a call to this function, please note:
7766
The code currently assumes that any piece of code running with
7767
the write-lock set can rely on the ffd->min_unpacked_rev and
7768
ffd->min_unpacked_revprop caches to be up-to-date (and, by
7769
extension, on not having to use a retry when calling
7770
svn_fs_fs__path_rev_absolute() and friends). If you add a call
7771
to this function, consider whether you have to call
7772
update_min_unpacked_rev() and update_min_unpacked_revprop()
7774
See this thread: http://thread.gmane.org/1291206765.3782.3309.camel@edith
7142
7776
static svn_error_t *
7143
7777
pack_body(void *baton,
7144
7778
apr_pool_t *pool)
7146
7780
struct pack_baton *pb = baton;
7147
7781
int format, max_files_per_dir;
7148
int completed_shards;
7782
apr_int64_t completed_shards;
7150
7784
svn_revnum_t youngest;
7151
7785
apr_pool_t *iterpool;
7152
7786
const char *data_path;
7153
7787
svn_revnum_t min_unpacked_rev;
7155
SVN_ERR(read_format(&format, &max_files_per_dir,
7156
svn_path_join(pb->fs->path, PATH_FORMAT, pool),
7789
SVN_ERR(read_format(&format, &max_files_per_dir, path_format(pb->fs, pool),
7791
SVN_ERR(check_format(format));
7159
7793
/* If the repository isn't a new enough format, we don't support packing.
7160
7794
Return a friendly error to that effect. */
7161
7795
if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
7162
return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
7163
_("FS format too old to pack, please upgrade."));
7796
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7797
_("FSFS format (%d) too old to pack; please upgrade the filesystem."),
7165
7800
/* If we aren't using sharding, we can't do any packing, so quit. */
7166
7801
if (!max_files_per_dir)
7167
7802
return SVN_NO_ERROR;
7169
7804
SVN_ERR(read_min_unpacked_rev(&min_unpacked_rev,
7170
svn_path_join(pb->fs->path,
7171
PATH_MIN_UNPACKED_REV, pool),
7805
path_min_unpacked_rev(pb->fs, pool),
7174
7808
SVN_ERR(get_youngest(&youngest, pb->fs->path, pool));