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