1 /* util.c --- utility functions for FSFS repo access
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 <assert.h>
24
25 #include "svn_ctype.h"
26 #include "svn_dirent_uri.h"
27 #include "private/svn_string_private.h"
28
29 #include "fs_fs.h"
30 #include "pack.h"
31 #include "util.h"
32
33 #include "../libsvn_fs/fs-loader.h"
34
35 #include "svn_private_config.h"
36
37 svn_boolean_t
svn_fs_fs__is_packed_rev(svn_fs_t * fs,svn_revnum_t rev)38 svn_fs_fs__is_packed_rev(svn_fs_t *fs,
39 svn_revnum_t rev)
40 {
41 fs_fs_data_t *ffd = fs->fsap_data;
42
43 return (rev < ffd->min_unpacked_rev);
44 }
45
46 svn_boolean_t
svn_fs_fs__is_packed_revprop(svn_fs_t * fs,svn_revnum_t rev)47 svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
48 svn_revnum_t rev)
49 {
50 fs_fs_data_t *ffd = fs->fsap_data;
51
52 /* rev 0 will not be packed */
53 return (rev < ffd->min_unpacked_rev)
54 && (rev != 0)
55 && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
56 }
57
58 svn_revnum_t
svn_fs_fs__packed_base_rev(svn_fs_t * fs,svn_revnum_t revision)59 svn_fs_fs__packed_base_rev(svn_fs_t *fs,
60 svn_revnum_t revision)
61 {
62 fs_fs_data_t *ffd = fs->fsap_data;
63 return (revision < ffd->min_unpacked_rev)
64 ? (revision - (revision % ffd->max_files_per_dir))
65 : revision;
66 }
67
68 const char *
svn_fs_fs__path_txn_current(svn_fs_t * fs,apr_pool_t * pool)69 svn_fs_fs__path_txn_current(svn_fs_t *fs,
70 apr_pool_t *pool)
71 {
72 return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
73 }
74
75 const char *
svn_fs_fs__path_txn_current_lock(svn_fs_t * fs,apr_pool_t * pool)76 svn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
77 apr_pool_t *pool)
78 {
79 return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
80 }
81
82 const char *
svn_fs_fs__path_lock(svn_fs_t * fs,apr_pool_t * pool)83 svn_fs_fs__path_lock(svn_fs_t *fs,
84 apr_pool_t *pool)
85 {
86 return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
87 }
88
89 const char *
svn_fs_fs__path_pack_lock(svn_fs_t * fs,apr_pool_t * pool)90 svn_fs_fs__path_pack_lock(svn_fs_t *fs,
91 apr_pool_t *pool)
92 {
93 return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
94 }
95
96 const char *
svn_fs_fs__path_revprop_generation(svn_fs_t * fs,apr_pool_t * pool)97 svn_fs_fs__path_revprop_generation(svn_fs_t *fs,
98 apr_pool_t *pool)
99 {
100 return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
101 }
102
103 const char *
svn_fs_fs__path_rev_packed(svn_fs_t * fs,svn_revnum_t rev,const char * kind,apr_pool_t * pool)104 svn_fs_fs__path_rev_packed(svn_fs_t *fs,
105 svn_revnum_t rev,
106 const char *kind,
107 apr_pool_t *pool)
108 {
109 fs_fs_data_t *ffd = fs->fsap_data;
110
111 assert(ffd->max_files_per_dir);
112 assert(svn_fs_fs__is_packed_rev(fs, rev));
113
114 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
115 apr_psprintf(pool,
116 "%ld" PATH_EXT_PACKED_SHARD,
117 rev / ffd->max_files_per_dir),
118 kind, SVN_VA_NULL);
119 }
120
121 const char *
svn_fs_fs__path_rev_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)122 svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
123 {
124 fs_fs_data_t *ffd = fs->fsap_data;
125
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),
130 SVN_VA_NULL);
131 }
132
133 const char *
svn_fs_fs__path_rev(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)134 svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
135 {
136 fs_fs_data_t *ffd = fs->fsap_data;
137
138 assert(! svn_fs_fs__is_packed_rev(fs, rev));
139
140 if (ffd->max_files_per_dir)
141 {
142 return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
143 apr_psprintf(pool, "%ld", rev),
144 pool);
145 }
146
147 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
148 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
149 }
150
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.
154 */
155 static const char *
path_rev_absolute_internal(svn_fs_t * fs,svn_revnum_t rev,svn_boolean_t packed,apr_pool_t * pool)156 path_rev_absolute_internal(svn_fs_t *fs,
157 svn_revnum_t rev,
158 svn_boolean_t packed,
159 apr_pool_t *pool)
160 {
161 return packed
162 ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
163 : svn_fs_fs__path_rev(fs, rev, pool);
164 }
165
166 const char *
svn_fs_fs__path_rev_absolute(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)167 svn_fs_fs__path_rev_absolute(svn_fs_t *fs,
168 svn_revnum_t rev,
169 apr_pool_t *pool)
170 {
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);
174
175 return path_rev_absolute_internal(fs, rev, is_packed, pool);
176 }
177
178 const char *
svn_fs_fs__path_revprops_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)179 svn_fs_fs__path_revprops_shard(svn_fs_t *fs,
180 svn_revnum_t rev,
181 apr_pool_t *pool)
182 {
183 fs_fs_data_t *ffd = fs->fsap_data;
184
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),
189 SVN_VA_NULL);
190 }
191
192 const char *
svn_fs_fs__path_revprops_pack_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)193 svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
194 svn_revnum_t rev,
195 apr_pool_t *pool)
196 {
197 fs_fs_data_t *ffd = fs->fsap_data;
198
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),
203 SVN_VA_NULL);
204 }
205
206 const char *
svn_fs_fs__path_revprops(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)207 svn_fs_fs__path_revprops(svn_fs_t *fs,
208 svn_revnum_t rev,
209 apr_pool_t *pool)
210 {
211 fs_fs_data_t *ffd = fs->fsap_data;
212
213 if (ffd->max_files_per_dir)
214 {
215 return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
216 apr_psprintf(pool, "%ld", rev),
217 pool);
218 }
219
220 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
221 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
222 }
223
224 /* Return TO_ADD appended to the C string representation of TXN_ID.
225 * Allocate the result in POOL.
226 */
227 static const char *
combine_txn_id_string(const svn_fs_fs__id_part_t * txn_id,const char * to_add,apr_pool_t * pool)228 combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
229 const char *to_add,
230 apr_pool_t *pool)
231 {
232 return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
233 to_add, SVN_VA_NULL);
234 }
235
236 const char *
svn_fs_fs__path_txns_dir(svn_fs_t * fs,apr_pool_t * pool)237 svn_fs_fs__path_txns_dir(svn_fs_t *fs,
238 apr_pool_t *pool)
239 {
240 return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
241 }
242
243 const char *
svn_fs_fs__path_txn_dir(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)244 svn_fs_fs__path_txn_dir(svn_fs_t *fs,
245 const svn_fs_fs__id_part_t *txn_id,
246 apr_pool_t *pool)
247 {
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),
251 pool);
252 }
253
254 const char*
svn_fs_fs__path_l2p_proto_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)255 svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
256 const svn_fs_fs__id_part_t *txn_id,
257 apr_pool_t *pool)
258 {
259 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
260 PATH_INDEX PATH_EXT_L2P_INDEX, pool);
261 }
262
263 const char*
svn_fs_fs__path_p2l_proto_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)264 svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
265 const svn_fs_fs__id_part_t *txn_id,
266 apr_pool_t *pool)
267 {
268 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
269 PATH_INDEX PATH_EXT_P2L_INDEX, pool);
270 }
271
272 const char *
svn_fs_fs__path_txn_item_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)273 svn_fs_fs__path_txn_item_index(svn_fs_t *fs,
274 const svn_fs_fs__id_part_t *txn_id,
275 apr_pool_t *pool)
276 {
277 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
278 PATH_TXN_ITEM_INDEX, pool);
279 }
280
281 const char *
svn_fs_fs__path_txn_proto_revs(svn_fs_t * fs,apr_pool_t * pool)282 svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
283 apr_pool_t *pool)
284 {
285 return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
286 }
287
288 const char *
svn_fs_fs__path_txn_proto_rev(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)289 svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
290 const svn_fs_fs__id_part_t *txn_id,
291 apr_pool_t *pool)
292 {
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),
297 pool);
298 else
299 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
300 PATH_REV, pool);
301 }
302
303
304 const char *
svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)305 svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
306 const svn_fs_fs__id_part_t *txn_id,
307 apr_pool_t *pool)
308 {
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,
313 pool),
314 pool);
315 else
316 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
317 PATH_REV_LOCK, pool);
318 }
319
320 const char *
svn_fs_fs__path_txn_node_rev(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)321 svn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
322 const svn_fs_id_t *id,
323 apr_pool_t *pool)
324 {
325 char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
326 *strrchr(filename, '.') = '\0';
327
328 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
329 pool),
330 apr_psprintf(pool, PATH_PREFIX_NODE "%s",
331 filename),
332 pool);
333 }
334
335 const char *
svn_fs_fs__path_txn_node_props(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)336 svn_fs_fs__path_txn_node_props(svn_fs_t *fs,
337 const svn_fs_id_t *id,
338 apr_pool_t *pool)
339 {
340 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
341 PATH_EXT_PROPS, SVN_VA_NULL);
342 }
343
344 const char *
svn_fs_fs__path_txn_node_children(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)345 svn_fs_fs__path_txn_node_children(svn_fs_t *fs,
346 const svn_fs_id_t *id,
347 apr_pool_t *pool)
348 {
349 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
350 PATH_EXT_CHILDREN, SVN_VA_NULL);
351 }
352
353 const char *
svn_fs_fs__path_node_origin(svn_fs_t * fs,const svn_fs_fs__id_part_t * node_id,apr_pool_t * pool)354 svn_fs_fs__path_node_origin(svn_fs_t *fs,
355 const svn_fs_fs__id_part_t *node_id,
356 apr_pool_t *pool)
357 {
358 char buffer[SVN_INT64_BUFFER_SIZE];
359 apr_size_t len = svn__ui64tobase36(buffer, node_id->number);
360
361 if (len > 1)
362 buffer[len - 1] = '\0';
363
364 return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
365 buffer, SVN_VA_NULL);
366 }
367
368 const char *
svn_fs_fs__path_min_unpacked_rev(svn_fs_t * fs,apr_pool_t * pool)369 svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
370 apr_pool_t *pool)
371 {
372 return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
373 }
374
375 svn_error_t *
svn_fs_fs__check_file_buffer_numeric(const char * buf,apr_off_t offset,const char * path,const char * title,apr_pool_t * pool)376 svn_fs_fs__check_file_buffer_numeric(const char *buf,
377 apr_off_t offset,
378 const char *path,
379 const char *title,
380 apr_pool_t *pool)
381 {
382 const char *p;
383
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);
389
390 return SVN_NO_ERROR;
391 }
392
393 svn_error_t *
svn_fs_fs__read_min_unpacked_rev(svn_revnum_t * min_unpacked_rev,svn_fs_t * fs,apr_pool_t * pool)394 svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
395 svn_fs_t *fs,
396 apr_pool_t *pool)
397 {
398 char buf[80];
399 apr_file_t *file;
400 apr_size_t len;
401
402 SVN_ERR(svn_io_file_open(&file,
403 svn_fs_fs__path_min_unpacked_rev(fs, pool),
404 APR_READ | APR_BUFFERED,
405 APR_OS_DEFAULT,
406 pool));
407 len = sizeof(buf);
408 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
409 SVN_ERR(svn_io_file_close(file, pool));
410
411 SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
412 return SVN_NO_ERROR;
413 }
414
415 svn_error_t *
svn_fs_fs__update_min_unpacked_rev(svn_fs_t * fs,apr_pool_t * pool)416 svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs,
417 apr_pool_t *pool)
418 {
419 fs_fs_data_t *ffd = fs->fsap_data;
420
421 SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
422
423 return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool);
424 }
425
426 svn_error_t *
svn_fs_fs__write_min_unpacked_rev(svn_fs_t * fs,svn_revnum_t revnum,apr_pool_t * scratch_pool)427 svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
428 svn_revnum_t revnum,
429 apr_pool_t *scratch_pool)
430 {
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);
435 buf[len] = '\n';
436
437 final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
438
439 SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
440 final_path /* copy_perms */,
441 ffd->flush_to_disk, scratch_pool));
442
443 return SVN_NO_ERROR;
444 }
445
446 svn_error_t *
svn_fs_fs__read_current(svn_revnum_t * rev,apr_uint64_t * next_node_id,apr_uint64_t * next_copy_id,svn_fs_t * fs,apr_pool_t * 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,
450 svn_fs_t *fs,
451 apr_pool_t *pool)
452 {
453 fs_fs_data_t *ffd = fs->fsap_data;
454 svn_stringbuf_t *content;
455
456 SVN_ERR(svn_fs_fs__read_content(&content,
457 svn_fs_fs__path_current(fs, pool),
458 pool));
459
460 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
461 {
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));
468
469 *next_node_id = 0;
470 *next_copy_id = 0;
471 }
472 else
473 {
474 const char *str;
475
476 SVN_ERR(svn_revnum_parse(rev, content->data, &str));
477 if (*str != ' ')
478 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
479 _("Corrupt 'current' file"));
480
481 *next_node_id = svn__base36toui64(&str, str + 1);
482 if (*str != ' ')
483 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
484 _("Corrupt 'current' file"));
485
486 *next_copy_id = svn__base36toui64(&str, str + 1);
487 if (*str != '\n')
488 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
489 _("Corrupt 'current' file"));
490 }
491
492 return SVN_NO_ERROR;
493 }
494
495 svn_error_t *
svn_fs_fs__write_current(svn_fs_t * fs,svn_revnum_t rev,apr_uint64_t next_node_id,apr_uint64_t next_copy_id,apr_pool_t * pool)496 svn_fs_fs__write_current(svn_fs_t *fs,
497 svn_revnum_t rev,
498 apr_uint64_t next_node_id,
499 apr_uint64_t next_copy_id,
500 apr_pool_t *pool)
501 {
502 char *buf;
503 const char *name;
504 fs_fs_data_t *ffd = fs->fsap_data;
505
506 /* Now we can just write out this line. */
507 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
508 {
509 buf = apr_psprintf(pool, "%ld\n", rev);
510 }
511 else
512 {
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);
517
518 buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
519 }
520
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));
525
526 return SVN_NO_ERROR;
527 }
528
529 svn_error_t *
svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t ** content,svn_boolean_t * missing,const char * path,svn_boolean_t last_attempt,apr_pool_t * pool)530 svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
531 svn_boolean_t *missing,
532 const char *path,
533 svn_boolean_t last_attempt,
534 apr_pool_t *pool)
535 {
536 svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
537 if (missing)
538 *missing = FALSE;
539
540 if (err)
541 {
542 *content = NULL;
543
544 if (APR_STATUS_IS_ENOENT(err->apr_err))
545 {
546 if (!last_attempt)
547 {
548 svn_error_clear(err);
549 if (missing)
550 *missing = TRUE;
551 return SVN_NO_ERROR;
552 }
553 }
554 #ifdef ESTALE
555 else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
556 || APR_TO_OS_ERROR(err->apr_err) == EIO)
557 {
558 if (!last_attempt)
559 {
560 svn_error_clear(err);
561 return SVN_NO_ERROR;
562 }
563 }
564 #endif
565 }
566
567 return svn_error_trace(err);
568 }
569
570 svn_error_t *
svn_fs_fs__read_content(svn_stringbuf_t ** content,const char * fname,apr_pool_t * pool)571 svn_fs_fs__read_content(svn_stringbuf_t **content,
572 const char *fname,
573 apr_pool_t *pool)
574 {
575 int i;
576 *content = NULL;
577
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,
581 pool));
582
583 if (!*content)
584 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
585 _("Can't read '%s'"),
586 svn_dirent_local_style(fname, pool));
587
588 return SVN_NO_ERROR;
589 }
590
591 svn_error_t *
svn_fs_fs__read_number_from_stream(apr_int64_t * result,svn_boolean_t * hit_eof,svn_stream_t * stream,apr_pool_t * scratch_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)
596 {
597 svn_stringbuf_t *sb;
598 svn_boolean_t eof;
599 svn_error_t *err;
600
601 SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
602 if (hit_eof)
603 *hit_eof = eof;
604 else
605 if (eof)
606 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
607
608 if (!eof)
609 {
610 err = svn_cstring_atoi64(result, sb->data);
611 if (err)
612 return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
613 _("Number '%s' invalid or too large"),
614 sb->data);
615 }
616
617 return SVN_NO_ERROR;
618 }
619
620 svn_error_t *
svn_fs_fs__move_into_place(const char * old_filename,const char * new_filename,const char * perms_reference,svn_boolean_t flush_to_disk,apr_pool_t * pool)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,
625 apr_pool_t *pool)
626 {
627 svn_error_t *err;
628 apr_file_t *file;
629
630 /* Copying permissions is a no-op on WIN32. */
631 SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
632
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))
636 {
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));
640
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. */
645 if (flush_to_disk)
646 {
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));
651 }
652
653 #ifdef SVN_ON_POSIX
654 if (flush_to_disk)
655 {
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. */
660 const char *dirname;
661
662 dirname = svn_dirent_dirname(new_filename, pool);
663 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
664 pool));
665 SVN_ERR(svn_io_file_flush_to_disk(file, pool));
666 SVN_ERR(svn_io_file_close(file, pool));
667 }
668 #endif
669 }
670 else if (err)
671 return svn_error_trace(err);
672
673 return SVN_NO_ERROR;
674 }
675
676 svn_boolean_t
svn_fs_fs__use_log_addressing(svn_fs_t * fs)677 svn_fs_fs__use_log_addressing(svn_fs_t *fs)
678 {
679 fs_fs_data_t *ffd = fs->fsap_data;
680 return ffd->use_log_addressing;
681 }
682