~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_x/fs_x.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* fs_x.c --- filesystem operations specific to fs_x
 
2
 *
 
3
 * ====================================================================
 
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
 
11
 *
 
12
 *      http://www.apache.org/licenses/LICENSE-2.0
 
13
 *
 
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
 
19
 *    under the License.
 
20
 * ====================================================================
 
21
 */
 
22
 
 
23
#include "fs_x.h"
 
24
 
 
25
#include <apr_uuid.h>
 
26
 
 
27
#include "svn_hash.h"
 
28
#include "svn_props.h"
 
29
#include "svn_time.h"
 
30
#include "svn_dirent_uri.h"
 
31
#include "svn_sorts.h"
 
32
#include "svn_version.h"
 
33
 
 
34
#include "cached_data.h"
 
35
#include "id.h"
 
36
#include "rep-cache.h"
 
37
#include "revprops.h"
 
38
#include "transaction.h"
 
39
#include "tree.h"
 
40
#include "util.h"
 
41
#include "index.h"
 
42
 
 
43
#include "private/svn_fs_util.h"
 
44
#include "private/svn_string_private.h"
 
45
#include "private/svn_subr_private.h"
 
46
#include "../libsvn_fs/fs-loader.h"
 
47
 
 
48
#include "svn_private_config.h"
 
49
 
 
50
/* The default maximum number of files per directory to store in the
 
51
   rev and revprops directory.  The number below is somewhat arbitrary,
 
52
   and can be overridden by defining the macro while compiling; the
 
53
   figure of 1000 is reasonable for VFAT filesystems, which are by far
 
54
   the worst performers in this area. */
 
55
#ifndef SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR
 
56
#define SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR 1000
 
57
#endif
 
58
 
 
59
/* Begin deltification after a node history exceeded this this limit.
 
60
   Useful values are 4 to 64 with 16 being a good compromise between
 
61
   computational overhead and repository size savings.
 
62
   Should be a power of 2.
 
63
   Values < 2 will result in standard skip-delta behavior. */
 
64
#define SVN_FS_X_MAX_LINEAR_DELTIFICATION 16
 
65
 
 
66
/* Finding a deltification base takes operations proportional to the
 
67
   number of changes being skipped. To prevent exploding runtime
 
68
   during commits, limit the deltification range to this value.
 
69
   Should be a power of 2 minus one.
 
70
   Values < 1 disable deltification. */
 
71
#define SVN_FS_X_MAX_DELTIFICATION_WALK 1023
 
72
 
 
73
 
 
74
 
 
75
 
 
76
/* Check that BUF, a nul-terminated buffer of text from format file PATH,
 
77
   contains only digits at OFFSET and beyond, raising an error if not.
 
78
 
 
79
   Uses SCRATCH_POOL for temporary allocation. */
 
80
static svn_error_t *
 
81
check_format_file_buffer_numeric(const char *buf,
 
82
                                 apr_off_t offset,
 
83
                                 const char *path,
 
84
                                 apr_pool_t *scratch_pool)
 
85
{
 
86
  return svn_fs_x__check_file_buffer_numeric(buf, offset, path, "Format",
 
87
                                             scratch_pool);
 
88
}
 
89
 
 
90
/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
 
91
   number is not the same as a format number supported by this
 
92
   Subversion. */
 
93
static svn_error_t *
 
94
check_format(int format)
 
95
{
 
96
  /* Put blacklisted versions here. */
 
97
 
 
98
  /* We support all formats from 1-current simultaneously */
 
99
  if (1 <= format && format <= SVN_FS_X__FORMAT_NUMBER)
 
100
    return SVN_NO_ERROR;
 
101
 
 
102
  return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
 
103
     _("Expected FS format between '1' and '%d'; found format '%d'"),
 
104
     SVN_FS_X__FORMAT_NUMBER, format);
 
105
}
 
106
 
 
107
/* Read the format file at PATH and set *PFORMAT to the format version found
 
108
 * and *MAX_FILES_PER_DIR to the shard size.  Use SCRATCH_POOL for temporary
 
109
 * allocations. */
 
110
static svn_error_t *
 
111
read_format(int *pformat,
 
112
            int *max_files_per_dir,
 
113
            const char *path,
 
114
            apr_pool_t *scratch_pool)
 
115
{
 
116
  svn_stream_t *stream;
 
117
  svn_stringbuf_t *content;
 
118
  svn_stringbuf_t *buf;
 
119
  svn_boolean_t eos = FALSE;
 
120
 
 
121
  SVN_ERR(svn_stringbuf_from_file2(&content, path, scratch_pool));
 
122
  stream = svn_stream_from_stringbuf(content, scratch_pool);
 
123
  SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool));
 
124
  if (buf->len == 0 && eos)
 
125
    {
 
126
      /* Return a more useful error message. */
 
127
      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
 
128
                               _("Can't read first line of format file '%s'"),
 
129
                               svn_dirent_local_style(path, scratch_pool));
 
130
    }
 
131
 
 
132
  /* Check that the first line contains only digits. */
 
133
  SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, scratch_pool));
 
134
  SVN_ERR(svn_cstring_atoi(pformat, buf->data));
 
135
 
 
136
  /* Check that we support this format at all */
 
137
  SVN_ERR(check_format(*pformat));
 
138
 
 
139
  /* Read any options. */
 
140
  SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool));
 
141
  if (!eos && strncmp(buf->data, "layout sharded ", 15) == 0)
 
142
    {
 
143
      /* Check that the argument is numeric. */
 
144
      SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path,
 
145
                                               scratch_pool));
 
146
      SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15));
 
147
    }
 
148
  else
 
149
    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
 
150
                  _("'%s' contains invalid filesystem format option '%s'"),
 
151
                  svn_dirent_local_style(path, scratch_pool), buf->data);
 
152
 
 
153
  return SVN_NO_ERROR;
 
154
}
 
155
 
 
156
/* Write the format number and maximum number of files per directory
 
157
   to a new format file in PATH, possibly expecting to overwrite a
 
158
   previously existing file.
 
159
 
 
160
   Use SCRATCH_POOL for temporary allocation. */
 
161
svn_error_t *
 
162
svn_fs_x__write_format(svn_fs_t *fs,
 
163
                       svn_boolean_t overwrite,
 
164
                       apr_pool_t *scratch_pool)
 
165
{
 
166
  svn_stringbuf_t *sb;
 
167
  const char *path = svn_fs_x__path_format(fs, scratch_pool);
 
168
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
169
 
 
170
  SVN_ERR_ASSERT(1 <= ffd->format && ffd->format <= SVN_FS_X__FORMAT_NUMBER);
 
171
 
 
172
  sb = svn_stringbuf_createf(scratch_pool, "%d\n", ffd->format);
 
173
  svn_stringbuf_appendcstr(sb, apr_psprintf(scratch_pool,
 
174
                                            "layout sharded %d\n",
 
175
                                            ffd->max_files_per_dir));
 
176
 
 
177
  /* svn_io_write_version_file() does a load of magic to allow it to
 
178
     replace version files that already exist.  We only need to do
 
179
     that when we're allowed to overwrite an existing file. */
 
180
  if (! overwrite)
 
181
    {
 
182
      /* Create the file */
 
183
      SVN_ERR(svn_io_file_create(path, sb->data, scratch_pool));
 
184
    }
 
185
  else
 
186
    {
 
187
      SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len,
 
188
                                  NULL /* copy_perms_path */, scratch_pool));
 
189
    }
 
190
 
 
191
  /* And set the perms to make it read only */
 
192
  return svn_io_set_file_read_only(path, FALSE, scratch_pool);
 
193
}
 
194
 
 
195
/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within
 
196
 * the range of what the current system may address in RAM and it is a
 
197
 * power of 2.  Assume that the element size within the block is ITEM_SIZE.
 
198
 * Use SCRATCH_POOL for temporary allocations.
 
199
 */
 
200
static svn_error_t *
 
201
verify_block_size(apr_int64_t block_size,
 
202
                  apr_size_t item_size,
 
203
                  const char *name,
 
204
                  apr_pool_t *scratch_pool)
 
205
{
 
206
  /* Limit range. */
 
207
  if (block_size <= 0)
 
208
    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
 
209
                             _("%s is too small for fsfs.conf setting '%s'."),
 
210
                             apr_psprintf(scratch_pool,
 
211
                                          "%" APR_INT64_T_FMT,
 
212
                                          block_size),
 
213
                             name);
 
214
 
 
215
  if (block_size > SVN_MAX_OBJECT_SIZE / item_size)
 
216
    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
 
217
                             _("%s is too large for fsfs.conf setting '%s'."),
 
218
                             apr_psprintf(scratch_pool,
 
219
                                          "%" APR_INT64_T_FMT,
 
220
                                          block_size),
 
221
                             name);
 
222
 
 
223
  /* Ensure it is a power of two.
 
224
   * For positive X,  X & (X-1) will reset the lowest bit set.
 
225
   * If the result is 0, at most one bit has been set. */
 
226
  if (0 != (block_size & (block_size - 1)))
 
227
    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
 
228
                             _("%s is invalid for fsfs.conf setting '%s' "
 
229
                               "because it is not a power of 2."),
 
230
                             apr_psprintf(scratch_pool,
 
231
                                          "%" APR_INT64_T_FMT,
 
232
                                          block_size),
 
233
                             name);
 
234
 
 
235
  return SVN_NO_ERROR;
 
236
}
 
237
 
 
238
/* Read the configuration information of the file system at FS_PATH
 
239
 * and set the respective values in FFD.  Use pools as usual.
 
240
 */
 
241
static svn_error_t *
 
242
read_config(svn_fs_x__data_t *ffd,
 
243
            const char *fs_path,
 
244
            apr_pool_t *result_pool,
 
245
            apr_pool_t *scratch_pool)
 
246
{
 
247
  svn_config_t *config;
 
248
  apr_int64_t compression_level;
 
249
 
 
250
  SVN_ERR(svn_config_read3(&config,
 
251
                           svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool),
 
252
                           FALSE, FALSE, FALSE, scratch_pool));
 
253
 
 
254
  /* Initialize ffd->rep_sharing_allowed. */
 
255
  SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed,
 
256
                              CONFIG_SECTION_REP_SHARING,
 
257
                              CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
 
258
 
 
259
  /* Initialize deltification settings in ffd. */
 
260
  SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk,
 
261
                               CONFIG_SECTION_DELTIFICATION,
 
262
                               CONFIG_OPTION_MAX_DELTIFICATION_WALK,
 
263
                               SVN_FS_X_MAX_DELTIFICATION_WALK));
 
264
  SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification,
 
265
                               CONFIG_SECTION_DELTIFICATION,
 
266
                               CONFIG_OPTION_MAX_LINEAR_DELTIFICATION,
 
267
                               SVN_FS_X_MAX_LINEAR_DELTIFICATION));
 
268
  SVN_ERR(svn_config_get_int64(config, &compression_level,
 
269
                               CONFIG_SECTION_DELTIFICATION,
 
270
                               CONFIG_OPTION_COMPRESSION_LEVEL,
 
271
                               SVN_DELTA_COMPRESSION_LEVEL_DEFAULT));
 
272
  ffd->delta_compression_level
 
273
    = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level),
 
274
                SVN_DELTA_COMPRESSION_LEVEL_MAX);
 
275
 
 
276
  /* Initialize revprop packing settings in ffd. */
 
277
  SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops,
 
278
                              CONFIG_SECTION_PACKED_REVPROPS,
 
279
                              CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
 
280
                              TRUE));
 
281
  SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size,
 
282
                               CONFIG_SECTION_PACKED_REVPROPS,
 
283
                               CONFIG_OPTION_REVPROP_PACK_SIZE,
 
284
                               ffd->compress_packed_revprops
 
285
                                   ? 0x100
 
286
                                   : 0x40));
 
287
 
 
288
  ffd->revprop_pack_size *= 1024;
 
289
 
 
290
  /* I/O settings in ffd. */
 
291
  SVN_ERR(svn_config_get_int64(config, &ffd->block_size,
 
292
                               CONFIG_SECTION_IO,
 
293
                               CONFIG_OPTION_BLOCK_SIZE,
 
294
                               64));
 
295
  SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size,
 
296
                               CONFIG_SECTION_IO,
 
297
                               CONFIG_OPTION_L2P_PAGE_SIZE,
 
298
                               0x2000));
 
299
  SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size,
 
300
                               CONFIG_SECTION_IO,
 
301
                               CONFIG_OPTION_P2L_PAGE_SIZE,
 
302
                               0x400));
 
303
 
 
304
  /* Don't accept unreasonable or illegal values.
 
305
   * Block size and P2L page size are in kbytes;
 
306
   * L2P blocks are arrays of apr_off_t. */
 
307
  SVN_ERR(verify_block_size(ffd->block_size, 0x400,
 
308
                            CONFIG_OPTION_BLOCK_SIZE, scratch_pool));
 
309
  SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400,
 
310
                            CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool));
 
311
  SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t),
 
312
                            CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool));
 
313
 
 
314
  /* convert kBytes to bytes */
 
315
  ffd->block_size *= 0x400;
 
316
  ffd->p2l_page_size *= 0x400;
 
317
  /* L2P pages are in entries - not in (k)Bytes */
 
318
 
 
319
  /* Debug options. */
 
320
  SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit,
 
321
                              CONFIG_SECTION_DEBUG,
 
322
                              CONFIG_OPTION_PACK_AFTER_COMMIT,
 
323
                              FALSE));
 
324
 
 
325
  /* memcached configuration */
 
326
  SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config,
 
327
                                               result_pool, scratch_pool));
 
328
 
 
329
  SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop,
 
330
                              CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
 
331
                              FALSE));
 
332
 
 
333
  return SVN_NO_ERROR;
 
334
}
 
335
 
 
336
/* Write FS' initial configuration file.
 
337
 * Use SCRATCH_POOL for temporary allocations. */
 
338
static svn_error_t *
 
339
write_config(svn_fs_t *fs,
 
340
             apr_pool_t *scratch_pool)
 
341
{
 
342
#define NL APR_EOL_STR
 
343
  static const char * const fsx_conf_contents =
 
344
"### This file controls the configuration of the FSX filesystem."            NL
 
345
""                                                                           NL
 
346
"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]"                          NL
 
347
"### These options name memcached servers used to cache internal FSX"        NL
 
348
"### data.  See http://www.danga.com/memcached/ for more information on"     NL
 
349
"### memcached.  To use memcached with FSX, run one or more memcached"       NL
 
350
"### servers, and specify each of them as an option like so:"                NL
 
351
"# first-server = 127.0.0.1:11211"                                           NL
 
352
"# remote-memcached = mymemcached.corp.example.com:11212"                    NL
 
353
"### The option name is ignored; the value is of the form HOST:PORT."        NL
 
354
"### memcached servers can be shared between multiple repositories;"         NL
 
355
"### however, if you do this, you *must* ensure that repositories have"      NL
 
356
"### distinct UUIDs and paths, or else cached data from one repository"      NL
 
357
"### might be used by another accidentally.  Note also that memcached has"   NL
 
358
"### no authentication for reads or writes, so you must ensure that your"    NL
 
359
"### memcached servers are only accessible by trusted users."                NL
 
360
""                                                                           NL
 
361
"[" CONFIG_SECTION_CACHES "]"                                                NL
 
362
"### When a cache-related error occurs, normally Subversion ignores it"      NL
 
363
"### and continues, logging an error if the server is appropriately"         NL
 
364
"### configured (and ignoring it with file:// access).  To make"             NL
 
365
"### Subversion never ignore cache errors, uncomment this line."             NL
 
366
"# " CONFIG_OPTION_FAIL_STOP " = true"                                       NL
 
367
""                                                                           NL
 
368
"[" CONFIG_SECTION_REP_SHARING "]"                                           NL
 
369
"### To conserve space, the filesystem can optionally avoid storing"         NL
 
370
"### duplicate representations.  This comes at a slight cost in"             NL
 
371
"### performance, as maintaining a database of shared representations can"   NL
 
372
"### increase commit times.  The space savings are dependent upon the size"  NL
 
373
"### of the repository, the number of objects it contains and the amount of" NL
 
374
"### duplication between them, usually a function of the branching and"      NL
 
375
"### merging process."                                                       NL
 
376
"###"                                                                        NL
 
377
"### The following parameter enables rep-sharing in the repository.  It can" NL
 
378
"### be switched on and off at will, but for best space-saving results"      NL
 
379
"### should be enabled consistently over the life of the repository."        NL
 
380
"### 'svnadmin verify' will check the rep-cache regardless of this setting." NL
 
381
"### rep-sharing is enabled by default."                                     NL
 
382
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true"                              NL
 
383
""                                                                           NL
 
384
"[" CONFIG_SECTION_DELTIFICATION "]"                                         NL
 
385
"### To conserve space, the filesystem stores data as differences against"   NL
 
386
"### existing representations.  This comes at a slight cost in performance," NL
 
387
"### as calculating differences can increase commit times.  Reading data"    NL
 
388
"### will also create higher CPU load and the data will be fragmented."      NL
 
389
"### Since deltification tends to save significant amounts of disk space,"   NL
 
390
"### the overall I/O load can actually be lower."                            NL
 
391
"###"                                                                        NL
 
392
"### The options in this section allow for tuning the deltification"         NL
 
393
"### strategy.  Their effects on data size and server performance may vary"  NL
 
394
"### from one repository to another."                                        NL
 
395
"###"                                                                        NL
 
396
"### During commit, the server may need to walk the whole change history of" NL
 
397
"### of a given node to find a suitable deltification base.  This linear"    NL
 
398
"### process can impact commit times, svnadmin load and similar operations." NL
 
399
"### This setting limits the depth of the deltification history.  If the"    NL
 
400
"### threshold has been reached, the node will be stored as fulltext and a"  NL
 
401
"### new deltification history begins."                                      NL
 
402
"### Note, this is unrelated to svn log."                                    NL
 
403
"### Very large values rarely provide significant additional savings but"    NL
 
404
"### can impact performance greatly - in particular if directory"            NL
 
405
"### deltification has been activated.  Very small values may be useful in"  NL
 
406
"### repositories that are dominated by large, changing binaries."           NL
 
407
"### Should be a power of two minus 1.  A value of 0 will effectively"       NL
 
408
"### disable deltification."                                                 NL
 
409
"### For 1.9, the default value is 1023."                                    NL
 
410
"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023"                          NL
 
411
"###"                                                                        NL
 
412
"### The skip-delta scheme used by FSX tends to repeatably store redundant"  NL
 
413
"### delta information where a simple delta against the latest version is"   NL
 
414
"### often smaller.  By default, 1.9+ will therefore use skip deltas only"   NL
 
415
"### after the linear chain of deltas has grown beyond the threshold"        NL
 
416
"### specified by this setting."                                             NL
 
417
"### Values up to 64 can result in some reduction in repository size for"    NL
 
418
"### the cost of quickly increasing I/O and CPU costs. Similarly, smaller"   NL
 
419
"### numbers can reduce those costs at the cost of more disk space.  For"    NL
 
420
"### rarely read repositories or those containing larger binaries, this may" NL
 
421
"### present a better trade-off."                                            NL
 
422
"### Should be a power of two.  A value of 1 or smaller will cause the"      NL
 
423
"### exclusive use of skip-deltas."                                          NL
 
424
"### For 1.8, the default value is 16."                                      NL
 
425
"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16"                          NL
 
426
"###"                                                                        NL
 
427
"### After deltification, we compress the data through zlib to minimize on-" NL
 
428
"### disk size.  That can be an expensive and ineffective process.  This"    NL
 
429
"### setting controls the usage of zlib in future revisions."                NL
 
430
"### Revisions with highly compressible data in them may shrink in size"     NL
 
431
"### if the setting is increased but may take much longer to commit.  The"   NL
 
432
"### time taken to uncompress that data again is widely independent of the"  NL
 
433
"### compression level."                                                     NL
 
434
"### Compression will be ineffective if the incoming content is already"     NL
 
435
"### highly compressed.  In that case, disabling the compression entirely"   NL
 
436
"### will speed up commits as well as reading the data.  Repositories with"  NL
 
437
"### many small compressible files (source code) but also a high percentage" NL
 
438
"### of large incompressible ones (artwork) may benefit from compression"    NL
 
439
"### levels lowered to e.g. 1."                                              NL
 
440
"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL
 
441
"### and 0 disabling it altogether."                                         NL
 
442
"### The default value is 5."                                                NL
 
443
"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5"                                  NL
 
444
""                                                                           NL
 
445
"[" CONFIG_SECTION_PACKED_REVPROPS "]"                                       NL
 
446
"### This parameter controls the size (in kBytes) of packed revprop files."  NL
 
447
"### Revprops of consecutive revisions will be concatenated into a single"   NL
 
448
"### file up to but not exceeding the threshold given here.  However, each"  NL
 
449
"### pack file may be much smaller and revprops of a single revision may be" NL
 
450
"### much larger than the limit set here.  The threshold will be applied"    NL
 
451
"### before optional compression takes place."                               NL
 
452
"### Large values will reduce disk space usage at the expense of increased"  NL
 
453
"### latency and CPU usage reading and changing individual revprops.  They"  NL
 
454
"### become an advantage when revprop caching has been enabled because a"    NL
 
455
"### lot of data can be read in one go.  Values smaller than 4 kByte will"   NL
 
456
"### not improve latency any further and quickly render revprop packing"     NL
 
457
"### ineffective."                                                           NL
 
458
"### revprop-pack-size is 64 kBytes by default for non-compressed revprop"   NL
 
459
"### pack files and 256 kBytes when compression has been enabled."           NL
 
460
"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64"                                 NL
 
461
"###"                                                                        NL
 
462
"### To save disk space, packed revprop files may be compressed.  Standard"  NL
 
463
"### revprops tend to allow for very effective compression.  Reading and"    NL
 
464
"### even more so writing, become significantly more CPU intensive.  With"   NL
 
465
"### revprop caching enabled, the overhead can be offset by reduced I/O"     NL
 
466
"### unless you often modify revprops after packing."                        NL
 
467
"### Compressing packed revprops is enabled by default."                     NL
 
468
"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = true"                        NL
 
469
""                                                                           NL
 
470
"[" CONFIG_SECTION_IO "]"                                                    NL
 
471
"### Parameters in this section control the data access granularity in"      NL
 
472
"### format 7 repositories and later.  The defaults should translate into"   NL
 
473
"### decent performance over a wide range of setups."                        NL
 
474
"###"                                                                        NL
 
475
"### When a specific piece of information needs to be read from disk,  a"    NL
 
476
"### data block is being read at once and its contents are being cached."    NL
 
477
"### If the repository is being stored on a RAID, the block size should be"  NL
 
478
"### either 50% or 100% of RAID block size / granularity.  Also, your file"  NL
 
479
"### system blocks/clusters should be properly aligned and sized.  In that"  NL
 
480
"### setup, each access will hit only one disk (minimizes I/O load) but"     NL
 
481
"### uses all the data provided by the disk in a single access."             NL
 
482
"### For SSD-based storage systems, slightly lower values around 16 kB"      NL
 
483
"### may improve latency while still maximizing throughput."                 NL
 
484
"### Can be changed at any time but must be a power of 2."                   NL
 
485
"### block-size is given in kBytes and with a default of 64 kBytes."         NL
 
486
"# " CONFIG_OPTION_BLOCK_SIZE " = 64"                                        NL
 
487
"###"                                                                        NL
 
488
"### The log-to-phys index maps data item numbers to offsets within the"     NL
 
489
"### rev or pack file.  This index is organized in pages of a fixed maximum" NL
 
490
"### capacity.  To access an item, the page table and the respective page"   NL
 
491
"### must be read."                                                          NL
 
492
"### This parameter only affects revisions with thousands of changed paths." NL
 
493
"### If you have several extremely large revisions (~1 mio changes), think"  NL
 
494
"### about increasing this setting.  Reducing the value will rarely result"  NL
 
495
"### in a net speedup."                                                      NL
 
496
"### This is an expert setting.  Must be a power of 2."                      NL
 
497
"### l2p-page-size is 8192 entries by default."                              NL
 
498
"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192"                                   NL
 
499
"###"                                                                        NL
 
500
"### The phys-to-log index maps positions within the rev or pack file to"    NL
 
501
"### to data items,  i.e. describes what piece of information is being"      NL
 
502
"### stored at any particular offset.  The index describes the rev file"     NL
 
503
"### in chunks (pages) and keeps a global list of all those pages.  Large"   NL
 
504
"### pages mean a shorter page table but a larger per-page description of"   NL
 
505
"### data items in it.  The latency sweet spot depends on the change size"   NL
 
506
"### distribution but covers a relatively wide range."                       NL
 
507
"### If the repository contains very large files,  i.e. individual changes"  NL
 
508
"### of tens of MB each,  increasing the page size will shorten the index"   NL
 
509
"### file at the expense of a slightly increased latency in sections with"   NL
 
510
"### smaller changes."                                                       NL
 
511
"### For source code repositories, this should be about 16x the block-size." NL
 
512
"### Must be a power of 2."                                                  NL
 
513
"### p2l-page-size is given in kBytes and with a default of 1024 kBytes."    NL
 
514
"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024"                                   NL
 
515
;
 
516
#undef NL
 
517
  return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG,
 
518
                                            scratch_pool),
 
519
                            fsx_conf_contents, scratch_pool);
 
520
}
 
521
 
 
522
/* Read FS's UUID file and store the data in the FS struct. */
 
523
static svn_error_t *
 
524
read_uuid(svn_fs_t *fs,
 
525
          apr_pool_t *scratch_pool)
 
526
{
 
527
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
528
  apr_file_t *uuid_file;
 
529
  char buf[APR_UUID_FORMATTED_LENGTH + 2];
 
530
  apr_size_t limit;
 
531
 
 
532
  /* Read the repository uuid. */
 
533
  SVN_ERR(svn_io_file_open(&uuid_file, svn_fs_x__path_uuid(fs, scratch_pool),
 
534
                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
 
535
                           scratch_pool));
 
536
 
 
537
  limit = sizeof(buf);
 
538
  SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool));
 
539
  fs->uuid = apr_pstrdup(fs->pool, buf);
 
540
 
 
541
  /* Read the instance ID. */
 
542
  limit = sizeof(buf);
 
543
  SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit,
 
544
                                  scratch_pool));
 
545
  ffd->instance_id = apr_pstrdup(fs->pool, buf);
 
546
 
 
547
  SVN_ERR(svn_io_file_close(uuid_file, scratch_pool));
 
548
 
 
549
  return SVN_NO_ERROR;
 
550
}
 
551
 
 
552
svn_error_t *
 
553
svn_fs_x__read_format_file(svn_fs_t *fs,
 
554
                           apr_pool_t *scratch_pool)
 
555
{
 
556
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
557
  int format, max_files_per_dir;
 
558
 
 
559
  /* Read info from format file. */
 
560
  SVN_ERR(read_format(&format, &max_files_per_dir,
 
561
                      svn_fs_x__path_format(fs, scratch_pool), scratch_pool));
 
562
 
 
563
  /* Now that we've got *all* info, store / update values in FFD. */
 
564
  ffd->format = format;
 
565
  ffd->max_files_per_dir = max_files_per_dir;
 
566
 
 
567
  return SVN_NO_ERROR;
 
568
}
 
569
 
 
570
svn_error_t *
 
571
svn_fs_x__open(svn_fs_t *fs,
 
572
               const char *path,
 
573
               apr_pool_t *scratch_pool)
 
574
{
 
575
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
576
  fs->path = apr_pstrdup(fs->pool, path);
 
577
 
 
578
  /* Read the FS format file. */
 
579
  SVN_ERR(svn_fs_x__read_format_file(fs, scratch_pool));
 
580
 
 
581
  /* Read in and cache the repository uuid. */
 
582
  SVN_ERR(read_uuid(fs, scratch_pool));
 
583
 
 
584
  /* Read the min unpacked revision. */
 
585
  SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool));
 
586
 
 
587
  /* Read the configuration file. */
 
588
  SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool));
 
589
 
 
590
  return svn_error_trace(svn_fs_x__read_current(&ffd->youngest_rev_cache,
 
591
                                                fs, scratch_pool));
 
592
}
 
593
 
 
594
/* Baton type bridging svn_fs_x__upgrade and upgrade_body carrying
 
595
 * parameters over between them. */
 
596
typedef struct upgrade_baton_t
 
597
{
 
598
  svn_fs_t *fs;
 
599
  svn_fs_upgrade_notify_t notify_func;
 
600
  void *notify_baton;
 
601
  svn_cancel_func_t cancel_func;
 
602
  void *cancel_baton;
 
603
} upgrade_baton_t;
 
604
 
 
605
/* Upgrade the FS given in upgrade_baton_t *)BATON to the latest format
 
606
 * version.  Apply options an invoke callback from that BATON.
 
607
 * Temporary allocations are to be made from SCRATCH_POOL.
 
608
 *
 
609
 * At the moment, this is a simple placeholder as we don't support upgrades
 
610
 * from experimental FSX versions.
 
611
 */
 
612
static svn_error_t *
 
613
upgrade_body(void *baton,
 
614
             apr_pool_t *scratch_pool)
 
615
{
 
616
  upgrade_baton_t *upgrade_baton = baton;
 
617
  svn_fs_t *fs = upgrade_baton->fs;
 
618
  int format, max_files_per_dir;
 
619
  const char *format_path = svn_fs_x__path_format(fs, scratch_pool);
 
620
 
 
621
  /* Read the FS format number and max-files-per-dir setting. */
 
622
  SVN_ERR(read_format(&format, &max_files_per_dir, format_path,
 
623
                      scratch_pool));
 
624
 
 
625
  /* If we're already up-to-date, there's nothing else to be done here. */
 
626
  if (format == SVN_FS_X__FORMAT_NUMBER)
 
627
    return SVN_NO_ERROR;
 
628
 
 
629
  /* Done */
 
630
  return SVN_NO_ERROR;
 
631
}
 
632
 
 
633
 
 
634
svn_error_t *
 
635
svn_fs_x__upgrade(svn_fs_t *fs,
 
636
                  svn_fs_upgrade_notify_t notify_func,
 
637
                  void *notify_baton,
 
638
                  svn_cancel_func_t cancel_func,
 
639
                  void *cancel_baton,
 
640
                  apr_pool_t *scratch_pool)
 
641
{
 
642
  upgrade_baton_t baton;
 
643
  baton.fs = fs;
 
644
  baton.notify_func = notify_func;
 
645
  baton.notify_baton = notify_baton;
 
646
  baton.cancel_func = cancel_func;
 
647
  baton.cancel_baton = cancel_baton;
 
648
 
 
649
  return svn_fs_x__with_all_locks(fs, upgrade_body, (void *)&baton,
 
650
                                  scratch_pool);
 
651
}
 
652
 
 
653
 
 
654
svn_error_t *
 
655
svn_fs_x__youngest_rev(svn_revnum_t *youngest_p,
 
656
                       svn_fs_t *fs,
 
657
                       apr_pool_t *scratch_pool)
 
658
{
 
659
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
660
  SVN_ERR(svn_fs_x__read_current(youngest_p, fs, scratch_pool));
 
661
  ffd->youngest_rev_cache = *youngest_p;
 
662
 
 
663
  return SVN_NO_ERROR;
 
664
}
 
665
 
 
666
svn_error_t *
 
667
svn_fs_x__ensure_revision_exists(svn_revnum_t rev,
 
668
                                 svn_fs_t *fs,
 
669
                                 apr_pool_t *scratch_pool)
 
670
{
 
671
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
672
 
 
673
  if (! SVN_IS_VALID_REVNUM(rev))
 
674
    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
 
675
                             _("Invalid revision number '%ld'"), rev);
 
676
 
 
677
 
 
678
  /* Did the revision exist the last time we checked the current
 
679
     file? */
 
680
  if (rev <= ffd->youngest_rev_cache)
 
681
    return SVN_NO_ERROR;
 
682
 
 
683
  SVN_ERR(svn_fs_x__read_current(&ffd->youngest_rev_cache, fs, scratch_pool));
 
684
 
 
685
  /* Check again. */
 
686
  if (rev <= ffd->youngest_rev_cache)
 
687
    return SVN_NO_ERROR;
 
688
 
 
689
  return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
 
690
                           _("No such revision %ld"), rev);
 
691
}
 
692
 
 
693
 
 
694
svn_error_t *
 
695
svn_fs_x__file_length(svn_filesize_t *length,
 
696
                      svn_fs_x__noderev_t *noderev)
 
697
{
 
698
  if (noderev->data_rep)
 
699
    *length = noderev->data_rep->expanded_size;
 
700
  else
 
701
    *length = 0;
 
702
 
 
703
  return SVN_NO_ERROR;
 
704
}
 
705
 
 
706
svn_boolean_t
 
707
svn_fs_x__file_text_rep_equal(svn_fs_x__representation_t *a,
 
708
                              svn_fs_x__representation_t *b)
 
709
{
 
710
  svn_boolean_t a_empty = a == NULL || a->expanded_size == 0;
 
711
  svn_boolean_t b_empty = b == NULL || b->expanded_size == 0;
 
712
 
 
713
  /* This makes sure that neither rep will be NULL later on */
 
714
  if (a_empty && b_empty)
 
715
    return TRUE;
 
716
 
 
717
  if (a_empty != b_empty)
 
718
    return FALSE;
 
719
 
 
720
  /* Same physical representation?  Note that these IDs are always up-to-date
 
721
     instead of e.g. being set lazily. */
 
722
  if (svn_fs_x__id_eq(&a->id, &b->id))
 
723
    return TRUE;
 
724
 
 
725
  /* Contents are equal if the checksums match.  These are also always known.
 
726
   */
 
727
  return memcmp(a->md5_digest, b->md5_digest, sizeof(a->md5_digest)) == 0
 
728
      && memcmp(a->sha1_digest, b->sha1_digest, sizeof(a->sha1_digest)) == 0;
 
729
}
 
730
 
 
731
svn_error_t *
 
732
svn_fs_x__prop_rep_equal(svn_boolean_t *equal,
 
733
                         svn_fs_t *fs,
 
734
                         svn_fs_x__noderev_t *a,
 
735
                         svn_fs_x__noderev_t *b,
 
736
                         svn_boolean_t strict,
 
737
                         apr_pool_t *scratch_pool)
 
738
{
 
739
  svn_fs_x__representation_t *rep_a = a->prop_rep;
 
740
  svn_fs_x__representation_t *rep_b = b->prop_rep;
 
741
  apr_hash_t *proplist_a;
 
742
  apr_hash_t *proplist_b;
 
743
 
 
744
  /* Mainly for a==b==NULL */
 
745
  if (rep_a == rep_b)
 
746
    {
 
747
      *equal = TRUE;
 
748
      return SVN_NO_ERROR;
 
749
    }
 
750
 
 
751
  /* Committed property lists can be compared quickly */
 
752
  if (   rep_a && rep_b
 
753
      && svn_fs_x__is_revision(rep_a->id.change_set)
 
754
      && svn_fs_x__is_revision(rep_b->id.change_set))
 
755
    {
 
756
      /* MD5 must be given. Having the same checksum is good enough for
 
757
         accepting the prop lists as equal. */
 
758
      *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest,
 
759
                      sizeof(rep_a->md5_digest)) == 0;
 
760
      return SVN_NO_ERROR;
 
761
    }
 
762
 
 
763
  /* Same path in same txn? */
 
764
  if (svn_fs_x__id_eq(&a->noderev_id, &b->noderev_id))
 
765
    {
 
766
      *equal = TRUE;
 
767
      return SVN_NO_ERROR;
 
768
    }
 
769
 
 
770
  /* Skip the expensive bits unless we are in strict mode.
 
771
     Simply assume that there is a different. */
 
772
  if (!strict)
 
773
    {
 
774
      *equal = FALSE;
 
775
      return SVN_NO_ERROR;
 
776
    }
 
777
 
 
778
  /* At least one of the reps has been modified in a txn.
 
779
     Fetch and compare them. */
 
780
  SVN_ERR(svn_fs_x__get_proplist(&proplist_a, fs, a, scratch_pool,
 
781
                                 scratch_pool));
 
782
  SVN_ERR(svn_fs_x__get_proplist(&proplist_b, fs, b, scratch_pool,
 
783
                                 scratch_pool));
 
784
 
 
785
  *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool);
 
786
  return SVN_NO_ERROR;
 
787
}
 
788
 
 
789
 
 
790
svn_error_t *
 
791
svn_fs_x__file_checksum(svn_checksum_t **checksum,
 
792
                        svn_fs_x__noderev_t *noderev,
 
793
                        svn_checksum_kind_t kind,
 
794
                        apr_pool_t *result_pool)
 
795
{
 
796
  *checksum = NULL;
 
797
 
 
798
  if (noderev->data_rep)
 
799
    {
 
800
      svn_checksum_t temp;
 
801
      temp.kind = kind;
 
802
 
 
803
      switch(kind)
 
804
        {
 
805
          case svn_checksum_md5:
 
806
            temp.digest = noderev->data_rep->md5_digest;
 
807
            break;
 
808
 
 
809
          case svn_checksum_sha1:
 
810
            if (! noderev->data_rep->has_sha1)
 
811
              return SVN_NO_ERROR;
 
812
 
 
813
            temp.digest = noderev->data_rep->sha1_digest;
 
814
            break;
 
815
 
 
816
          default:
 
817
            return SVN_NO_ERROR;
 
818
        }
 
819
 
 
820
      *checksum = svn_checksum_dup(&temp, result_pool);
 
821
    }
 
822
 
 
823
  return SVN_NO_ERROR;
 
824
}
 
825
 
 
826
svn_fs_x__representation_t *
 
827
svn_fs_x__rep_copy(svn_fs_x__representation_t *rep,
 
828
                   apr_pool_t *result_pool)
 
829
{
 
830
  if (rep == NULL)
 
831
    return NULL;
 
832
 
 
833
  return apr_pmemdup(result_pool, rep, sizeof(*rep));
 
834
}
 
835
 
 
836
 
 
837
/* Write out the zeroth revision for filesystem FS.
 
838
   Perform temporary allocations in SCRATCH_POOL. */
 
839
static svn_error_t *
 
840
write_revision_zero(svn_fs_t *fs,
 
841
                    apr_pool_t *scratch_pool)
 
842
{
 
843
  /* Use an explicit sub-pool to have full control over temp file lifetimes.
 
844
   * Since we have it, use it for everything else as well. */
 
845
  apr_pool_t *subpool = svn_pool_create(scratch_pool);
 
846
  const char *path_revision_zero = svn_fs_x__path_rev(fs, 0, subpool);
 
847
  apr_hash_t *proplist;
 
848
  svn_string_t date;
 
849
 
 
850
  apr_array_header_t *index_entries;
 
851
  svn_fs_x__p2l_entry_t *entry;
 
852
  svn_fs_x__revision_file_t *rev_file;
 
853
  const char *l2p_proto_index, *p2l_proto_index;
 
854
 
 
855
  /* Construct a skeleton r0 with no indexes. */
 
856
  svn_string_t *noderev_str = svn_string_create("id: 2+0\n"
 
857
                                                "node: 0+0\n"
 
858
                                                "copy: 0+0\n"
 
859
                                                "type: dir\n"
 
860
                                                "count: 0\n"
 
861
                                                "cpath: /\n"
 
862
                                                "\n",
 
863
                                                subpool);
 
864
  svn_string_t *changes_str = svn_string_create("\n",
 
865
                                                subpool);
 
866
  svn_string_t *r0 = svn_string_createf(subpool, "%s%s",
 
867
                                        noderev_str->data,
 
868
                                        changes_str->data);
 
869
 
 
870
  /* Write skeleton r0 to disk. */
 
871
  SVN_ERR(svn_io_file_create(path_revision_zero, r0->data, subpool));
 
872
 
 
873
  /* Construct the index P2L contents: describe the 2 items we have.
 
874
     Be sure to create them in on-disk order. */
 
875
  index_entries = apr_array_make(subpool, 2, sizeof(entry));
 
876
 
 
877
  entry = apr_pcalloc(subpool, sizeof(*entry));
 
878
  entry->offset = 0;
 
879
  entry->size = (apr_off_t)noderev_str->len;
 
880
  entry->type = SVN_FS_X__ITEM_TYPE_NODEREV;
 
881
  entry->item_count = 1;
 
882
  entry->items = apr_pcalloc(subpool, sizeof(*entry->items));
 
883
  entry->items[0].change_set = 0;
 
884
  entry->items[0].number = SVN_FS_X__ITEM_INDEX_ROOT_NODE;
 
885
  APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry;
 
886
 
 
887
  entry = apr_pcalloc(subpool, sizeof(*entry));
 
888
  entry->offset = (apr_off_t)noderev_str->len;
 
889
  entry->size = (apr_off_t)changes_str->len;
 
890
  entry->type = SVN_FS_X__ITEM_TYPE_CHANGES;
 
891
  entry->item_count = 1;
 
892
  entry->items = apr_pcalloc(subpool, sizeof(*entry->items));
 
893
  entry->items[0].change_set = 0;
 
894
  entry->items[0].number = SVN_FS_X__ITEM_INDEX_CHANGES;
 
895
  APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry;
 
896
 
 
897
  /* Now re-open r0, create proto-index files from our entries and
 
898
      rewrite the index section of r0. */
 
899
  SVN_ERR(svn_fs_x__open_pack_or_rev_file_writable(&rev_file, fs, 0,
 
900
                                                   subpool, subpool));
 
901
  SVN_ERR(svn_fs_x__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
 
902
                                               rev_file, index_entries,
 
903
                                               subpool, subpool));
 
904
  SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&l2p_proto_index, fs,
 
905
                                               index_entries,
 
906
                                               subpool, subpool));
 
907
  SVN_ERR(svn_fs_x__add_index_data(fs, rev_file->file, l2p_proto_index,
 
908
                                   p2l_proto_index, 0, subpool));
 
909
  SVN_ERR(svn_fs_x__close_revision_file(rev_file));
 
910
 
 
911
  SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool));
 
912
 
 
913
  /* Set a date on revision 0. */
 
914
  date.data = svn_time_to_cstring(apr_time_now(), fs->pool);
 
915
  date.len = strlen(date.data);
 
916
  proplist = apr_hash_make(fs->pool);
 
917
  svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
 
918
  return svn_fs_x__set_revision_proplist(fs, 0, proplist, fs->pool);
 
919
}
 
920
 
 
921
svn_error_t *
 
922
svn_fs_x__create_file_tree(svn_fs_t *fs,
 
923
                           const char *path,
 
924
                           int format,
 
925
                           int shard_size,
 
926
                           apr_pool_t *scratch_pool)
 
927
{
 
928
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
929
 
 
930
  fs->path = apr_pstrdup(fs->pool, path);
 
931
  ffd->format = format;
 
932
 
 
933
  /* Use an appropriate sharding mode if supported by the format. */
 
934
  ffd->max_files_per_dir = shard_size;
 
935
 
 
936
  /* Create the revision data directories. */
 
937
  SVN_ERR(svn_io_make_dir_recursively(
 
938
                              svn_fs_x__path_rev_shard(fs, 0, scratch_pool),
 
939
                              scratch_pool));
 
940
 
 
941
  /* Create the revprops directory. */
 
942
  SVN_ERR(svn_io_make_dir_recursively(
 
943
                         svn_fs_x__path_revprops_shard(fs, 0, scratch_pool),
 
944
                         scratch_pool));
 
945
 
 
946
  /* Create the transaction directory. */
 
947
  SVN_ERR(svn_io_make_dir_recursively(
 
948
                                  svn_fs_x__path_txns_dir(fs, scratch_pool),
 
949
                                  scratch_pool));
 
950
 
 
951
  /* Create the protorevs directory. */
 
952
  SVN_ERR(svn_io_make_dir_recursively(
 
953
                            svn_fs_x__path_txn_proto_revs(fs, scratch_pool),
 
954
                            scratch_pool));
 
955
 
 
956
  /* Create the 'current' file. */
 
957
  SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_current(fs, scratch_pool),
 
958
                                   scratch_pool));
 
959
  SVN_ERR(svn_fs_x__write_current(fs, 0, scratch_pool));
 
960
 
 
961
  /* Create the 'uuid' file. */
 
962
  SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_lock(fs, scratch_pool),
 
963
                                   scratch_pool));
 
964
  SVN_ERR(svn_fs_x__set_uuid(fs, NULL, NULL, scratch_pool));
 
965
 
 
966
  /* Create the fsfs.conf file. */
 
967
  SVN_ERR(write_config(fs, scratch_pool));
 
968
  SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool));
 
969
 
 
970
  /* Add revision 0. */
 
971
  SVN_ERR(write_revision_zero(fs, scratch_pool));
 
972
 
 
973
  /* Create the min unpacked rev file. */
 
974
  SVN_ERR(svn_io_file_create(
 
975
                          svn_fs_x__path_min_unpacked_rev(fs, scratch_pool),
 
976
                          "0\n", scratch_pool));
 
977
 
 
978
  /* Create the txn-current file if the repository supports
 
979
     the transaction sequence file. */
 
980
  SVN_ERR(svn_io_file_create(svn_fs_x__path_txn_current(fs, scratch_pool),
 
981
                             "0\n", scratch_pool));
 
982
  SVN_ERR(svn_io_file_create_empty(
 
983
                          svn_fs_x__path_txn_current_lock(fs, scratch_pool),
 
984
                          scratch_pool));
 
985
 
 
986
  /* Initialize the revprop caching info. */
 
987
  SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, scratch_pool));
 
988
 
 
989
  ffd->youngest_rev_cache = 0;
 
990
  return SVN_NO_ERROR;
 
991
}
 
992
 
 
993
svn_error_t *
 
994
svn_fs_x__create(svn_fs_t *fs,
 
995
                 const char *path,
 
996
                 apr_pool_t *scratch_pool)
 
997
{
 
998
  int format = SVN_FS_X__FORMAT_NUMBER;
 
999
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
1000
 
 
1001
  fs->path = apr_pstrdup(fs->pool, path);
 
1002
  /* See if compatibility with older versions was explicitly requested. */
 
1003
  if (fs->config)
 
1004
    {
 
1005
      svn_version_t *compatible_version;
 
1006
      SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
 
1007
                                         scratch_pool));
 
1008
 
 
1009
      /* select format number */
 
1010
      switch(compatible_version->minor)
 
1011
        {
 
1012
          case 0:
 
1013
          case 1:
 
1014
          case 2:
 
1015
          case 3:
 
1016
          case 4:
 
1017
          case 5:
 
1018
          case 6:
 
1019
          case 7:
 
1020
          case 8: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
 
1021
                  _("FSX is not compatible with Subversion prior to 1.9"));
 
1022
 
 
1023
          default:format = SVN_FS_X__FORMAT_NUMBER;
 
1024
        }
 
1025
    }
 
1026
 
 
1027
  /* Actual FS creation. */
 
1028
  SVN_ERR(svn_fs_x__create_file_tree(fs, path, format,
 
1029
                                     SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR,
 
1030
                                     scratch_pool));
 
1031
 
 
1032
  /* This filesystem is ready.  Stamp it with a format number. */
 
1033
  SVN_ERR(svn_fs_x__write_format(fs, FALSE, scratch_pool));
 
1034
 
 
1035
  ffd->youngest_rev_cache = 0;
 
1036
  return SVN_NO_ERROR;
 
1037
}
 
1038
 
 
1039
svn_error_t *
 
1040
svn_fs_x__set_uuid(svn_fs_t *fs,
 
1041
                   const char *uuid,
 
1042
                   const char *instance_id,
 
1043
                   apr_pool_t *scratch_pool)
 
1044
{
 
1045
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
1046
  const char *uuid_path = svn_fs_x__path_uuid(fs, scratch_pool);
 
1047
  svn_stringbuf_t *contents = svn_stringbuf_create_empty(scratch_pool);
 
1048
 
 
1049
  if (! uuid)
 
1050
    uuid = svn_uuid_generate(scratch_pool);
 
1051
 
 
1052
  if (! instance_id)
 
1053
    instance_id = svn_uuid_generate(scratch_pool);
 
1054
 
 
1055
  svn_stringbuf_appendcstr(contents, uuid);
 
1056
  svn_stringbuf_appendcstr(contents, "\n");
 
1057
  svn_stringbuf_appendcstr(contents, instance_id);
 
1058
  svn_stringbuf_appendcstr(contents, "\n");
 
1059
 
 
1060
  /* We use the permissions of the 'current' file, because the 'uuid'
 
1061
     file does not exist during repository creation. */
 
1062
  SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len,
 
1063
                              /* perms */
 
1064
                              svn_fs_x__path_current(fs, scratch_pool),
 
1065
                              scratch_pool));
 
1066
 
 
1067
  fs->uuid = apr_pstrdup(fs->pool, uuid);
 
1068
  ffd->instance_id = apr_pstrdup(fs->pool, instance_id);
 
1069
 
 
1070
  return SVN_NO_ERROR;
 
1071
}
 
1072
 
 
1073
/** Node origin lazy cache. */
 
1074
 
 
1075
/* If directory PATH does not exist, create it and give it the same
 
1076
   permissions as FS_path.*/
 
1077
svn_error_t *
 
1078
svn_fs_x__ensure_dir_exists(const char *path,
 
1079
                            const char *fs_path,
 
1080
                            apr_pool_t *scratch_pool)
 
1081
{
 
1082
  svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, scratch_pool);
 
1083
  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
 
1084
    {
 
1085
      svn_error_clear(err);
 
1086
      return SVN_NO_ERROR;
 
1087
    }
 
1088
  SVN_ERR(err);
 
1089
 
 
1090
  /* We successfully created a new directory.  Dup the permissions
 
1091
     from FS->path. */
 
1092
  return svn_io_copy_perms(fs_path, path, scratch_pool);
 
1093
}
 
1094
 
 
1095
 
 
1096
/*** Revisions ***/
 
1097
 
 
1098
svn_error_t *
 
1099
svn_fs_x__revision_prop(svn_string_t **value_p,
 
1100
                        svn_fs_t *fs,
 
1101
                        svn_revnum_t rev,
 
1102
                        const char *propname,
 
1103
                        apr_pool_t *result_pool,
 
1104
                        apr_pool_t *scratch_pool)
 
1105
{
 
1106
  apr_hash_t *table;
 
1107
 
 
1108
  SVN_ERR(svn_fs__check_fs(fs, TRUE));
 
1109
  SVN_ERR(svn_fs_x__get_revision_proplist(&table, fs, rev, FALSE,
 
1110
                                          scratch_pool, scratch_pool));
 
1111
 
 
1112
  *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool);
 
1113
 
 
1114
  return SVN_NO_ERROR;
 
1115
}
 
1116
 
 
1117
 
 
1118
/* Baton used for change_rev_prop_body below. */
 
1119
typedef struct change_rev_prop_baton_t {
 
1120
  svn_fs_t *fs;
 
1121
  svn_revnum_t rev;
 
1122
  const char *name;
 
1123
  const svn_string_t *const *old_value_p;
 
1124
  const svn_string_t *value;
 
1125
} change_rev_prop_baton_t;
 
1126
 
 
1127
/* The work-horse for svn_fs_x__change_rev_prop, called with the FS
 
1128
   write lock.  This implements the svn_fs_x__with_write_lock()
 
1129
   'body' callback type.  BATON is a 'change_rev_prop_baton_t *'. */
 
1130
static svn_error_t *
 
1131
change_rev_prop_body(void *baton,
 
1132
                     apr_pool_t *scratch_pool)
 
1133
{
 
1134
  change_rev_prop_baton_t *cb = baton;
 
1135
  apr_hash_t *table;
 
1136
 
 
1137
  /* Read current revprop values from disk (never from cache).
 
1138
     Even if somehow the cache got out of sync, we want to make sure that
 
1139
     we read, update and write up-to-date data. */
 
1140
  SVN_ERR(svn_fs_x__get_revision_proplist(&table, cb->fs, cb->rev, TRUE,
 
1141
                                          scratch_pool, scratch_pool));
 
1142
 
 
1143
  if (cb->old_value_p)
 
1144
    {
 
1145
      const svn_string_t *wanted_value = *cb->old_value_p;
 
1146
      const svn_string_t *present_value = svn_hash_gets(table, cb->name);
 
1147
      if ((!wanted_value != !present_value)
 
1148
          || (wanted_value && present_value
 
1149
              && !svn_string_compare(wanted_value, present_value)))
 
1150
        {
 
1151
          /* What we expected isn't what we found. */
 
1152
          return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
 
1153
                                   _("revprop '%s' has unexpected value in "
 
1154
                                     "filesystem"),
 
1155
                                   cb->name);
 
1156
        }
 
1157
      /* Fall through. */
 
1158
    }
 
1159
  svn_hash_sets(table, cb->name, cb->value);
 
1160
 
 
1161
  return svn_fs_x__set_revision_proplist(cb->fs, cb->rev, table,
 
1162
                                         scratch_pool);
 
1163
}
 
1164
 
 
1165
svn_error_t *
 
1166
svn_fs_x__change_rev_prop(svn_fs_t *fs,
 
1167
                          svn_revnum_t rev,
 
1168
                          const char *name,
 
1169
                          const svn_string_t *const *old_value_p,
 
1170
                          const svn_string_t *value,
 
1171
                          apr_pool_t *scratch_pool)
 
1172
{
 
1173
  change_rev_prop_baton_t cb;
 
1174
 
 
1175
  SVN_ERR(svn_fs__check_fs(fs, TRUE));
 
1176
 
 
1177
  cb.fs = fs;
 
1178
  cb.rev = rev;
 
1179
  cb.name = name;
 
1180
  cb.old_value_p = old_value_p;
 
1181
  cb.value = value;
 
1182
 
 
1183
  return svn_fs_x__with_write_lock(fs, change_rev_prop_body, &cb,
 
1184
                                   scratch_pool);
 
1185
}
 
1186
 
 
1187
 
 
1188
svn_error_t *
 
1189
svn_fs_x__info_format(int *fs_format,
 
1190
                      svn_version_t **supports_version,
 
1191
                      svn_fs_t *fs,
 
1192
                      apr_pool_t *result_pool,
 
1193
                      apr_pool_t *scratch_pool)
 
1194
{
 
1195
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
1196
  *fs_format = ffd->format;
 
1197
  *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
 
1198
 
 
1199
  (*supports_version)->major = SVN_VER_MAJOR;
 
1200
  (*supports_version)->minor = 9;
 
1201
  (*supports_version)->patch = 0;
 
1202
  (*supports_version)->tag = "";
 
1203
 
 
1204
  switch (ffd->format)
 
1205
    {
 
1206
    case 1:
 
1207
      break;
 
1208
#ifdef SVN_DEBUG
 
1209
# if SVN_FS_X__FORMAT_NUMBER != 1
 
1210
#  error "Need to add a 'case' statement here"
 
1211
# endif
 
1212
#endif
 
1213
    }
 
1214
 
 
1215
  return SVN_NO_ERROR;
 
1216
}
 
1217
 
 
1218
svn_error_t *
 
1219
svn_fs_x__info_config_files(apr_array_header_t **files,
 
1220
                            svn_fs_t *fs,
 
1221
                            apr_pool_t *result_pool,
 
1222
                            apr_pool_t *scratch_pool)
 
1223
{
 
1224
  *files = apr_array_make(result_pool, 1, sizeof(const char *));
 
1225
  APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG,
 
1226
                                                         result_pool);
 
1227
  return SVN_NO_ERROR;
 
1228
}