1 /*
2 * fs_loader.c: Front-end to the various FS back ends
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25 #include <string.h>
26 #include <apr.h>
27 #include <apr_hash.h>
28 #include <apr_md5.h>
29 #include <apr_thread_mutex.h>
30 #include <apr_uuid.h>
31 #include <apr_strings.h>
32
33 #include "svn_hash.h"
34 #include "svn_ctype.h"
35 #include "svn_types.h"
36 #include "svn_dso.h"
37 #include "svn_version.h"
38 #include "svn_fs.h"
39 #include "svn_path.h"
40 #include "svn_xml.h"
41 #include "svn_pools.h"
42 #include "svn_string.h"
43 #include "svn_private_config.h"
44
45 #include "private/svn_fs_private.h"
46 #include "private/svn_fs_util.h"
47 #include "private/svn_utf_private.h"
48 #include "private/svn_mutex.h"
49 #include "private/svn_subr_private.h"
50
51 #include "fs-loader.h"
52
53 /* This is defined by configure on platforms which use configure, but
54 we need to define a fallback for Windows. */
55 #ifndef DEFAULT_FS_TYPE
56 #define DEFAULT_FS_TYPE "fsfs"
57 #endif
58
59 #define FS_TYPE_FILENAME "fs-type"
60
61 /* A pool common to all FS objects. See the documentation on the
62 open/create functions in fs-loader.h and for svn_fs_initialize(). */
63 static apr_pool_t *common_pool;
64 svn_mutex__t *common_pool_lock;
65
66
67 /* --- Utility functions for the loader --- */
68
69 struct fs_type_defn {
70 const char *fs_type;
71 const char *fsap_name;
72 fs_init_func_t initfunc;
73 struct fs_type_defn *next;
74 };
75
76 static struct fs_type_defn base_defn =
77 {
78 SVN_FS_TYPE_BDB, "base",
79 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
80 svn_fs_base__init,
81 #else
82 NULL,
83 #endif
84 NULL /* End of static list: this needs to be reset to NULL if the
85 common_pool used when setting it has been cleared. */
86 };
87
88 static struct fs_type_defn fsfs_defn =
89 {
90 SVN_FS_TYPE_FSFS, "fs",
91 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
92 svn_fs_fs__init,
93 #else
94 NULL,
95 #endif
96 &base_defn
97 };
98
99 static struct fs_type_defn *fs_modules = &fsfs_defn;
100
101
102 static svn_error_t *
load_module(fs_init_func_t * initfunc,const char * name,apr_pool_t * pool)103 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
104 {
105 *initfunc = NULL;
106
107 #if defined(SVN_USE_DSO) && APR_HAS_DSO
108 {
109 apr_dso_handle_t *dso;
110 apr_dso_handle_sym_t symbol;
111 const char *libname;
112 const char *funcname;
113 apr_status_t status;
114 const char *p;
115
116 /* Demand a simple alphanumeric name so that the generated DSO
117 name is sensible. */
118 for (p = name; *p; ++p)
119 if (!svn_ctype_isalnum(*p))
120 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
121 _("Invalid name for FS type '%s'"),
122 name);
123
124 libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
125 name, SVN_VER_MAJOR, SVN_SOVERSION);
126 funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
127
128 /* Find/load the specified library. If we get an error, assume
129 the library doesn't exist. The library will be unloaded when
130 pool is destroyed. */
131 SVN_ERR(svn_dso_load(&dso, libname));
132 if (! dso)
133 return SVN_NO_ERROR;
134
135 /* find the initialization routine */
136 status = apr_dso_sym(&symbol, dso, funcname);
137 if (status)
138 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
139 libname, funcname);
140
141 *initfunc = (fs_init_func_t) symbol;
142 }
143 #endif /* APR_HAS_DSO */
144
145 return SVN_NO_ERROR;
146 }
147
148 /* Fetch a library vtable by a pointer into the library definitions array. */
149 static svn_error_t *
get_library_vtable_direct(fs_library_vtable_t ** vtable,const struct fs_type_defn * fst,apr_pool_t * pool)150 get_library_vtable_direct(fs_library_vtable_t **vtable,
151 const struct fs_type_defn *fst,
152 apr_pool_t *pool)
153 {
154 fs_init_func_t initfunc = NULL;
155 const svn_version_t *my_version = svn_fs_version();
156 const svn_version_t *fs_version;
157
158 initfunc = fst->initfunc;
159 if (! initfunc)
160 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
161
162 if (! initfunc)
163 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
164 _("Failed to load module for FS type '%s'"),
165 fst->fs_type);
166
167 {
168 /* Per our API compatibility rules, we cannot ensure that
169 svn_fs_initialize is called by the application. If not, we
170 cannot create the common pool and lock in a thread-safe fashion,
171 nor can we clean up the common pool if libsvn_fs is dynamically
172 unloaded. This function makes a best effort by creating the
173 common pool as a child of the global pool; the window of failure
174 due to thread collision is small. */
175 if (!common_pool)
176 SVN_ERR(svn_fs_initialize(NULL));
177
178 /* Invoke the FS module's initfunc function with the common
179 pool protected by a lock. */
180 SVN_MUTEX__WITH_LOCK(common_pool_lock,
181 initfunc(my_version, vtable, common_pool));
182 }
183 fs_version = (*vtable)->get_version();
184 if (!svn_ver_equal(my_version, fs_version))
185 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
186 _("Mismatched FS module version for '%s':"
187 " found %d.%d.%d%s,"
188 " expected %d.%d.%d%s"),
189 fst->fs_type,
190 my_version->major, my_version->minor,
191 my_version->patch, my_version->tag,
192 fs_version->major, fs_version->minor,
193 fs_version->patch, fs_version->tag);
194 return SVN_NO_ERROR;
195 }
196
197 #if defined(SVN_USE_DSO) && APR_HAS_DSO
198 /* Return *FST for the third party FS_TYPE */
199 static svn_error_t *
get_or_allocate_third(struct fs_type_defn ** fst,const char * fs_type)200 get_or_allocate_third(struct fs_type_defn **fst,
201 const char *fs_type)
202 {
203 while (*fst)
204 {
205 if (strcmp(fs_type, (*fst)->fs_type) == 0)
206 return SVN_NO_ERROR;
207 fst = &(*fst)->next;
208 }
209
210 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
211 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
212 (*fst)->fsap_name = (*fst)->fs_type;
213 (*fst)->initfunc = NULL;
214 (*fst)->next = NULL;
215
216 return SVN_NO_ERROR;
217 }
218 #endif
219
220 /* Fetch a library vtable by FS type. */
221 static svn_error_t *
get_library_vtable(fs_library_vtable_t ** vtable,const char * fs_type,apr_pool_t * pool)222 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
223 apr_pool_t *pool)
224 {
225 struct fs_type_defn **fst = &fs_modules;
226 svn_boolean_t known = FALSE;
227
228 /* There are two FS module definitions known at compile time. We
229 want to check these without any locking overhead even when
230 dynamic third party modules are enabled. The third party modules
231 cannot be checked until the lock is held. */
232 if (strcmp(fs_type, (*fst)->fs_type) == 0)
233 known = TRUE;
234 else
235 {
236 fst = &(*fst)->next;
237 if (strcmp(fs_type, (*fst)->fs_type) == 0)
238 known = TRUE;
239 }
240
241 #if defined(SVN_USE_DSO) && APR_HAS_DSO
242 /* Third party FS modules that are unknown at compile time.
243
244 A third party FS is identified by the file fs-type containing a
245 third party name, say "foo". The loader will load the DSO with
246 the name "libsvn_fs_foo" and use the entry point with the name
247 "svn_fs_foo__init".
248
249 Note: the BDB and FSFS modules don't follow this naming scheme
250 and this allows them to be used to test the third party loader.
251 Change the content of fs-type to "base" in a BDB filesystem or to
252 "fs" in an FSFS filesystem and they will be loaded as third party
253 modules. */
254 if (!known)
255 {
256 fst = &(*fst)->next;
257 if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */
258 SVN_ERR(svn_fs_initialize(NULL));
259 SVN_MUTEX__WITH_LOCK(common_pool_lock,
260 get_or_allocate_third(fst, fs_type));
261 known = TRUE;
262 }
263 #endif
264 if (!known)
265 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
266 _("Unknown FS type '%s'"), fs_type);
267 return get_library_vtable_direct(vtable, *fst, pool);
268 }
269
270 svn_error_t *
svn_fs_type(const char ** fs_type,const char * path,apr_pool_t * pool)271 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
272 {
273 const char *filename;
274 char buf[128];
275 svn_error_t *err;
276 apr_file_t *file;
277 apr_size_t len;
278
279 /* Read the fsap-name file to get the FSAP name, or assume the (old)
280 default. For old repositories I suppose we could check some
281 other file, DB_CONFIG or strings say, but for now just check the
282 directory exists. */
283 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
284 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
285 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
286 {
287 svn_node_kind_t kind;
288 svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
289 if (err2)
290 {
291 svn_error_clear(err2);
292 return err;
293 }
294 if (kind == svn_node_dir)
295 {
296 svn_error_clear(err);
297 *fs_type = SVN_FS_TYPE_BDB;
298 return SVN_NO_ERROR;
299 }
300 return err;
301 }
302 else if (err)
303 return err;
304
305 len = sizeof(buf);
306 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
307 SVN_ERR(svn_io_file_close(file, pool));
308 *fs_type = apr_pstrdup(pool, buf);
309
310 return SVN_NO_ERROR;
311 }
312
313 /* Fetch the library vtable for an existing FS. */
314 static svn_error_t *
fs_library_vtable(fs_library_vtable_t ** vtable,const char * path,apr_pool_t * pool)315 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
316 apr_pool_t *pool)
317 {
318 const char *fs_type;
319
320 SVN_ERR(svn_fs_type(&fs_type, path, pool));
321
322 /* Fetch the library vtable by name, now that we've chosen one. */
323 return svn_error_trace(get_library_vtable(vtable, fs_type, pool));
324 }
325
326 static svn_error_t *
write_fs_type(const char * path,const char * fs_type,apr_pool_t * pool)327 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
328 {
329 const char *filename;
330 apr_file_t *file;
331
332 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
333 SVN_ERR(svn_io_file_open(&file, filename,
334 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
335 APR_OS_DEFAULT, pool));
336 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
337 pool));
338 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
339 return svn_error_trace(svn_io_file_close(file, pool));
340 }
341
342
343 /* --- Functions for operating on filesystems by pathname --- */
344
uninit(void * data)345 static apr_status_t uninit(void *data)
346 {
347 common_pool = NULL;
348 return APR_SUCCESS;
349 }
350
351 svn_error_t *
svn_fs_initialize(apr_pool_t * pool)352 svn_fs_initialize(apr_pool_t *pool)
353 {
354 /* Protect against multiple calls. */
355 if (common_pool)
356 return SVN_NO_ERROR;
357
358 common_pool = svn_pool_create(pool);
359 base_defn.next = NULL;
360 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
361
362 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
363 ### (via libsvn_ra_local say) since the global common_pool will live
364 ### longer than the DSO, which gets unloaded when the pool used to
365 ### load it is cleared, and so when the handler runs it will refer to
366 ### a function that no longer exists. libsvn_ra_local attempts to
367 ### work around this by explicitly calling svn_fs_initialize. */
368 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
369 return SVN_NO_ERROR;
370 }
371
372 /* A default warning handling function. */
373 static void
default_warning_func(void * baton,svn_error_t * err)374 default_warning_func(void *baton, svn_error_t *err)
375 {
376 /* The one unforgiveable sin is to fail silently. Dumping to stderr
377 or /dev/tty is not acceptable default behavior for server
378 processes, since those may both be equivalent to /dev/null. */
379 SVN_ERR_MALFUNCTION_NO_RETURN();
380 }
381
382 svn_error_t *
svn_fs__path_valid(const char * path,apr_pool_t * pool)383 svn_fs__path_valid(const char *path, apr_pool_t *pool)
384 {
385 /* UTF-8 encoded string without NULs. */
386 if (! svn_utf__cstring_is_valid(path))
387 {
388 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
389 _("Path '%s' is not in UTF-8"), path);
390 }
391
392 /* No "." or ".." elements. */
393 if (svn_path_is_backpath_present(path)
394 || svn_path_is_dotpath_present(path))
395 {
396 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
397 _("Path '%s' contains '.' or '..' element"),
398 path);
399 }
400
401 /* That's good enough. */
402 return SVN_NO_ERROR;
403 }
404
405 /* Allocate svn_fs_t structure. */
406 static svn_fs_t *
fs_new(apr_hash_t * fs_config,apr_pool_t * pool)407 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
408 {
409 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
410 fs->pool = pool;
411 fs->path = NULL;
412 fs->warning = default_warning_func;
413 fs->warning_baton = NULL;
414 fs->config = fs_config;
415 fs->access_ctx = NULL;
416 fs->vtable = NULL;
417 fs->fsap_data = NULL;
418 fs->uuid = NULL;
419 return fs;
420 }
421
422 svn_fs_t *
svn_fs_new(apr_hash_t * fs_config,apr_pool_t * pool)423 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
424 {
425 return fs_new(fs_config, pool);
426 }
427
428 void
svn_fs_set_warning_func(svn_fs_t * fs,svn_fs_warning_callback_t warning,void * warning_baton)429 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
430 void *warning_baton)
431 {
432 fs->warning = warning;
433 fs->warning_baton = warning_baton;
434 }
435
436 svn_error_t *
svn_fs_create(svn_fs_t ** fs_p,const char * path,apr_hash_t * fs_config,apr_pool_t * pool)437 svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
438 apr_pool_t *pool)
439 {
440 fs_library_vtable_t *vtable;
441
442 const char *fs_type = svn_hash__get_cstring(fs_config,
443 SVN_FS_CONFIG_FS_TYPE,
444 DEFAULT_FS_TYPE);
445 SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
446
447 /* Create the FS directory and write out the fsap-name file. */
448 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
449 SVN_ERR(write_fs_type(path, fs_type, pool));
450
451 /* Perform the actual creation. */
452 *fs_p = fs_new(fs_config, pool);
453
454 SVN_MUTEX__WITH_LOCK(common_pool_lock,
455 vtable->create(*fs_p, path, pool, common_pool));
456 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
457
458 return SVN_NO_ERROR;
459 }
460
461 svn_error_t *
svn_fs_open(svn_fs_t ** fs_p,const char * path,apr_hash_t * fs_config,apr_pool_t * pool)462 svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
463 apr_pool_t *pool)
464 {
465 fs_library_vtable_t *vtable;
466
467 SVN_ERR(fs_library_vtable(&vtable, path, pool));
468 *fs_p = fs_new(fs_config, pool);
469 SVN_MUTEX__WITH_LOCK(common_pool_lock,
470 vtable->open_fs(*fs_p, path, pool, common_pool));
471 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
472
473 return SVN_NO_ERROR;
474 }
475
476 svn_error_t *
svn_fs_upgrade(const char * path,apr_pool_t * pool)477 svn_fs_upgrade(const char *path, apr_pool_t *pool)
478 {
479 fs_library_vtable_t *vtable;
480 svn_fs_t *fs;
481
482 SVN_ERR(fs_library_vtable(&vtable, path, pool));
483 fs = fs_new(NULL, pool);
484
485 SVN_MUTEX__WITH_LOCK(common_pool_lock,
486 vtable->upgrade_fs(fs, path, pool, common_pool));
487 return SVN_NO_ERROR;
488 }
489
490 svn_error_t *
svn_fs_verify(const char * path,apr_hash_t * fs_config,svn_revnum_t start,svn_revnum_t end,svn_fs_progress_notify_func_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)491 svn_fs_verify(const char *path,
492 apr_hash_t *fs_config,
493 svn_revnum_t start,
494 svn_revnum_t end,
495 svn_fs_progress_notify_func_t notify_func,
496 void *notify_baton,
497 svn_cancel_func_t cancel_func,
498 void *cancel_baton,
499 apr_pool_t *pool)
500 {
501 fs_library_vtable_t *vtable;
502 svn_fs_t *fs;
503
504 SVN_ERR(fs_library_vtable(&vtable, path, pool));
505 fs = fs_new(fs_config, pool);
506
507 SVN_MUTEX__WITH_LOCK(common_pool_lock,
508 vtable->verify_fs(fs, path, start, end,
509 notify_func, notify_baton,
510 cancel_func, cancel_baton,
511 pool, common_pool));
512 return SVN_NO_ERROR;
513 }
514
515 const char *
svn_fs_path(svn_fs_t * fs,apr_pool_t * pool)516 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
517 {
518 return apr_pstrdup(pool, fs->path);
519 }
520
521 apr_hash_t *
svn_fs_config(svn_fs_t * fs,apr_pool_t * pool)522 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
523 {
524 if (fs->config)
525 return apr_hash_copy(pool, fs->config);
526
527 return NULL;
528 }
529
530 svn_error_t *
svn_fs_delete_fs(const char * path,apr_pool_t * pool)531 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
532 {
533 fs_library_vtable_t *vtable;
534
535 SVN_ERR(fs_library_vtable(&vtable, path, pool));
536 return svn_error_trace(vtable->delete_fs(path, pool));
537 }
538
539 svn_error_t *
svn_fs_hotcopy2(const char * src_path,const char * dst_path,svn_boolean_t clean,svn_boolean_t incremental,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)540 svn_fs_hotcopy2(const char *src_path, const char *dst_path,
541 svn_boolean_t clean, svn_boolean_t incremental,
542 svn_cancel_func_t cancel_func, void *cancel_baton,
543 apr_pool_t *scratch_pool)
544 {
545 fs_library_vtable_t *vtable;
546 const char *src_fs_type;
547 svn_fs_t *src_fs;
548 svn_fs_t *dst_fs;
549 const char *dst_fs_type;
550 svn_node_kind_t dst_kind;
551
552 if (strcmp(src_path, dst_path) == 0)
553 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
554 _("Hotcopy source and destination are equal"));
555
556 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
557 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
558 src_fs = fs_new(NULL, scratch_pool);
559 dst_fs = fs_new(NULL, scratch_pool);
560
561 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
562 if (dst_kind == svn_node_file)
563 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
564 _("'%s' already exists and is a file"),
565 svn_dirent_local_style(dst_path,
566 scratch_pool));
567 if (dst_kind == svn_node_unknown)
568 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
569 _("'%s' already exists and has an unknown "
570 "node kind"),
571 svn_dirent_local_style(dst_path,
572 scratch_pool));
573 if (dst_kind == svn_node_dir)
574 {
575 svn_node_kind_t type_file_kind;
576
577 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
578 FS_TYPE_FILENAME,
579 scratch_pool),
580 &type_file_kind, scratch_pool));
581 if (type_file_kind != svn_node_none)
582 {
583 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
584 if (strcmp(src_fs_type, dst_fs_type) != 0)
585 return svn_error_createf(
586 SVN_ERR_ILLEGAL_TARGET, NULL,
587 _("The filesystem type of the hotcopy source "
588 "('%s') does not match the filesystem "
589 "type of the hotcopy destination ('%s')"),
590 src_fs_type, dst_fs_type);
591 }
592 }
593
594 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
595 incremental, cancel_func, cancel_baton,
596 scratch_pool));
597 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
598 }
599
600 svn_error_t *
svn_fs_hotcopy(const char * src_path,const char * dest_path,svn_boolean_t clean,apr_pool_t * pool)601 svn_fs_hotcopy(const char *src_path, const char *dest_path,
602 svn_boolean_t clean, apr_pool_t *pool)
603 {
604 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean,
605 FALSE, NULL, NULL, pool));
606 }
607
608 svn_error_t *
svn_fs_pack(const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)609 svn_fs_pack(const char *path,
610 svn_fs_pack_notify_t notify_func,
611 void *notify_baton,
612 svn_cancel_func_t cancel_func,
613 void *cancel_baton,
614 apr_pool_t *pool)
615 {
616 fs_library_vtable_t *vtable;
617 svn_fs_t *fs;
618
619 SVN_ERR(fs_library_vtable(&vtable, path, pool));
620 fs = fs_new(NULL, pool);
621
622 SVN_MUTEX__WITH_LOCK(common_pool_lock,
623 vtable->pack_fs(fs, path, notify_func, notify_baton,
624 cancel_func, cancel_baton, pool,
625 common_pool));
626 return SVN_NO_ERROR;
627 }
628
629 svn_error_t *
svn_fs_recover(const char * path,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)630 svn_fs_recover(const char *path,
631 svn_cancel_func_t cancel_func, void *cancel_baton,
632 apr_pool_t *pool)
633 {
634 fs_library_vtable_t *vtable;
635 svn_fs_t *fs;
636
637 SVN_ERR(fs_library_vtable(&vtable, path, pool));
638 fs = fs_new(NULL, pool);
639
640 SVN_MUTEX__WITH_LOCK(common_pool_lock,
641 vtable->open_fs_for_recovery(fs, path, pool,
642 common_pool));
643 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
644 pool));
645 }
646
647 svn_error_t *
svn_fs_verify_root(svn_fs_root_t * root,apr_pool_t * scratch_pool)648 svn_fs_verify_root(svn_fs_root_t *root,
649 apr_pool_t *scratch_pool)
650 {
651 svn_fs_t *fs = root->fs;
652 SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
653
654 return SVN_NO_ERROR;
655 }
656
657 svn_error_t *
svn_fs_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * pool)658 svn_fs_freeze(svn_fs_t *fs,
659 svn_fs_freeze_func_t freeze_func,
660 void *freeze_baton,
661 apr_pool_t *pool)
662 {
663 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
664
665 return SVN_NO_ERROR;
666 }
667
668
669 /* --- Berkeley-specific functions --- */
670
671 svn_error_t *
svn_fs_create_berkeley(svn_fs_t * fs,const char * path)672 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
673 {
674 fs_library_vtable_t *vtable;
675
676 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
677
678 /* Create the FS directory and write out the fsap-name file. */
679 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
680 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
681
682 /* Perform the actual creation. */
683 SVN_MUTEX__WITH_LOCK(common_pool_lock,
684 vtable->create(fs, path, fs->pool, common_pool));
685 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
686
687 return SVN_NO_ERROR;
688 }
689
690 svn_error_t *
svn_fs_open_berkeley(svn_fs_t * fs,const char * path)691 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
692 {
693 fs_library_vtable_t *vtable;
694
695 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
696 SVN_MUTEX__WITH_LOCK(common_pool_lock,
697 vtable->open_fs(fs, path, fs->pool, common_pool));
698 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
699
700 return SVN_NO_ERROR;
701 }
702
703 const char *
svn_fs_berkeley_path(svn_fs_t * fs,apr_pool_t * pool)704 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
705 {
706 return svn_fs_path(fs, pool);
707 }
708
709 svn_error_t *
svn_fs_delete_berkeley(const char * path,apr_pool_t * pool)710 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
711 {
712 return svn_error_trace(svn_fs_delete_fs(path, pool));
713 }
714
715 svn_error_t *
svn_fs_hotcopy_berkeley(const char * src_path,const char * dest_path,svn_boolean_t clean_logs,apr_pool_t * pool)716 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
717 svn_boolean_t clean_logs, apr_pool_t *pool)
718 {
719 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs,
720 FALSE, NULL, NULL, pool));
721 }
722
723 svn_error_t *
svn_fs_berkeley_recover(const char * path,apr_pool_t * pool)724 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
725 {
726 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
727 }
728
729 svn_error_t *
svn_fs_set_berkeley_errcall(svn_fs_t * fs,void (* handler)(const char * errpfx,char * msg))730 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
731 void (*handler)(const char *errpfx, char *msg))
732 {
733 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
734 }
735
736 svn_error_t *
svn_fs_berkeley_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)737 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
738 const char *path,
739 svn_boolean_t only_unused,
740 apr_pool_t *pool)
741 {
742 fs_library_vtable_t *vtable;
743
744 SVN_ERR(fs_library_vtable(&vtable, path, pool));
745 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
746 pool));
747 }
748
749
750 /* --- Transaction functions --- */
751
752 svn_error_t *
svn_fs_begin_txn2(svn_fs_txn_t ** txn_p,svn_fs_t * fs,svn_revnum_t rev,apr_uint32_t flags,apr_pool_t * pool)753 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
754 apr_uint32_t flags, apr_pool_t *pool)
755 {
756 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
757 }
758
759
760 svn_error_t *
svn_fs_begin_txn(svn_fs_txn_t ** txn_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)761 svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
762 apr_pool_t *pool)
763 {
764 return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool));
765 }
766
767
768 svn_error_t *
svn_fs_commit_txn(const char ** conflict_p,svn_revnum_t * new_rev,svn_fs_txn_t * txn,apr_pool_t * pool)769 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
770 svn_fs_txn_t *txn, apr_pool_t *pool)
771 {
772 svn_error_t *err;
773
774 *new_rev = SVN_INVALID_REVNUM;
775 if (conflict_p)
776 *conflict_p = NULL;
777
778 err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
779
780 #ifdef SVN_DEBUG
781 /* Check postconditions. */
782 if (conflict_p)
783 {
784 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
785 err);
786 SVN_ERR_ASSERT_E((*conflict_p != NULL)
787 == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
788 err);
789 }
790 #endif
791
792 SVN_ERR(err);
793
794 #ifdef PACK_AFTER_EVERY_COMMIT
795 {
796 svn_fs_t *fs = txn->fs;
797 const char *fs_path = svn_fs_path(fs, pool);
798 err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool);
799 if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
800 /* Pre-1.6 filesystem. */
801 svn_error_clear(err);
802 else if (err)
803 /* Real error. */
804 return svn_error_trace(err);
805 }
806 #endif
807
808 return SVN_NO_ERROR;
809 }
810
811
812 svn_error_t *
svn_fs_abort_txn(svn_fs_txn_t * txn,apr_pool_t * pool)813 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
814 {
815 return svn_error_trace(txn->vtable->abort(txn, pool));
816 }
817
818 svn_error_t *
svn_fs_purge_txn(svn_fs_t * fs,const char * txn_id,apr_pool_t * pool)819 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
820 {
821 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
822 }
823
824 svn_error_t *
svn_fs_txn_name(const char ** name_p,svn_fs_txn_t * txn,apr_pool_t * pool)825 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
826 {
827 *name_p = apr_pstrdup(pool, txn->id);
828 return SVN_NO_ERROR;
829 }
830
831 svn_revnum_t
svn_fs_txn_base_revision(svn_fs_txn_t * txn)832 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
833 {
834 return txn->base_rev;
835 }
836
837 svn_error_t *
svn_fs_open_txn(svn_fs_txn_t ** txn,svn_fs_t * fs,const char * name,apr_pool_t * pool)838 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
839 apr_pool_t *pool)
840 {
841 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
842 }
843
844 svn_error_t *
svn_fs_list_transactions(apr_array_header_t ** names_p,svn_fs_t * fs,apr_pool_t * pool)845 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
846 apr_pool_t *pool)
847 {
848 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
849 }
850
851 svn_error_t *
svn_fs_txn_prop(svn_string_t ** value_p,svn_fs_txn_t * txn,const char * propname,apr_pool_t * pool)852 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
853 const char *propname, apr_pool_t *pool)
854 {
855 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
856 }
857
858 svn_error_t *
svn_fs_txn_proplist(apr_hash_t ** table_p,svn_fs_txn_t * txn,apr_pool_t * pool)859 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
860 {
861 return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool));
862 }
863
864 svn_error_t *
svn_fs_change_txn_prop(svn_fs_txn_t * txn,const char * name,const svn_string_t * value,apr_pool_t * pool)865 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
866 const svn_string_t *value, apr_pool_t *pool)
867 {
868 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
869 }
870
871 svn_error_t *
svn_fs_change_txn_props(svn_fs_txn_t * txn,const apr_array_header_t * props,apr_pool_t * pool)872 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
873 apr_pool_t *pool)
874 {
875 return svn_error_trace(txn->vtable->change_props(txn, props, pool));
876 }
877
878
879 /* --- Root functions --- */
880
881 svn_error_t *
svn_fs_revision_root(svn_fs_root_t ** root_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)882 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
883 apr_pool_t *pool)
884 {
885 /* We create a subpool for each root object to allow us to implement
886 svn_fs_close_root. */
887 apr_pool_t *subpool = svn_pool_create(pool);
888 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
889 }
890
891 svn_error_t *
svn_fs_txn_root(svn_fs_root_t ** root_p,svn_fs_txn_t * txn,apr_pool_t * pool)892 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
893 {
894 /* We create a subpool for each root object to allow us to implement
895 svn_fs_close_root. */
896 apr_pool_t *subpool = svn_pool_create(pool);
897 return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
898 }
899
900 void
svn_fs_close_root(svn_fs_root_t * root)901 svn_fs_close_root(svn_fs_root_t *root)
902 {
903 svn_pool_destroy(root->pool);
904 }
905
906 svn_fs_t *
svn_fs_root_fs(svn_fs_root_t * root)907 svn_fs_root_fs(svn_fs_root_t *root)
908 {
909 return root->fs;
910 }
911
912 svn_boolean_t
svn_fs_is_txn_root(svn_fs_root_t * root)913 svn_fs_is_txn_root(svn_fs_root_t *root)
914 {
915 return root->is_txn_root;
916 }
917
918 svn_boolean_t
svn_fs_is_revision_root(svn_fs_root_t * root)919 svn_fs_is_revision_root(svn_fs_root_t *root)
920 {
921 return !root->is_txn_root;
922 }
923
924 const char *
svn_fs_txn_root_name(svn_fs_root_t * root,apr_pool_t * pool)925 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
926 {
927 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
928 }
929
930 svn_revnum_t
svn_fs_txn_root_base_revision(svn_fs_root_t * root)931 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
932 {
933 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
934 }
935
936 svn_revnum_t
svn_fs_revision_root_revision(svn_fs_root_t * root)937 svn_fs_revision_root_revision(svn_fs_root_t *root)
938 {
939 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
940 }
941
942 svn_error_t *
svn_fs_paths_changed2(apr_hash_t ** changed_paths_p,svn_fs_root_t * root,apr_pool_t * pool)943 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
944 apr_pool_t *pool)
945 {
946 return root->vtable->paths_changed(changed_paths_p, root, pool);
947 }
948
949 svn_error_t *
svn_fs_paths_changed(apr_hash_t ** changed_paths_p,svn_fs_root_t * root,apr_pool_t * pool)950 svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
951 apr_pool_t *pool)
952 {
953 apr_hash_t *changed_paths_new_structs;
954 apr_hash_index_t *hi;
955
956 SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
957 *changed_paths_p = apr_hash_make(pool);
958 for (hi = apr_hash_first(pool, changed_paths_new_structs);
959 hi;
960 hi = apr_hash_next(hi))
961 {
962 const void *vkey;
963 apr_ssize_t klen;
964 void *vval;
965 svn_fs_path_change2_t *val;
966 svn_fs_path_change_t *change;
967 apr_hash_this(hi, &vkey, &klen, &vval);
968 val = vval;
969 change = apr_palloc(pool, sizeof(*change));
970 change->node_rev_id = val->node_rev_id;
971 change->change_kind = val->change_kind;
972 change->text_mod = val->text_mod;
973 change->prop_mod = val->prop_mod;
974 apr_hash_set(*changed_paths_p, vkey, klen, change);
975 }
976 return SVN_NO_ERROR;
977 }
978
979 svn_error_t *
svn_fs_check_path(svn_node_kind_t * kind_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)980 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
981 const char *path, apr_pool_t *pool)
982 {
983 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
984 }
985
986 svn_error_t *
svn_fs_node_history(svn_fs_history_t ** history_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)987 svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
988 const char *path, apr_pool_t *pool)
989 {
990 return svn_error_trace(root->vtable->node_history(history_p, root, path,
991 pool));
992 }
993
994 svn_error_t *
svn_fs_is_dir(svn_boolean_t * is_dir,svn_fs_root_t * root,const char * path,apr_pool_t * pool)995 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
996 apr_pool_t *pool)
997 {
998 svn_node_kind_t kind;
999
1000 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1001 *is_dir = (kind == svn_node_dir);
1002 return SVN_NO_ERROR;
1003 }
1004
1005 svn_error_t *
svn_fs_is_file(svn_boolean_t * is_file,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1006 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1007 apr_pool_t *pool)
1008 {
1009 svn_node_kind_t kind;
1010
1011 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1012 *is_file = (kind == svn_node_file);
1013 return SVN_NO_ERROR;
1014 }
1015
1016 svn_error_t *
svn_fs_node_id(const svn_fs_id_t ** id_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1017 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1018 const char *path, apr_pool_t *pool)
1019 {
1020 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1021 }
1022
1023 svn_error_t *
svn_fs_node_created_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1024 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1025 const char *path, apr_pool_t *pool)
1026 {
1027 return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1028 pool));
1029 }
1030
1031 svn_error_t *
svn_fs_node_origin_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1032 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1033 const char *path, apr_pool_t *pool)
1034 {
1035 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1036 pool));
1037 }
1038
1039 svn_error_t *
svn_fs_node_created_path(const char ** created_path,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1040 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1041 const char *path, apr_pool_t *pool)
1042 {
1043 return svn_error_trace(root->vtable->node_created_path(created_path, root,
1044 path, pool));
1045 }
1046
1047 svn_error_t *
svn_fs_node_prop(svn_string_t ** value_p,svn_fs_root_t * root,const char * path,const char * propname,apr_pool_t * pool)1048 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1049 const char *path, const char *propname, apr_pool_t *pool)
1050 {
1051 return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1052 propname, pool));
1053 }
1054
1055 svn_error_t *
svn_fs_node_proplist(apr_hash_t ** table_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1056 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1057 const char *path, apr_pool_t *pool)
1058 {
1059 return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1060 pool));
1061 }
1062
1063 svn_error_t *
svn_fs_change_node_prop(svn_fs_root_t * root,const char * path,const char * name,const svn_string_t * value,apr_pool_t * pool)1064 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1065 const char *name, const svn_string_t *value,
1066 apr_pool_t *pool)
1067 {
1068 return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1069 value, pool));
1070 }
1071
1072 svn_error_t *
svn_fs_props_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,apr_pool_t * pool)1073 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1074 const char *path1, svn_fs_root_t *root2,
1075 const char *path2, apr_pool_t *pool)
1076 {
1077 return svn_error_trace(root1->vtable->props_changed(changed_p,
1078 root1, path1,
1079 root2, path2,
1080 pool));
1081 }
1082
1083 svn_error_t *
svn_fs_copied_from(svn_revnum_t * rev_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1084 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1085 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1086 {
1087 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1088 pool));
1089 }
1090
1091 svn_error_t *
svn_fs_closest_copy(svn_fs_root_t ** root_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1092 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1093 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1094 {
1095 return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1096 root, path, pool));
1097 }
1098
1099 svn_error_t *
svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t * catalog,svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,svn_boolean_t adjust_inherited_mergeinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1100 svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1101 svn_fs_root_t *root,
1102 const apr_array_header_t *paths,
1103 svn_mergeinfo_inheritance_t inherit,
1104 svn_boolean_t include_descendants,
1105 svn_boolean_t adjust_inherited_mergeinfo,
1106 apr_pool_t *result_pool,
1107 apr_pool_t *scratch_pool)
1108 {
1109 return svn_error_trace(root->vtable->get_mergeinfo(
1110 catalog, root, paths, inherit, include_descendants,
1111 adjust_inherited_mergeinfo, result_pool, scratch_pool));
1112 }
1113
1114 svn_error_t *
svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t * catalog,svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,apr_pool_t * pool)1115 svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1116 svn_fs_root_t *root,
1117 const apr_array_header_t *paths,
1118 svn_mergeinfo_inheritance_t inherit,
1119 svn_boolean_t include_descendants,
1120 apr_pool_t *pool)
1121 {
1122 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1123 inherit,
1124 include_descendants,
1125 TRUE, pool, pool));
1126 }
1127
1128 svn_error_t *
svn_fs_merge(const char ** conflict_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,svn_fs_root_t * ancestor_root,const char * ancestor_path,apr_pool_t * pool)1129 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1130 const char *source_path, svn_fs_root_t *target_root,
1131 const char *target_path, svn_fs_root_t *ancestor_root,
1132 const char *ancestor_path, apr_pool_t *pool)
1133 {
1134 return svn_error_trace(target_root->vtable->merge(conflict_p,
1135 source_root, source_path,
1136 target_root, target_path,
1137 ancestor_root,
1138 ancestor_path, pool));
1139 }
1140
1141 svn_error_t *
svn_fs_dir_entries(apr_hash_t ** entries_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1142 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1143 const char *path, apr_pool_t *pool)
1144 {
1145 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1146 pool));
1147 }
1148
1149 svn_error_t *
svn_fs_make_dir(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1150 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1151 {
1152 SVN_ERR(svn_fs__path_valid(path, pool));
1153 return svn_error_trace(root->vtable->make_dir(root, path, pool));
1154 }
1155
1156 svn_error_t *
svn_fs_delete(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1157 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1158 {
1159 return svn_error_trace(root->vtable->delete_node(root, path, pool));
1160 }
1161
1162 svn_error_t *
svn_fs_copy(svn_fs_root_t * from_root,const char * from_path,svn_fs_root_t * to_root,const char * to_path,apr_pool_t * pool)1163 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1164 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1165 {
1166 SVN_ERR(svn_fs__path_valid(to_path, pool));
1167 return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1168 to_root, to_path, pool));
1169 }
1170
1171 svn_error_t *
svn_fs_revision_link(svn_fs_root_t * from_root,svn_fs_root_t * to_root,const char * path,apr_pool_t * pool)1172 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1173 const char *path, apr_pool_t *pool)
1174 {
1175 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1176 path, pool));
1177 }
1178
1179 svn_error_t *
svn_fs_file_length(svn_filesize_t * length_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1180 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1181 const char *path, apr_pool_t *pool)
1182 {
1183 return svn_error_trace(root->vtable->file_length(length_p, root, path,
1184 pool));
1185 }
1186
1187 svn_error_t *
svn_fs_file_checksum(svn_checksum_t ** checksum,svn_checksum_kind_t kind,svn_fs_root_t * root,const char * path,svn_boolean_t force,apr_pool_t * pool)1188 svn_fs_file_checksum(svn_checksum_t **checksum,
1189 svn_checksum_kind_t kind,
1190 svn_fs_root_t *root,
1191 const char *path,
1192 svn_boolean_t force,
1193 apr_pool_t *pool)
1194 {
1195 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1196
1197 if (force && (*checksum == NULL || (*checksum)->kind != kind))
1198 {
1199 svn_stream_t *contents, *checksum_contents;
1200
1201 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1202 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1203 kind, TRUE, pool);
1204
1205 /* This will force a read of any remaining data (which is all of it in
1206 this case) and dump the checksum into checksum->digest. */
1207 SVN_ERR(svn_stream_close(checksum_contents));
1208 }
1209
1210 return SVN_NO_ERROR;
1211 }
1212
1213 svn_error_t *
svn_fs_file_md5_checksum(unsigned char digest[],svn_fs_root_t * root,const char * path,apr_pool_t * pool)1214 svn_fs_file_md5_checksum(unsigned char digest[],
1215 svn_fs_root_t *root,
1216 const char *path,
1217 apr_pool_t *pool)
1218 {
1219 svn_checksum_t *md5sum;
1220
1221 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1222 pool));
1223 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1224
1225 return SVN_NO_ERROR;
1226 }
1227
1228 svn_error_t *
svn_fs_file_contents(svn_stream_t ** contents,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1229 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1230 const char *path, apr_pool_t *pool)
1231 {
1232 return svn_error_trace(root->vtable->file_contents(contents, root, path,
1233 pool));
1234 }
1235
1236 svn_error_t *
svn_fs_try_process_file_contents(svn_boolean_t * success,svn_fs_root_t * root,const char * path,svn_fs_process_contents_func_t processor,void * baton,apr_pool_t * pool)1237 svn_fs_try_process_file_contents(svn_boolean_t *success,
1238 svn_fs_root_t *root,
1239 const char *path,
1240 svn_fs_process_contents_func_t processor,
1241 void* baton,
1242 apr_pool_t *pool)
1243 {
1244 /* if the FS doesn't implement this function, report a "failed" attempt */
1245 if (root->vtable->try_process_file_contents == NULL)
1246 {
1247 *success = FALSE;
1248 return SVN_NO_ERROR;
1249 }
1250
1251 return svn_error_trace(root->vtable->try_process_file_contents(
1252 success,
1253 root, path,
1254 processor, baton, pool));
1255 }
1256
1257 svn_error_t *
svn_fs_make_file(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1258 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1259 {
1260 SVN_ERR(svn_fs__path_valid(path, pool));
1261 return svn_error_trace(root->vtable->make_file(root, path, pool));
1262 }
1263
1264 svn_error_t *
svn_fs_apply_textdelta(svn_txdelta_window_handler_t * contents_p,void ** contents_baton_p,svn_fs_root_t * root,const char * path,const char * base_checksum,const char * result_checksum,apr_pool_t * pool)1265 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1266 void **contents_baton_p, svn_fs_root_t *root,
1267 const char *path, const char *base_checksum,
1268 const char *result_checksum, apr_pool_t *pool)
1269 {
1270 svn_checksum_t *base, *result;
1271
1272 /* TODO: If we ever rev this API, we should make the supplied checksums
1273 svn_checksum_t structs. */
1274 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1275 pool));
1276 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1277 pool));
1278
1279 return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1280 contents_baton_p,
1281 root,
1282 path,
1283 base,
1284 result,
1285 pool));
1286 }
1287
1288 svn_error_t *
svn_fs_apply_text(svn_stream_t ** contents_p,svn_fs_root_t * root,const char * path,const char * result_checksum,apr_pool_t * pool)1289 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1290 const char *path, const char *result_checksum,
1291 apr_pool_t *pool)
1292 {
1293 svn_checksum_t *result;
1294
1295 /* TODO: If we ever rev this API, we should make the supplied checksum an
1296 svn_checksum_t struct. */
1297 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1298 pool));
1299
1300 return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1301 result, pool));
1302 }
1303
1304 svn_error_t *
svn_fs_contents_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,apr_pool_t * pool)1305 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1306 const char *path1, svn_fs_root_t *root2,
1307 const char *path2, apr_pool_t *pool)
1308 {
1309 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1310 root1, path1,
1311 root2, path2,
1312 pool));
1313 }
1314
1315 svn_error_t *
svn_fs_youngest_rev(svn_revnum_t * youngest_p,svn_fs_t * fs,apr_pool_t * pool)1316 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1317 {
1318 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1319 }
1320
1321 svn_error_t *
svn_fs_deltify_revision(svn_fs_t * fs,svn_revnum_t revision,apr_pool_t * pool)1322 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1323 {
1324 return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1325 }
1326
1327 svn_error_t *
svn_fs_revision_prop(svn_string_t ** value_p,svn_fs_t * fs,svn_revnum_t rev,const char * propname,apr_pool_t * pool)1328 svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1329 const char *propname, apr_pool_t *pool)
1330 {
1331 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1332 propname, pool));
1333 }
1334
1335 svn_error_t *
svn_fs_revision_proplist(apr_hash_t ** table_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)1336 svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1337 apr_pool_t *pool)
1338 {
1339 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1340 pool));
1341 }
1342
1343 svn_error_t *
svn_fs_change_rev_prop2(svn_fs_t * fs,svn_revnum_t rev,const char * name,const svn_string_t * const * old_value_p,const svn_string_t * value,apr_pool_t * pool)1344 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1345 const svn_string_t *const *old_value_p,
1346 const svn_string_t *value, apr_pool_t *pool)
1347 {
1348 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1349 old_value_p,
1350 value, pool));
1351 }
1352
1353 svn_error_t *
svn_fs_change_rev_prop(svn_fs_t * fs,svn_revnum_t rev,const char * name,const svn_string_t * value,apr_pool_t * pool)1354 svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1355 const svn_string_t *value, apr_pool_t *pool)
1356 {
1357 return svn_error_trace(
1358 svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool));
1359 }
1360
1361 svn_error_t *
svn_fs_get_file_delta_stream(svn_txdelta_stream_t ** stream_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,apr_pool_t * pool)1362 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1363 svn_fs_root_t *source_root,
1364 const char *source_path,
1365 svn_fs_root_t *target_root,
1366 const char *target_path, apr_pool_t *pool)
1367 {
1368 return svn_error_trace(target_root->vtable->get_file_delta_stream(
1369 stream_p,
1370 source_root, source_path,
1371 target_root, target_path, pool));
1372 }
1373
1374 svn_error_t *
svn_fs_get_uuid(svn_fs_t * fs,const char ** uuid,apr_pool_t * pool)1375 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1376 {
1377 /* If you change this, consider changing svn_fs__identifier(). */
1378 *uuid = apr_pstrdup(pool, fs->uuid);
1379 return SVN_NO_ERROR;
1380 }
1381
1382 svn_error_t *
svn_fs_set_uuid(svn_fs_t * fs,const char * uuid,apr_pool_t * pool)1383 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1384 {
1385 if (! uuid)
1386 {
1387 uuid = svn_uuid_generate(pool);
1388 }
1389 else
1390 {
1391 apr_uuid_t parsed_uuid;
1392 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1393 if (apr_err)
1394 return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1395 _("Malformed UUID '%s'"), uuid);
1396 }
1397 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1398 }
1399
1400 svn_error_t *
svn_fs_lock(svn_lock_t ** lock,svn_fs_t * fs,const char * path,const char * token,const char * comment,svn_boolean_t is_dav_comment,apr_time_t expiration_date,svn_revnum_t current_rev,svn_boolean_t steal_lock,apr_pool_t * pool)1401 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1402 const char *token, const char *comment,
1403 svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1404 svn_revnum_t current_rev, svn_boolean_t steal_lock,
1405 apr_pool_t *pool)
1406 {
1407 /* Enforce that the comment be xml-escapable. */
1408 if (comment)
1409 {
1410 if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1411 return svn_error_create
1412 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1413 _("Lock comment contains illegal characters"));
1414 }
1415
1416 /* Enforce that the token be an XML-safe URI. */
1417 if (token)
1418 {
1419 const char *c;
1420
1421 if (strncmp(token, "opaquelocktoken:", 16))
1422 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1423 _("Lock token URI '%s' has bad scheme; "
1424 "expected '%s'"),
1425 token, "opaquelocktoken");
1426
1427 for (c = token; *c; c++)
1428 if (! svn_ctype_isascii(*c))
1429 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1430 _("Lock token '%s' is not ASCII "
1431 "at byte %u"),
1432 token, (unsigned)(c - token));
1433
1434 /* strlen(token) == c - token. */
1435 if (! svn_xml_is_xml_safe(token, c - token))
1436 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1437 _("Lock token URI '%s' is not XML-safe"),
1438 token);
1439 }
1440
1441 if (expiration_date < 0)
1442 return svn_error_create
1443 (SVN_ERR_INCORRECT_PARAMS, NULL,
1444 _("Negative expiration date passed to svn_fs_lock"));
1445
1446 return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment,
1447 is_dav_comment, expiration_date,
1448 current_rev, steal_lock, pool));
1449 }
1450
1451 svn_error_t *
svn_fs_generate_lock_token(const char ** token,svn_fs_t * fs,apr_pool_t * pool)1452 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1453 {
1454 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1455 }
1456
1457 svn_error_t *
svn_fs_unlock(svn_fs_t * fs,const char * path,const char * token,svn_boolean_t break_lock,apr_pool_t * pool)1458 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1459 svn_boolean_t break_lock, apr_pool_t *pool)
1460 {
1461 return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock,
1462 pool));
1463 }
1464
1465 svn_error_t *
svn_fs_get_lock(svn_lock_t ** lock,svn_fs_t * fs,const char * path,apr_pool_t * pool)1466 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1467 apr_pool_t *pool)
1468 {
1469 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1470 }
1471
1472 svn_error_t *
svn_fs_get_locks2(svn_fs_t * fs,const char * path,svn_depth_t depth,svn_fs_get_locks_callback_t get_locks_func,void * get_locks_baton,apr_pool_t * pool)1473 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1474 svn_fs_get_locks_callback_t get_locks_func,
1475 void *get_locks_baton, apr_pool_t *pool)
1476 {
1477 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1478 (depth == svn_depth_files) ||
1479 (depth == svn_depth_immediates) ||
1480 (depth == svn_depth_infinity));
1481 return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1482 get_locks_func,
1483 get_locks_baton, pool));
1484 }
1485
1486 svn_error_t *
svn_fs_get_locks(svn_fs_t * fs,const char * path,svn_fs_get_locks_callback_t get_locks_func,void * get_locks_baton,apr_pool_t * pool)1487 svn_fs_get_locks(svn_fs_t *fs, const char *path,
1488 svn_fs_get_locks_callback_t get_locks_func,
1489 void *get_locks_baton, apr_pool_t *pool)
1490 {
1491 return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity,
1492 get_locks_func, get_locks_baton,
1493 pool));
1494 }
1495
1496
1497 /* --- History functions --- */
1498
1499 svn_error_t *
svn_fs_history_prev(svn_fs_history_t ** prev_history_p,svn_fs_history_t * history,svn_boolean_t cross_copies,apr_pool_t * pool)1500 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
1501 svn_fs_history_t *history, svn_boolean_t cross_copies,
1502 apr_pool_t *pool)
1503 {
1504 return svn_error_trace(history->vtable->prev(prev_history_p, history,
1505 cross_copies, pool));
1506 }
1507
1508 svn_error_t *
svn_fs_history_location(const char ** path,svn_revnum_t * revision,svn_fs_history_t * history,apr_pool_t * pool)1509 svn_fs_history_location(const char **path, svn_revnum_t *revision,
1510 svn_fs_history_t *history, apr_pool_t *pool)
1511 {
1512 return svn_error_trace(history->vtable->location(path, revision, history,
1513 pool));
1514 }
1515
1516
1517 /* --- Node-ID functions --- */
1518
1519 svn_fs_id_t *
svn_fs_parse_id(const char * data,apr_size_t len,apr_pool_t * pool)1520 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1521 {
1522 fs_library_vtable_t *vtable;
1523 svn_error_t *err;
1524
1525 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1526 if (err)
1527 {
1528 svn_error_clear(err);
1529 return NULL;
1530 }
1531 return vtable->parse_id(data, len, pool);
1532 }
1533
1534 svn_string_t *
svn_fs_unparse_id(const svn_fs_id_t * id,apr_pool_t * pool)1535 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1536 {
1537 return id->vtable->unparse(id, pool);
1538 }
1539
1540 svn_boolean_t
svn_fs_check_related(const svn_fs_id_t * a,const svn_fs_id_t * b)1541 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1542 {
1543 return (a->vtable->compare(a, b) != -1);
1544 }
1545
1546 int
svn_fs_compare_ids(const svn_fs_id_t * a,const svn_fs_id_t * b)1547 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1548 {
1549 return a->vtable->compare(a, b);
1550 }
1551
1552 svn_error_t *
svn_fs_print_modules(svn_stringbuf_t * output,apr_pool_t * pool)1553 svn_fs_print_modules(svn_stringbuf_t *output,
1554 apr_pool_t *pool)
1555 {
1556 const struct fs_type_defn *defn = fs_modules;
1557 fs_library_vtable_t *vtable;
1558 apr_pool_t *iterpool = svn_pool_create(pool);
1559
1560 while (defn)
1561 {
1562 char *line;
1563 svn_error_t *err;
1564
1565 svn_pool_clear(iterpool);
1566
1567 err = get_library_vtable_direct(&vtable, defn, iterpool);
1568 if (err)
1569 {
1570 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1571 {
1572 svn_error_clear(err);
1573 defn = defn->next;
1574 continue;
1575 }
1576 else
1577 return err;
1578 }
1579
1580 line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1581 defn->fsap_name, vtable->get_description());
1582 svn_stringbuf_appendcstr(output, line);
1583 defn = defn->next;
1584 }
1585
1586 svn_pool_destroy(iterpool);
1587
1588 return SVN_NO_ERROR;
1589 }
1590
1591 svn_fs_path_change2_t *
svn_fs_path_change2_create(const svn_fs_id_t * node_rev_id,svn_fs_path_change_kind_t change_kind,apr_pool_t * pool)1592 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1593 svn_fs_path_change_kind_t change_kind,
1594 apr_pool_t *pool)
1595 {
1596 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1597 }
1598
1599 /* Return the library version number. */
1600 const svn_version_t *
svn_fs_version(void)1601 svn_fs_version(void)
1602 {
1603 SVN_VERSION_BODY;
1604 }
1605