]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/fs_fs.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_fs / fs_fs.c
1 /* fs_fs.c --- filesystem operations specific to fs_fs
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_fs.h"
24
25 #include <apr_uuid.h>
26
27 #include "svn_private_config.h"
28
29 #include "svn_checksum.h"
30 #include "svn_hash.h"
31 #include "svn_props.h"
32 #include "svn_time.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_sorts.h"
35 #include "svn_version.h"
36
37 #include "cached_data.h"
38 #include "id.h"
39 #include "index.h"
40 #include "rep-cache.h"
41 #include "revprops.h"
42 #include "transaction.h"
43 #include "tree.h"
44 #include "util.h"
45
46 #include "private/svn_fs_util.h"
47 #include "private/svn_io_private.h"
48 #include "private/svn_string_private.h"
49 #include "private/svn_subr_private.h"
50 #include "../libsvn_fs/fs-loader.h"
51
52 /* The default maximum number of files per directory to store in the
53    rev and revprops directory.  The number below is somewhat arbitrary,
54    and can be overridden by defining the macro while compiling; the
55    figure of 1000 is reasonable for VFAT filesystems, which are by far
56    the worst performers in this area. */
57 #ifndef SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR
58 #define SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR 1000
59 #endif
60
61 /* Begin deltification after a node history exceeded this this limit.
62    Useful values are 4 to 64 with 16 being a good compromise between
63    computational overhead and repository size savings.
64    Should be a power of 2.
65    Values < 2 will result in standard skip-delta behavior. */
66 #define SVN_FS_FS_MAX_LINEAR_DELTIFICATION 16
67
68 /* Finding a deltification base takes operations proportional to the
69    number of changes being skipped. To prevent exploding runtime
70    during commits, limit the deltification range to this value.
71    Should be a power of 2 minus one.
72    Values < 1 disable deltification. */
73 #define SVN_FS_FS_MAX_DELTIFICATION_WALK 1023
74
75 /* Notes:
76
77 To avoid opening and closing the rev-files all the time, it would
78 probably be advantageous to keep each rev-file open for the
79 lifetime of the transaction object.  I'll leave that as a later
80 optimization for now.
81
82 I didn't keep track of pool lifetimes at all in this code.  There
83 are likely some errors because of that.
84
85 */
86
87 /* Declarations. */
88
89 static svn_error_t *
90 get_youngest(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool);
91
92 /* Pathname helper functions */
93
94 static const char *
95 path_format(svn_fs_t *fs, apr_pool_t *pool)
96 {
97   return svn_dirent_join(fs->path, PATH_FORMAT, pool);
98 }
99
100 static APR_INLINE const char *
101 path_uuid(svn_fs_t *fs, apr_pool_t *pool)
102 {
103   return svn_dirent_join(fs->path, PATH_UUID, pool);
104 }
105
106 const char *
107 svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
108 {
109   return svn_dirent_join(fs->path, PATH_CURRENT, pool);
110 }
111
112
113 \f
114 /* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */
115 static svn_error_t *
116 get_lock_on_filesystem(const char *lock_filename,
117                        apr_pool_t *pool)
118 {
119   return svn_error_trace(svn_io__file_lock_autocreate(lock_filename, pool));
120 }
121
122 /* Reset the HAS_WRITE_LOCK member in the FFD given as BATON_VOID.
123    When registered with the pool holding the lock on the lock file,
124    this makes sure the flag gets reset just before we release the lock. */
125 static apr_status_t
126 reset_lock_flag(void *baton_void)
127 {
128   fs_fs_data_t *ffd = baton_void;
129   ffd->has_write_lock = FALSE;
130   return APR_SUCCESS;
131 }
132
133 /* Structure defining a file system lock to be acquired and the function
134    to be executed while the lock is held.
135
136    Instances of this structure may be nested to allow for multiple locks to
137    be taken out before executing the user-provided body.  In that case, BODY
138    and BATON of the outer instances will be with_lock and a with_lock_baton_t
139    instance (transparently, no special treatment is required.).  It is
140    illegal to attempt to acquire the same lock twice within the same lock
141    chain or via nesting calls using separate lock chains.
142
143    All instances along the chain share the same LOCK_POOL such that only one
144    pool needs to be created and cleared for all locks.  We also allocate as
145    much data from that lock pool as possible to minimize memory usage in
146    caller pools. */
147 typedef struct with_lock_baton_t
148 {
149   /* The filesystem we operate on.  Same for all instances along the chain. */
150   svn_fs_t *fs;
151
152   /* Mutex to complement the lock file in an APR threaded process.
153      No-op object for non-threaded processes but never NULL. */
154   svn_mutex__t *mutex;
155
156   /* Path to the file to lock. */
157   const char *lock_path;
158
159   /* If true, set FS->HAS_WRITE_LOCK after we acquired the lock. */
160   svn_boolean_t is_global_lock;
161
162   /* Function body to execute after we acquired the lock.
163      This may be user-provided or a nested call to with_lock(). */
164   svn_error_t *(*body)(void *baton,
165                        apr_pool_t *pool);
166
167   /* Baton to pass to BODY; possibly NULL.
168      This may be user-provided or a nested lock baton instance. */
169   void *baton;
170
171   /* Pool for all allocations along the lock chain and BODY.  Will hold the
172      file locks and gets destroyed after the outermost BODY returned,
173      releasing all file locks.
174      Same for all instances along the chain. */
175   apr_pool_t *lock_pool;
176
177   /* TRUE, iff BODY is the user-provided body. */
178   svn_boolean_t is_inner_most_lock;
179
180   /* TRUE, iff this is not a nested lock.
181      Then responsible for destroying LOCK_POOL. */
182   svn_boolean_t is_outer_most_lock;
183 } with_lock_baton_t;
184
185 /* Obtain a write lock on the file BATON->LOCK_PATH and call BATON->BODY
186    with BATON->BATON.  If this is the outermost lock call, release all file
187    locks after the body returned.  If BATON->IS_GLOBAL_LOCK is set, set the
188    HAS_WRITE_LOCK flag while we keep the write lock. */
189 static svn_error_t *
190 with_some_lock_file(with_lock_baton_t *baton)
191 {
192   apr_pool_t *pool = baton->lock_pool;
193   svn_error_t *err = get_lock_on_filesystem(baton->lock_path, pool);
194
195   if (!err)
196     {
197       svn_fs_t *fs = baton->fs;
198       fs_fs_data_t *ffd = fs->fsap_data;
199
200       if (baton->is_global_lock)
201         {
202           /* set the "got the lock" flag and register reset function */
203           apr_pool_cleanup_register(pool,
204                                     ffd,
205                                     reset_lock_flag,
206                                     apr_pool_cleanup_null);
207           ffd->has_write_lock = TRUE;
208         }
209
210       /* nobody else will modify the repo state
211          => read HEAD & pack info once */
212       if (baton->is_inner_most_lock)
213         {
214           if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
215             err = svn_fs_fs__update_min_unpacked_rev(fs, pool);
216           if (!err)
217             err = get_youngest(&ffd->youngest_rev_cache, fs, pool);
218         }
219
220       if (!err)
221         err = baton->body(baton->baton, pool);
222     }
223
224   if (baton->is_outer_most_lock)
225     svn_pool_destroy(pool);
226
227   return svn_error_trace(err);
228 }
229
230 /* Wraps with_some_lock_file, protecting it with BATON->MUTEX.
231
232    POOL is unused here and only provided for signature compatibility with
233    WITH_LOCK_BATON_T.BODY. */
234 static svn_error_t *
235 with_lock(void *baton,
236           apr_pool_t *pool)
237 {
238   with_lock_baton_t *lock_baton = baton;
239   SVN_MUTEX__WITH_LOCK(lock_baton->mutex, with_some_lock_file(lock_baton));
240
241   return SVN_NO_ERROR;
242 }
243
244 /* Enum identifying a filesystem lock. */
245 typedef enum lock_id_t
246 {
247   write_lock,
248   txn_lock,
249   pack_lock
250 } lock_id_t;
251
252 /* Initialize BATON->MUTEX, BATON->LOCK_PATH and BATON->IS_GLOBAL_LOCK
253    according to the LOCK_ID.  All other members of BATON must already be
254    valid. */
255 static void
256 init_lock_baton(with_lock_baton_t *baton,
257                 lock_id_t lock_id)
258 {
259   fs_fs_data_t *ffd = baton->fs->fsap_data;
260   fs_fs_shared_data_t *ffsd = ffd->shared;
261
262   switch (lock_id)
263     {
264     case write_lock:
265       baton->mutex = ffsd->fs_write_lock;
266       baton->lock_path = svn_fs_fs__path_lock(baton->fs, baton->lock_pool);
267       baton->is_global_lock = TRUE;
268       break;
269
270     case txn_lock:
271       baton->mutex = ffsd->txn_current_lock;
272       baton->lock_path = svn_fs_fs__path_txn_current_lock(baton->fs,
273                                                           baton->lock_pool);
274       baton->is_global_lock = FALSE;
275       break;
276
277     case pack_lock:
278       baton->mutex = ffsd->fs_pack_lock;
279       baton->lock_path = svn_fs_fs__path_pack_lock(baton->fs,
280                                                    baton->lock_pool);
281       baton->is_global_lock = FALSE;
282       break;
283     }
284 }
285
286 /* Return the  baton for the innermost lock of a (potential) lock chain.
287    The baton shall take out LOCK_ID from FS and execute BODY with BATON
288    while the lock is being held.  Allocate the result in a sub-pool of POOL.
289  */
290 static with_lock_baton_t *
291 create_lock_baton(svn_fs_t *fs,
292                   lock_id_t lock_id,
293                   svn_error_t *(*body)(void *baton,
294                                        apr_pool_t *pool),
295                   void *baton,
296                   apr_pool_t *pool)
297 {
298   /* Allocate everything along the lock chain into a single sub-pool.
299      This minimizes memory usage and cleanup overhead. */
300   apr_pool_t *lock_pool = svn_pool_create(pool);
301   with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result));
302
303   /* Store parameters. */
304   result->fs = fs;
305   result->body = body;
306   result->baton = baton;
307
308   /* File locks etc. will use this pool as well for easy cleanup. */
309   result->lock_pool = lock_pool;
310
311   /* Right now, we are the first, (only, ) and last struct in the chain. */
312   result->is_inner_most_lock = TRUE;
313   result->is_outer_most_lock = TRUE;
314
315   /* Select mutex and lock file path depending on LOCK_ID.
316      Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */
317   init_lock_baton(result, lock_id);
318
319   return result;
320 }
321
322 /* Return a baton that wraps NESTED and requests LOCK_ID as additional lock.
323  *
324  * That means, when you create a lock chain, start with the last / innermost
325  * lock to take out and add the first / outermost lock last.
326  */
327 static with_lock_baton_t *
328 chain_lock_baton(lock_id_t lock_id,
329                  with_lock_baton_t *nested)
330 {
331   /* Use the same pool for batons along the lock chain. */
332   apr_pool_t *lock_pool = nested->lock_pool;
333   with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result));
334
335   /* All locks along the chain operate on the same FS. */
336   result->fs = nested->fs;
337
338   /* Execution of this baton means acquiring the nested lock and its
339      execution. */
340   result->body = with_lock;
341   result->baton = nested;
342
343   /* Shared among all locks along the chain. */
344   result->lock_pool = lock_pool;
345
346   /* We are the new outermost lock but surely not the innermost lock. */
347   result->is_inner_most_lock = FALSE;
348   result->is_outer_most_lock = TRUE;
349   nested->is_outer_most_lock = FALSE;
350
351   /* Select mutex and lock file path depending on LOCK_ID.
352      Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */
353   init_lock_baton(result, lock_id);
354
355   return result;
356 }
357
358 svn_error_t *
359 svn_fs_fs__with_write_lock(svn_fs_t *fs,
360                            svn_error_t *(*body)(void *baton,
361                                                 apr_pool_t *pool),
362                            void *baton,
363                            apr_pool_t *pool)
364 {
365   return svn_error_trace(
366            with_lock(create_lock_baton(fs, write_lock, body, baton, pool),
367                      pool));
368 }
369
370 svn_error_t *
371 svn_fs_fs__with_pack_lock(svn_fs_t *fs,
372                           svn_error_t *(*body)(void *baton,
373                                                apr_pool_t *pool),
374                           void *baton,
375                           apr_pool_t *pool)
376 {
377   return svn_error_trace(
378            with_lock(create_lock_baton(fs, pack_lock, body, baton, pool),
379                      pool));
380 }
381
382 svn_error_t *
383 svn_fs_fs__with_txn_current_lock(svn_fs_t *fs,
384                                  svn_error_t *(*body)(void *baton,
385                                                       apr_pool_t *pool),
386                                  void *baton,
387                                  apr_pool_t *pool)
388 {
389   return svn_error_trace(
390            with_lock(create_lock_baton(fs, txn_lock, body, baton, pool),
391                      pool));
392 }
393
394 svn_error_t *
395 svn_fs_fs__with_all_locks(svn_fs_t *fs,
396                           svn_error_t *(*body)(void *baton,
397                                                apr_pool_t *pool),
398                           void *baton,
399                           apr_pool_t *pool)
400 {
401   fs_fs_data_t *ffd = fs->fsap_data;
402
403   /* Be sure to use the correct lock ordering as documented in
404      fs_fs_shared_data_t.  The lock chain is being created in
405      innermost (last to acquire) -> outermost (first to acquire) order. */
406   with_lock_baton_t *lock_baton
407     = create_lock_baton(fs, write_lock, body, baton, pool);
408
409   if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
410     lock_baton = chain_lock_baton(pack_lock, lock_baton);
411
412   if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
413     lock_baton = chain_lock_baton(txn_lock, lock_baton);
414
415   return svn_error_trace(with_lock(lock_baton, pool));
416 }
417
418
419 \f
420
421
422 /* Check that BUF, a nul-terminated buffer of text from format file PATH,
423    contains only digits at OFFSET and beyond, raising an error if not.
424
425    Uses POOL for temporary allocation. */
426 static svn_error_t *
427 check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
428                                  const char *path, apr_pool_t *pool)
429 {
430   return svn_fs_fs__check_file_buffer_numeric(buf, offset, path, "Format",
431                                               pool);
432 }
433
434 /* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
435    number is not the same as a format number supported by this
436    Subversion. */
437 static svn_error_t *
438 check_format(int format)
439 {
440   /* Blacklist.  These formats may be either younger or older than
441      SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */
442   if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT)
443     return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
444                              _("Found format '%d', only created by "
445                                "unreleased dev builds; see "
446                                "http://subversion.apache.org"
447                                "/docs/release-notes/1.7#revprop-packing"),
448                              format);
449
450   /* We support all formats from 1-current simultaneously */
451   if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
452     return SVN_NO_ERROR;
453
454   return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
455      _("Expected FS format between '1' and '%d'; found format '%d'"),
456      SVN_FS_FS__FORMAT_NUMBER, format);
457 }
458
459 /* Read the format number and maximum number of files per directory
460    from PATH and return them in *PFORMAT, *MAX_FILES_PER_DIR and
461    USE_LOG_ADDRESSIONG respectively.
462
463    *MAX_FILES_PER_DIR is obtained from the 'layout' format option, and
464    will be set to zero if a linear scheme should be used.
465    *USE_LOG_ADDRESSIONG is obtained from the 'addressing' format option,
466    and will be set to FALSE for physical addressing.
467
468    Use POOL for temporary allocation. */
469 static svn_error_t *
470 read_format(int *pformat,
471             int *max_files_per_dir,
472             svn_boolean_t *use_log_addressing,
473             const char *path,
474             apr_pool_t *pool)
475 {
476   svn_error_t *err;
477   svn_stream_t *stream;
478   svn_stringbuf_t *content;
479   svn_stringbuf_t *buf;
480   svn_boolean_t eos = FALSE;
481
482   err = svn_stringbuf_from_file2(&content, path, pool);
483   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
484     {
485       /* Treat an absent format file as format 1.  Do not try to
486          create the format file on the fly, because the repository
487          might be read-only for us, or this might be a read-only
488          operation, and the spirit of FSFS is to make no changes
489          whatseover in read-only operations.  See thread starting at
490          http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=97600
491          for more. */
492       svn_error_clear(err);
493       *pformat = 1;
494       *max_files_per_dir = 0;
495
496       return SVN_NO_ERROR;
497     }
498   SVN_ERR(err);
499
500   stream = svn_stream_from_stringbuf(content, pool);
501   SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool));
502   if (buf->len == 0 && eos)
503     {
504       /* Return a more useful error message. */
505       return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
506                                _("Can't read first line of format file '%s'"),
507                                svn_dirent_local_style(path, pool));
508     }
509
510   /* Check that the first line contains only digits. */
511   SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, pool));
512   SVN_ERR(svn_cstring_atoi(pformat, buf->data));
513
514   /* Check that we support this format at all */
515   SVN_ERR(check_format(*pformat));
516
517   /* Set the default values for anything that can be set via an option. */
518   *max_files_per_dir = 0;
519   *use_log_addressing = FALSE;
520
521   /* Read any options. */
522   while (!eos)
523     {
524       SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool));
525       if (buf->len == 0)
526         break;
527
528       if (*pformat >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT &&
529           strncmp(buf->data, "layout ", 7) == 0)
530         {
531           if (strcmp(buf->data + 7, "linear") == 0)
532             {
533               *max_files_per_dir = 0;
534               continue;
535             }
536
537           if (strncmp(buf->data + 7, "sharded ", 8) == 0)
538             {
539               /* Check that the argument is numeric. */
540               SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path, pool));
541               SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15));
542               continue;
543             }
544         }
545
546       if (*pformat >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT &&
547           strncmp(buf->data, "addressing ", 11) == 0)
548         {
549           if (strcmp(buf->data + 11, "physical") == 0)
550             {
551               *use_log_addressing = FALSE;
552               continue;
553             }
554
555           if (strcmp(buf->data + 11, "logical") == 0)
556             {
557               *use_log_addressing = TRUE;
558               continue;
559             }
560         }
561
562       return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
563          _("'%s' contains invalid filesystem format option '%s'"),
564          svn_dirent_local_style(path, pool), buf->data);
565     }
566
567   /* Non-sharded repositories never use logical addressing.
568    * If the format file is inconsistent in that respect, something
569    * probably went wrong.
570    */
571   if (*use_log_addressing && !*max_files_per_dir)
572     return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
573        _("'%s' specifies logical addressing for a non-sharded repository"),
574        svn_dirent_local_style(path, pool));
575
576   return SVN_NO_ERROR;
577 }
578
579 /* Write the format number, maximum number of files per directory and
580    the addressing scheme to a new format file in PATH, possibly expecting
581    to overwrite a previously existing file.
582
583    Use POOL for temporary allocation. */
584 svn_error_t *
585 svn_fs_fs__write_format(svn_fs_t *fs,
586                         svn_boolean_t overwrite,
587                         apr_pool_t *pool)
588 {
589   svn_stringbuf_t *sb;
590   fs_fs_data_t *ffd = fs->fsap_data;
591   const char *path = path_format(fs, pool);
592
593   SVN_ERR_ASSERT(1 <= ffd->format
594                  && ffd->format <= SVN_FS_FS__FORMAT_NUMBER);
595
596   sb = svn_stringbuf_createf(pool, "%d\n", ffd->format);
597
598   if (ffd->format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
599     {
600       if (ffd->max_files_per_dir)
601         svn_stringbuf_appendcstr(sb, apr_psprintf(pool, "layout sharded %d\n",
602                                                   ffd->max_files_per_dir));
603       else
604         svn_stringbuf_appendcstr(sb, "layout linear\n");
605     }
606
607   if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
608     {
609       if (ffd->use_log_addressing)
610         svn_stringbuf_appendcstr(sb, "addressing logical\n");
611       else
612         svn_stringbuf_appendcstr(sb, "addressing physical\n");
613     }
614
615   /* svn_io_write_version_file() does a load of magic to allow it to
616      replace version files that already exist.  We only need to do
617      that when we're allowed to overwrite an existing file. */
618   if (! overwrite)
619     {
620       /* Create the file */
621       SVN_ERR(svn_io_file_create(path, sb->data, pool));
622     }
623   else
624     {
625       SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len,
626                                   NULL /* copy_perms_path */, pool));
627     }
628
629   /* And set the perms to make it read only */
630   return svn_io_set_file_read_only(path, FALSE, pool);
631 }
632
633 svn_boolean_t
634 svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs)
635 {
636   fs_fs_data_t *ffd = fs->fsap_data;
637   return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT;
638 }
639
640 /* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within
641  * the range of what the current system may address in RAM and it is a
642  * power of 2.  Assume that the element size within the block is ITEM_SIZE.
643  * Use SCRATCH_POOL for temporary allocations.
644  */
645 static svn_error_t *
646 verify_block_size(apr_int64_t block_size,
647                   apr_size_t item_size,
648                   const char *name,
649                   apr_pool_t *scratch_pool
650                  )
651 {
652   /* Limit range. */
653   if (block_size <= 0)
654     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
655                              _("%s is too small for fsfs.conf setting '%s'."),
656                              apr_psprintf(scratch_pool,
657                                           "%" APR_INT64_T_FMT,
658                                           block_size),
659                              name);
660
661   if (block_size > SVN_MAX_OBJECT_SIZE / item_size)
662     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
663                              _("%s is too large for fsfs.conf setting '%s'."),
664                              apr_psprintf(scratch_pool,
665                                           "%" APR_INT64_T_FMT,
666                                           block_size),
667                              name);
668
669   /* Ensure it is a power of two.
670    * For positive X,  X & (X-1) will reset the lowest bit set.
671    * If the result is 0, at most one bit has been set. */
672   if (0 != (block_size & (block_size - 1)))
673     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
674                              _("%s is invalid for fsfs.conf setting '%s' "
675                                "because it is not a power of 2."),
676                              apr_psprintf(scratch_pool,
677                                           "%" APR_INT64_T_FMT,
678                                           block_size),
679                              name);
680
681   return SVN_NO_ERROR;
682 }
683
684 /* Read the configuration information of the file system at FS_PATH
685  * and set the respective values in FFD.  Use pools as usual.
686  */
687 static svn_error_t *
688 read_config(fs_fs_data_t *ffd,
689             const char *fs_path,
690             apr_pool_t *result_pool,
691             apr_pool_t *scratch_pool)
692 {
693   svn_config_t *config;
694
695   SVN_ERR(svn_config_read3(&config,
696                            svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool),
697                            FALSE, FALSE, FALSE, scratch_pool));
698
699   /* Initialize ffd->rep_sharing_allowed. */
700   if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
701     SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed,
702                                 CONFIG_SECTION_REP_SHARING,
703                                 CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
704   else
705     ffd->rep_sharing_allowed = FALSE;
706
707   /* Initialize deltification settings in ffd. */
708   if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT)
709     {
710       apr_int64_t compression_level;
711
712       SVN_ERR(svn_config_get_bool(config, &ffd->deltify_directories,
713                                   CONFIG_SECTION_DELTIFICATION,
714                                   CONFIG_OPTION_ENABLE_DIR_DELTIFICATION,
715                                   TRUE));
716       SVN_ERR(svn_config_get_bool(config, &ffd->deltify_properties,
717                                   CONFIG_SECTION_DELTIFICATION,
718                                   CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION,
719                                   TRUE));
720       SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk,
721                                    CONFIG_SECTION_DELTIFICATION,
722                                    CONFIG_OPTION_MAX_DELTIFICATION_WALK,
723                                    SVN_FS_FS_MAX_DELTIFICATION_WALK));
724       SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification,
725                                    CONFIG_SECTION_DELTIFICATION,
726                                    CONFIG_OPTION_MAX_LINEAR_DELTIFICATION,
727                                    SVN_FS_FS_MAX_LINEAR_DELTIFICATION));
728
729       SVN_ERR(svn_config_get_int64(config, &compression_level,
730                                    CONFIG_SECTION_DELTIFICATION,
731                                    CONFIG_OPTION_COMPRESSION_LEVEL,
732                                    SVN_DELTA_COMPRESSION_LEVEL_DEFAULT));
733       ffd->delta_compression_level
734         = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level),
735                    SVN_DELTA_COMPRESSION_LEVEL_MAX);
736     }
737   else
738     {
739       ffd->deltify_directories = FALSE;
740       ffd->deltify_properties = FALSE;
741       ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK;
742       ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION;
743       ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
744     }
745
746   /* Initialize revprop packing settings in ffd. */
747   if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
748     {
749       SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops,
750                                   CONFIG_SECTION_PACKED_REVPROPS,
751                                   CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
752                                   FALSE));
753       SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size,
754                                    CONFIG_SECTION_PACKED_REVPROPS,
755                                    CONFIG_OPTION_REVPROP_PACK_SIZE,
756                                    ffd->compress_packed_revprops
757                                        ? 0x10
758                                        : 0x4));
759
760       ffd->revprop_pack_size *= 1024;
761     }
762   else
763     {
764       ffd->revprop_pack_size = 0x10000;
765       ffd->compress_packed_revprops = FALSE;
766     }
767
768   if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
769     {
770       SVN_ERR(svn_config_get_int64(config, &ffd->block_size,
771                                    CONFIG_SECTION_IO,
772                                    CONFIG_OPTION_BLOCK_SIZE,
773                                    64));
774       SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size,
775                                    CONFIG_SECTION_IO,
776                                    CONFIG_OPTION_L2P_PAGE_SIZE,
777                                    0x2000));
778       SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size,
779                                    CONFIG_SECTION_IO,
780                                    CONFIG_OPTION_P2L_PAGE_SIZE,
781                                    0x400));
782
783       /* Don't accept unreasonable or illegal values.
784        * Block size and P2L page size are in kbytes;
785        * L2P blocks are arrays of apr_off_t. */
786       SVN_ERR(verify_block_size(ffd->block_size, 0x400,
787                                 CONFIG_OPTION_BLOCK_SIZE, scratch_pool));
788       SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400,
789                                 CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool));
790       SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t),
791                                 CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool));
792
793       /* convert kBytes to bytes */
794       ffd->block_size *= 0x400;
795       ffd->p2l_page_size *= 0x400;
796       /* L2P pages are in entries - not in (k)Bytes */
797     }
798   else
799     {
800       /* should be irrelevant but we initialize them anyway */
801       ffd->block_size = 0x1000; /* Matches default APR file buffer size. */
802       ffd->l2p_page_size = 0x2000;    /* Matches above default. */
803       ffd->p2l_page_size = 0x100000;  /* Matches above default in bytes. */
804     }
805
806   if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
807     {
808       SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit,
809                                   CONFIG_SECTION_DEBUG,
810                                   CONFIG_OPTION_PACK_AFTER_COMMIT,
811                                   FALSE));
812     }
813   else
814     {
815       ffd->pack_after_commit = FALSE;
816     }
817
818   /* memcached configuration */
819   SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config,
820                                                result_pool, scratch_pool));
821
822   SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop,
823                               CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
824                               FALSE));
825
826   return SVN_NO_ERROR;
827 }
828
829 static svn_error_t *
830 write_config(svn_fs_t *fs,
831              apr_pool_t *pool)
832 {
833 #define NL APR_EOL_STR
834   static const char * const fsfs_conf_contents =
835 "### This file controls the configuration of the FSFS filesystem."           NL
836 ""                                                                           NL
837 "[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]"                          NL
838 "### These options name memcached servers used to cache internal FSFS"       NL
839 "### data.  See http://www.danga.com/memcached/ for more information on"     NL
840 "### memcached.  To use memcached with FSFS, run one or more memcached"      NL
841 "### servers, and specify each of them as an option like so:"                NL
842 "# first-server = 127.0.0.1:11211"                                           NL
843 "# remote-memcached = mymemcached.corp.example.com:11212"                    NL
844 "### The option name is ignored; the value is of the form HOST:PORT."        NL
845 "### memcached servers can be shared between multiple repositories;"         NL
846 "### however, if you do this, you *must* ensure that repositories have"      NL
847 "### distinct UUIDs and paths, or else cached data from one repository"      NL
848 "### might be used by another accidentally.  Note also that memcached has"   NL
849 "### no authentication for reads or writes, so you must ensure that your"    NL
850 "### memcached servers are only accessible by trusted users."                NL
851 ""                                                                           NL
852 "[" CONFIG_SECTION_CACHES "]"                                                NL
853 "### When a cache-related error occurs, normally Subversion ignores it"      NL
854 "### and continues, logging an error if the server is appropriately"         NL
855 "### configured (and ignoring it with file:// access).  To make"             NL
856 "### Subversion never ignore cache errors, uncomment this line."             NL
857 "# " CONFIG_OPTION_FAIL_STOP " = true"                                       NL
858 ""                                                                           NL
859 "[" CONFIG_SECTION_REP_SHARING "]"                                           NL
860 "### To conserve space, the filesystem can optionally avoid storing"         NL
861 "### duplicate representations.  This comes at a slight cost in"             NL
862 "### performance, as maintaining a database of shared representations can"   NL
863 "### increase commit times.  The space savings are dependent upon the size"  NL
864 "### of the repository, the number of objects it contains and the amount of" NL
865 "### duplication between them, usually a function of the branching and"      NL
866 "### merging process."                                                       NL
867 "###"                                                                        NL
868 "### The following parameter enables rep-sharing in the repository.  It can" NL
869 "### be switched on and off at will, but for best space-saving results"      NL
870 "### should be enabled consistently over the life of the repository."        NL
871 "### 'svnadmin verify' will check the rep-cache regardless of this setting." NL
872 "### rep-sharing is enabled by default."                                     NL
873 "# " CONFIG_OPTION_ENABLE_REP_SHARING " = true"                              NL
874 ""                                                                           NL
875 "[" CONFIG_SECTION_DELTIFICATION "]"                                         NL
876 "### To conserve space, the filesystem stores data as differences against"   NL
877 "### existing representations.  This comes at a slight cost in performance," NL
878 "### as calculating differences can increase commit times.  Reading data"    NL
879 "### will also create higher CPU load and the data will be fragmented."      NL
880 "### Since deltification tends to save significant amounts of disk space,"   NL
881 "### the overall I/O load can actually be lower."                            NL
882 "###"                                                                        NL
883 "### The options in this section allow for tuning the deltification"         NL
884 "### strategy.  Their effects on data size and server performance may vary"  NL
885 "### from one repository to another.  Versions prior to 1.8 will ignore"     NL
886 "### this section."                                                          NL
887 "###"                                                                        NL
888 "### The following parameter enables deltification for directories. It can"  NL
889 "### be switched on and off at will, but for best space-saving results"      NL
890 "### should be enabled consistently over the lifetime of the repository."    NL
891 "### Repositories containing large directories will benefit greatly."        NL
892 "### In rarely accessed repositories, the I/O overhead may be significant"   NL
893 "### as caches will most likely be low."                                     NL
894 "### directory deltification is enabled by default."                         NL
895 "# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = true"                        NL
896 "###"                                                                        NL
897 "### The following parameter enables deltification for properties on files"  NL
898 "### and directories.  Overall, this is a minor tuning option but can save"  NL
899 "### some disk space if you merge frequently or frequently change node"      NL
900 "### properties.  You should not activate this if rep-sharing has been"      NL
901 "### disabled because this may result in a net increase in repository size." NL
902 "### property deltification is enabled by default."                          NL
903 "# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = true"                      NL
904 "###"                                                                        NL
905 "### During commit, the server may need to walk the whole change history of" NL
906 "### of a given node to find a suitable deltification base.  This linear"    NL
907 "### process can impact commit times, svnadmin load and similar operations." NL
908 "### This setting limits the depth of the deltification history.  If the"    NL
909 "### threshold has been reached, the node will be stored as fulltext and a"  NL
910 "### new deltification history begins."                                      NL
911 "### Note, this is unrelated to svn log."                                    NL
912 "### Very large values rarely provide significant additional savings but"    NL
913 "### can impact performance greatly - in particular if directory"            NL
914 "### deltification has been activated.  Very small values may be useful in"  NL
915 "### repositories that are dominated by large, changing binaries."           NL
916 "### Should be a power of two minus 1.  A value of 0 will effectively"       NL
917 "### disable deltification."                                                 NL
918 "### For 1.8, the default value is 1023; earlier versions have no limit."    NL
919 "# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023"                          NL
920 "###"                                                                        NL
921 "### The skip-delta scheme used by FSFS tends to repeatably store redundant" NL
922 "### delta information where a simple delta against the latest version is"   NL
923 "### often smaller.  By default, 1.8+ will therefore use skip deltas only"   NL
924 "### after the linear chain of deltas has grown beyond the threshold"        NL
925 "### specified by this setting."                                             NL
926 "### Values up to 64 can result in some reduction in repository size for"    NL
927 "### the cost of quickly increasing I/O and CPU costs. Similarly, smaller"   NL
928 "### numbers can reduce those costs at the cost of more disk space.  For"    NL
929 "### rarely read repositories or those containing larger binaries, this may" NL
930 "### present a better trade-off."                                            NL
931 "### Should be a power of two.  A value of 1 or smaller will cause the"      NL
932 "### exclusive use of skip-deltas (as in pre-1.8)."                          NL
933 "### For 1.8, the default value is 16; earlier versions use 1."              NL
934 "# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16"                          NL
935 "###"                                                                        NL
936 "### After deltification, we compress the data through zlib to minimize on-" NL
937 "### disk size.  That can be an expensive and ineffective process.  This"    NL
938 "### setting controls the usage of zlib in future revisions."                NL
939 "### Revisions with highly compressible data in them may shrink in size"     NL
940 "### if the setting is increased but may take much longer to commit.  The"   NL
941 "### time taken to uncompress that data again is widely independent of the"  NL
942 "### compression level."                                                     NL
943 "### Compression will be ineffective if the incoming content is already"     NL
944 "### highly compressed.  In that case, disabling the compression entirely"   NL
945 "### will speed up commits as well as reading the data.  Repositories with"  NL
946 "### many small compressible files (source code) but also a high percentage" NL
947 "### of large incompressible ones (artwork) may benefit from compression"    NL
948 "### levels lowered to e.g. 1."                                              NL
949 "### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL
950 "### and 0 disabling it altogether."                                         NL
951 "### The default value is 5."                                                NL
952 "# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5"                                  NL
953 ""                                                                           NL
954 "[" CONFIG_SECTION_PACKED_REVPROPS "]"                                       NL
955 "### This parameter controls the size (in kBytes) of packed revprop files."  NL
956 "### Revprops of consecutive revisions will be concatenated into a single"   NL
957 "### file up to but not exceeding the threshold given here.  However, each"  NL
958 "### pack file may be much smaller and revprops of a single revision may be" NL
959 "### much larger than the limit set here.  The threshold will be applied"    NL
960 "### before optional compression takes place."                               NL
961 "### Large values will reduce disk space usage at the expense of increased"  NL
962 "### latency and CPU usage reading and changing individual revprops."        NL
963 "### Values smaller than 4 kByte will not improve latency any further and "  NL
964 "### quickly render revprop packing ineffective."                            NL
965 "### revprop-pack-size is 4 kBytes by default for non-compressed revprop"    NL
966 "### pack files and 16 kBytes when compression has been enabled."            NL
967 "# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4"                                  NL
968 "###"                                                                        NL
969 "### To save disk space, packed revprop files may be compressed.  Standard"  NL
970 "### revprops tend to allow for very effective compression.  Reading and"    NL
971 "### even more so writing, become significantly more CPU intensive."         NL
972 "### Compressing packed revprops is disabled by default."                    NL
973 "# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false"                       NL
974 ""                                                                           NL
975 "[" CONFIG_SECTION_IO "]"                                                    NL
976 "### Parameters in this section control the data access granularity in"      NL
977 "### format 7 repositories and later.  The defaults should translate into"   NL
978 "### decent performance over a wide range of setups."                        NL
979 "###"                                                                        NL
980 "### When a specific piece of information needs to be read from disk,  a"    NL
981 "### data block is being read at once and its contents are being cached."    NL
982 "### If the repository is being stored on a RAID, the block size should be"  NL
983 "### either 50% or 100% of RAID block size / granularity.  Also, your file"  NL
984 "### system blocks/clusters should be properly aligned and sized.  In that"  NL
985 "### setup, each access will hit only one disk (minimizes I/O load) but"     NL
986 "### uses all the data provided by the disk in a single access."             NL
987 "### For SSD-based storage systems, slightly lower values around 16 kB"      NL
988 "### may improve latency while still maximizing throughput.  If block-read"  NL
989 "### has not been enabled, this will be capped to 4 kBytes."                 NL
990 "### Can be changed at any time but must be a power of 2."                   NL
991 "### block-size is given in kBytes and with a default of 64 kBytes."         NL
992 "# " CONFIG_OPTION_BLOCK_SIZE " = 64"                                        NL
993 "###"                                                                        NL
994 "### The log-to-phys index maps data item numbers to offsets within the"     NL
995 "### rev or pack file.  This index is organized in pages of a fixed maximum" NL
996 "### capacity.  To access an item, the page table and the respective page"   NL
997 "### must be read."                                                          NL
998 "### This parameter only affects revisions with thousands of changed paths." NL
999 "### If you have several extremely large revisions (~1 mio changes), think"  NL
1000 "### about increasing this setting.  Reducing the value will rarely result"  NL
1001 "### in a net speedup."                                                      NL
1002 "### This is an expert setting.  Must be a power of 2."                      NL
1003 "### l2p-page-size is 8192 entries by default."                              NL
1004 "# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192"                                   NL
1005 "###"                                                                        NL
1006 "### The phys-to-log index maps positions within the rev or pack file to"    NL
1007 "### to data items,  i.e. describes what piece of information is being"      NL
1008 "### stored at any particular offset.  The index describes the rev file"     NL
1009 "### in chunks (pages) and keeps a global list of all those pages.  Large"   NL
1010 "### pages mean a shorter page table but a larger per-page description of"   NL
1011 "### data items in it.  The latency sweetspot depends on the change size"    NL
1012 "### distribution but covers a relatively wide range."                       NL
1013 "### If the repository contains very large files,  i.e. individual changes"  NL
1014 "### of tens of MB each,  increasing the page size will shorten the index"   NL
1015 "### file at the expense of a slightly increased latency in sections with"   NL
1016 "### smaller changes."                                                       NL
1017 "### For source code repositories, this should be about 16x the block-size." NL
1018 "### Must be a power of 2."                                                  NL
1019 "### p2l-page-size is given in kBytes and with a default of 1024 kBytes."    NL
1020 "# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024"                                   NL
1021 ;
1022 #undef NL
1023   return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1024                             fsfs_conf_contents, pool);
1025 }
1026
1027 /* Read / Evaluate the global configuration in FS->CONFIG to set up
1028  * parameters in FS. */
1029 static svn_error_t *
1030 read_global_config(svn_fs_t *fs)
1031 {
1032   fs_fs_data_t *ffd = fs->fsap_data;
1033
1034   /* Providing a config hash is optional. */
1035   if (fs->config)
1036     ffd->use_block_read = svn_hash__get_bool(fs->config,
1037                                              SVN_FS_CONFIG_FSFS_BLOCK_READ,
1038                                              FALSE);
1039   else
1040     ffd->use_block_read = FALSE;
1041
1042   /* Ignore the user-specified larger block size if we don't use block-read.
1043      Defaulting to 4k gives us the same access granularity in format 7 as in
1044      older formats. */
1045   if (!ffd->use_block_read)
1046     ffd->block_size = MIN(0x1000, ffd->block_size);
1047
1048   return SVN_NO_ERROR;
1049 }
1050
1051 /* Read FS's UUID file and store the data in the FS struct. */
1052 static svn_error_t *
1053 read_uuid(svn_fs_t *fs,
1054           apr_pool_t *scratch_pool)
1055 {
1056   fs_fs_data_t *ffd = fs->fsap_data;
1057   apr_file_t *uuid_file;
1058   char buf[APR_UUID_FORMATTED_LENGTH + 2];
1059   apr_size_t limit;
1060
1061   /* Read the repository uuid. */
1062   SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, scratch_pool),
1063                            APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
1064                            scratch_pool));
1065
1066   limit = sizeof(buf);
1067   SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool));
1068   fs->uuid = apr_pstrdup(fs->pool, buf);
1069
1070   /* Read the instance ID. */
1071   if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
1072     {
1073       limit = sizeof(buf);
1074       SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit,
1075                                       scratch_pool));
1076       ffd->instance_id = apr_pstrdup(fs->pool, buf);
1077     }
1078   else
1079     {
1080       ffd->instance_id = fs->uuid;
1081     }
1082
1083   SVN_ERR(svn_io_file_close(uuid_file, scratch_pool));
1084
1085   return SVN_NO_ERROR;
1086 }
1087
1088 svn_error_t *
1089 svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool)
1090 {
1091   fs_fs_data_t *ffd = fs->fsap_data;
1092   int format, max_files_per_dir;
1093   svn_boolean_t use_log_addressing;
1094
1095   /* Read info from format file. */
1096   SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing,
1097                       path_format(fs, scratch_pool), scratch_pool));
1098
1099   /* Now that we've got *all* info, store / update values in FFD. */
1100   ffd->format = format;
1101   ffd->max_files_per_dir = max_files_per_dir;
1102   ffd->use_log_addressing = use_log_addressing;
1103
1104   return SVN_NO_ERROR;
1105 }
1106
1107 svn_error_t *
1108 svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
1109 {
1110   fs_fs_data_t *ffd = fs->fsap_data;
1111   fs->path = apr_pstrdup(fs->pool, path);
1112
1113   /* Read the FS format file. */
1114   SVN_ERR(svn_fs_fs__read_format_file(fs, pool));
1115
1116   /* Read in and cache the repository uuid. */
1117   SVN_ERR(read_uuid(fs, pool));
1118
1119   /* Read the min unpacked revision. */
1120   if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1121     SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool));
1122
1123   /* Read the configuration file. */
1124   SVN_ERR(read_config(ffd, fs->path, fs->pool, pool));
1125
1126   /* Global configuration options. */
1127   SVN_ERR(read_global_config(fs));
1128
1129   return get_youngest(&(ffd->youngest_rev_cache), fs, pool);
1130 }
1131
1132 /* Wrapper around svn_io_file_create which ignores EEXIST. */
1133 static svn_error_t *
1134 create_file_ignore_eexist(const char *file,
1135                           const char *contents,
1136                           apr_pool_t *pool)
1137 {
1138   svn_error_t *err = svn_io_file_create(file, contents, pool);
1139   if (err && APR_STATUS_IS_EEXIST(err->apr_err))
1140     {
1141       svn_error_clear(err);
1142       err = SVN_NO_ERROR;
1143     }
1144   return svn_error_trace(err);
1145 }
1146
1147 /* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying
1148  * parameters over between them. */
1149 struct upgrade_baton_t
1150 {
1151   svn_fs_t *fs;
1152   svn_fs_upgrade_notify_t notify_func;
1153   void *notify_baton;
1154   svn_cancel_func_t cancel_func;
1155   void *cancel_baton;
1156 };
1157
1158 static svn_error_t *
1159 upgrade_body(void *baton, apr_pool_t *pool)
1160 {
1161   struct upgrade_baton_t *upgrade_baton = baton;
1162   svn_fs_t *fs = upgrade_baton->fs;
1163   fs_fs_data_t *ffd = fs->fsap_data;
1164   int format, max_files_per_dir;
1165   svn_boolean_t use_log_addressing;
1166   const char *format_path = path_format(fs, pool);
1167   svn_node_kind_t kind;
1168   svn_boolean_t needs_revprop_shard_cleanup = FALSE;
1169
1170   /* Read the FS format number and max-files-per-dir setting. */
1171   SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing,
1172                       format_path, pool));
1173
1174   /* If the config file does not exist, create one. */
1175   SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1176                             &kind, pool));
1177   switch (kind)
1178     {
1179     case svn_node_none:
1180       SVN_ERR(write_config(fs, pool));
1181       break;
1182     case svn_node_file:
1183       break;
1184     default:
1185       return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
1186                                _("'%s' is not a regular file."
1187                                  " Please move it out of "
1188                                  "the way and try again"),
1189                                svn_dirent_join(fs->path, PATH_CONFIG, pool));
1190     }
1191
1192   /* If we're already up-to-date, there's nothing else to be done here. */
1193   if (format == SVN_FS_FS__FORMAT_NUMBER)
1194     return SVN_NO_ERROR;
1195
1196   /* If our filesystem predates the existence of the 'txn-current
1197      file', make that file and its corresponding lock file. */
1198   if (format < SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1199     {
1200       SVN_ERR(create_file_ignore_eexist(
1201                            svn_fs_fs__path_txn_current(fs, pool), "0\n",
1202                            pool));
1203       SVN_ERR(create_file_ignore_eexist(
1204                            svn_fs_fs__path_txn_current_lock(fs, pool), "",
1205                            pool));
1206     }
1207
1208   /* If our filesystem predates the existence of the 'txn-protorevs'
1209      dir, make that directory.  */
1210   if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1211     {
1212       SVN_ERR(svn_io_make_dir_recursively(
1213           svn_fs_fs__path_txn_proto_revs(fs, pool), pool));
1214     }
1215
1216   /* If our filesystem is new enough, write the min unpacked rev file. */
1217   if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
1218     SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool),
1219                                "0\n", pool));
1220
1221   /* If the file system supports revision packing but not revprop packing
1222      *and* the FS has been sharded, pack the revprops up to the point that
1223      revision data has been packed.  However, keep the non-packed revprop
1224      files around until after the format bump */
1225   if (   format >= SVN_FS_FS__MIN_PACKED_FORMAT
1226       && format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT
1227       && max_files_per_dir > 0)
1228     {
1229       needs_revprop_shard_cleanup = TRUE;
1230       SVN_ERR(svn_fs_fs__upgrade_pack_revprops(fs,
1231                                                upgrade_baton->notify_func,
1232                                                upgrade_baton->notify_baton,
1233                                                upgrade_baton->cancel_func,
1234                                                upgrade_baton->cancel_baton,
1235                                                pool));
1236     }
1237
1238   /* We will need the UUID info shortly ...
1239      Read it before the format bump as the UUID file still uses the old
1240      format. */
1241   SVN_ERR(read_uuid(fs, pool));
1242
1243   /* Update the format info in the FS struct.  Upgrade steps further
1244      down will use the format from FS to create missing info. */
1245   ffd->format = SVN_FS_FS__FORMAT_NUMBER;
1246   ffd->max_files_per_dir = max_files_per_dir;
1247   ffd->use_log_addressing = use_log_addressing;
1248
1249   /* Always add / bump the instance ID such that no form of caching
1250      accidentally uses outdated information.  Keep the UUID. */
1251   SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool));
1252
1253   /* Bump the format file. */
1254   SVN_ERR(svn_fs_fs__write_format(fs, TRUE, pool));
1255
1256   if (upgrade_baton->notify_func)
1257     SVN_ERR(upgrade_baton->notify_func(upgrade_baton->notify_baton,
1258                                        SVN_FS_FS__FORMAT_NUMBER,
1259                                        svn_fs_upgrade_format_bumped,
1260                                        pool));
1261
1262   /* Now, it is safe to remove the redundant revprop files. */
1263   if (needs_revprop_shard_cleanup)
1264     SVN_ERR(svn_fs_fs__upgrade_cleanup_pack_revprops(fs,
1265                                                upgrade_baton->notify_func,
1266                                                upgrade_baton->notify_baton,
1267                                                upgrade_baton->cancel_func,
1268                                                upgrade_baton->cancel_baton,
1269                                                pool));
1270
1271   /* Done */
1272   return SVN_NO_ERROR;
1273 }
1274
1275
1276 svn_error_t *
1277 svn_fs_fs__upgrade(svn_fs_t *fs,
1278                    svn_fs_upgrade_notify_t notify_func,
1279                    void *notify_baton,
1280                    svn_cancel_func_t cancel_func,
1281                    void *cancel_baton,
1282                    apr_pool_t *pool)
1283 {
1284   struct upgrade_baton_t baton;
1285   baton.fs = fs;
1286   baton.notify_func = notify_func;
1287   baton.notify_baton = notify_baton;
1288   baton.cancel_func = cancel_func;
1289   baton.cancel_baton = cancel_baton;
1290
1291   return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool);
1292 }
1293
1294 /* Find the youngest revision in a repository at path FS_PATH and
1295    return it in *YOUNGEST_P.  Perform temporary allocations in
1296    POOL. */
1297 static svn_error_t *
1298 get_youngest(svn_revnum_t *youngest_p,
1299              svn_fs_t *fs,
1300              apr_pool_t *pool)
1301 {
1302   apr_uint64_t dummy;
1303   SVN_ERR(svn_fs_fs__read_current(youngest_p, &dummy, &dummy, fs, pool));
1304   return SVN_NO_ERROR;
1305 }
1306
1307
1308 svn_error_t *
1309 svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p,
1310                         svn_fs_t *fs,
1311                         apr_pool_t *pool)
1312 {
1313   fs_fs_data_t *ffd = fs->fsap_data;
1314
1315   SVN_ERR(get_youngest(youngest_p, fs, pool));
1316   ffd->youngest_rev_cache = *youngest_p;
1317
1318   return SVN_NO_ERROR;
1319 }
1320
1321 int
1322 svn_fs_fs__shard_size(svn_fs_t *fs)
1323 {
1324   fs_fs_data_t *ffd = fs->fsap_data;
1325
1326   return ffd->max_files_per_dir;
1327 }
1328
1329 svn_error_t *
1330 svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked,
1331                             svn_fs_t *fs,
1332                             apr_pool_t *pool)
1333 {
1334   fs_fs_data_t *ffd = fs->fsap_data;
1335
1336   SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool));
1337   *min_unpacked = ffd->min_unpacked_rev;
1338
1339   return SVN_NO_ERROR;
1340 }
1341
1342 svn_error_t *
1343 svn_fs_fs__ensure_revision_exists(svn_revnum_t rev,
1344                                   svn_fs_t *fs,
1345                                   apr_pool_t *pool)
1346 {
1347   fs_fs_data_t *ffd = fs->fsap_data;
1348
1349   if (! SVN_IS_VALID_REVNUM(rev))
1350     return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1351                              _("Invalid revision number '%ld'"), rev);
1352
1353
1354   /* Did the revision exist the last time we checked the current
1355      file? */
1356   if (rev <= ffd->youngest_rev_cache)
1357     return SVN_NO_ERROR;
1358
1359   SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs, pool));
1360
1361   /* Check again. */
1362   if (rev <= ffd->youngest_rev_cache)
1363     return SVN_NO_ERROR;
1364
1365   return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1366                            _("No such revision %ld"), rev);
1367 }
1368
1369 svn_error_t *
1370 svn_fs_fs__file_length(svn_filesize_t *length,
1371                        node_revision_t *noderev,
1372                        apr_pool_t *pool)
1373 {
1374   representation_t *data_rep = noderev->data_rep;
1375   if (!data_rep)
1376     {
1377       /* Treat "no representation" as "empty file". */
1378       *length = 0;
1379     }
1380   else if (data_rep->expanded_size)
1381     {
1382       /* Standard case: a non-empty file. */
1383       *length = data_rep->expanded_size;
1384     }
1385   else
1386     {
1387       /* Work around a FSFS format quirk (see issue #4554).
1388
1389          A plain representation may specify its EXPANDED LENGTH as "0"
1390          in which case, the SIZE value is what we want.
1391
1392          Because EXPANDED_LENGTH will also be 0 for empty files, while
1393          SIZE is non-null, we need to check wether the content is
1394          actually empty.  We simply compare with the MD5 checksum of
1395          empty content (sha-1 is not always available).
1396        */
1397       svn_checksum_t *empty_md5
1398         = svn_checksum_empty_checksum(svn_checksum_md5, pool);
1399
1400       if (memcmp(empty_md5->digest, data_rep->md5_digest,
1401                  sizeof(data_rep->md5_digest)))
1402         {
1403           /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the
1404              actual file length. */
1405           *length = data_rep->size;
1406         }
1407       else
1408         {
1409           /* Contents is empty. */
1410           *length = 0;
1411         }
1412     }
1413
1414   return SVN_NO_ERROR;
1415 }
1416
1417 svn_boolean_t
1418 svn_fs_fs__noderev_same_rep_key(representation_t *a,
1419                                 representation_t *b)
1420 {
1421   if (a == b)
1422     return TRUE;
1423
1424   if (a == NULL || b == NULL)
1425     return FALSE;
1426
1427   if (a->item_index != b->item_index)
1428     return FALSE;
1429
1430   if (a->revision != b->revision)
1431     return FALSE;
1432
1433   return memcmp(&a->uniquifier, &b->uniquifier, sizeof(a->uniquifier)) == 0;
1434 }
1435
1436 svn_error_t *
1437 svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal,
1438                                svn_fs_t *fs,
1439                                node_revision_t *a,
1440                                node_revision_t *b,
1441                                apr_pool_t *scratch_pool)
1442 {
1443   svn_stream_t *contents_a, *contents_b;
1444   representation_t *rep_a = a->data_rep;
1445   representation_t *rep_b = b->data_rep;
1446   svn_boolean_t a_empty = !rep_a;
1447   svn_boolean_t b_empty = !rep_b;
1448
1449   /* This makes sure that neither rep will be NULL later on */
1450   if (a_empty && b_empty)
1451     {
1452       *equal = TRUE;
1453       return SVN_NO_ERROR;
1454     }
1455
1456   /* Same path in same rev or txn? */
1457   if (svn_fs_fs__id_eq(a->id, b->id))
1458     {
1459       *equal = TRUE;
1460       return SVN_NO_ERROR;
1461     }
1462
1463   /* Beware of the combination NULL rep and possibly empty rep.
1464    * Due to EXPANDED_SIZE not being reliable, we can't easily detect empty
1465    * reps. So, we can only take further shortcuts if both reps are given. */
1466   if (!a_empty && !b_empty)
1467     {
1468       /* File text representations always know their checksums -
1469        * even in a txn. */
1470       if (memcmp(rep_a->md5_digest, rep_b->md5_digest,
1471                  sizeof(rep_a->md5_digest)))
1472         {
1473           *equal = FALSE;
1474           return SVN_NO_ERROR;
1475         }
1476
1477       /* Paranoia. Compare SHA1 checksums because that's the level of
1478          confidence we require for e.g. the working copy. */
1479       if (rep_a->has_sha1 && rep_b->has_sha1)
1480         {
1481           *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest,
1482                           sizeof(rep_a->sha1_digest)) == 0;
1483           return SVN_NO_ERROR;
1484         }
1485     }
1486
1487   SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE,
1488                                   scratch_pool));
1489   SVN_ERR(svn_fs_fs__get_contents(&contents_b, fs, rep_b, TRUE,
1490                                   scratch_pool));
1491   SVN_ERR(svn_stream_contents_same2(equal, contents_a, contents_b,
1492                                    scratch_pool));
1493
1494   return SVN_NO_ERROR;
1495 }
1496
1497 svn_error_t *
1498 svn_fs_fs__prop_rep_equal(svn_boolean_t *equal,
1499                           svn_fs_t *fs,
1500                           node_revision_t *a,
1501                           node_revision_t *b,
1502                           apr_pool_t *scratch_pool)
1503 {
1504   representation_t *rep_a = a->prop_rep;
1505   representation_t *rep_b = b->prop_rep;
1506   apr_hash_t *proplist_a;
1507   apr_hash_t *proplist_b;
1508
1509   /* Mainly for a==b==NULL */
1510   if (rep_a == rep_b)
1511     {
1512       *equal = TRUE;
1513       return SVN_NO_ERROR;
1514     }
1515
1516   /* Committed property lists can be compared quickly */
1517   if (   rep_a && rep_b
1518       && !svn_fs_fs__id_txn_used(&rep_a->txn_id)
1519       && !svn_fs_fs__id_txn_used(&rep_b->txn_id))
1520     {
1521       /* MD5 must be given. Having the same checksum is good enough for
1522          accepting the prop lists as equal. */
1523       *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest,
1524                       sizeof(rep_a->md5_digest)) == 0;
1525       return SVN_NO_ERROR;
1526     }
1527
1528   /* Same path in same txn? */
1529   if (svn_fs_fs__id_eq(a->id, b->id))
1530     {
1531       *equal = TRUE;
1532       return SVN_NO_ERROR;
1533     }
1534
1535   /* At least one of the reps has been modified in a txn.
1536      Fetch and compare them. */
1537   SVN_ERR(svn_fs_fs__get_proplist(&proplist_a, fs, a, scratch_pool));
1538   SVN_ERR(svn_fs_fs__get_proplist(&proplist_b, fs, b, scratch_pool));
1539
1540   *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool);
1541   return SVN_NO_ERROR;
1542 }
1543
1544
1545 svn_error_t *
1546 svn_fs_fs__file_checksum(svn_checksum_t **checksum,
1547                          node_revision_t *noderev,
1548                          svn_checksum_kind_t kind,
1549                          apr_pool_t *pool)
1550 {
1551   *checksum = NULL;
1552
1553   if (noderev->data_rep)
1554     {
1555       svn_checksum_t temp;
1556       temp.kind = kind;
1557
1558       switch(kind)
1559         {
1560           case svn_checksum_md5:
1561             temp.digest = noderev->data_rep->md5_digest;
1562             break;
1563
1564           case svn_checksum_sha1:
1565             if (! noderev->data_rep->has_sha1)
1566               return SVN_NO_ERROR;
1567
1568             temp.digest = noderev->data_rep->sha1_digest;
1569             break;
1570
1571           default:
1572             return SVN_NO_ERROR;
1573         }
1574
1575       *checksum = svn_checksum_dup(&temp, pool);
1576     }
1577
1578   return SVN_NO_ERROR;
1579 }
1580
1581 representation_t *
1582 svn_fs_fs__rep_copy(representation_t *rep,
1583                     apr_pool_t *pool)
1584 {
1585   if (rep == NULL)
1586     return NULL;
1587
1588   return apr_pmemdup(pool, rep, sizeof(*rep));
1589 }
1590
1591
1592 /* Write out the zeroth revision for filesystem FS.
1593    Perform temporary allocations in SCRATCH_POOL. */
1594 static svn_error_t *
1595 write_revision_zero(svn_fs_t *fs,
1596                     apr_pool_t *scratch_pool)
1597 {
1598   /* Use an explicit sub-pool to have full control over temp file lifetimes.
1599    * Since we have it, use it for everything else as well. */
1600   apr_pool_t *subpool = svn_pool_create(scratch_pool);
1601   const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, subpool);
1602   apr_hash_t *proplist;
1603   svn_string_t date;
1604
1605   /* Write out a rev file for revision 0. */
1606   if (svn_fs_fs__use_log_addressing(fs))
1607     {
1608       apr_array_header_t *index_entries;
1609       svn_fs_fs__p2l_entry_t *entry;
1610       svn_fs_fs__revision_file_t *rev_file;
1611       const char *l2p_proto_index, *p2l_proto_index;
1612
1613       /* Write a skeleton r0 with no indexes. */
1614       SVN_ERR(svn_io_file_create(path_revision_zero,
1615                     "PLAIN\nEND\nENDREP\n"
1616                     "id: 0.0.r0/2\n"
1617                     "type: dir\n"
1618                     "count: 0\n"
1619                     "text: 0 3 4 4 "
1620                     "2d2977d1c96f487abe4a1e202dd03b4e\n"
1621                     "cpath: /\n"
1622                     "\n\n", subpool));
1623
1624       /* Construct the index P2L contents: describe the 3 items we have.
1625          Be sure to create them in on-disk order. */
1626       index_entries = apr_array_make(subpool, 3, sizeof(entry));
1627
1628       entry = apr_pcalloc(subpool, sizeof(*entry));
1629       entry->offset = 0;
1630       entry->size = 17;
1631       entry->type = SVN_FS_FS__ITEM_TYPE_DIR_REP;
1632       entry->item.revision = 0;
1633       entry->item.number = SVN_FS_FS__ITEM_INDEX_FIRST_USER;
1634       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1635
1636       entry = apr_pcalloc(subpool, sizeof(*entry));
1637       entry->offset = 17;
1638       entry->size = 89;
1639       entry->type = SVN_FS_FS__ITEM_TYPE_NODEREV;
1640       entry->item.revision = 0;
1641       entry->item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE;
1642       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1643
1644       entry = apr_pcalloc(subpool, sizeof(*entry));
1645       entry->offset = 106;
1646       entry->size = 1;
1647       entry->type = SVN_FS_FS__ITEM_TYPE_CHANGES;
1648       entry->item.revision = 0;
1649       entry->item.number = SVN_FS_FS__ITEM_INDEX_CHANGES;
1650       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1651
1652       /* Now re-open r0, create proto-index files from our entries and
1653          rewrite the index section of r0. */
1654       SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0,
1655                                                         subpool, subpool));
1656       SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
1657                                                     rev_file, index_entries,
1658                                                     subpool, subpool));
1659       SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs,
1660                                                     index_entries,
1661                                                     subpool, subpool));
1662       SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index,
1663                                         p2l_proto_index, 0, subpool));
1664       SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
1665     }
1666   else
1667     SVN_ERR(svn_io_file_create(path_revision_zero,
1668                                "PLAIN\nEND\nENDREP\n"
1669                                "id: 0.0.r0/17\n"
1670                                "type: dir\n"
1671                                "count: 0\n"
1672                                "text: 0 0 4 4 "
1673                                "2d2977d1c96f487abe4a1e202dd03b4e\n"
1674                                "cpath: /\n"
1675                                "\n\n17 107\n", subpool));
1676
1677   SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, subpool));
1678
1679   /* Set a date on revision 0. */
1680   date.data = svn_time_to_cstring(apr_time_now(), subpool);
1681   date.len = strlen(date.data);
1682   proplist = apr_hash_make(subpool);
1683   svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
1684   SVN_ERR(svn_fs_fs__set_revision_proplist(fs, 0, proplist, subpool));
1685
1686   svn_pool_destroy(subpool);
1687   return SVN_NO_ERROR;
1688 }
1689
1690 svn_error_t *
1691 svn_fs_fs__create_file_tree(svn_fs_t *fs,
1692                             const char *path,
1693                             int format,
1694                             int shard_size,
1695                             svn_boolean_t use_log_addressing,
1696                             apr_pool_t *pool)
1697 {
1698   fs_fs_data_t *ffd = fs->fsap_data;
1699
1700   fs->path = apr_pstrdup(fs->pool, path);
1701   ffd->format = format;
1702
1703   /* Use an appropriate sharding mode if supported by the format. */
1704   if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
1705     ffd->max_files_per_dir = shard_size;
1706   else
1707     ffd->max_files_per_dir = 0;
1708
1709   /* Select the addressing mode depending on the format. */
1710   if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
1711     ffd->use_log_addressing = use_log_addressing;
1712   else
1713     ffd->use_log_addressing = FALSE;
1714
1715   /* Create the revision data directories. */
1716   if (ffd->max_files_per_dir)
1717     SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_rev_shard(fs, 0,
1718                                                                   pool),
1719                                         pool));
1720   else
1721     SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR,
1722                                                         pool),
1723                                         pool));
1724
1725   /* Create the revprops directory. */
1726   if (ffd->max_files_per_dir)
1727     SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_revprops_shard(fs, 0,
1728                                                                        pool),
1729                                         pool));
1730   else
1731     SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path,
1732                                                         PATH_REVPROPS_DIR,
1733                                                         pool),
1734                                         pool));
1735
1736   /* Create the transaction directory. */
1737   SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txns_dir(fs, pool),
1738                                       pool));
1739
1740   /* Create the protorevs directory. */
1741   if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1742     SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txn_proto_revs(fs,
1743                                                                        pool),
1744                                         pool));
1745
1746   /* Create the 'current' file. */
1747   SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_current(fs, pool), pool));
1748   SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, pool));
1749
1750   /* Create the 'uuid' file. */
1751   SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(fs, pool), pool));
1752   SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, NULL, pool));
1753
1754   /* Create the fsfs.conf file if supported.  Older server versions would
1755      simply ignore the file but that might result in a different behavior
1756      than with the later releases.  Also, hotcopy would ignore, i.e. not
1757      copy, a fsfs.conf with old formats. */
1758   if (ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
1759     SVN_ERR(write_config(fs, pool));
1760
1761   SVN_ERR(read_config(ffd, fs->path, fs->pool, pool));
1762
1763   /* Global configuration options. */
1764   SVN_ERR(read_global_config(fs));
1765
1766   /* Add revision 0. */
1767   SVN_ERR(write_revision_zero(fs, pool));
1768
1769   /* Create the min unpacked rev file. */
1770   if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1771     SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool),
1772                                "0\n", pool));
1773
1774   /* Create the txn-current file if the repository supports
1775      the transaction sequence file. */
1776   if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1777     {
1778       SVN_ERR(svn_io_file_create(svn_fs_fs__path_txn_current(fs, pool),
1779                                  "0\n", pool));
1780       SVN_ERR(svn_io_file_create_empty(
1781                                  svn_fs_fs__path_txn_current_lock(fs, pool),
1782                                  pool));
1783     }
1784
1785   ffd->youngest_rev_cache = 0;
1786   return SVN_NO_ERROR;
1787 }
1788
1789 svn_error_t *
1790 svn_fs_fs__create(svn_fs_t *fs,
1791                   const char *path,
1792                   apr_pool_t *pool)
1793 {
1794   int format = SVN_FS_FS__FORMAT_NUMBER;
1795   int shard_size = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
1796   svn_boolean_t log_addressing;
1797
1798   /* Process the given filesystem config. */
1799   if (fs->config)
1800     {
1801       svn_version_t *compatible_version;
1802       const char *shard_size_str;
1803       SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
1804                                          pool));
1805
1806       /* select format number */
1807       switch(compatible_version->minor)
1808         {
1809           case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
1810                  _("FSFS is not compatible with Subversion prior to 1.1"));
1811
1812           case 1:
1813           case 2:
1814           case 3: format = 1;
1815                   break;
1816
1817           case 4: format = 2;
1818                   break;
1819
1820           case 5: format = 3;
1821                   break;
1822
1823           case 6:
1824           case 7: format = 4;
1825                   break;
1826
1827           case 8: format = 6;
1828                   break;
1829
1830           default:format = SVN_FS_FS__FORMAT_NUMBER;
1831         }
1832
1833       shard_size_str = svn_hash_gets(fs->config, SVN_FS_CONFIG_FSFS_SHARD_SIZE);
1834       if (shard_size_str)
1835         {
1836           apr_int64_t val;
1837           SVN_ERR(svn_cstring_strtoi64(&val, shard_size_str, 0,
1838                                        APR_INT32_MAX, 10));
1839
1840           shard_size = (int) val;
1841         }
1842     }
1843
1844   log_addressing = svn_hash__get_bool(fs->config,
1845                                       SVN_FS_CONFIG_FSFS_LOG_ADDRESSING,
1846                                       TRUE);
1847
1848   /* Actual FS creation. */
1849   SVN_ERR(svn_fs_fs__create_file_tree(fs, path, format, shard_size,
1850                                       log_addressing, pool));
1851
1852   /* This filesystem is ready.  Stamp it with a format number. */
1853   SVN_ERR(svn_fs_fs__write_format(fs, FALSE, pool));
1854
1855   return SVN_NO_ERROR;
1856 }
1857
1858 svn_error_t *
1859 svn_fs_fs__set_uuid(svn_fs_t *fs,
1860                     const char *uuid,
1861                     const char *instance_id,
1862                     apr_pool_t *pool)
1863 {
1864   fs_fs_data_t *ffd = fs->fsap_data;
1865   const char *uuid_path = path_uuid(fs, pool);
1866   svn_stringbuf_t *contents = svn_stringbuf_create_empty(pool);
1867
1868   if (! uuid)
1869     uuid = svn_uuid_generate(pool);
1870
1871   if (! instance_id)
1872     instance_id = svn_uuid_generate(pool);
1873
1874   svn_stringbuf_appendcstr(contents, uuid);
1875   svn_stringbuf_appendcstr(contents, "\n");
1876
1877   if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
1878     {
1879       svn_stringbuf_appendcstr(contents, instance_id);
1880       svn_stringbuf_appendcstr(contents, "\n");
1881     }
1882
1883   /* We use the permissions of the 'current' file, because the 'uuid'
1884      file does not exist during repository creation. */
1885   SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len,
1886                               svn_fs_fs__path_current(fs, pool) /* perms */,
1887                               pool));
1888
1889   fs->uuid = apr_pstrdup(fs->pool, uuid);
1890
1891   if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
1892     ffd->instance_id = apr_pstrdup(fs->pool, instance_id);
1893   else
1894     ffd->instance_id = fs->uuid;
1895
1896   return SVN_NO_ERROR;
1897 }
1898
1899 /** Node origin lazy cache. */
1900
1901 /* If directory PATH does not exist, create it and give it the same
1902    permissions as FS_path.*/
1903 svn_error_t *
1904 svn_fs_fs__ensure_dir_exists(const char *path,
1905                              const char *fs_path,
1906                              apr_pool_t *pool)
1907 {
1908   svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
1909   if (err && APR_STATUS_IS_EEXIST(err->apr_err))
1910     {
1911       svn_error_clear(err);
1912       return SVN_NO_ERROR;
1913     }
1914   SVN_ERR(err);
1915
1916   /* We successfully created a new directory.  Dup the permissions
1917      from FS->path. */
1918   return svn_io_copy_perms(fs_path, path, pool);
1919 }
1920
1921 /* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to
1922    'svn_string_t *' node revision IDs.  Use POOL for allocations. */
1923 static svn_error_t *
1924 get_node_origins_from_file(svn_fs_t *fs,
1925                            apr_hash_t **node_origins,
1926                            const char *node_origins_file,
1927                            apr_pool_t *pool)
1928 {
1929   apr_file_t *fd;
1930   svn_error_t *err;
1931   svn_stream_t *stream;
1932
1933   *node_origins = NULL;
1934   err = svn_io_file_open(&fd, node_origins_file,
1935                          APR_READ, APR_OS_DEFAULT, pool);
1936   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
1937     {
1938       svn_error_clear(err);
1939       return SVN_NO_ERROR;
1940     }
1941   SVN_ERR(err);
1942
1943   stream = svn_stream_from_aprfile2(fd, FALSE, pool);
1944   *node_origins = apr_hash_make(pool);
1945   err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool);
1946   if (err)
1947     return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"),
1948                                  node_origins_file);
1949   return svn_stream_close(stream);
1950 }
1951
1952 svn_error_t *
1953 svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id,
1954                            svn_fs_t *fs,
1955                            const svn_fs_fs__id_part_t *node_id,
1956                            apr_pool_t *pool)
1957 {
1958   apr_hash_t *node_origins;
1959
1960   *origin_id = NULL;
1961   SVN_ERR(get_node_origins_from_file(fs, &node_origins,
1962                                      svn_fs_fs__path_node_origin(fs, node_id,
1963                                                                  pool),
1964                                      pool));
1965   if (node_origins)
1966     {
1967       char node_id_ptr[SVN_INT64_BUFFER_SIZE];
1968       apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number);
1969       svn_string_t *origin_id_str
1970         = apr_hash_get(node_origins, node_id_ptr, len);
1971
1972       if (origin_id_str)
1973         SVN_ERR(svn_fs_fs__id_parse(origin_id,
1974                                     apr_pstrdup(pool, origin_id_str->data),
1975                                     pool));
1976     }
1977   return SVN_NO_ERROR;
1978 }
1979
1980
1981 /* Helper for svn_fs_fs__set_node_origin.  Takes a NODE_ID/NODE_REV_ID
1982    pair and adds it to the NODE_ORIGINS_PATH file.  */
1983 static svn_error_t *
1984 set_node_origins_for_file(svn_fs_t *fs,
1985                           const char *node_origins_path,
1986                           const svn_fs_fs__id_part_t *node_id,
1987                           svn_string_t *node_rev_id,
1988                           apr_pool_t *pool)
1989 {
1990   const char *path_tmp;
1991   svn_stream_t *stream;
1992   apr_hash_t *origins_hash;
1993   svn_string_t *old_node_rev_id;
1994
1995   /* the hash serialization functions require strings as keys */
1996   char node_id_ptr[SVN_INT64_BUFFER_SIZE];
1997   apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number);
1998
1999   SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path,
2000                                                        PATH_NODE_ORIGINS_DIR,
2001                                                        pool),
2002                                        fs->path, pool));
2003
2004   /* Read the previously existing origins (if any), and merge our
2005      update with it. */
2006   SVN_ERR(get_node_origins_from_file(fs, &origins_hash,
2007                                      node_origins_path, pool));
2008   if (! origins_hash)
2009     origins_hash = apr_hash_make(pool);
2010
2011   old_node_rev_id = apr_hash_get(origins_hash, node_id_ptr, len);
2012
2013   if (old_node_rev_id && !svn_string_compare(node_rev_id, old_node_rev_id))
2014     return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2015                              _("Node origin for '%s' exists with a different "
2016                                "value (%s) than what we were about to store "
2017                                "(%s)"),
2018                              node_id_ptr, old_node_rev_id->data,
2019                              node_rev_id->data);
2020
2021   apr_hash_set(origins_hash, node_id_ptr, len, node_rev_id);
2022
2023   /* Sure, there's a race condition here.  Two processes could be
2024      trying to add different cache elements to the same file at the
2025      same time, and the entries added by the first one to write will
2026      be lost.  But this is just a cache of reconstructible data, so
2027      we'll accept this problem in return for not having to deal with
2028      locking overhead. */
2029
2030   /* Create a temporary file, write out our hash, and close the file. */
2031   SVN_ERR(svn_stream_open_unique(&stream, &path_tmp,
2032                                  svn_dirent_dirname(node_origins_path, pool),
2033                                  svn_io_file_del_none, pool, pool));
2034   SVN_ERR(svn_hash_write2(origins_hash, stream, SVN_HASH_TERMINATOR, pool));
2035   SVN_ERR(svn_stream_close(stream));
2036
2037   /* Rename the temp file as the real destination */
2038   return svn_io_file_rename(path_tmp, node_origins_path, pool);
2039 }
2040
2041
2042 svn_error_t *
2043 svn_fs_fs__set_node_origin(svn_fs_t *fs,
2044                            const svn_fs_fs__id_part_t *node_id,
2045                            const svn_fs_id_t *node_rev_id,
2046                            apr_pool_t *pool)
2047 {
2048   svn_error_t *err;
2049   const char *filename = svn_fs_fs__path_node_origin(fs, node_id, pool);
2050
2051   err = set_node_origins_for_file(fs, filename,
2052                                   node_id,
2053                                   svn_fs_fs__id_unparse(node_rev_id, pool),
2054                                   pool);
2055   if (err && APR_STATUS_IS_EACCES(err->apr_err))
2056     {
2057       /* It's just a cache; stop trying if I can't write. */
2058       svn_error_clear(err);
2059       err = NULL;
2060     }
2061   return svn_error_trace(err);
2062 }
2063
2064
2065 \f
2066 /*** Revisions ***/
2067
2068 svn_error_t *
2069 svn_fs_fs__revision_prop(svn_string_t **value_p,
2070                          svn_fs_t *fs,
2071                          svn_revnum_t rev,
2072                          const char *propname,
2073                          apr_pool_t *pool)
2074 {
2075   apr_hash_t *table;
2076
2077   SVN_ERR(svn_fs__check_fs(fs, TRUE));
2078   SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, pool));
2079
2080   *value_p = svn_hash_gets(table, propname);
2081
2082   return SVN_NO_ERROR;
2083 }
2084
2085
2086 /* Baton used for change_rev_prop_body below. */
2087 struct change_rev_prop_baton {
2088   svn_fs_t *fs;
2089   svn_revnum_t rev;
2090   const char *name;
2091   const svn_string_t *const *old_value_p;
2092   const svn_string_t *value;
2093 };
2094
2095 /* The work-horse for svn_fs_fs__change_rev_prop, called with the FS
2096    write lock.  This implements the svn_fs_fs__with_write_lock()
2097    'body' callback type.  BATON is a 'struct change_rev_prop_baton *'. */
2098 static svn_error_t *
2099 change_rev_prop_body(void *baton, apr_pool_t *pool)
2100 {
2101   struct change_rev_prop_baton *cb = baton;
2102   apr_hash_t *table;
2103
2104   SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, pool));
2105
2106   if (cb->old_value_p)
2107     {
2108       const svn_string_t *wanted_value = *cb->old_value_p;
2109       const svn_string_t *present_value = svn_hash_gets(table, cb->name);
2110       if ((!wanted_value != !present_value)
2111           || (wanted_value && present_value
2112               && !svn_string_compare(wanted_value, present_value)))
2113         {
2114           /* What we expected isn't what we found. */
2115           return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
2116                                    _("revprop '%s' has unexpected value in "
2117                                      "filesystem"),
2118                                    cb->name);
2119         }
2120       /* Fall through. */
2121     }
2122   svn_hash_sets(table, cb->name, cb->value);
2123
2124   return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool);
2125 }
2126
2127 svn_error_t *
2128 svn_fs_fs__change_rev_prop(svn_fs_t *fs,
2129                            svn_revnum_t rev,
2130                            const char *name,
2131                            const svn_string_t *const *old_value_p,
2132                            const svn_string_t *value,
2133                            apr_pool_t *pool)
2134 {
2135   struct change_rev_prop_baton cb;
2136
2137   SVN_ERR(svn_fs__check_fs(fs, TRUE));
2138
2139   cb.fs = fs;
2140   cb.rev = rev;
2141   cb.name = name;
2142   cb.old_value_p = old_value_p;
2143   cb.value = value;
2144
2145   return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool);
2146 }
2147
2148 \f
2149 svn_error_t *
2150 svn_fs_fs__info_format(int *fs_format,
2151                        svn_version_t **supports_version,
2152                        svn_fs_t *fs,
2153                        apr_pool_t *result_pool,
2154                        apr_pool_t *scratch_pool)
2155 {
2156   fs_fs_data_t *ffd = fs->fsap_data;
2157   *fs_format = ffd->format;
2158   *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
2159
2160   (*supports_version)->major = SVN_VER_MAJOR;
2161   (*supports_version)->minor = 1;
2162   (*supports_version)->patch = 0;
2163   (*supports_version)->tag = "";
2164
2165   switch (ffd->format)
2166     {
2167     case 1:
2168       break;
2169     case 2:
2170       (*supports_version)->minor = 4;
2171       break;
2172     case 3:
2173       (*supports_version)->minor = 5;
2174       break;
2175     case 4:
2176       (*supports_version)->minor = 6;
2177       break;
2178     case 6:
2179       (*supports_version)->minor = 8;
2180       break;
2181     case 7:
2182       (*supports_version)->minor = 9;
2183       break;
2184 #ifdef SVN_DEBUG
2185 # if SVN_FS_FS__FORMAT_NUMBER != 7
2186 #  error "Need to add a 'case' statement here"
2187 # endif
2188 #endif
2189     }
2190
2191   return SVN_NO_ERROR;
2192 }
2193
2194 svn_error_t *
2195 svn_fs_fs__info_config_files(apr_array_header_t **files,
2196                              svn_fs_t *fs,
2197                              apr_pool_t *result_pool,
2198                              apr_pool_t *scratch_pool)
2199 {
2200   *files = apr_array_make(result_pool, 1, sizeof(const char *));
2201   APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG,
2202                                                          result_pool);
2203   return SVN_NO_ERROR;
2204 }