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