1 /*
2 * wc_db_pristine.c : Pristine ("text base") management
3 *
4 * See the spec in 'notes/wc-ng/pristine-store'.
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 */
25
26 #define SVN_WC__I_AM_WC_DB
27
28 #include "svn_pools.h"
29 #include "svn_io.h"
30 #include "svn_dirent_uri.h"
31
32 #include "private/svn_io_private.h"
33
34 #include "wc.h"
35 #include "wc_db.h"
36 #include "wc-queries.h"
37 #include "wc_db_private.h"
38
39 #define PRISTINE_STORAGE_EXT ".svn-base"
40 #define PRISTINE_STORAGE_RELPATH "pristine"
41 #define PRISTINE_TEMPDIR_RELPATH "tmp"
42
43
44
45 /* Returns in PRISTINE_ABSPATH a new string allocated from RESULT_POOL,
46 holding the local absolute path to the file location that is dedicated
47 to hold CHECKSUM's pristine file, relating to the pristine store
48 configured for the working copy indicated by PDH. The returned path
49 does not necessarily currently exist.
50
51 Any other allocations are made in SCRATCH_POOL. */
52 static svn_error_t *
get_pristine_fname(const char ** pristine_abspath,const char * wcroot_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)53 get_pristine_fname(const char **pristine_abspath,
54 const char *wcroot_abspath,
55 const svn_checksum_t *sha1_checksum,
56 apr_pool_t *result_pool,
57 apr_pool_t *scratch_pool)
58 {
59 const char *base_dir_abspath;
60 const char *hexdigest = svn_checksum_to_cstring(sha1_checksum, scratch_pool);
61 char subdir[3];
62
63 /* ### code is in transition. make sure we have the proper data. */
64 SVN_ERR_ASSERT(pristine_abspath != NULL);
65 SVN_ERR_ASSERT(svn_dirent_is_absolute(wcroot_abspath));
66 SVN_ERR_ASSERT(sha1_checksum != NULL);
67 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
68
69 base_dir_abspath = svn_dirent_join_many(scratch_pool,
70 wcroot_abspath,
71 svn_wc_get_adm_dir(scratch_pool),
72 PRISTINE_STORAGE_RELPATH,
73 SVN_VA_NULL);
74
75 /* We should have a valid checksum and (thus) a valid digest. */
76 SVN_ERR_ASSERT(hexdigest != NULL);
77
78 /* Get the first two characters of the digest, for the subdir. */
79 subdir[0] = hexdigest[0];
80 subdir[1] = hexdigest[1];
81 subdir[2] = '\0';
82
83 hexdigest = apr_pstrcat(scratch_pool, hexdigest, PRISTINE_STORAGE_EXT,
84 SVN_VA_NULL);
85
86 /* The file is located at DIR/.svn/pristine/XX/XXYYZZ...svn-base */
87 *pristine_abspath = svn_dirent_join_many(result_pool,
88 base_dir_abspath,
89 subdir,
90 hexdigest,
91 SVN_VA_NULL);
92 return SVN_NO_ERROR;
93 }
94
95
96 svn_error_t *
svn_wc__db_pristine_get_path(const char ** pristine_abspath,svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)97 svn_wc__db_pristine_get_path(const char **pristine_abspath,
98 svn_wc__db_t *db,
99 const char *wri_abspath,
100 const svn_checksum_t *sha1_checksum,
101 apr_pool_t *result_pool,
102 apr_pool_t *scratch_pool)
103 {
104 svn_wc__db_wcroot_t *wcroot;
105 const char *local_relpath;
106 svn_boolean_t present;
107
108 SVN_ERR_ASSERT(pristine_abspath != NULL);
109 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
110 SVN_ERR_ASSERT(sha1_checksum != NULL);
111 /* ### Transitional: accept MD-5 and look up the SHA-1. Return an error
112 * if the pristine text is not in the store. */
113 if (sha1_checksum->kind != svn_checksum_sha1)
114 SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
115 sha1_checksum,
116 scratch_pool, scratch_pool));
117 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
118
119 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
120 db, wri_abspath,
121 scratch_pool, scratch_pool));
122 VERIFY_USABLE_WCROOT(wcroot);
123
124 SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
125 scratch_pool));
126 if (! present)
127 return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
128 _("The pristine text with checksum '%s' was "
129 "not found"),
130 svn_checksum_to_cstring_display(sha1_checksum,
131 scratch_pool));
132
133 SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
134 sha1_checksum,
135 result_pool, scratch_pool));
136
137 return SVN_NO_ERROR;
138 }
139
140 svn_error_t *
svn_wc__db_pristine_get_future_path(const char ** pristine_abspath,const char * wcroot_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)141 svn_wc__db_pristine_get_future_path(const char **pristine_abspath,
142 const char *wcroot_abspath,
143 const svn_checksum_t *sha1_checksum,
144 apr_pool_t *result_pool,
145 apr_pool_t *scratch_pool)
146 {
147 SVN_ERR(get_pristine_fname(pristine_abspath, wcroot_abspath,
148 sha1_checksum,
149 result_pool, scratch_pool));
150 return SVN_NO_ERROR;
151 }
152
153 /* Set *CONTENTS to a readable stream from which the pristine text
154 * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the
155 * pristine store of WCROOT. If SIZE is not null, set *SIZE to the size
156 * in bytes of that text. If that text is not in the pristine store,
157 * return an error.
158 *
159 * Even if the pristine text is removed from the store while it is being
160 * read, the stream will remain valid and readable until it is closed.
161 *
162 * Allocate the stream in RESULT_POOL.
163 *
164 * This function expects to be executed inside a SQLite txn.
165 *
166 * Implements 'notes/wc-ng/pristine-store' section A-3(d).
167 */
168 static svn_error_t *
pristine_read_txn(svn_stream_t ** contents,svn_filesize_t * size,svn_wc__db_wcroot_t * wcroot,const svn_checksum_t * sha1_checksum,const char * pristine_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)169 pristine_read_txn(svn_stream_t **contents,
170 svn_filesize_t *size,
171 svn_wc__db_wcroot_t *wcroot,
172 const svn_checksum_t *sha1_checksum,
173 const char *pristine_abspath,
174 apr_pool_t *result_pool,
175 apr_pool_t *scratch_pool)
176 {
177 svn_sqlite__stmt_t *stmt;
178 svn_boolean_t have_row;
179
180 /* Check that this pristine text is present in the store. (The presence
181 * of the file is not sufficient.) */
182 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
183 STMT_SELECT_PRISTINE_SIZE));
184 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
185 SVN_ERR(svn_sqlite__step(&have_row, stmt));
186
187 if (size)
188 *size = svn_sqlite__column_int64(stmt, 0);
189
190 SVN_ERR(svn_sqlite__reset(stmt));
191 if (! have_row)
192 {
193 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
194 _("Pristine text '%s' not present"),
195 svn_checksum_to_cstring_display(
196 sha1_checksum, scratch_pool));
197 }
198
199 /* Open the file as a readable stream. It will remain readable even when
200 * deleted from disk; APR guarantees that on Windows as well as Unix.
201 *
202 * We also don't enable APR_BUFFERED on this file to maximize throughput
203 * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers
204 * where needed in streams, there is no point in having another layer of
205 * buffers. */
206 if (contents)
207 {
208 apr_file_t *file;
209 SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
210 APR_OS_DEFAULT, result_pool));
211 *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
212 }
213
214 return SVN_NO_ERROR;
215 }
216
217 svn_error_t *
svn_wc__db_pristine_read(svn_stream_t ** contents,svn_filesize_t * size,svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)218 svn_wc__db_pristine_read(svn_stream_t **contents,
219 svn_filesize_t *size,
220 svn_wc__db_t *db,
221 const char *wri_abspath,
222 const svn_checksum_t *sha1_checksum,
223 apr_pool_t *result_pool,
224 apr_pool_t *scratch_pool)
225 {
226 svn_wc__db_wcroot_t *wcroot;
227 const char *local_relpath;
228 const char *pristine_abspath;
229
230 SVN_ERR_ASSERT(contents != NULL);
231 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
232
233 /* Some 1.6-to-1.7 wc upgrades created rows without checksums and
234 updating such a row passes NULL here. */
235 if (!sha1_checksum)
236 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
237 _("Can't read '%s' from pristine store "
238 "because no checksum supplied"),
239 svn_dirent_local_style(wri_abspath, scratch_pool));
240
241 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
242
243 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
244 wri_abspath, scratch_pool, scratch_pool));
245 VERIFY_USABLE_WCROOT(wcroot);
246
247 SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
248 sha1_checksum,
249 scratch_pool, scratch_pool));
250 SVN_WC__DB_WITH_TXN(
251 pristine_read_txn(contents, size,
252 wcroot, sha1_checksum, pristine_abspath,
253 result_pool, scratch_pool),
254 wcroot);
255
256 return SVN_NO_ERROR;
257 }
258
259
260 /* Return the absolute path to the temporary directory for pristine text
261 files within WCROOT. */
262 static char *
pristine_get_tempdir(svn_wc__db_wcroot_t * wcroot,apr_pool_t * result_pool,apr_pool_t * scratch_pool)263 pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot,
264 apr_pool_t *result_pool,
265 apr_pool_t *scratch_pool)
266 {
267 return svn_dirent_join_many(result_pool, wcroot->abspath,
268 svn_wc_get_adm_dir(scratch_pool),
269 PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL);
270 }
271
272 /* Install the pristine text described by BATON into the pristine store of
273 * SDB. If it is already stored then just delete the new file
274 * BATON->tempfile_abspath.
275 *
276 * This function expects to be executed inside a SQLite txn that has already
277 * acquired a 'RESERVED' lock.
278 *
279 * Implements 'notes/wc-ng/pristine-store' section A-3(a).
280 */
281 static svn_error_t *
pristine_install_txn(svn_sqlite__db_t * sdb,svn_stream_t * install_stream,const char * pristine_abspath,const svn_checksum_t * sha1_checksum,const svn_checksum_t * md5_checksum,apr_pool_t * scratch_pool)282 pristine_install_txn(svn_sqlite__db_t *sdb,
283 /* The path to the source file that is to be moved into place. */
284 svn_stream_t *install_stream,
285 /* The target path for the file (within the pristine store). */
286 const char *pristine_abspath,
287 /* The pristine text's SHA-1 checksum. */
288 const svn_checksum_t *sha1_checksum,
289 /* The pristine text's MD-5 checksum. */
290 const svn_checksum_t *md5_checksum,
291 apr_pool_t *scratch_pool)
292 {
293 svn_sqlite__stmt_t *stmt;
294 svn_boolean_t have_row;
295
296 /* If this pristine text is already present in the store, just keep it:
297 * delete the new one and return. */
298 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
299 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
300 SVN_ERR(svn_sqlite__step(&have_row, stmt));
301 SVN_ERR(svn_sqlite__reset(stmt));
302
303 if (have_row)
304 {
305 #ifdef SVN_DEBUG
306 /* Consistency checks. Verify both files exist and match.
307 * ### We could check much more. */
308 {
309 apr_finfo_t finfo1, finfo2;
310
311 SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE,
312 scratch_pool));
313
314 SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE,
315 scratch_pool));
316 if (finfo1.size != finfo2.size)
317 {
318 return svn_error_createf(
319 SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
320 _("New pristine text '%s' has different size: %ld versus %ld"),
321 svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
322 (long int)finfo1.size, (long int)finfo2.size);
323 }
324 }
325 #endif
326
327 /* Remove the temp file: it's already there */
328 SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
329 return SVN_NO_ERROR;
330 }
331
332 /* Move the file to its target location. (If it is already there, it is
333 * an orphan file and it doesn't matter if we overwrite it.) */
334 {
335 apr_finfo_t finfo;
336 SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, APR_FINFO_SIZE,
337 scratch_pool));
338 SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
339 TRUE, scratch_pool));
340
341 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
342 STMT_INSERT_PRISTINE));
343 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
344 SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
345 SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
346 SVN_ERR(svn_sqlite__insert(NULL, stmt));
347
348 SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool));
349 }
350
351 return SVN_NO_ERROR;
352 }
353
354 struct svn_wc__db_install_data_t
355 {
356 svn_wc__db_wcroot_t *wcroot;
357 svn_stream_t *inner_stream;
358 };
359
360 svn_error_t *
svn_wc__db_pristine_prepare_install(svn_stream_t ** stream,svn_wc__db_install_data_t ** install_data,svn_checksum_t ** sha1_checksum,svn_checksum_t ** md5_checksum,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)361 svn_wc__db_pristine_prepare_install(svn_stream_t **stream,
362 svn_wc__db_install_data_t **install_data,
363 svn_checksum_t **sha1_checksum,
364 svn_checksum_t **md5_checksum,
365 svn_wc__db_t *db,
366 const char *wri_abspath,
367 apr_pool_t *result_pool,
368 apr_pool_t *scratch_pool)
369 {
370 svn_wc__db_wcroot_t *wcroot;
371 const char *local_relpath;
372 const char *temp_dir_abspath;
373
374 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
375
376 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
377 wri_abspath, scratch_pool, scratch_pool));
378 VERIFY_USABLE_WCROOT(wcroot);
379
380 temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
381
382 *install_data = apr_pcalloc(result_pool, sizeof(**install_data));
383 (*install_data)->wcroot = wcroot;
384
385 SVN_ERR_W(svn_stream__create_for_install(stream,
386 temp_dir_abspath,
387 result_pool, scratch_pool),
388 _("Unable to create pristine install stream"));
389
390 (*install_data)->inner_stream = *stream;
391
392 if (md5_checksum)
393 *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
394 svn_checksum_md5, FALSE, result_pool);
395 if (sha1_checksum)
396 *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
397 svn_checksum_sha1, FALSE, result_pool);
398
399 return SVN_NO_ERROR;
400 }
401
402 svn_error_t *
svn_wc__db_pristine_install(svn_wc__db_install_data_t * install_data,const svn_checksum_t * sha1_checksum,const svn_checksum_t * md5_checksum,apr_pool_t * scratch_pool)403 svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data,
404 const svn_checksum_t *sha1_checksum,
405 const svn_checksum_t *md5_checksum,
406 apr_pool_t *scratch_pool)
407 {
408 svn_wc__db_wcroot_t *wcroot = install_data->wcroot;
409 const char *pristine_abspath;
410
411 SVN_ERR_ASSERT(sha1_checksum != NULL);
412 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
413 SVN_ERR_ASSERT(md5_checksum != NULL);
414 SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
415
416 SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
417 sha1_checksum,
418 scratch_pool, scratch_pool));
419
420 /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
421 * at the disk, to ensure no concurrent pristine install/delete txn. */
422 SVN_SQLITE__WITH_IMMEDIATE_TXN(
423 pristine_install_txn(wcroot->sdb,
424 install_data->inner_stream, pristine_abspath,
425 sha1_checksum, md5_checksum,
426 scratch_pool),
427 wcroot->sdb);
428
429 return SVN_NO_ERROR;
430 }
431
432 svn_error_t *
svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t * install_data,apr_pool_t * scratch_pool)433 svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data,
434 apr_pool_t *scratch_pool)
435 {
436 return svn_error_trace(svn_stream__install_delete(install_data->inner_stream,
437 scratch_pool));
438 }
439
440
441 svn_error_t *
svn_wc__db_pristine_get_md5(const svn_checksum_t ** md5_checksum,svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)442 svn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum,
443 svn_wc__db_t *db,
444 const char *wri_abspath,
445 const svn_checksum_t *sha1_checksum,
446 apr_pool_t *result_pool,
447 apr_pool_t *scratch_pool)
448 {
449 svn_wc__db_wcroot_t *wcroot;
450 const char *local_relpath;
451 svn_sqlite__stmt_t *stmt;
452 svn_boolean_t have_row;
453
454 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
455 SVN_ERR_ASSERT(sha1_checksum != NULL);
456 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
457
458 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
459 wri_abspath, scratch_pool, scratch_pool));
460 VERIFY_USABLE_WCROOT(wcroot);
461
462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
463 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
464 SVN_ERR(svn_sqlite__step(&have_row, stmt));
465 if (!have_row)
466 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
467 _("The pristine text with checksum '%s' was "
468 "not found"),
469 svn_checksum_to_cstring_display(sha1_checksum,
470 scratch_pool));
471
472 SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
473 SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5);
474
475 return svn_error_trace(svn_sqlite__reset(stmt));
476 }
477
478
479 svn_error_t *
svn_wc__db_pristine_get_sha1(const svn_checksum_t ** sha1_checksum,svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * md5_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)480 svn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum,
481 svn_wc__db_t *db,
482 const char *wri_abspath,
483 const svn_checksum_t *md5_checksum,
484 apr_pool_t *result_pool,
485 apr_pool_t *scratch_pool)
486 {
487 svn_wc__db_wcroot_t *wcroot;
488 const char *local_relpath;
489 svn_sqlite__stmt_t *stmt;
490 svn_boolean_t have_row;
491
492 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
493 SVN_ERR_ASSERT(sha1_checksum != NULL);
494 SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
495
496 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
497 wri_abspath, scratch_pool, scratch_pool));
498 VERIFY_USABLE_WCROOT(wcroot);
499
500 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
501 STMT_SELECT_PRISTINE_BY_MD5));
502 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, md5_checksum, scratch_pool));
503 SVN_ERR(svn_sqlite__step(&have_row, stmt));
504 if (!have_row)
505 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
506 _("The pristine text with MD5 checksum '%s' was "
507 "not found"),
508 svn_checksum_to_cstring_display(md5_checksum,
509 scratch_pool));
510
511 SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool));
512 SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1);
513
514 return svn_error_trace(svn_sqlite__reset(stmt));
515 }
516
517 /* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
518 pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */
519 static svn_error_t *
maybe_transfer_one_pristine(svn_wc__db_wcroot_t * src_wcroot,svn_wc__db_wcroot_t * dst_wcroot,const svn_checksum_t * checksum,const svn_checksum_t * md5_checksum,apr_int64_t size,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)520 maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
521 svn_wc__db_wcroot_t *dst_wcroot,
522 const svn_checksum_t *checksum,
523 const svn_checksum_t *md5_checksum,
524 apr_int64_t size,
525 svn_cancel_func_t cancel_func,
526 void *cancel_baton,
527 apr_pool_t *scratch_pool)
528 {
529 const char *pristine_abspath;
530 svn_sqlite__stmt_t *stmt;
531 svn_stream_t *src_stream;
532 svn_stream_t *dst_stream;
533 const char *tmp_abspath;
534 const char *src_abspath;
535 int affected_rows;
536 svn_error_t *err;
537
538 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
539 STMT_INSERT_OR_IGNORE_PRISTINE));
540 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
541 SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
542 SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
543
544 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
545
546 if (affected_rows == 0)
547 return SVN_NO_ERROR;
548
549 SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
550 pristine_get_tempdir(dst_wcroot,
551 scratch_pool,
552 scratch_pool),
553 svn_io_file_del_on_pool_cleanup,
554 scratch_pool, scratch_pool));
555
556 SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
557 scratch_pool, scratch_pool));
558
559 SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
560 scratch_pool, scratch_pool));
561
562 /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
563 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
564 cancel_func, cancel_baton,
565 scratch_pool));
566
567 SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
568 scratch_pool, scratch_pool));
569
570 /* Move the file to its target location. (If it is already there, it is
571 * an orphan file and it doesn't matter if we overwrite it.) */
572 err = svn_io_file_rename(tmp_abspath, pristine_abspath, scratch_pool);
573
574 /* Maybe the directory doesn't exist yet? */
575 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
576 {
577 svn_error_t *err2;
578
579 err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
580 scratch_pool),
581 APR_OS_DEFAULT, scratch_pool);
582
583 if (err2)
584 /* Creating directory didn't work: Return all errors */
585 return svn_error_trace(svn_error_compose_create(err, err2));
586 else
587 /* We could create a directory: retry install */
588 svn_error_clear(err);
589
590 SVN_ERR(svn_io_file_rename(tmp_abspath, pristine_abspath, scratch_pool));
591 }
592 else
593 SVN_ERR(err);
594
595 return SVN_NO_ERROR;
596 }
597
598 /* Transaction implementation of svn_wc__db_pristine_transfer().
599 We have a lock on DST_WCROOT.
600 */
601 static svn_error_t *
pristine_transfer_txn(svn_wc__db_wcroot_t * src_wcroot,svn_wc__db_wcroot_t * dst_wcroot,const char * src_relpath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)602 pristine_transfer_txn(svn_wc__db_wcroot_t *src_wcroot,
603 svn_wc__db_wcroot_t *dst_wcroot,
604 const char *src_relpath,
605 svn_cancel_func_t cancel_func,
606 void *cancel_baton,
607 apr_pool_t *scratch_pool)
608 {
609 svn_sqlite__stmt_t *stmt;
610 svn_boolean_t got_row;
611 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
612
613 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
614 STMT_SELECT_COPY_PRISTINES));
615 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
616
617 /* This obtains an sqlite read lock on src_wcroot */
618 SVN_ERR(svn_sqlite__step(&got_row, stmt));
619
620 while (got_row)
621 {
622 const svn_checksum_t *checksum;
623 const svn_checksum_t *md5_checksum;
624 apr_int64_t size;
625 svn_error_t *err;
626
627 svn_pool_clear(iterpool);
628
629 SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
630 SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
631 size = svn_sqlite__column_int64(stmt, 2);
632
633 err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
634 checksum, md5_checksum, size,
635 cancel_func, cancel_baton,
636 iterpool);
637
638 if (err)
639 return svn_error_trace(svn_error_compose_create(
640 err,
641 svn_sqlite__reset(stmt)));
642
643 SVN_ERR(svn_sqlite__step(&got_row, stmt));
644 }
645 SVN_ERR(svn_sqlite__reset(stmt));
646
647 svn_pool_destroy(iterpool);
648
649 return SVN_NO_ERROR;
650 }
651
652 svn_error_t *
svn_wc__db_pristine_transfer(svn_wc__db_t * db,const char * src_local_abspath,const char * dst_wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)653 svn_wc__db_pristine_transfer(svn_wc__db_t *db,
654 const char *src_local_abspath,
655 const char *dst_wri_abspath,
656 svn_cancel_func_t cancel_func,
657 void *cancel_baton,
658 apr_pool_t *scratch_pool)
659 {
660 svn_wc__db_wcroot_t *src_wcroot, *dst_wcroot;
661 const char *src_relpath, *dst_relpath;
662
663 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&src_wcroot, &src_relpath,
664 db, src_local_abspath,
665 scratch_pool, scratch_pool));
666 VERIFY_USABLE_WCROOT(src_wcroot);
667 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&dst_wcroot, &dst_relpath,
668 db, dst_wri_abspath,
669 scratch_pool, scratch_pool));
670 VERIFY_USABLE_WCROOT(dst_wcroot);
671
672 if (src_wcroot == dst_wcroot
673 || src_wcroot->sdb == dst_wcroot->sdb)
674 {
675 return SVN_NO_ERROR; /* Nothing to transfer */
676 }
677
678 SVN_WC__DB_WITH_TXN(
679 pristine_transfer_txn(src_wcroot, dst_wcroot, src_relpath,
680 cancel_func, cancel_baton, scratch_pool),
681 dst_wcroot);
682
683 return SVN_NO_ERROR;
684 }
685
686
687
688
689 /* Remove the file at FILE_ABSPATH in such a way that we could re-create a
690 * new file of the same name at any time thereafter.
691 *
692 * On Windows, the file will not disappear immediately from the directory if
693 * it is still being read so the best thing to do is first rename it to a
694 * unique name. */
695 static svn_error_t *
remove_file(const char * file_abspath,svn_wc__db_wcroot_t * wcroot,svn_boolean_t ignore_enoent,apr_pool_t * scratch_pool)696 remove_file(const char *file_abspath,
697 svn_wc__db_wcroot_t *wcroot,
698 svn_boolean_t ignore_enoent,
699 apr_pool_t *scratch_pool)
700 {
701 #ifdef WIN32
702 svn_error_t *err;
703 const char *temp_abspath;
704 const char *temp_dir_abspath
705 = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
706
707 /* To rename the file to a unique name in the temp dir, first create a
708 * uniquely named file in the temp dir and then overwrite it. */
709 SVN_ERR(svn_io_open_unique_file3(NULL, &temp_abspath, temp_dir_abspath,
710 svn_io_file_del_none,
711 scratch_pool, scratch_pool));
712 err = svn_io_file_rename(file_abspath, temp_abspath, scratch_pool);
713 if (err && ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
714 svn_error_clear(err);
715 else
716 SVN_ERR(err);
717 file_abspath = temp_abspath;
718 #endif
719
720 SVN_ERR(svn_io_remove_file2(file_abspath, ignore_enoent, scratch_pool));
721
722 return SVN_NO_ERROR;
723 }
724
725 /* If the pristine text referenced by SHA1_CHECKSUM in WCROOT/SDB, whose path
726 * within the pristine store is PRISTINE_ABSPATH, has a reference count of
727 * zero, delete it (both the database row and the disk file).
728 *
729 * This function expects to be executed inside a SQLite txn that has already
730 * acquired a 'RESERVED' lock.
731 */
732 static svn_error_t *
pristine_remove_if_unreferenced_txn(svn_sqlite__db_t * sdb,svn_wc__db_wcroot_t * wcroot,const svn_checksum_t * sha1_checksum,const char * pristine_abspath,apr_pool_t * scratch_pool)733 pristine_remove_if_unreferenced_txn(svn_sqlite__db_t *sdb,
734 svn_wc__db_wcroot_t *wcroot,
735 const svn_checksum_t *sha1_checksum,
736 const char *pristine_abspath,
737 apr_pool_t *scratch_pool)
738 {
739 svn_sqlite__stmt_t *stmt;
740 int affected_rows;
741
742 /* Remove the DB row, if refcount is 0. */
743 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
744 STMT_DELETE_PRISTINE_IF_UNREFERENCED));
745 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
746 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
747
748 /* If we removed the DB row, then remove the file. */
749 if (affected_rows > 0)
750 {
751 /* If the file is not present, something has gone wrong, but at this
752 * point it no longer matters. In a debug build, raise an error, but
753 * in a release build, it is more helpful to ignore it and continue. */
754 #ifdef SVN_DEBUG
755 svn_boolean_t ignore_enoent = FALSE;
756 #else
757 svn_boolean_t ignore_enoent = TRUE;
758 #endif
759
760 SVN_ERR(remove_file(pristine_abspath, wcroot, ignore_enoent,
761 scratch_pool));
762 }
763
764 return SVN_NO_ERROR;
765 }
766
767 /* If the pristine text referenced by SHA1_CHECKSUM in WCROOT has a
768 * reference count of zero, delete it (both the database row and the disk
769 * file).
770 *
771 * Implements 'notes/wc-ng/pristine-store' section A-3(b). */
772 static svn_error_t *
pristine_remove_if_unreferenced(svn_wc__db_wcroot_t * wcroot,const svn_checksum_t * sha1_checksum,apr_pool_t * scratch_pool)773 pristine_remove_if_unreferenced(svn_wc__db_wcroot_t *wcroot,
774 const svn_checksum_t *sha1_checksum,
775 apr_pool_t *scratch_pool)
776 {
777 const char *pristine_abspath;
778
779 SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
780 sha1_checksum, scratch_pool, scratch_pool));
781
782 /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
783 * at the disk, to ensure no concurrent pristine install/delete txn. */
784 SVN_SQLITE__WITH_IMMEDIATE_TXN(
785 pristine_remove_if_unreferenced_txn(
786 wcroot->sdb, wcroot, sha1_checksum, pristine_abspath, scratch_pool),
787 wcroot->sdb);
788
789 return SVN_NO_ERROR;
790 }
791
792 svn_error_t *
svn_wc__db_pristine_remove(svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * scratch_pool)793 svn_wc__db_pristine_remove(svn_wc__db_t *db,
794 const char *wri_abspath,
795 const svn_checksum_t *sha1_checksum,
796 apr_pool_t *scratch_pool)
797 {
798 svn_wc__db_wcroot_t *wcroot;
799 const char *local_relpath;
800
801 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
802 SVN_ERR_ASSERT(sha1_checksum != NULL);
803 /* ### Transitional: accept MD-5 and look up the SHA-1. Return an error
804 * if the pristine text is not in the store. */
805 if (sha1_checksum->kind != svn_checksum_sha1)
806 SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
807 sha1_checksum,
808 scratch_pool, scratch_pool));
809 SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
810
811 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
812 wri_abspath, scratch_pool, scratch_pool));
813 VERIFY_USABLE_WCROOT(wcroot);
814
815 /* If the work queue is not empty, don't delete any pristine text because
816 * the work queue may contain a reference to it. */
817 {
818 svn_sqlite__stmt_t *stmt;
819 svn_boolean_t have_row;
820
821 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_LOOK_FOR_WORK));
822 SVN_ERR(svn_sqlite__step(&have_row, stmt));
823 SVN_ERR(svn_sqlite__reset(stmt));
824
825 if (have_row)
826 return SVN_NO_ERROR;
827 }
828
829 /* If not referenced, remove the PRISTINE table row and the file. */
830 SVN_ERR(pristine_remove_if_unreferenced(wcroot, sha1_checksum, scratch_pool));
831
832 return SVN_NO_ERROR;
833 }
834
835
836 /* Remove all unreferenced pristines in the WC DB in WCROOT.
837 *
838 * Look for pristine texts whose 'refcount' in the DB is zero, and remove
839 * them from the 'pristine' table and from disk.
840 *
841 * TODO: At least check that any zero refcount is really correct, before
842 * using it. See dev@ email thread "Pristine text missing - cleanup
843 * doesn't work", <http://svn.haxx.se/dev/archive-2013-04/0426.shtml>.
844 *
845 * TODO: Ideas for possible extra clean-up operations:
846 *
847 * * Check and correct all the refcounts. Identify any rows missing
848 * from the 'pristine' table. (Create a temporary index for speed
849 * if necessary?)
850 *
851 * * Check the checksums. (Very expensive to check them all, so find
852 * a way to not check them all.)
853 *
854 * * Check for pristine files missing from disk but referenced in the
855 * 'pristine' table.
856 *
857 * * Repair any pristine files missing from disk and/or rows missing
858 * from the 'pristine' table and/or bad checksums. Generally
859 * requires contacting the server, so requires support at a higher
860 * level than this function.
861 *
862 * * Identify any pristine text files on disk that are not referenced
863 * in the DB, and delete them.
864 *
865 * TODO: Provide feedback about any errors found and any corrections made.
866 */
867 static svn_error_t *
pristine_cleanup_wcroot(svn_wc__db_wcroot_t * wcroot,apr_pool_t * scratch_pool)868 pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot,
869 apr_pool_t *scratch_pool)
870 {
871 svn_sqlite__stmt_t *stmt;
872 svn_error_t *err = NULL;
873 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
874
875 /* Find each unreferenced pristine in the DB and remove it. */
876 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
877 STMT_SELECT_UNREFERENCED_PRISTINES));
878 while (! err)
879 {
880 svn_boolean_t have_row;
881 const svn_checksum_t *sha1_checksum;
882
883 svn_pool_clear(iterpool);
884
885 SVN_ERR(svn_sqlite__step(&have_row, stmt));
886 if (! have_row)
887 break;
888
889 SVN_ERR(svn_sqlite__column_checksum(&sha1_checksum, stmt, 0,
890 iterpool));
891 err = pristine_remove_if_unreferenced(wcroot, sha1_checksum,
892 iterpool);
893 }
894
895 svn_pool_destroy(iterpool);
896
897 return svn_error_trace(
898 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
899 }
900
901 svn_error_t *
svn_wc__db_pristine_cleanup(svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * scratch_pool)902 svn_wc__db_pristine_cleanup(svn_wc__db_t *db,
903 const char *wri_abspath,
904 apr_pool_t *scratch_pool)
905 {
906 svn_wc__db_wcroot_t *wcroot;
907 const char *local_relpath;
908
909 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
910
911 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
912 wri_abspath, scratch_pool, scratch_pool));
913 VERIFY_USABLE_WCROOT(wcroot);
914
915 SVN_ERR(pristine_cleanup_wcroot(wcroot, scratch_pool));
916
917 return SVN_NO_ERROR;
918 }
919
920
921 svn_error_t *
svn_wc__db_pristine_check(svn_boolean_t * present,svn_wc__db_t * db,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * scratch_pool)922 svn_wc__db_pristine_check(svn_boolean_t *present,
923 svn_wc__db_t *db,
924 const char *wri_abspath,
925 const svn_checksum_t *sha1_checksum,
926 apr_pool_t *scratch_pool)
927 {
928 svn_wc__db_wcroot_t *wcroot;
929 const char *local_relpath;
930 svn_sqlite__stmt_t *stmt;
931 svn_boolean_t have_row;
932
933 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
934 SVN_ERR_ASSERT(sha1_checksum != NULL);
935
936 if (sha1_checksum->kind != svn_checksum_sha1)
937 {
938 *present = FALSE;
939 return SVN_NO_ERROR;
940 }
941
942 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
943 wri_abspath, scratch_pool, scratch_pool));
944 VERIFY_USABLE_WCROOT(wcroot);
945
946 /* A filestat is much cheaper than a sqlite transaction especially on NFS,
947 so first check if there is a pristine file and then if we are allowed
948 to use it. */
949 {
950 const char *pristine_abspath;
951 svn_node_kind_t kind_on_disk;
952
953 SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
954 sha1_checksum, scratch_pool, scratch_pool));
955 SVN_ERR(svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool));
956 if (kind_on_disk != svn_node_file)
957 {
958 *present = FALSE;
959 return SVN_NO_ERROR;
960 }
961 }
962
963 /* Check that there is an entry in the PRISTINE table. */
964 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
965 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
966 SVN_ERR(svn_sqlite__step(&have_row, stmt));
967 SVN_ERR(svn_sqlite__reset(stmt));
968
969 *present = have_row;
970 return SVN_NO_ERROR;
971 }
972