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 fs_fs_data_t *ffd = fs->fsap_data;
432 const char *final_path;
433 char buf[SVN_INT64_BUFFER_SIZE];
434 apr_size_t len = svn__i64toa(buf, revnum);
437 final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
439 SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
440 final_path /* copy_perms */,
441 ffd->flush_to_disk, scratch_pool));
447 svn_fs_fs__read_current(svn_revnum_t *rev,
448 apr_uint64_t *next_node_id,
449 apr_uint64_t *next_copy_id,
453 fs_fs_data_t *ffd = fs->fsap_data;
454 svn_stringbuf_t *content;
456 SVN_ERR(svn_fs_fs__read_content(&content,
457 svn_fs_fs__path_current(fs, pool),
460 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
462 /* When format 1 and 2 filesystems are upgraded, the 'current' file is
463 left intact. As a consequence, there is a window when a filesystem
464 has a new format, but this file still contains the IDs left from an
465 old format, i.e. looks like "359 j5 v\n". Do not be too strict here
466 and only expect a parseable revision number. */
467 SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
476 SVN_ERR(svn_revnum_parse(rev, content->data, &str));
478 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
479 _("Corrupt 'current' file"));
481 *next_node_id = svn__base36toui64(&str, str + 1);
483 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
484 _("Corrupt 'current' file"));
486 *next_copy_id = svn__base36toui64(&str, str + 1);
488 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
489 _("Corrupt 'current' file"));
496 svn_fs_fs__write_current(svn_fs_t *fs,
498 apr_uint64_t next_node_id,
499 apr_uint64_t next_copy_id,
504 fs_fs_data_t *ffd = fs->fsap_data;
506 /* Now we can just write out this line. */
507 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
509 buf = apr_psprintf(pool, "%ld\n", rev);
513 char node_id_str[SVN_INT64_BUFFER_SIZE];
514 char copy_id_str[SVN_INT64_BUFFER_SIZE];
515 svn__ui64tobase36(node_id_str, next_node_id);
516 svn__ui64tobase36(copy_id_str, next_copy_id);
518 buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
521 name = svn_fs_fs__path_current(fs, pool);
522 SVN_ERR(svn_io_write_atomic2(name, buf, strlen(buf),
523 name /* copy_perms_path */,
524 ffd->flush_to_disk, pool));
530 svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
531 svn_boolean_t *missing,
533 svn_boolean_t last_attempt,
536 svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
544 if (APR_STATUS_IS_ENOENT(err->apr_err))
548 svn_error_clear(err);
555 else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
556 || APR_TO_OS_ERROR(err->apr_err) == EIO)
560 svn_error_clear(err);
567 return svn_error_trace(err);
571 svn_fs_fs__read_content(svn_stringbuf_t **content,
578 for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
579 SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
580 fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
584 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
585 _("Can't read '%s'"),
586 svn_dirent_local_style(fname, pool));
592 svn_fs_fs__read_number_from_stream(apr_int64_t *result,
593 svn_boolean_t *hit_eof,
594 svn_stream_t *stream,
595 apr_pool_t *scratch_pool)
601 SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
606 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
610 err = svn_cstring_atoi64(result, sb->data);
612 return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
613 _("Number '%s' invalid or too large"),
621 svn_fs_fs__move_into_place(const char *old_filename,
622 const char *new_filename,
623 const char *perms_reference,
624 svn_boolean_t flush_to_disk,
630 /* Copying permissions is a no-op on WIN32. */
631 SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
633 /* Move the file into place. */
634 err = svn_io_file_rename2(old_filename, new_filename, flush_to_disk, pool);
635 if (err && APR_STATUS_IS_EXDEV(err->apr_err))
637 /* Can't rename across devices; fall back to copying. */
638 svn_error_clear(err);
639 SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
641 /* Flush the target of the copy to disk.
642 ### The code below is duplicates svn_io_file_rename2(), because
643 currently we don't have the svn_io_copy_file2() function with
644 a flush_to_disk argument. */
647 SVN_ERR(svn_io_file_open(&file, new_filename, APR_WRITE,
648 APR_OS_DEFAULT, pool));
649 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
650 SVN_ERR(svn_io_file_close(file, pool));
656 /* On POSIX, the file name is stored in the file's directory entry.
657 Hence, we need to fsync() that directory as well.
658 On other operating systems, we'd only be asking for trouble
659 by trying to open and fsync a directory. */
662 dirname = svn_dirent_dirname(new_filename, pool);
663 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
665 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
666 SVN_ERR(svn_io_file_close(file, pool));
671 return svn_error_trace(err);
677 svn_fs_fs__use_log_addressing(svn_fs_t *fs)
679 fs_fs_data_t *ffd = fs->fsap_data;
680 return ffd->use_log_addressing;