1289177Speter/* fs_x.c --- filesystem operations specific to fs_x
2289177Speter *
3289177Speter * ====================================================================
4289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
5289177Speter *    or more contributor license agreements.  See the NOTICE file
6289177Speter *    distributed with this work for additional information
7289177Speter *    regarding copyright ownership.  The ASF licenses this file
8289177Speter *    to you under the Apache License, Version 2.0 (the
9289177Speter *    "License"); you may not use this file except in compliance
10289177Speter *    with the License.  You may obtain a copy of the License at
11289177Speter *
12289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
13289177Speter *
14289177Speter *    Unless required by applicable law or agreed to in writing,
15289177Speter *    software distributed under the License is distributed on an
16289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17289177Speter *    KIND, either express or implied.  See the License for the
18289177Speter *    specific language governing permissions and limitations
19289177Speter *    under the License.
20289177Speter * ====================================================================
21289177Speter */
22289177Speter
23289177Speter#include "fs_x.h"
24289177Speter
25289177Speter#include <apr_uuid.h>
26289177Speter
27289177Speter#include "svn_hash.h"
28289177Speter#include "svn_props.h"
29289177Speter#include "svn_time.h"
30289177Speter#include "svn_dirent_uri.h"
31289177Speter#include "svn_sorts.h"
32289177Speter#include "svn_version.h"
33289177Speter
34289177Speter#include "cached_data.h"
35289177Speter#include "id.h"
36289177Speter#include "rep-cache.h"
37289177Speter#include "revprops.h"
38289177Speter#include "transaction.h"
39289177Speter#include "tree.h"
40289177Speter#include "util.h"
41289177Speter#include "index.h"
42289177Speter
43289177Speter#include "private/svn_fs_util.h"
44289177Speter#include "private/svn_string_private.h"
45289177Speter#include "private/svn_subr_private.h"
46289177Speter#include "../libsvn_fs/fs-loader.h"
47289177Speter
48289177Speter#include "svn_private_config.h"
49289177Speter
50289177Speter/* The default maximum number of files per directory to store in the
51289177Speter   rev and revprops directory.  The number below is somewhat arbitrary,
52289177Speter   and can be overridden by defining the macro while compiling; the
53289177Speter   figure of 1000 is reasonable for VFAT filesystems, which are by far
54289177Speter   the worst performers in this area. */
55289177Speter#ifndef SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR
56289177Speter#define SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR 1000
57289177Speter#endif
58289177Speter
59289177Speter/* Begin deltification after a node history exceeded this this limit.
60289177Speter   Useful values are 4 to 64 with 16 being a good compromise between
61289177Speter   computational overhead and repository size savings.
62289177Speter   Should be a power of 2.
63289177Speter   Values < 2 will result in standard skip-delta behavior. */
64289177Speter#define SVN_FS_X_MAX_LINEAR_DELTIFICATION 16
65289177Speter
66289177Speter/* Finding a deltification base takes operations proportional to the
67289177Speter   number of changes being skipped. To prevent exploding runtime
68289177Speter   during commits, limit the deltification range to this value.
69289177Speter   Should be a power of 2 minus one.
70289177Speter   Values < 1 disable deltification. */
71289177Speter#define SVN_FS_X_MAX_DELTIFICATION_WALK 1023
72289177Speter
73289177Speter
74289177Speter
75289177Speter
76289177Speter/* Check that BUF, a nul-terminated buffer of text from format file PATH,
77289177Speter   contains only digits at OFFSET and beyond, raising an error if not.
78289177Speter
79289177Speter   Uses SCRATCH_POOL for temporary allocation. */
80289177Speterstatic svn_error_t *
81289177Spetercheck_format_file_buffer_numeric(const char *buf,
82289177Speter                                 apr_off_t offset,
83289177Speter                                 const char *path,
84289177Speter                                 apr_pool_t *scratch_pool)
85289177Speter{
86289177Speter  return svn_fs_x__check_file_buffer_numeric(buf, offset, path, "Format",
87289177Speter                                             scratch_pool);
88289177Speter}
89289177Speter
90289177Speter/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
91289177Speter   number is not the same as a format number supported by this
92289177Speter   Subversion. */
93289177Speterstatic svn_error_t *
94289177Spetercheck_format(int format)
95289177Speter{
96289177Speter  /* Put blacklisted versions here. */
97289177Speter
98289177Speter  /* We support all formats from 1-current simultaneously */
99289177Speter  if (1 <= format && format <= SVN_FS_X__FORMAT_NUMBER)
100289177Speter    return SVN_NO_ERROR;
101289177Speter
102289177Speter  return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
103289177Speter     _("Expected FS format between '1' and '%d'; found format '%d'"),
104289177Speter     SVN_FS_X__FORMAT_NUMBER, format);
105289177Speter}
106289177Speter
107289177Speter/* Read the format file at PATH and set *PFORMAT to the format version found
108289177Speter * and *MAX_FILES_PER_DIR to the shard size.  Use SCRATCH_POOL for temporary
109289177Speter * allocations. */
110289177Speterstatic svn_error_t *
111289177Speterread_format(int *pformat,
112289177Speter            int *max_files_per_dir,
113289177Speter            const char *path,
114289177Speter            apr_pool_t *scratch_pool)
115289177Speter{
116289177Speter  svn_stream_t *stream;
117289177Speter  svn_stringbuf_t *content;
118289177Speter  svn_stringbuf_t *buf;
119289177Speter  svn_boolean_t eos = FALSE;
120289177Speter
121289177Speter  SVN_ERR(svn_stringbuf_from_file2(&content, path, scratch_pool));
122289177Speter  stream = svn_stream_from_stringbuf(content, scratch_pool);
123289177Speter  SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool));
124289177Speter  if (buf->len == 0 && eos)
125289177Speter    {
126289177Speter      /* Return a more useful error message. */
127289177Speter      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
128289177Speter                               _("Can't read first line of format file '%s'"),
129289177Speter                               svn_dirent_local_style(path, scratch_pool));
130289177Speter    }
131289177Speter
132289177Speter  /* Check that the first line contains only digits. */
133289177Speter  SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, scratch_pool));
134289177Speter  SVN_ERR(svn_cstring_atoi(pformat, buf->data));
135289177Speter
136289177Speter  /* Check that we support this format at all */
137289177Speter  SVN_ERR(check_format(*pformat));
138289177Speter
139289177Speter  /* Read any options. */
140289177Speter  SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool));
141289177Speter  if (!eos && strncmp(buf->data, "layout sharded ", 15) == 0)
142289177Speter    {
143289177Speter      /* Check that the argument is numeric. */
144289177Speter      SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path,
145289177Speter                                               scratch_pool));
146289177Speter      SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15));
147289177Speter    }
148289177Speter  else
149289177Speter    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
150289177Speter                  _("'%s' contains invalid filesystem format option '%s'"),
151289177Speter                  svn_dirent_local_style(path, scratch_pool), buf->data);
152289177Speter
153289177Speter  return SVN_NO_ERROR;
154289177Speter}
155289177Speter
156289177Speter/* Write the format number and maximum number of files per directory
157289177Speter   to a new format file in PATH, possibly expecting to overwrite a
158289177Speter   previously existing file.
159289177Speter
160289177Speter   Use SCRATCH_POOL for temporary allocation. */
161289177Spetersvn_error_t *
162289177Spetersvn_fs_x__write_format(svn_fs_t *fs,
163289177Speter                       svn_boolean_t overwrite,
164289177Speter                       apr_pool_t *scratch_pool)
165289177Speter{
166289177Speter  svn_stringbuf_t *sb;
167289177Speter  const char *path = svn_fs_x__path_format(fs, scratch_pool);
168289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
169289177Speter
170289177Speter  SVN_ERR_ASSERT(1 <= ffd->format && ffd->format <= SVN_FS_X__FORMAT_NUMBER);
171289177Speter
172289177Speter  sb = svn_stringbuf_createf(scratch_pool, "%d\n", ffd->format);
173289177Speter  svn_stringbuf_appendcstr(sb, apr_psprintf(scratch_pool,
174289177Speter                                            "layout sharded %d\n",
175289177Speter                                            ffd->max_files_per_dir));
176289177Speter
177289177Speter  /* svn_io_write_version_file() does a load of magic to allow it to
178289177Speter     replace version files that already exist.  We only need to do
179289177Speter     that when we're allowed to overwrite an existing file. */
180289177Speter  if (! overwrite)
181289177Speter    {
182289177Speter      /* Create the file */
183289177Speter      SVN_ERR(svn_io_file_create(path, sb->data, scratch_pool));
184289177Speter    }
185289177Speter  else
186289177Speter    {
187289177Speter      SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len,
188289177Speter                                  NULL /* copy_perms_path */, scratch_pool));
189289177Speter    }
190289177Speter
191289177Speter  /* And set the perms to make it read only */
192289177Speter  return svn_io_set_file_read_only(path, FALSE, scratch_pool);
193289177Speter}
194289177Speter
195289177Speter/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within
196289177Speter * the range of what the current system may address in RAM and it is a
197289177Speter * power of 2.  Assume that the element size within the block is ITEM_SIZE.
198289177Speter * Use SCRATCH_POOL for temporary allocations.
199289177Speter */
200289177Speterstatic svn_error_t *
201289177Speterverify_block_size(apr_int64_t block_size,
202289177Speter                  apr_size_t item_size,
203289177Speter                  const char *name,
204289177Speter                  apr_pool_t *scratch_pool)
205289177Speter{
206289177Speter  /* Limit range. */
207289177Speter  if (block_size <= 0)
208289177Speter    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
209289177Speter                             _("%s is too small for fsfs.conf setting '%s'."),
210289177Speter                             apr_psprintf(scratch_pool,
211289177Speter                                          "%" APR_INT64_T_FMT,
212289177Speter                                          block_size),
213289177Speter                             name);
214289177Speter
215289177Speter  if (block_size > SVN_MAX_OBJECT_SIZE / item_size)
216289177Speter    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
217289177Speter                             _("%s is too large for fsfs.conf setting '%s'."),
218289177Speter                             apr_psprintf(scratch_pool,
219289177Speter                                          "%" APR_INT64_T_FMT,
220289177Speter                                          block_size),
221289177Speter                             name);
222289177Speter
223289177Speter  /* Ensure it is a power of two.
224289177Speter   * For positive X,  X & (X-1) will reset the lowest bit set.
225289177Speter   * If the result is 0, at most one bit has been set. */
226289177Speter  if (0 != (block_size & (block_size - 1)))
227289177Speter    return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
228289177Speter                             _("%s is invalid for fsfs.conf setting '%s' "
229289177Speter                               "because it is not a power of 2."),
230289177Speter                             apr_psprintf(scratch_pool,
231289177Speter                                          "%" APR_INT64_T_FMT,
232289177Speter                                          block_size),
233289177Speter                             name);
234289177Speter
235289177Speter  return SVN_NO_ERROR;
236289177Speter}
237289177Speter
238289177Speter/* Read the configuration information of the file system at FS_PATH
239289177Speter * and set the respective values in FFD.  Use pools as usual.
240289177Speter */
241289177Speterstatic svn_error_t *
242289177Speterread_config(svn_fs_x__data_t *ffd,
243289177Speter            const char *fs_path,
244289177Speter            apr_pool_t *result_pool,
245289177Speter            apr_pool_t *scratch_pool)
246289177Speter{
247289177Speter  svn_config_t *config;
248289177Speter  apr_int64_t compression_level;
249289177Speter
250289177Speter  SVN_ERR(svn_config_read3(&config,
251289177Speter                           svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool),
252289177Speter                           FALSE, FALSE, FALSE, scratch_pool));
253289177Speter
254289177Speter  /* Initialize ffd->rep_sharing_allowed. */
255289177Speter  SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed,
256289177Speter                              CONFIG_SECTION_REP_SHARING,
257289177Speter                              CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
258289177Speter
259289177Speter  /* Initialize deltification settings in ffd. */
260289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk,
261289177Speter                               CONFIG_SECTION_DELTIFICATION,
262289177Speter                               CONFIG_OPTION_MAX_DELTIFICATION_WALK,
263289177Speter                               SVN_FS_X_MAX_DELTIFICATION_WALK));
264289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification,
265289177Speter                               CONFIG_SECTION_DELTIFICATION,
266289177Speter                               CONFIG_OPTION_MAX_LINEAR_DELTIFICATION,
267289177Speter                               SVN_FS_X_MAX_LINEAR_DELTIFICATION));
268289177Speter  SVN_ERR(svn_config_get_int64(config, &compression_level,
269289177Speter                               CONFIG_SECTION_DELTIFICATION,
270289177Speter                               CONFIG_OPTION_COMPRESSION_LEVEL,
271289177Speter                               SVN_DELTA_COMPRESSION_LEVEL_DEFAULT));
272289177Speter  ffd->delta_compression_level
273289177Speter    = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level),
274289177Speter                SVN_DELTA_COMPRESSION_LEVEL_MAX);
275289177Speter
276289177Speter  /* Initialize revprop packing settings in ffd. */
277289177Speter  SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops,
278289177Speter                              CONFIG_SECTION_PACKED_REVPROPS,
279289177Speter                              CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
280289177Speter                              TRUE));
281289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size,
282289177Speter                               CONFIG_SECTION_PACKED_REVPROPS,
283289177Speter                               CONFIG_OPTION_REVPROP_PACK_SIZE,
284289177Speter                               ffd->compress_packed_revprops
285289177Speter                                   ? 0x100
286289177Speter                                   : 0x40));
287289177Speter
288289177Speter  ffd->revprop_pack_size *= 1024;
289289177Speter
290289177Speter  /* I/O settings in ffd. */
291289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->block_size,
292289177Speter                               CONFIG_SECTION_IO,
293289177Speter                               CONFIG_OPTION_BLOCK_SIZE,
294289177Speter                               64));
295289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size,
296289177Speter                               CONFIG_SECTION_IO,
297289177Speter                               CONFIG_OPTION_L2P_PAGE_SIZE,
298289177Speter                               0x2000));
299289177Speter  SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size,
300289177Speter                               CONFIG_SECTION_IO,
301289177Speter                               CONFIG_OPTION_P2L_PAGE_SIZE,
302289177Speter                               0x400));
303289177Speter
304289177Speter  /* Don't accept unreasonable or illegal values.
305289177Speter   * Block size and P2L page size are in kbytes;
306289177Speter   * L2P blocks are arrays of apr_off_t. */
307289177Speter  SVN_ERR(verify_block_size(ffd->block_size, 0x400,
308289177Speter                            CONFIG_OPTION_BLOCK_SIZE, scratch_pool));
309289177Speter  SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400,
310289177Speter                            CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool));
311289177Speter  SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t),
312289177Speter                            CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool));
313289177Speter
314289177Speter  /* convert kBytes to bytes */
315289177Speter  ffd->block_size *= 0x400;
316289177Speter  ffd->p2l_page_size *= 0x400;
317289177Speter  /* L2P pages are in entries - not in (k)Bytes */
318289177Speter
319289177Speter  /* Debug options. */
320289177Speter  SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit,
321289177Speter                              CONFIG_SECTION_DEBUG,
322289177Speter                              CONFIG_OPTION_PACK_AFTER_COMMIT,
323289177Speter                              FALSE));
324289177Speter
325289177Speter  /* memcached configuration */
326289177Speter  SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config,
327289177Speter                                               result_pool, scratch_pool));
328289177Speter
329289177Speter  SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop,
330289177Speter                              CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
331289177Speter                              FALSE));
332289177Speter
333289177Speter  return SVN_NO_ERROR;
334289177Speter}
335289177Speter
336289177Speter/* Write FS' initial configuration file.
337289177Speter * Use SCRATCH_POOL for temporary allocations. */
338289177Speterstatic svn_error_t *
339289177Speterwrite_config(svn_fs_t *fs,
340289177Speter             apr_pool_t *scratch_pool)
341289177Speter{
342289177Speter#define NL APR_EOL_STR
343289177Speter  static const char * const fsx_conf_contents =
344289177Speter"### This file controls the configuration of the FSX filesystem."            NL
345289177Speter""                                                                           NL
346289177Speter"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]"                          NL
347289177Speter"### These options name memcached servers used to cache internal FSX"        NL
348289177Speter"### data.  See http://www.danga.com/memcached/ for more information on"     NL
349289177Speter"### memcached.  To use memcached with FSX, run one or more memcached"       NL
350289177Speter"### servers, and specify each of them as an option like so:"                NL
351289177Speter"# first-server = 127.0.0.1:11211"                                           NL
352289177Speter"# remote-memcached = mymemcached.corp.example.com:11212"                    NL
353289177Speter"### The option name is ignored; the value is of the form HOST:PORT."        NL
354289177Speter"### memcached servers can be shared between multiple repositories;"         NL
355289177Speter"### however, if you do this, you *must* ensure that repositories have"      NL
356289177Speter"### distinct UUIDs and paths, or else cached data from one repository"      NL
357289177Speter"### might be used by another accidentally.  Note also that memcached has"   NL
358289177Speter"### no authentication for reads or writes, so you must ensure that your"    NL
359289177Speter"### memcached servers are only accessible by trusted users."                NL
360289177Speter""                                                                           NL
361289177Speter"[" CONFIG_SECTION_CACHES "]"                                                NL
362289177Speter"### When a cache-related error occurs, normally Subversion ignores it"      NL
363289177Speter"### and continues, logging an error if the server is appropriately"         NL
364289177Speter"### configured (and ignoring it with file:// access).  To make"             NL
365289177Speter"### Subversion never ignore cache errors, uncomment this line."             NL
366289177Speter"# " CONFIG_OPTION_FAIL_STOP " = true"                                       NL
367289177Speter""                                                                           NL
368289177Speter"[" CONFIG_SECTION_REP_SHARING "]"                                           NL
369289177Speter"### To conserve space, the filesystem can optionally avoid storing"         NL
370289177Speter"### duplicate representations.  This comes at a slight cost in"             NL
371289177Speter"### performance, as maintaining a database of shared representations can"   NL
372289177Speter"### increase commit times.  The space savings are dependent upon the size"  NL
373289177Speter"### of the repository, the number of objects it contains and the amount of" NL
374289177Speter"### duplication between them, usually a function of the branching and"      NL
375289177Speter"### merging process."                                                       NL
376289177Speter"###"                                                                        NL
377289177Speter"### The following parameter enables rep-sharing in the repository.  It can" NL
378289177Speter"### be switched on and off at will, but for best space-saving results"      NL
379289177Speter"### should be enabled consistently over the life of the repository."        NL
380289177Speter"### 'svnadmin verify' will check the rep-cache regardless of this setting." NL
381289177Speter"### rep-sharing is enabled by default."                                     NL
382289177Speter"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true"                              NL
383289177Speter""                                                                           NL
384289177Speter"[" CONFIG_SECTION_DELTIFICATION "]"                                         NL
385289177Speter"### To conserve space, the filesystem stores data as differences against"   NL
386289177Speter"### existing representations.  This comes at a slight cost in performance," NL
387289177Speter"### as calculating differences can increase commit times.  Reading data"    NL
388289177Speter"### will also create higher CPU load and the data will be fragmented."      NL
389289177Speter"### Since deltification tends to save significant amounts of disk space,"   NL
390289177Speter"### the overall I/O load can actually be lower."                            NL
391289177Speter"###"                                                                        NL
392289177Speter"### The options in this section allow for tuning the deltification"         NL
393289177Speter"### strategy.  Their effects on data size and server performance may vary"  NL
394289177Speter"### from one repository to another."                                        NL
395289177Speter"###"                                                                        NL
396289177Speter"### During commit, the server may need to walk the whole change history of" NL
397289177Speter"### of a given node to find a suitable deltification base.  This linear"    NL
398289177Speter"### process can impact commit times, svnadmin load and similar operations." NL
399289177Speter"### This setting limits the depth of the deltification history.  If the"    NL
400289177Speter"### threshold has been reached, the node will be stored as fulltext and a"  NL
401289177Speter"### new deltification history begins."                                      NL
402289177Speter"### Note, this is unrelated to svn log."                                    NL
403289177Speter"### Very large values rarely provide significant additional savings but"    NL
404289177Speter"### can impact performance greatly - in particular if directory"            NL
405289177Speter"### deltification has been activated.  Very small values may be useful in"  NL
406289177Speter"### repositories that are dominated by large, changing binaries."           NL
407289177Speter"### Should be a power of two minus 1.  A value of 0 will effectively"       NL
408289177Speter"### disable deltification."                                                 NL
409289177Speter"### For 1.9, the default value is 1023."                                    NL
410289177Speter"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023"                          NL
411289177Speter"###"                                                                        NL
412289177Speter"### The skip-delta scheme used by FSX tends to repeatably store redundant"  NL
413289177Speter"### delta information where a simple delta against the latest version is"   NL
414289177Speter"### often smaller.  By default, 1.9+ will therefore use skip deltas only"   NL
415289177Speter"### after the linear chain of deltas has grown beyond the threshold"        NL
416289177Speter"### specified by this setting."                                             NL
417289177Speter"### Values up to 64 can result in some reduction in repository size for"    NL
418289177Speter"### the cost of quickly increasing I/O and CPU costs. Similarly, smaller"   NL
419289177Speter"### numbers can reduce those costs at the cost of more disk space.  For"    NL
420289177Speter"### rarely read repositories or those containing larger binaries, this may" NL
421289177Speter"### present a better trade-off."                                            NL
422289177Speter"### Should be a power of two.  A value of 1 or smaller will cause the"      NL
423289177Speter"### exclusive use of skip-deltas."                                          NL
424289177Speter"### For 1.8, the default value is 16."                                      NL
425289177Speter"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16"                          NL
426289177Speter"###"                                                                        NL
427289177Speter"### After deltification, we compress the data through zlib to minimize on-" NL
428289177Speter"### disk size.  That can be an expensive and ineffective process.  This"    NL
429289177Speter"### setting controls the usage of zlib in future revisions."                NL
430289177Speter"### Revisions with highly compressible data in them may shrink in size"     NL
431289177Speter"### if the setting is increased but may take much longer to commit.  The"   NL
432289177Speter"### time taken to uncompress that data again is widely independent of the"  NL
433289177Speter"### compression level."                                                     NL
434289177Speter"### Compression will be ineffective if the incoming content is already"     NL
435289177Speter"### highly compressed.  In that case, disabling the compression entirely"   NL
436289177Speter"### will speed up commits as well as reading the data.  Repositories with"  NL
437289177Speter"### many small compressible files (source code) but also a high percentage" NL
438289177Speter"### of large incompressible ones (artwork) may benefit from compression"    NL
439289177Speter"### levels lowered to e.g. 1."                                              NL
440289177Speter"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL
441289177Speter"### and 0 disabling it altogether."                                         NL
442289177Speter"### The default value is 5."                                                NL
443289177Speter"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5"                                  NL
444289177Speter""                                                                           NL
445289177Speter"[" CONFIG_SECTION_PACKED_REVPROPS "]"                                       NL
446289177Speter"### This parameter controls the size (in kBytes) of packed revprop files."  NL
447289177Speter"### Revprops of consecutive revisions will be concatenated into a single"   NL
448289177Speter"### file up to but not exceeding the threshold given here.  However, each"  NL
449289177Speter"### pack file may be much smaller and revprops of a single revision may be" NL
450289177Speter"### much larger than the limit set here.  The threshold will be applied"    NL
451289177Speter"### before optional compression takes place."                               NL
452289177Speter"### Large values will reduce disk space usage at the expense of increased"  NL
453289177Speter"### latency and CPU usage reading and changing individual revprops.  They"  NL
454289177Speter"### become an advantage when revprop caching has been enabled because a"    NL
455289177Speter"### lot of data can be read in one go.  Values smaller than 4 kByte will"   NL
456289177Speter"### not improve latency any further and quickly render revprop packing"     NL
457289177Speter"### ineffective."                                                           NL
458289177Speter"### revprop-pack-size is 64 kBytes by default for non-compressed revprop"   NL
459289177Speter"### pack files and 256 kBytes when compression has been enabled."           NL
460289177Speter"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64"                                 NL
461289177Speter"###"                                                                        NL
462289177Speter"### To save disk space, packed revprop files may be compressed.  Standard"  NL
463289177Speter"### revprops tend to allow for very effective compression.  Reading and"    NL
464289177Speter"### even more so writing, become significantly more CPU intensive.  With"   NL
465289177Speter"### revprop caching enabled, the overhead can be offset by reduced I/O"     NL
466289177Speter"### unless you often modify revprops after packing."                        NL
467289177Speter"### Compressing packed revprops is enabled by default."                     NL
468289177Speter"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = true"                        NL
469289177Speter""                                                                           NL
470289177Speter"[" CONFIG_SECTION_IO "]"                                                    NL
471289177Speter"### Parameters in this section control the data access granularity in"      NL
472289177Speter"### format 7 repositories and later.  The defaults should translate into"   NL
473289177Speter"### decent performance over a wide range of setups."                        NL
474289177Speter"###"                                                                        NL
475289177Speter"### When a specific piece of information needs to be read from disk,  a"    NL
476289177Speter"### data block is being read at once and its contents are being cached."    NL
477289177Speter"### If the repository is being stored on a RAID, the block size should be"  NL
478289177Speter"### either 50% or 100% of RAID block size / granularity.  Also, your file"  NL
479289177Speter"### system blocks/clusters should be properly aligned and sized.  In that"  NL
480289177Speter"### setup, each access will hit only one disk (minimizes I/O load) but"     NL
481289177Speter"### uses all the data provided by the disk in a single access."             NL
482289177Speter"### For SSD-based storage systems, slightly lower values around 16 kB"      NL
483289177Speter"### may improve latency while still maximizing throughput."                 NL
484289177Speter"### Can be changed at any time but must be a power of 2."                   NL
485289177Speter"### block-size is given in kBytes and with a default of 64 kBytes."         NL
486289177Speter"# " CONFIG_OPTION_BLOCK_SIZE " = 64"                                        NL
487289177Speter"###"                                                                        NL
488289177Speter"### The log-to-phys index maps data item numbers to offsets within the"     NL
489289177Speter"### rev or pack file.  This index is organized in pages of a fixed maximum" NL
490289177Speter"### capacity.  To access an item, the page table and the respective page"   NL
491289177Speter"### must be read."                                                          NL
492289177Speter"### This parameter only affects revisions with thousands of changed paths." NL
493289177Speter"### If you have several extremely large revisions (~1 mio changes), think"  NL
494289177Speter"### about increasing this setting.  Reducing the value will rarely result"  NL
495289177Speter"### in a net speedup."                                                      NL
496289177Speter"### This is an expert setting.  Must be a power of 2."                      NL
497289177Speter"### l2p-page-size is 8192 entries by default."                              NL
498289177Speter"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192"                                   NL
499289177Speter"###"                                                                        NL
500289177Speter"### The phys-to-log index maps positions within the rev or pack file to"    NL
501289177Speter"### to data items,  i.e. describes what piece of information is being"      NL
502289177Speter"### stored at any particular offset.  The index describes the rev file"     NL
503289177Speter"### in chunks (pages) and keeps a global list of all those pages.  Large"   NL
504289177Speter"### pages mean a shorter page table but a larger per-page description of"   NL
505289177Speter"### data items in it.  The latency sweet spot depends on the change size"   NL
506289177Speter"### distribution but covers a relatively wide range."                       NL
507289177Speter"### If the repository contains very large files,  i.e. individual changes"  NL
508289177Speter"### of tens of MB each,  increasing the page size will shorten the index"   NL
509289177Speter"### file at the expense of a slightly increased latency in sections with"   NL
510289177Speter"### smaller changes."                                                       NL
511289177Speter"### For source code repositories, this should be about 16x the block-size." NL
512289177Speter"### Must be a power of 2."                                                  NL
513289177Speter"### p2l-page-size is given in kBytes and with a default of 1024 kBytes."    NL
514289177Speter"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024"                                   NL
515289177Speter;
516289177Speter#undef NL
517289177Speter  return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG,
518289177Speter                                            scratch_pool),
519289177Speter                            fsx_conf_contents, scratch_pool);
520289177Speter}
521289177Speter
522289177Speter/* Read FS's UUID file and store the data in the FS struct. */
523289177Speterstatic svn_error_t *
524289177Speterread_uuid(svn_fs_t *fs,
525289177Speter          apr_pool_t *scratch_pool)
526289177Speter{
527289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
528289177Speter  apr_file_t *uuid_file;
529289177Speter  char buf[APR_UUID_FORMATTED_LENGTH + 2];
530289177Speter  apr_size_t limit;
531289177Speter
532289177Speter  /* Read the repository uuid. */
533289177Speter  SVN_ERR(svn_io_file_open(&uuid_file, svn_fs_x__path_uuid(fs, scratch_pool),
534289177Speter                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
535289177Speter                           scratch_pool));
536289177Speter
537289177Speter  limit = sizeof(buf);
538289177Speter  SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool));
539289177Speter  fs->uuid = apr_pstrdup(fs->pool, buf);
540289177Speter
541289177Speter  /* Read the instance ID. */
542289177Speter  limit = sizeof(buf);
543289177Speter  SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit,
544289177Speter                                  scratch_pool));
545289177Speter  ffd->instance_id = apr_pstrdup(fs->pool, buf);
546289177Speter
547289177Speter  SVN_ERR(svn_io_file_close(uuid_file, scratch_pool));
548289177Speter
549289177Speter  return SVN_NO_ERROR;
550289177Speter}
551289177Speter
552289177Spetersvn_error_t *
553289177Spetersvn_fs_x__read_format_file(svn_fs_t *fs,
554289177Speter                           apr_pool_t *scratch_pool)
555289177Speter{
556289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
557289177Speter  int format, max_files_per_dir;
558289177Speter
559289177Speter  /* Read info from format file. */
560289177Speter  SVN_ERR(read_format(&format, &max_files_per_dir,
561289177Speter                      svn_fs_x__path_format(fs, scratch_pool), scratch_pool));
562289177Speter
563289177Speter  /* Now that we've got *all* info, store / update values in FFD. */
564289177Speter  ffd->format = format;
565289177Speter  ffd->max_files_per_dir = max_files_per_dir;
566289177Speter
567289177Speter  return SVN_NO_ERROR;
568289177Speter}
569289177Speter
570289177Spetersvn_error_t *
571289177Spetersvn_fs_x__open(svn_fs_t *fs,
572289177Speter               const char *path,
573289177Speter               apr_pool_t *scratch_pool)
574289177Speter{
575289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
576289177Speter  fs->path = apr_pstrdup(fs->pool, path);
577289177Speter
578289177Speter  /* Read the FS format file. */
579289177Speter  SVN_ERR(svn_fs_x__read_format_file(fs, scratch_pool));
580289177Speter
581289177Speter  /* Read in and cache the repository uuid. */
582289177Speter  SVN_ERR(read_uuid(fs, scratch_pool));
583289177Speter
584289177Speter  /* Read the min unpacked revision. */
585289177Speter  SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool));
586289177Speter
587289177Speter  /* Read the configuration file. */
588289177Speter  SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool));
589289177Speter
590289177Speter  return svn_error_trace(svn_fs_x__read_current(&ffd->youngest_rev_cache,
591289177Speter                                                fs, scratch_pool));
592289177Speter}
593289177Speter
594289177Speter/* Baton type bridging svn_fs_x__upgrade and upgrade_body carrying
595289177Speter * parameters over between them. */
596289177Spetertypedef struct upgrade_baton_t
597289177Speter{
598289177Speter  svn_fs_t *fs;
599289177Speter  svn_fs_upgrade_notify_t notify_func;
600289177Speter  void *notify_baton;
601289177Speter  svn_cancel_func_t cancel_func;
602289177Speter  void *cancel_baton;
603289177Speter} upgrade_baton_t;
604289177Speter
605289177Speter/* Upgrade the FS given in upgrade_baton_t *)BATON to the latest format
606289177Speter * version.  Apply options an invoke callback from that BATON.
607289177Speter * Temporary allocations are to be made from SCRATCH_POOL.
608289177Speter *
609289177Speter * At the moment, this is a simple placeholder as we don't support upgrades
610289177Speter * from experimental FSX versions.
611289177Speter */
612289177Speterstatic svn_error_t *
613289177Speterupgrade_body(void *baton,
614289177Speter             apr_pool_t *scratch_pool)
615289177Speter{
616289177Speter  upgrade_baton_t *upgrade_baton = baton;
617289177Speter  svn_fs_t *fs = upgrade_baton->fs;
618289177Speter  int format, max_files_per_dir;
619289177Speter  const char *format_path = svn_fs_x__path_format(fs, scratch_pool);
620289177Speter
621289177Speter  /* Read the FS format number and max-files-per-dir setting. */
622289177Speter  SVN_ERR(read_format(&format, &max_files_per_dir, format_path,
623289177Speter                      scratch_pool));
624289177Speter
625289177Speter  /* If we're already up-to-date, there's nothing else to be done here. */
626289177Speter  if (format == SVN_FS_X__FORMAT_NUMBER)
627289177Speter    return SVN_NO_ERROR;
628289177Speter
629289177Speter  /* Done */
630289177Speter  return SVN_NO_ERROR;
631289177Speter}
632289177Speter
633289177Speter
634289177Spetersvn_error_t *
635289177Spetersvn_fs_x__upgrade(svn_fs_t *fs,
636289177Speter                  svn_fs_upgrade_notify_t notify_func,
637289177Speter                  void *notify_baton,
638289177Speter                  svn_cancel_func_t cancel_func,
639289177Speter                  void *cancel_baton,
640289177Speter                  apr_pool_t *scratch_pool)
641289177Speter{
642289177Speter  upgrade_baton_t baton;
643289177Speter  baton.fs = fs;
644289177Speter  baton.notify_func = notify_func;
645289177Speter  baton.notify_baton = notify_baton;
646289177Speter  baton.cancel_func = cancel_func;
647289177Speter  baton.cancel_baton = cancel_baton;
648289177Speter
649289177Speter  return svn_fs_x__with_all_locks(fs, upgrade_body, (void *)&baton,
650289177Speter                                  scratch_pool);
651289177Speter}
652289177Speter
653289177Speter
654289177Spetersvn_error_t *
655289177Spetersvn_fs_x__youngest_rev(svn_revnum_t *youngest_p,
656289177Speter                       svn_fs_t *fs,
657289177Speter                       apr_pool_t *scratch_pool)
658289177Speter{
659289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
660289177Speter  SVN_ERR(svn_fs_x__read_current(youngest_p, fs, scratch_pool));
661289177Speter  ffd->youngest_rev_cache = *youngest_p;
662289177Speter
663289177Speter  return SVN_NO_ERROR;
664289177Speter}
665289177Speter
666289177Spetersvn_error_t *
667289177Spetersvn_fs_x__ensure_revision_exists(svn_revnum_t rev,
668289177Speter                                 svn_fs_t *fs,
669289177Speter                                 apr_pool_t *scratch_pool)
670289177Speter{
671289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
672289177Speter
673289177Speter  if (! SVN_IS_VALID_REVNUM(rev))
674289177Speter    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
675289177Speter                             _("Invalid revision number '%ld'"), rev);
676289177Speter
677289177Speter
678289177Speter  /* Did the revision exist the last time we checked the current
679289177Speter     file? */
680289177Speter  if (rev <= ffd->youngest_rev_cache)
681289177Speter    return SVN_NO_ERROR;
682289177Speter
683289177Speter  SVN_ERR(svn_fs_x__read_current(&ffd->youngest_rev_cache, fs, scratch_pool));
684289177Speter
685289177Speter  /* Check again. */
686289177Speter  if (rev <= ffd->youngest_rev_cache)
687289177Speter    return SVN_NO_ERROR;
688289177Speter
689289177Speter  return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
690289177Speter                           _("No such revision %ld"), rev);
691289177Speter}
692289177Speter
693289177Speter
694289177Spetersvn_error_t *
695289177Spetersvn_fs_x__file_length(svn_filesize_t *length,
696289177Speter                      svn_fs_x__noderev_t *noderev)
697289177Speter{
698289177Speter  if (noderev->data_rep)
699289177Speter    *length = noderev->data_rep->expanded_size;
700289177Speter  else
701289177Speter    *length = 0;
702289177Speter
703289177Speter  return SVN_NO_ERROR;
704289177Speter}
705289177Speter
706289177Spetersvn_boolean_t
707289177Spetersvn_fs_x__file_text_rep_equal(svn_fs_x__representation_t *a,
708289177Speter                              svn_fs_x__representation_t *b)
709289177Speter{
710289177Speter  svn_boolean_t a_empty = a == NULL || a->expanded_size == 0;
711289177Speter  svn_boolean_t b_empty = b == NULL || b->expanded_size == 0;
712289177Speter
713289177Speter  /* This makes sure that neither rep will be NULL later on */
714289177Speter  if (a_empty && b_empty)
715289177Speter    return TRUE;
716289177Speter
717289177Speter  if (a_empty != b_empty)
718289177Speter    return FALSE;
719289177Speter
720289177Speter  /* Same physical representation?  Note that these IDs are always up-to-date
721289177Speter     instead of e.g. being set lazily. */
722289177Speter  if (svn_fs_x__id_eq(&a->id, &b->id))
723289177Speter    return TRUE;
724289177Speter
725289177Speter  /* Contents are equal if the checksums match.  These are also always known.
726289177Speter   */
727289177Speter  return memcmp(a->md5_digest, b->md5_digest, sizeof(a->md5_digest)) == 0
728289177Speter      && memcmp(a->sha1_digest, b->sha1_digest, sizeof(a->sha1_digest)) == 0;
729289177Speter}
730289177Speter
731289177Spetersvn_error_t *
732289177Spetersvn_fs_x__prop_rep_equal(svn_boolean_t *equal,
733289177Speter                         svn_fs_t *fs,
734289177Speter                         svn_fs_x__noderev_t *a,
735289177Speter                         svn_fs_x__noderev_t *b,
736289177Speter                         svn_boolean_t strict,
737289177Speter                         apr_pool_t *scratch_pool)
738289177Speter{
739289177Speter  svn_fs_x__representation_t *rep_a = a->prop_rep;
740289177Speter  svn_fs_x__representation_t *rep_b = b->prop_rep;
741289177Speter  apr_hash_t *proplist_a;
742289177Speter  apr_hash_t *proplist_b;
743289177Speter
744289177Speter  /* Mainly for a==b==NULL */
745289177Speter  if (rep_a == rep_b)
746289177Speter    {
747289177Speter      *equal = TRUE;
748289177Speter      return SVN_NO_ERROR;
749289177Speter    }
750289177Speter
751289177Speter  /* Committed property lists can be compared quickly */
752289177Speter  if (   rep_a && rep_b
753289177Speter      && svn_fs_x__is_revision(rep_a->id.change_set)
754289177Speter      && svn_fs_x__is_revision(rep_b->id.change_set))
755289177Speter    {
756289177Speter      /* MD5 must be given. Having the same checksum is good enough for
757289177Speter         accepting the prop lists as equal. */
758289177Speter      *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest,
759289177Speter                      sizeof(rep_a->md5_digest)) == 0;
760289177Speter      return SVN_NO_ERROR;
761289177Speter    }
762289177Speter
763289177Speter  /* Same path in same txn? */
764289177Speter  if (svn_fs_x__id_eq(&a->noderev_id, &b->noderev_id))
765289177Speter    {
766289177Speter      *equal = TRUE;
767289177Speter      return SVN_NO_ERROR;
768289177Speter    }
769289177Speter
770289177Speter  /* Skip the expensive bits unless we are in strict mode.
771289177Speter     Simply assume that there is a different. */
772289177Speter  if (!strict)
773289177Speter    {
774289177Speter      *equal = FALSE;
775289177Speter      return SVN_NO_ERROR;
776289177Speter    }
777289177Speter
778289177Speter  /* At least one of the reps has been modified in a txn.
779289177Speter     Fetch and compare them. */
780289177Speter  SVN_ERR(svn_fs_x__get_proplist(&proplist_a, fs, a, scratch_pool,
781289177Speter                                 scratch_pool));
782289177Speter  SVN_ERR(svn_fs_x__get_proplist(&proplist_b, fs, b, scratch_pool,
783289177Speter                                 scratch_pool));
784289177Speter
785289177Speter  *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool);
786289177Speter  return SVN_NO_ERROR;
787289177Speter}
788289177Speter
789289177Speter
790289177Spetersvn_error_t *
791289177Spetersvn_fs_x__file_checksum(svn_checksum_t **checksum,
792289177Speter                        svn_fs_x__noderev_t *noderev,
793289177Speter                        svn_checksum_kind_t kind,
794289177Speter                        apr_pool_t *result_pool)
795289177Speter{
796289177Speter  *checksum = NULL;
797289177Speter
798289177Speter  if (noderev->data_rep)
799289177Speter    {
800289177Speter      svn_checksum_t temp;
801289177Speter      temp.kind = kind;
802289177Speter
803289177Speter      switch(kind)
804289177Speter        {
805289177Speter          case svn_checksum_md5:
806289177Speter            temp.digest = noderev->data_rep->md5_digest;
807289177Speter            break;
808289177Speter
809289177Speter          case svn_checksum_sha1:
810289177Speter            if (! noderev->data_rep->has_sha1)
811289177Speter              return SVN_NO_ERROR;
812289177Speter
813289177Speter            temp.digest = noderev->data_rep->sha1_digest;
814289177Speter            break;
815289177Speter
816289177Speter          default:
817289177Speter            return SVN_NO_ERROR;
818289177Speter        }
819289177Speter
820289177Speter      *checksum = svn_checksum_dup(&temp, result_pool);
821289177Speter    }
822289177Speter
823289177Speter  return SVN_NO_ERROR;
824289177Speter}
825289177Speter
826289177Spetersvn_fs_x__representation_t *
827289177Spetersvn_fs_x__rep_copy(svn_fs_x__representation_t *rep,
828289177Speter                   apr_pool_t *result_pool)
829289177Speter{
830289177Speter  if (rep == NULL)
831289177Speter    return NULL;
832289177Speter
833289177Speter  return apr_pmemdup(result_pool, rep, sizeof(*rep));
834289177Speter}
835289177Speter
836289177Speter
837289177Speter/* Write out the zeroth revision for filesystem FS.
838289177Speter   Perform temporary allocations in SCRATCH_POOL. */
839289177Speterstatic svn_error_t *
840289177Speterwrite_revision_zero(svn_fs_t *fs,
841289177Speter                    apr_pool_t *scratch_pool)
842289177Speter{
843289177Speter  /* Use an explicit sub-pool to have full control over temp file lifetimes.
844289177Speter   * Since we have it, use it for everything else as well. */
845289177Speter  apr_pool_t *subpool = svn_pool_create(scratch_pool);
846289177Speter  const char *path_revision_zero = svn_fs_x__path_rev(fs, 0, subpool);
847289177Speter  apr_hash_t *proplist;
848289177Speter  svn_string_t date;
849289177Speter
850289177Speter  apr_array_header_t *index_entries;
851289177Speter  svn_fs_x__p2l_entry_t *entry;
852289177Speter  svn_fs_x__revision_file_t *rev_file;
853289177Speter  const char *l2p_proto_index, *p2l_proto_index;
854289177Speter
855289177Speter  /* Construct a skeleton r0 with no indexes. */
856289177Speter  svn_string_t *noderev_str = svn_string_create("id: 2+0\n"
857289177Speter                                                "node: 0+0\n"
858289177Speter                                                "copy: 0+0\n"
859289177Speter                                                "type: dir\n"
860289177Speter                                                "count: 0\n"
861289177Speter                                                "cpath: /\n"
862289177Speter                                                "\n",
863289177Speter                                                subpool);
864289177Speter  svn_string_t *changes_str = svn_string_create("\n",
865289177Speter                                                subpool);
866289177Speter  svn_string_t *r0 = svn_string_createf(subpool, "%s%s",
867289177Speter                                        noderev_str->data,
868289177Speter                                        changes_str->data);
869289177Speter
870289177Speter  /* Write skeleton r0 to disk. */
871289177Speter  SVN_ERR(svn_io_file_create(path_revision_zero, r0->data, subpool));
872289177Speter
873289177Speter  /* Construct the index P2L contents: describe the 2 items we have.
874289177Speter     Be sure to create them in on-disk order. */
875289177Speter  index_entries = apr_array_make(subpool, 2, sizeof(entry));
876289177Speter
877289177Speter  entry = apr_pcalloc(subpool, sizeof(*entry));
878289177Speter  entry->offset = 0;
879289177Speter  entry->size = (apr_off_t)noderev_str->len;
880289177Speter  entry->type = SVN_FS_X__ITEM_TYPE_NODEREV;
881289177Speter  entry->item_count = 1;
882289177Speter  entry->items = apr_pcalloc(subpool, sizeof(*entry->items));
883289177Speter  entry->items[0].change_set = 0;
884289177Speter  entry->items[0].number = SVN_FS_X__ITEM_INDEX_ROOT_NODE;
885289177Speter  APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry;
886289177Speter
887289177Speter  entry = apr_pcalloc(subpool, sizeof(*entry));
888289177Speter  entry->offset = (apr_off_t)noderev_str->len;
889289177Speter  entry->size = (apr_off_t)changes_str->len;
890289177Speter  entry->type = SVN_FS_X__ITEM_TYPE_CHANGES;
891289177Speter  entry->item_count = 1;
892289177Speter  entry->items = apr_pcalloc(subpool, sizeof(*entry->items));
893289177Speter  entry->items[0].change_set = 0;
894289177Speter  entry->items[0].number = SVN_FS_X__ITEM_INDEX_CHANGES;
895289177Speter  APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry;
896289177Speter
897289177Speter  /* Now re-open r0, create proto-index files from our entries and
898289177Speter      rewrite the index section of r0. */
899289177Speter  SVN_ERR(svn_fs_x__open_pack_or_rev_file_writable(&rev_file, fs, 0,
900289177Speter                                                   subpool, subpool));
901289177Speter  SVN_ERR(svn_fs_x__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
902289177Speter                                               rev_file, index_entries,
903289177Speter                                               subpool, subpool));
904289177Speter  SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&l2p_proto_index, fs,
905289177Speter                                               index_entries,
906289177Speter                                               subpool, subpool));
907289177Speter  SVN_ERR(svn_fs_x__add_index_data(fs, rev_file->file, l2p_proto_index,
908289177Speter                                   p2l_proto_index, 0, subpool));
909289177Speter  SVN_ERR(svn_fs_x__close_revision_file(rev_file));
910289177Speter
911289177Speter  SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool));
912289177Speter
913289177Speter  /* Set a date on revision 0. */
914289177Speter  date.data = svn_time_to_cstring(apr_time_now(), fs->pool);
915289177Speter  date.len = strlen(date.data);
916289177Speter  proplist = apr_hash_make(fs->pool);
917289177Speter  svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
918289177Speter  return svn_fs_x__set_revision_proplist(fs, 0, proplist, fs->pool);
919289177Speter}
920289177Speter
921289177Spetersvn_error_t *
922289177Spetersvn_fs_x__create_file_tree(svn_fs_t *fs,
923289177Speter                           const char *path,
924289177Speter                           int format,
925289177Speter                           int shard_size,
926289177Speter                           apr_pool_t *scratch_pool)
927289177Speter{
928289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
929289177Speter
930289177Speter  fs->path = apr_pstrdup(fs->pool, path);
931289177Speter  ffd->format = format;
932289177Speter
933289177Speter  /* Use an appropriate sharding mode if supported by the format. */
934289177Speter  ffd->max_files_per_dir = shard_size;
935289177Speter
936289177Speter  /* Create the revision data directories. */
937289177Speter  SVN_ERR(svn_io_make_dir_recursively(
938289177Speter                              svn_fs_x__path_rev_shard(fs, 0, scratch_pool),
939289177Speter                              scratch_pool));
940289177Speter
941289177Speter  /* Create the revprops directory. */
942289177Speter  SVN_ERR(svn_io_make_dir_recursively(
943289177Speter                         svn_fs_x__path_revprops_shard(fs, 0, scratch_pool),
944289177Speter                         scratch_pool));
945289177Speter
946289177Speter  /* Create the transaction directory. */
947289177Speter  SVN_ERR(svn_io_make_dir_recursively(
948289177Speter                                  svn_fs_x__path_txns_dir(fs, scratch_pool),
949289177Speter                                  scratch_pool));
950289177Speter
951289177Speter  /* Create the protorevs directory. */
952289177Speter  SVN_ERR(svn_io_make_dir_recursively(
953289177Speter                            svn_fs_x__path_txn_proto_revs(fs, scratch_pool),
954289177Speter                            scratch_pool));
955289177Speter
956289177Speter  /* Create the 'current' file. */
957289177Speter  SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_current(fs, scratch_pool),
958289177Speter                                   scratch_pool));
959289177Speter  SVN_ERR(svn_fs_x__write_current(fs, 0, scratch_pool));
960289177Speter
961289177Speter  /* Create the 'uuid' file. */
962289177Speter  SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_lock(fs, scratch_pool),
963289177Speter                                   scratch_pool));
964289177Speter  SVN_ERR(svn_fs_x__set_uuid(fs, NULL, NULL, scratch_pool));
965289177Speter
966289177Speter  /* Create the fsfs.conf file. */
967289177Speter  SVN_ERR(write_config(fs, scratch_pool));
968289177Speter  SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool));
969289177Speter
970289177Speter  /* Add revision 0. */
971289177Speter  SVN_ERR(write_revision_zero(fs, scratch_pool));
972289177Speter
973289177Speter  /* Create the min unpacked rev file. */
974289177Speter  SVN_ERR(svn_io_file_create(
975289177Speter                          svn_fs_x__path_min_unpacked_rev(fs, scratch_pool),
976289177Speter                          "0\n", scratch_pool));
977289177Speter
978289177Speter  /* Create the txn-current file if the repository supports
979289177Speter     the transaction sequence file. */
980289177Speter  SVN_ERR(svn_io_file_create(svn_fs_x__path_txn_current(fs, scratch_pool),
981289177Speter                             "0\n", scratch_pool));
982289177Speter  SVN_ERR(svn_io_file_create_empty(
983289177Speter                          svn_fs_x__path_txn_current_lock(fs, scratch_pool),
984289177Speter                          scratch_pool));
985289177Speter
986289177Speter  /* Initialize the revprop caching info. */
987289177Speter  SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, scratch_pool));
988289177Speter
989289177Speter  ffd->youngest_rev_cache = 0;
990289177Speter  return SVN_NO_ERROR;
991289177Speter}
992289177Speter
993289177Spetersvn_error_t *
994289177Spetersvn_fs_x__create(svn_fs_t *fs,
995289177Speter                 const char *path,
996289177Speter                 apr_pool_t *scratch_pool)
997289177Speter{
998289177Speter  int format = SVN_FS_X__FORMAT_NUMBER;
999289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
1000289177Speter
1001289177Speter  fs->path = apr_pstrdup(fs->pool, path);
1002289177Speter  /* See if compatibility with older versions was explicitly requested. */
1003289177Speter  if (fs->config)
1004289177Speter    {
1005289177Speter      svn_version_t *compatible_version;
1006289177Speter      SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
1007289177Speter                                         scratch_pool));
1008289177Speter
1009289177Speter      /* select format number */
1010289177Speter      switch(compatible_version->minor)
1011289177Speter        {
1012289177Speter          case 0:
1013289177Speter          case 1:
1014289177Speter          case 2:
1015289177Speter          case 3:
1016289177Speter          case 4:
1017289177Speter          case 5:
1018289177Speter          case 6:
1019289177Speter          case 7:
1020289177Speter          case 8: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
1021289177Speter                  _("FSX is not compatible with Subversion prior to 1.9"));
1022289177Speter
1023289177Speter          default:format = SVN_FS_X__FORMAT_NUMBER;
1024289177Speter        }
1025289177Speter    }
1026289177Speter
1027289177Speter  /* Actual FS creation. */
1028289177Speter  SVN_ERR(svn_fs_x__create_file_tree(fs, path, format,
1029289177Speter                                     SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR,
1030289177Speter                                     scratch_pool));
1031289177Speter
1032289177Speter  /* This filesystem is ready.  Stamp it with a format number. */
1033289177Speter  SVN_ERR(svn_fs_x__write_format(fs, FALSE, scratch_pool));
1034289177Speter
1035289177Speter  ffd->youngest_rev_cache = 0;
1036289177Speter  return SVN_NO_ERROR;
1037289177Speter}
1038289177Speter
1039289177Spetersvn_error_t *
1040289177Spetersvn_fs_x__set_uuid(svn_fs_t *fs,
1041289177Speter                   const char *uuid,
1042289177Speter                   const char *instance_id,
1043289177Speter                   apr_pool_t *scratch_pool)
1044289177Speter{
1045289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
1046289177Speter  const char *uuid_path = svn_fs_x__path_uuid(fs, scratch_pool);
1047289177Speter  svn_stringbuf_t *contents = svn_stringbuf_create_empty(scratch_pool);
1048289177Speter
1049289177Speter  if (! uuid)
1050289177Speter    uuid = svn_uuid_generate(scratch_pool);
1051289177Speter
1052289177Speter  if (! instance_id)
1053289177Speter    instance_id = svn_uuid_generate(scratch_pool);
1054289177Speter
1055289177Speter  svn_stringbuf_appendcstr(contents, uuid);
1056289177Speter  svn_stringbuf_appendcstr(contents, "\n");
1057289177Speter  svn_stringbuf_appendcstr(contents, instance_id);
1058289177Speter  svn_stringbuf_appendcstr(contents, "\n");
1059289177Speter
1060289177Speter  /* We use the permissions of the 'current' file, because the 'uuid'
1061289177Speter     file does not exist during repository creation. */
1062289177Speter  SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len,
1063289177Speter                              /* perms */
1064289177Speter                              svn_fs_x__path_current(fs, scratch_pool),
1065289177Speter                              scratch_pool));
1066289177Speter
1067289177Speter  fs->uuid = apr_pstrdup(fs->pool, uuid);
1068289177Speter  ffd->instance_id = apr_pstrdup(fs->pool, instance_id);
1069289177Speter
1070289177Speter  return SVN_NO_ERROR;
1071289177Speter}
1072289177Speter
1073289177Speter/** Node origin lazy cache. */
1074289177Speter
1075289177Speter/* If directory PATH does not exist, create it and give it the same
1076289177Speter   permissions as FS_path.*/
1077289177Spetersvn_error_t *
1078289177Spetersvn_fs_x__ensure_dir_exists(const char *path,
1079289177Speter                            const char *fs_path,
1080289177Speter                            apr_pool_t *scratch_pool)
1081289177Speter{
1082289177Speter  svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, scratch_pool);
1083289177Speter  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
1084289177Speter    {
1085289177Speter      svn_error_clear(err);
1086289177Speter      return SVN_NO_ERROR;
1087289177Speter    }
1088289177Speter  SVN_ERR(err);
1089289177Speter
1090289177Speter  /* We successfully created a new directory.  Dup the permissions
1091289177Speter     from FS->path. */
1092289177Speter  return svn_io_copy_perms(fs_path, path, scratch_pool);
1093289177Speter}
1094289177Speter
1095289177Speter
1096289177Speter/*** Revisions ***/
1097289177Speter
1098289177Spetersvn_error_t *
1099289177Spetersvn_fs_x__revision_prop(svn_string_t **value_p,
1100289177Speter                        svn_fs_t *fs,
1101289177Speter                        svn_revnum_t rev,
1102289177Speter                        const char *propname,
1103289177Speter                        apr_pool_t *result_pool,
1104289177Speter                        apr_pool_t *scratch_pool)
1105289177Speter{
1106289177Speter  apr_hash_t *table;
1107289177Speter
1108289177Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
1109289177Speter  SVN_ERR(svn_fs_x__get_revision_proplist(&table, fs, rev, FALSE,
1110289177Speter                                          scratch_pool, scratch_pool));
1111289177Speter
1112289177Speter  *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool);
1113289177Speter
1114289177Speter  return SVN_NO_ERROR;
1115289177Speter}
1116289177Speter
1117289177Speter
1118289177Speter/* Baton used for change_rev_prop_body below. */
1119289177Spetertypedef struct change_rev_prop_baton_t {
1120289177Speter  svn_fs_t *fs;
1121289177Speter  svn_revnum_t rev;
1122289177Speter  const char *name;
1123289177Speter  const svn_string_t *const *old_value_p;
1124289177Speter  const svn_string_t *value;
1125289177Speter} change_rev_prop_baton_t;
1126289177Speter
1127289177Speter/* The work-horse for svn_fs_x__change_rev_prop, called with the FS
1128289177Speter   write lock.  This implements the svn_fs_x__with_write_lock()
1129289177Speter   'body' callback type.  BATON is a 'change_rev_prop_baton_t *'. */
1130289177Speterstatic svn_error_t *
1131289177Speterchange_rev_prop_body(void *baton,
1132289177Speter                     apr_pool_t *scratch_pool)
1133289177Speter{
1134289177Speter  change_rev_prop_baton_t *cb = baton;
1135289177Speter  apr_hash_t *table;
1136289177Speter
1137289177Speter  /* Read current revprop values from disk (never from cache).
1138289177Speter     Even if somehow the cache got out of sync, we want to make sure that
1139289177Speter     we read, update and write up-to-date data. */
1140289177Speter  SVN_ERR(svn_fs_x__get_revision_proplist(&table, cb->fs, cb->rev, TRUE,
1141289177Speter                                          scratch_pool, scratch_pool));
1142289177Speter
1143289177Speter  if (cb->old_value_p)
1144289177Speter    {
1145289177Speter      const svn_string_t *wanted_value = *cb->old_value_p;
1146289177Speter      const svn_string_t *present_value = svn_hash_gets(table, cb->name);
1147289177Speter      if ((!wanted_value != !present_value)
1148289177Speter          || (wanted_value && present_value
1149289177Speter              && !svn_string_compare(wanted_value, present_value)))
1150289177Speter        {
1151289177Speter          /* What we expected isn't what we found. */
1152289177Speter          return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
1153289177Speter                                   _("revprop '%s' has unexpected value in "
1154289177Speter                                     "filesystem"),
1155289177Speter                                   cb->name);
1156289177Speter        }
1157289177Speter      /* Fall through. */
1158289177Speter    }
1159289177Speter  svn_hash_sets(table, cb->name, cb->value);
1160289177Speter
1161289177Speter  return svn_fs_x__set_revision_proplist(cb->fs, cb->rev, table,
1162289177Speter                                         scratch_pool);
1163289177Speter}
1164289177Speter
1165289177Spetersvn_error_t *
1166289177Spetersvn_fs_x__change_rev_prop(svn_fs_t *fs,
1167289177Speter                          svn_revnum_t rev,
1168289177Speter                          const char *name,
1169289177Speter                          const svn_string_t *const *old_value_p,
1170289177Speter                          const svn_string_t *value,
1171289177Speter                          apr_pool_t *scratch_pool)
1172289177Speter{
1173289177Speter  change_rev_prop_baton_t cb;
1174289177Speter
1175289177Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
1176289177Speter
1177289177Speter  cb.fs = fs;
1178289177Speter  cb.rev = rev;
1179289177Speter  cb.name = name;
1180289177Speter  cb.old_value_p = old_value_p;
1181289177Speter  cb.value = value;
1182289177Speter
1183289177Speter  return svn_fs_x__with_write_lock(fs, change_rev_prop_body, &cb,
1184289177Speter                                   scratch_pool);
1185289177Speter}
1186289177Speter
1187289177Speter
1188289177Spetersvn_error_t *
1189289177Spetersvn_fs_x__info_format(int *fs_format,
1190289177Speter                      svn_version_t **supports_version,
1191289177Speter                      svn_fs_t *fs,
1192289177Speter                      apr_pool_t *result_pool,
1193289177Speter                      apr_pool_t *scratch_pool)
1194289177Speter{
1195289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
1196289177Speter  *fs_format = ffd->format;
1197289177Speter  *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
1198289177Speter
1199289177Speter  (*supports_version)->major = SVN_VER_MAJOR;
1200289177Speter  (*supports_version)->minor = 9;
1201289177Speter  (*supports_version)->patch = 0;
1202289177Speter  (*supports_version)->tag = "";
1203289177Speter
1204289177Speter  switch (ffd->format)
1205289177Speter    {
1206289177Speter    case 1:
1207289177Speter      break;
1208289177Speter#ifdef SVN_DEBUG
1209289177Speter# if SVN_FS_X__FORMAT_NUMBER != 1
1210289177Speter#  error "Need to add a 'case' statement here"
1211289177Speter# endif
1212289177Speter#endif
1213289177Speter    }
1214289177Speter
1215289177Speter  return SVN_NO_ERROR;
1216289177Speter}
1217289177Speter
1218289177Spetersvn_error_t *
1219289177Spetersvn_fs_x__info_config_files(apr_array_header_t **files,
1220289177Speter                            svn_fs_t *fs,
1221289177Speter                            apr_pool_t *result_pool,
1222289177Speter                            apr_pool_t *scratch_pool)
1223289177Speter{
1224289177Speter  *files = apr_array_make(result_pool, 1, sizeof(const char *));
1225289177Speter  APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG,
1226289177Speter                                                         result_pool);
1227289177Speter  return SVN_NO_ERROR;
1228289177Speter}
1229