wc_db_pristine.c revision 362181
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. */
52static svn_error_t *
53get_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
96svn_error_t *
97svn_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
140svn_error_t *
141svn_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 */
168static svn_error_t *
169pristine_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
217svn_error_t *
218svn_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(svn_dirent_is_absolute(wri_abspath));
231
232  /* Some 1.6-to-1.7 wc upgrades created rows without checksums and
233     updating such a row passes NULL here. */
234  if (!sha1_checksum)
235    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
236                             _("Can't read '%s' from pristine store "
237                               "because no checksum supplied"),
238                             svn_dirent_local_style(wri_abspath, scratch_pool));
239
240  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
241
242  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
243                              wri_abspath, scratch_pool, scratch_pool));
244  VERIFY_USABLE_WCROOT(wcroot);
245
246  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
247                             sha1_checksum,
248                             scratch_pool, scratch_pool));
249  SVN_WC__DB_WITH_TXN(
250    pristine_read_txn(contents, size,
251                      wcroot, sha1_checksum, pristine_abspath,
252                      result_pool, scratch_pool),
253    wcroot);
254
255  return SVN_NO_ERROR;
256}
257
258
259/* Return the absolute path to the temporary directory for pristine text
260   files within WCROOT. */
261static char *
262pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot,
263                     apr_pool_t *result_pool,
264                     apr_pool_t *scratch_pool)
265{
266  return svn_dirent_join_many(result_pool, wcroot->abspath,
267                              svn_wc_get_adm_dir(scratch_pool),
268                              PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL);
269}
270
271/* Install the pristine text described by BATON into the pristine store of
272 * SDB.  If it is already stored then just delete the new file
273 * BATON->tempfile_abspath.
274 *
275 * This function expects to be executed inside a SQLite txn that has already
276 * acquired a 'RESERVED' lock.
277 *
278 * Implements 'notes/wc-ng/pristine-store' section A-3(a).
279 */
280static svn_error_t *
281pristine_install_txn(svn_sqlite__db_t *sdb,
282                     /* The path to the source file that is to be moved into place. */
283                     svn_stream_t *install_stream,
284                     /* The target path for the file (within the pristine store). */
285                     const char *pristine_abspath,
286                     /* The pristine text's SHA-1 checksum. */
287                     const svn_checksum_t *sha1_checksum,
288                     /* The pristine text's MD-5 checksum. */
289                     const svn_checksum_t *md5_checksum,
290                     apr_pool_t *scratch_pool)
291{
292  svn_sqlite__stmt_t *stmt;
293  svn_boolean_t have_row;
294
295  /* If this pristine text is already present in the store, just keep it:
296   * delete the new one and return. */
297  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
298  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
299  SVN_ERR(svn_sqlite__step(&have_row, stmt));
300  SVN_ERR(svn_sqlite__reset(stmt));
301
302  if (have_row)
303    {
304#ifdef SVN_DEBUG
305      /* Consistency checks.  Verify both files exist and match.
306       * ### We could check much more. */
307      {
308        apr_finfo_t finfo1, finfo2;
309
310        SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE,
311                                             scratch_pool));
312
313        SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE,
314                            scratch_pool));
315        if (finfo1.size != finfo2.size)
316          {
317            return svn_error_createf(
318              SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
319              _("New pristine text '%s' has different size: %s versus %s"),
320              svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
321              apr_off_t_toa(scratch_pool, finfo1.size),
322              apr_off_t_toa(scratch_pool, 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,
337                                         APR_FINFO_SIZE, 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, STMT_INSERT_PRISTINE));
342    SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
343    SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
344    SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
345    SVN_ERR(svn_sqlite__insert(NULL, stmt));
346
347    SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool));
348  }
349
350  return SVN_NO_ERROR;
351}
352
353struct svn_wc__db_install_data_t
354{
355  svn_wc__db_wcroot_t *wcroot;
356  svn_stream_t *inner_stream;
357};
358
359svn_error_t *
360svn_wc__db_pristine_prepare_install(svn_stream_t **stream,
361                                    svn_wc__db_install_data_t **install_data,
362                                    svn_checksum_t **sha1_checksum,
363                                    svn_checksum_t **md5_checksum,
364                                    svn_wc__db_t *db,
365                                    const char *wri_abspath,
366                                    apr_pool_t *result_pool,
367                                    apr_pool_t *scratch_pool)
368{
369  svn_wc__db_wcroot_t *wcroot;
370  const char *local_relpath;
371  const char *temp_dir_abspath;
372
373  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
374
375  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
376                              wri_abspath, scratch_pool, scratch_pool));
377  VERIFY_USABLE_WCROOT(wcroot);
378
379  temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
380
381  *install_data = apr_pcalloc(result_pool, sizeof(**install_data));
382  (*install_data)->wcroot = wcroot;
383
384  SVN_ERR_W(svn_stream__create_for_install(stream,
385                                           temp_dir_abspath,
386                                           result_pool, scratch_pool),
387            _("Unable to create pristine install stream"));
388
389  (*install_data)->inner_stream = *stream;
390
391  if (md5_checksum)
392    *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
393                                      svn_checksum_md5, FALSE, result_pool);
394  if (sha1_checksum)
395    *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
396                                      svn_checksum_sha1, FALSE, result_pool);
397
398  return SVN_NO_ERROR;
399}
400
401svn_error_t *
402svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data,
403                            const svn_checksum_t *sha1_checksum,
404                            const svn_checksum_t *md5_checksum,
405                            apr_pool_t *scratch_pool)
406{
407  svn_wc__db_wcroot_t *wcroot = install_data->wcroot;
408  const char *pristine_abspath;
409
410  SVN_ERR_ASSERT(sha1_checksum != NULL);
411  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
412  SVN_ERR_ASSERT(md5_checksum != NULL);
413  SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
414
415  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
416                             sha1_checksum,
417                             scratch_pool, scratch_pool));
418
419  /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
420   * at the disk, to ensure no concurrent pristine install/delete txn. */
421  SVN_SQLITE__WITH_IMMEDIATE_TXN(
422    pristine_install_txn(wcroot->sdb,
423                         install_data->inner_stream, pristine_abspath,
424                         sha1_checksum, md5_checksum,
425                         scratch_pool),
426    wcroot->sdb);
427
428  return SVN_NO_ERROR;
429}
430
431svn_error_t *
432svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data,
433                                  apr_pool_t *scratch_pool)
434{
435  return svn_error_trace(svn_stream__install_delete(install_data->inner_stream,
436                                                    scratch_pool));
437}
438
439
440svn_error_t *
441svn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum,
442                            svn_wc__db_t *db,
443                            const char *wri_abspath,
444                            const svn_checksum_t *sha1_checksum,
445                            apr_pool_t *result_pool,
446                            apr_pool_t *scratch_pool)
447{
448  svn_wc__db_wcroot_t *wcroot;
449  const char *local_relpath;
450  svn_sqlite__stmt_t *stmt;
451  svn_boolean_t have_row;
452
453  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
454  SVN_ERR_ASSERT(sha1_checksum != NULL);
455  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
456
457  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
458                              wri_abspath, scratch_pool, scratch_pool));
459  VERIFY_USABLE_WCROOT(wcroot);
460
461  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
462  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
463  SVN_ERR(svn_sqlite__step(&have_row, stmt));
464  if (!have_row)
465    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
466                             _("The pristine text with checksum '%s' was "
467                               "not found"),
468                             svn_checksum_to_cstring_display(sha1_checksum,
469                                                             scratch_pool));
470
471  SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
472  SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5);
473
474  return svn_error_trace(svn_sqlite__reset(stmt));
475}
476
477
478svn_error_t *
479svn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum,
480                             svn_wc__db_t *db,
481                             const char *wri_abspath,
482                             const svn_checksum_t *md5_checksum,
483                             apr_pool_t *result_pool,
484                             apr_pool_t *scratch_pool)
485{
486  svn_wc__db_wcroot_t *wcroot;
487  const char *local_relpath;
488  svn_sqlite__stmt_t *stmt;
489  svn_boolean_t have_row;
490
491  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
492  SVN_ERR_ASSERT(sha1_checksum != NULL);
493  SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
494
495  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
496                              wri_abspath, scratch_pool, scratch_pool));
497  VERIFY_USABLE_WCROOT(wcroot);
498
499  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
500                                    STMT_SELECT_PRISTINE_BY_MD5));
501  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, md5_checksum, scratch_pool));
502  SVN_ERR(svn_sqlite__step(&have_row, stmt));
503  if (!have_row)
504    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
505                             _("The pristine text with MD5 checksum '%s' was "
506                               "not found"),
507                             svn_checksum_to_cstring_display(md5_checksum,
508                                                             scratch_pool));
509
510  SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool));
511  SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1);
512
513  return svn_error_trace(svn_sqlite__reset(stmt));
514}
515
516/* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
517   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */
518static svn_error_t *
519maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
520                            svn_wc__db_wcroot_t *dst_wcroot,
521                            const svn_checksum_t *checksum,
522                            const svn_checksum_t *md5_checksum,
523                            apr_int64_t size,
524                            svn_cancel_func_t cancel_func,
525                            void *cancel_baton,
526                            apr_pool_t *scratch_pool)
527{
528  const char *pristine_abspath;
529  svn_sqlite__stmt_t *stmt;
530  svn_stream_t *src_stream;
531  svn_stream_t *dst_stream;
532  const char *tmp_abspath;
533  const char *src_abspath;
534  int affected_rows;
535  svn_error_t *err;
536
537  SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
538                                    STMT_INSERT_OR_IGNORE_PRISTINE));
539  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
540  SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
541  SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
542
543  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
544
545  if (affected_rows == 0)
546    return SVN_NO_ERROR;
547
548  SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
549                                 pristine_get_tempdir(dst_wcroot,
550                                                      scratch_pool,
551                                                      scratch_pool),
552                                 svn_io_file_del_on_pool_cleanup,
553                                 scratch_pool, scratch_pool));
554
555  SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
556                             scratch_pool, scratch_pool));
557
558  SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
559                                   scratch_pool, scratch_pool));
560
561  /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
562  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
563                           cancel_func, cancel_baton,
564                           scratch_pool));
565
566  SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
567                             scratch_pool, scratch_pool));
568
569  /* Move the file to its target location.  (If it is already there, it is
570   * an orphan file and it doesn't matter if we overwrite it.) */
571  err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
572                            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_rename2(tmp_abspath, pristine_abspath, FALSE,
591                                  scratch_pool));
592    }
593  else
594    SVN_ERR(err);
595
596  return SVN_NO_ERROR;
597}
598
599/* Transaction implementation of svn_wc__db_pristine_transfer().
600   We have a lock on DST_WCROOT.
601 */
602static svn_error_t *
603pristine_transfer_txn(svn_wc__db_wcroot_t *src_wcroot,
604                       svn_wc__db_wcroot_t *dst_wcroot,
605                       const char *src_relpath,
606                       svn_cancel_func_t cancel_func,
607                       void *cancel_baton,
608                       apr_pool_t *scratch_pool)
609{
610  svn_sqlite__stmt_t *stmt;
611  svn_boolean_t got_row;
612  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
613
614  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
615                                    STMT_SELECT_COPY_PRISTINES));
616  SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
617
618  /* This obtains an sqlite read lock on src_wcroot */
619  SVN_ERR(svn_sqlite__step(&got_row, stmt));
620
621  while (got_row)
622    {
623      const svn_checksum_t *checksum;
624      const svn_checksum_t *md5_checksum;
625      apr_int64_t size;
626      svn_error_t *err;
627
628      svn_pool_clear(iterpool);
629
630      SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
631      SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
632      size = svn_sqlite__column_int64(stmt, 2);
633
634      err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
635                                        checksum, md5_checksum, size,
636                                        cancel_func, cancel_baton,
637                                        iterpool);
638
639      if (err)
640        return svn_error_trace(svn_error_compose_create(
641                                    err,
642                                    svn_sqlite__reset(stmt)));
643
644      SVN_ERR(svn_sqlite__step(&got_row, stmt));
645    }
646  SVN_ERR(svn_sqlite__reset(stmt));
647
648  svn_pool_destroy(iterpool);
649
650  return SVN_NO_ERROR;
651}
652
653svn_error_t *
654svn_wc__db_pristine_transfer(svn_wc__db_t *db,
655                             const char *src_local_abspath,
656                             const char *dst_wri_abspath,
657                             svn_cancel_func_t cancel_func,
658                             void *cancel_baton,
659                             apr_pool_t *scratch_pool)
660{
661  svn_wc__db_wcroot_t *src_wcroot, *dst_wcroot;
662  const char *src_relpath, *dst_relpath;
663
664  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&src_wcroot, &src_relpath,
665                                                db, src_local_abspath,
666                                                scratch_pool, scratch_pool));
667  VERIFY_USABLE_WCROOT(src_wcroot);
668  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&dst_wcroot, &dst_relpath,
669                                                db, dst_wri_abspath,
670                                                scratch_pool, scratch_pool));
671  VERIFY_USABLE_WCROOT(dst_wcroot);
672
673  if (src_wcroot == dst_wcroot
674      || src_wcroot->sdb == dst_wcroot->sdb)
675    {
676      return SVN_NO_ERROR; /* Nothing to transfer */
677    }
678
679  SVN_WC__DB_WITH_TXN(
680    pristine_transfer_txn(src_wcroot, dst_wcroot, src_relpath,
681                          cancel_func, cancel_baton, scratch_pool),
682    dst_wcroot);
683
684  return SVN_NO_ERROR;
685}
686
687
688
689
690/* If the pristine text referenced by SHA1_CHECKSUM in WCROOT/SDB, whose path
691 * within the pristine store is PRISTINE_ABSPATH, has a reference count of
692 * zero, delete it (both the database row and the disk file).
693 *
694 * This function expects to be executed inside a SQLite txn that has already
695 * acquired a 'RESERVED' lock.
696 */
697static svn_error_t *
698pristine_remove_if_unreferenced_txn(svn_sqlite__db_t *sdb,
699                                    svn_wc__db_wcroot_t *wcroot,
700                                    const svn_checksum_t *sha1_checksum,
701                                    const char *pristine_abspath,
702                                    apr_pool_t *scratch_pool)
703{
704  svn_sqlite__stmt_t *stmt;
705  int affected_rows;
706
707  /* Remove the DB row, if refcount is 0. */
708  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
709                                    STMT_DELETE_PRISTINE_IF_UNREFERENCED));
710  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
711  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
712
713  /* If we removed the DB row, then remove the file. */
714  if (affected_rows > 0)
715    {
716      /* If the file is not present, something has gone wrong, but at this
717       * point it no longer matters.  In a debug build, raise an error, but
718       * in a release build, it is more helpful to ignore it and continue. */
719#ifdef SVN_DEBUG
720      svn_boolean_t ignore_enoent = FALSE;
721#else
722      svn_boolean_t ignore_enoent = TRUE;
723#endif
724
725      SVN_ERR(svn_io_remove_file2(pristine_abspath, ignore_enoent,
726                                  scratch_pool));
727    }
728
729  return SVN_NO_ERROR;
730}
731
732/* If the pristine text referenced by SHA1_CHECKSUM in WCROOT has a
733 * reference count of zero, delete it (both the database row and the disk
734 * file).
735 *
736 * Implements 'notes/wc-ng/pristine-store' section A-3(b). */
737static svn_error_t *
738pristine_remove_if_unreferenced(svn_wc__db_wcroot_t *wcroot,
739                                const svn_checksum_t *sha1_checksum,
740                                apr_pool_t *scratch_pool)
741{
742  const char *pristine_abspath;
743
744  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
745                             sha1_checksum, scratch_pool, scratch_pool));
746
747  /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
748   * at the disk, to ensure no concurrent pristine install/delete txn. */
749  SVN_SQLITE__WITH_IMMEDIATE_TXN(
750    pristine_remove_if_unreferenced_txn(
751      wcroot->sdb, wcroot, sha1_checksum, pristine_abspath, scratch_pool),
752    wcroot->sdb);
753
754  return SVN_NO_ERROR;
755}
756
757svn_error_t *
758svn_wc__db_pristine_remove(svn_wc__db_t *db,
759                           const char *wri_abspath,
760                           const svn_checksum_t *sha1_checksum,
761                           apr_pool_t *scratch_pool)
762{
763  svn_wc__db_wcroot_t *wcroot;
764  const char *local_relpath;
765
766  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
767  SVN_ERR_ASSERT(sha1_checksum != NULL);
768  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
769   * if the pristine text is not in the store. */
770  if (sha1_checksum->kind != svn_checksum_sha1)
771    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
772                                         sha1_checksum,
773                                         scratch_pool, scratch_pool));
774  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
775
776  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
777                              wri_abspath, scratch_pool, scratch_pool));
778  VERIFY_USABLE_WCROOT(wcroot);
779
780  /* If the work queue is not empty, don't delete any pristine text because
781   * the work queue may contain a reference to it. */
782  {
783    svn_sqlite__stmt_t *stmt;
784    svn_boolean_t have_row;
785
786    SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_LOOK_FOR_WORK));
787    SVN_ERR(svn_sqlite__step(&have_row, stmt));
788    SVN_ERR(svn_sqlite__reset(stmt));
789
790    if (have_row)
791      return SVN_NO_ERROR;
792  }
793
794  /* If not referenced, remove the PRISTINE table row and the file. */
795  SVN_ERR(pristine_remove_if_unreferenced(wcroot, sha1_checksum, scratch_pool));
796
797  return SVN_NO_ERROR;
798}
799
800
801/* Remove all unreferenced pristines in the WC DB in WCROOT.
802 *
803 * Look for pristine texts whose 'refcount' in the DB is zero, and remove
804 * them from the 'pristine' table and from disk.
805 *
806 * TODO: At least check that any zero refcount is really correct, before
807 *       using it.  See dev@ email thread "Pristine text missing - cleanup
808 *       doesn't work", <http://svn.haxx.se/dev/archive-2013-04/0426.shtml>.
809 *
810 * TODO: Ideas for possible extra clean-up operations:
811 *
812 *       * Check and correct all the refcounts.  Identify any rows missing
813 *         from the 'pristine' table.  (Create a temporary index for speed
814 *         if necessary?)
815 *
816 *       * Check the checksums.  (Very expensive to check them all, so find
817 *         a way to not check them all.)
818 *
819 *       * Check for pristine files missing from disk but referenced in the
820 *         'pristine' table.
821 *
822 *       * Repair any pristine files missing from disk and/or rows missing
823 *         from the 'pristine' table and/or bad checksums.  Generally
824 *         requires contacting the server, so requires support at a higher
825 *         level than this function.
826 *
827 *       * Identify any pristine text files on disk that are not referenced
828 *         in the DB, and delete them.
829 *
830 * TODO: Provide feedback about any errors found and any corrections made.
831 */
832static svn_error_t *
833pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot,
834                        apr_pool_t *scratch_pool)
835{
836  svn_sqlite__stmt_t *stmt;
837  svn_error_t *err = NULL;
838  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
839
840  /* Find each unreferenced pristine in the DB and remove it. */
841  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
842                                    STMT_SELECT_UNREFERENCED_PRISTINES));
843  while (! err)
844    {
845      svn_boolean_t have_row;
846      const svn_checksum_t *sha1_checksum;
847
848      svn_pool_clear(iterpool);
849
850      SVN_ERR(svn_sqlite__step(&have_row, stmt));
851      if (! have_row)
852        break;
853
854      SVN_ERR(svn_sqlite__column_checksum(&sha1_checksum, stmt, 0,
855                                          iterpool));
856      err = pristine_remove_if_unreferenced(wcroot, sha1_checksum,
857                                            iterpool);
858    }
859
860  svn_pool_destroy(iterpool);
861
862  return svn_error_trace(
863      svn_error_compose_create(err, svn_sqlite__reset(stmt)));
864}
865
866svn_error_t *
867svn_wc__db_pristine_cleanup(svn_wc__db_t *db,
868                            const char *wri_abspath,
869                            apr_pool_t *scratch_pool)
870{
871  svn_wc__db_wcroot_t *wcroot;
872  const char *local_relpath;
873
874  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
875
876  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
877                              wri_abspath, scratch_pool, scratch_pool));
878  VERIFY_USABLE_WCROOT(wcroot);
879
880  SVN_ERR(pristine_cleanup_wcroot(wcroot, scratch_pool));
881
882  return SVN_NO_ERROR;
883}
884
885
886svn_error_t *
887svn_wc__db_pristine_check(svn_boolean_t *present,
888                          svn_wc__db_t *db,
889                          const char *wri_abspath,
890                          const svn_checksum_t *sha1_checksum,
891                          apr_pool_t *scratch_pool)
892{
893  svn_wc__db_wcroot_t *wcroot;
894  const char *local_relpath;
895  svn_sqlite__stmt_t *stmt;
896  svn_boolean_t have_row;
897
898  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
899  SVN_ERR_ASSERT(sha1_checksum != NULL);
900
901  if (sha1_checksum->kind != svn_checksum_sha1)
902    {
903      *present = FALSE;
904      return SVN_NO_ERROR;
905    }
906
907  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
908                              wri_abspath, scratch_pool, scratch_pool));
909  VERIFY_USABLE_WCROOT(wcroot);
910
911  /* A filestat is much cheaper than a sqlite transaction especially on NFS,
912     so first check if there is a pristine file and then if we are allowed
913     to use it. */
914  {
915    const char *pristine_abspath;
916    svn_node_kind_t kind_on_disk;
917    svn_error_t *err;
918
919    SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
920                               sha1_checksum, scratch_pool, scratch_pool));
921    err = svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool);
922#ifdef WIN32
923    if (err && err->apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
924      {
925        svn_error_clear(err);
926        /* Possible race condition: The filename is locked, but there is no
927           file or dir with this name. Let's fall back on checking the DB.
928
929           This case is triggered by the pristine store tests on deleting
930           a file that is still open via another handle, where this other
931           handle has a FILE_SHARE_DELETE share mode.
932         */
933      }
934    else
935#endif
936    if (err)
937      return svn_error_trace(err);
938    else if (kind_on_disk != svn_node_file)
939      {
940        *present = FALSE;
941        return SVN_NO_ERROR;
942      }
943  }
944
945  /* Check that there is an entry in the PRISTINE table. */
946  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
947  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
948  SVN_ERR(svn_sqlite__step(&have_row, stmt));
949  SVN_ERR(svn_sqlite__reset(stmt));
950
951  *present = have_row;
952  return SVN_NO_ERROR;
953}
954