1 /* util.c --- utility functions for FSFS repo access
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
25 #include "svn_ctype.h"
26 #include "svn_dirent_uri.h"
27 #include "private/svn_string_private.h"
33 #include "../libsvn_fs/fs-loader.h"
35 #include "svn_private_config.h"
38 svn_fs_fs__is_packed_rev(svn_fs_t *fs,
41 fs_fs_data_t *ffd = fs->fsap_data;
43 return (rev < ffd->min_unpacked_rev);
47 svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
50 fs_fs_data_t *ffd = fs->fsap_data;
52 /* rev 0 will not be packed */
53 return (rev < ffd->min_unpacked_rev)
55 && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
59 svn_fs_fs__packed_base_rev(svn_fs_t *fs,
60 svn_revnum_t revision)
62 fs_fs_data_t *ffd = fs->fsap_data;
63 return (revision < ffd->min_unpacked_rev)
64 ? (revision - (revision % ffd->max_files_per_dir))
69 svn_fs_fs__path_txn_current(svn_fs_t *fs,
72 return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
76 svn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
79 return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
83 svn_fs_fs__path_lock(svn_fs_t *fs,
86 return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
90 svn_fs_fs__path_pack_lock(svn_fs_t *fs,
93 return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
97 svn_fs_fs__path_revprop_generation(svn_fs_t *fs,
100 return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
104 svn_fs_fs__path_rev_packed(svn_fs_t *fs,
109 fs_fs_data_t *ffd = fs->fsap_data;
111 assert(ffd->max_files_per_dir);
112 assert(svn_fs_fs__is_packed_rev(fs, rev));
114 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
116 "%ld" PATH_EXT_PACKED_SHARD,
117 rev / ffd->max_files_per_dir),
122 svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
124 fs_fs_data_t *ffd = fs->fsap_data;
126 assert(ffd->max_files_per_dir);
127 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
128 apr_psprintf(pool, "%ld",
129 rev / ffd->max_files_per_dir),
134 svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
136 fs_fs_data_t *ffd = fs->fsap_data;
138 assert(! svn_fs_fs__is_packed_rev(fs, rev));
140 if (ffd->max_files_per_dir)
142 return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
143 apr_psprintf(pool, "%ld", rev),
147 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
148 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
151 /* Set *PATH to the path of REV in FS with PACKED selecting whether the
152 (potential) pack file or single revision file name is returned.
153 Allocate *PATH in POOL.
156 path_rev_absolute_internal(svn_fs_t *fs,
158 svn_boolean_t packed,
162 ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
163 : svn_fs_fs__path_rev(fs, rev, pool);
167 svn_fs_fs__path_rev_absolute(svn_fs_t *fs,
171 fs_fs_data_t *ffd = fs->fsap_data;
172 svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT
173 && svn_fs_fs__is_packed_rev(fs, rev);
175 return path_rev_absolute_internal(fs, rev, is_packed, pool);
179 svn_fs_fs__path_revprops_shard(svn_fs_t *fs,
183 fs_fs_data_t *ffd = fs->fsap_data;
185 assert(ffd->max_files_per_dir);
186 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
187 apr_psprintf(pool, "%ld",
188 rev / ffd->max_files_per_dir),
193 svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
197 fs_fs_data_t *ffd = fs->fsap_data;
199 assert(ffd->max_files_per_dir);
200 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
201 apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
202 rev / ffd->max_files_per_dir),
207 svn_fs_fs__path_revprops(svn_fs_t *fs,
211 fs_fs_data_t *ffd = fs->fsap_data;
213 if (ffd->max_files_per_dir)
215 return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
216 apr_psprintf(pool, "%ld", rev),
220 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
221 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
224 /* Return TO_ADD appended to the C string representation of TXN_ID.
225 * Allocate the result in POOL.
228 combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
232 return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
233 to_add, SVN_VA_NULL);
237 svn_fs_fs__path_txns_dir(svn_fs_t *fs,
240 return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
244 svn_fs_fs__path_txn_dir(svn_fs_t *fs,
245 const svn_fs_fs__id_part_t *txn_id,
248 SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
249 return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
250 combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
255 svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
256 const svn_fs_fs__id_part_t *txn_id,
259 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
260 PATH_INDEX PATH_EXT_L2P_INDEX, pool);
264 svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
265 const svn_fs_fs__id_part_t *txn_id,
268 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
269 PATH_INDEX PATH_EXT_P2L_INDEX, pool);
273 svn_fs_fs__path_txn_item_index(svn_fs_t *fs,
274 const svn_fs_fs__id_part_t *txn_id,
277 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
278 PATH_TXN_ITEM_INDEX, pool);
282 svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
285 return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
289 svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
290 const svn_fs_fs__id_part_t *txn_id,
293 fs_fs_data_t *ffd = fs->fsap_data;
294 if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
295 return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
296 combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
299 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
305 svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
306 const svn_fs_fs__id_part_t *txn_id,
309 fs_fs_data_t *ffd = fs->fsap_data;
310 if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
311 return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
312 combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
316 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
317 PATH_REV_LOCK, pool);
321 svn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
322 const svn_fs_id_t *id,
325 char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
326 *strrchr(filename, '.') = '\0';
328 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
330 apr_psprintf(pool, PATH_PREFIX_NODE "%s",
336 svn_fs_fs__path_txn_node_props(svn_fs_t *fs,
337 const svn_fs_id_t *id,
340 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
341 PATH_EXT_PROPS, SVN_VA_NULL);
345 svn_fs_fs__path_txn_node_children(svn_fs_t *fs,
346 const svn_fs_id_t *id,
349 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
350 PATH_EXT_CHILDREN, SVN_VA_NULL);
354 svn_fs_fs__path_node_origin(svn_fs_t *fs,
355 const svn_fs_fs__id_part_t *node_id,
358 char buffer[SVN_INT64_BUFFER_SIZE];
359 apr_size_t len = svn__ui64tobase36(buffer, node_id->number);
362 buffer[len - 1] = '\0';
364 return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
365 buffer, SVN_VA_NULL);
369 svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
372 return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
376 svn_fs_fs__check_file_buffer_numeric(const char *buf,
384 for (p = buf + offset; *p; p++)
385 if (!svn_ctype_isdigit(*p))
386 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
387 _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
388 title, svn_dirent_local_style(path, pool), *p, buf);
394 svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
402 SVN_ERR(svn_io_file_open(&file,
403 svn_fs_fs__path_min_unpacked_rev(fs, pool),
404 APR_READ | APR_BUFFERED,
408 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
409 SVN_ERR(svn_io_file_close(file, pool));
411 SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
416 svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs,
419 fs_fs_data_t *ffd = fs->fsap_data;
421 SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
423 return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool);
427 svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
429 apr_pool_t *scratch_pool)
431 const char *final_path;
432 char buf[SVN_INT64_BUFFER_SIZE];
433 apr_size_t len = svn__i64toa(buf, revnum);
436 final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
438 SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
439 final_path /* copy_perms */, scratch_pool));
445 svn_fs_fs__read_current(svn_revnum_t *rev,
446 apr_uint64_t *next_node_id,
447 apr_uint64_t *next_copy_id,
451 fs_fs_data_t *ffd = fs->fsap_data;
452 svn_stringbuf_t *content;
454 SVN_ERR(svn_fs_fs__read_content(&content,
455 svn_fs_fs__path_current(fs, pool),
458 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
460 /* When format 1 and 2 filesystems are upgraded, the 'current' file is
461 left intact. As a consequence, there is a window when a filesystem
462 has a new format, but this file still contains the IDs left from an
463 old format, i.e. looks like "359 j5 v\n". Do not be too strict here
464 and only expect a parseable revision number. */
465 SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
474 SVN_ERR(svn_revnum_parse(rev, content->data, &str));
476 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
477 _("Corrupt 'current' file"));
479 *next_node_id = svn__base36toui64(&str, str + 1);
481 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
482 _("Corrupt 'current' file"));
484 *next_copy_id = svn__base36toui64(&str, str + 1);
486 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
487 _("Corrupt 'current' file"));
494 svn_fs_fs__write_current(svn_fs_t *fs,
496 apr_uint64_t next_node_id,
497 apr_uint64_t next_copy_id,
502 fs_fs_data_t *ffd = fs->fsap_data;
504 /* Now we can just write out this line. */
505 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
507 buf = apr_psprintf(pool, "%ld\n", rev);
511 char node_id_str[SVN_INT64_BUFFER_SIZE];
512 char copy_id_str[SVN_INT64_BUFFER_SIZE];
513 svn__ui64tobase36(node_id_str, next_node_id);
514 svn__ui64tobase36(copy_id_str, next_copy_id);
516 buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
519 name = svn_fs_fs__path_current(fs, pool);
520 SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf),
521 name /* copy_perms_path */, pool));
527 svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
528 svn_boolean_t *missing,
530 svn_boolean_t last_attempt,
533 svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
541 if (APR_STATUS_IS_ENOENT(err->apr_err))
545 svn_error_clear(err);
552 else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
553 || APR_TO_OS_ERROR(err->apr_err) == EIO)
557 svn_error_clear(err);
564 return svn_error_trace(err);
568 svn_fs_fs__get_file_offset(apr_off_t *offset_p,
574 /* Note that, for buffered files, one (possibly surprising) side-effect
575 of this call is to flush any unwritten data to disk. */
577 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
584 svn_fs_fs__read_content(svn_stringbuf_t **content,
591 for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
592 SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
593 fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
597 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
598 _("Can't read '%s'"),
599 svn_dirent_local_style(fname, pool));
605 svn_fs_fs__read_number_from_stream(apr_int64_t *result,
606 svn_boolean_t *hit_eof,
607 svn_stream_t *stream,
608 apr_pool_t *scratch_pool)
614 SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
619 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
623 err = svn_cstring_atoi64(result, sb->data);
625 return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
626 _("Number '%s' invalid or too large"),
634 svn_fs_fs__move_into_place(const char *old_filename,
635 const char *new_filename,
636 const char *perms_reference,
641 SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
643 /* Move the file into place. */
644 err = svn_io_file_rename(old_filename, new_filename, pool);
645 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
649 /* Can't rename across devices; fall back to copying. */
650 svn_error_clear(err);
652 SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
654 /* Flush the target of the copy to disk. */
655 SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
656 APR_OS_DEFAULT, pool));
657 /* ### BH: Does this really guarantee a flush of the data written
658 ### via a completely different handle on all operating systems?
660 ### Maybe we should perform the copy ourselves instead of making
661 ### apr do that and flush the real handle? */
662 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
663 SVN_ERR(svn_io_file_close(file, pool));
666 return svn_error_trace(err);
670 /* Linux has the unusual feature that fsync() on a file is not
671 enough to ensure that a file's directory entries have been
672 flushed to disk; you have to fsync the directory as well.
673 On other operating systems, we'd only be asking for trouble
674 by trying to open and fsync a directory. */
678 dirname = svn_dirent_dirname(new_filename, pool);
679 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
681 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
682 SVN_ERR(svn_io_file_close(file, pool));
690 svn_fs_fs__use_log_addressing(svn_fs_t *fs)
692 fs_fs_data_t *ffd = fs->fsap_data;
693 return ffd->use_log_addressing;