1251881Speter/*
2251881Speter * wc_db_pristine.c :  Pristine ("text base") management
3251881Speter *
4251881Speter * See the spec in 'notes/wc-ng/pristine-store'.
5251881Speter *
6251881Speter * ====================================================================
7251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
8251881Speter *    or more contributor license agreements.  See the NOTICE file
9251881Speter *    distributed with this work for additional information
10251881Speter *    regarding copyright ownership.  The ASF licenses this file
11251881Speter *    to you under the Apache License, Version 2.0 (the
12251881Speter *    "License"); you may not use this file except in compliance
13251881Speter *    with the License.  You may obtain a copy of the License at
14251881Speter *
15251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
16251881Speter *
17251881Speter *    Unless required by applicable law or agreed to in writing,
18251881Speter *    software distributed under the License is distributed on an
19251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20251881Speter *    KIND, either express or implied.  See the License for the
21251881Speter *    specific language governing permissions and limitations
22251881Speter *    under the License.
23251881Speter * ====================================================================
24251881Speter */
25251881Speter
26251881Speter#define SVN_WC__I_AM_WC_DB
27251881Speter
28251881Speter#include "svn_pools.h"
29299742Sdim#include "svn_io.h"
30251881Speter#include "svn_dirent_uri.h"
31251881Speter
32299742Sdim#include "private/svn_io_private.h"
33299742Sdim
34251881Speter#include "wc.h"
35251881Speter#include "wc_db.h"
36251881Speter#include "wc-queries.h"
37251881Speter#include "wc_db_private.h"
38251881Speter
39251881Speter#define PRISTINE_STORAGE_EXT ".svn-base"
40251881Speter#define PRISTINE_STORAGE_RELPATH "pristine"
41251881Speter#define PRISTINE_TEMPDIR_RELPATH "tmp"
42251881Speter
43251881Speter
44251881Speter
45251881Speter/* Returns in PRISTINE_ABSPATH a new string allocated from RESULT_POOL,
46251881Speter   holding the local absolute path to the file location that is dedicated
47251881Speter   to hold CHECKSUM's pristine file, relating to the pristine store
48251881Speter   configured for the working copy indicated by PDH. The returned path
49251881Speter   does not necessarily currently exist.
50251881Speter
51251881Speter   Any other allocations are made in SCRATCH_POOL. */
52251881Speterstatic svn_error_t *
53251881Speterget_pristine_fname(const char **pristine_abspath,
54251881Speter                   const char *wcroot_abspath,
55251881Speter                   const svn_checksum_t *sha1_checksum,
56251881Speter                   apr_pool_t *result_pool,
57251881Speter                   apr_pool_t *scratch_pool)
58251881Speter{
59251881Speter  const char *base_dir_abspath;
60251881Speter  const char *hexdigest = svn_checksum_to_cstring(sha1_checksum, scratch_pool);
61251881Speter  char subdir[3];
62251881Speter
63251881Speter  /* ### code is in transition. make sure we have the proper data.  */
64251881Speter  SVN_ERR_ASSERT(pristine_abspath != NULL);
65251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wcroot_abspath));
66251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
67251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
68251881Speter
69251881Speter  base_dir_abspath = svn_dirent_join_many(scratch_pool,
70251881Speter                                          wcroot_abspath,
71251881Speter                                          svn_wc_get_adm_dir(scratch_pool),
72251881Speter                                          PRISTINE_STORAGE_RELPATH,
73299742Sdim                                          SVN_VA_NULL);
74251881Speter
75251881Speter  /* We should have a valid checksum and (thus) a valid digest. */
76251881Speter  SVN_ERR_ASSERT(hexdigest != NULL);
77251881Speter
78251881Speter  /* Get the first two characters of the digest, for the subdir. */
79251881Speter  subdir[0] = hexdigest[0];
80251881Speter  subdir[1] = hexdigest[1];
81251881Speter  subdir[2] = '\0';
82251881Speter
83251881Speter  hexdigest = apr_pstrcat(scratch_pool, hexdigest, PRISTINE_STORAGE_EXT,
84299742Sdim                          SVN_VA_NULL);
85251881Speter
86251881Speter  /* The file is located at DIR/.svn/pristine/XX/XXYYZZ...svn-base */
87251881Speter  *pristine_abspath = svn_dirent_join_many(result_pool,
88251881Speter                                           base_dir_abspath,
89251881Speter                                           subdir,
90251881Speter                                           hexdigest,
91299742Sdim                                           SVN_VA_NULL);
92251881Speter  return SVN_NO_ERROR;
93251881Speter}
94251881Speter
95251881Speter
96251881Spetersvn_error_t *
97251881Spetersvn_wc__db_pristine_get_path(const char **pristine_abspath,
98251881Speter                             svn_wc__db_t *db,
99251881Speter                             const char *wri_abspath,
100251881Speter                             const svn_checksum_t *sha1_checksum,
101251881Speter                             apr_pool_t *result_pool,
102251881Speter                             apr_pool_t *scratch_pool)
103251881Speter{
104251881Speter  svn_wc__db_wcroot_t *wcroot;
105251881Speter  const char *local_relpath;
106251881Speter  svn_boolean_t present;
107251881Speter
108251881Speter  SVN_ERR_ASSERT(pristine_abspath != NULL);
109251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
110251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
111251881Speter  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
112251881Speter   * if the pristine text is not in the store. */
113251881Speter  if (sha1_checksum->kind != svn_checksum_sha1)
114251881Speter    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
115251881Speter                                         sha1_checksum,
116251881Speter                                         scratch_pool, scratch_pool));
117251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
118251881Speter
119251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
120251881Speter                                             db, wri_abspath,
121251881Speter                                             scratch_pool, scratch_pool));
122251881Speter  VERIFY_USABLE_WCROOT(wcroot);
123251881Speter
124251881Speter  SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
125251881Speter                                    scratch_pool));
126251881Speter  if (! present)
127251881Speter    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
128251881Speter                             _("The pristine text with checksum '%s' was "
129251881Speter                               "not found"),
130251881Speter                             svn_checksum_to_cstring_display(sha1_checksum,
131251881Speter                                                             scratch_pool));
132251881Speter
133251881Speter  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
134251881Speter                             sha1_checksum,
135251881Speter                             result_pool, scratch_pool));
136251881Speter
137251881Speter  return SVN_NO_ERROR;
138251881Speter}
139251881Speter
140251881Spetersvn_error_t *
141251881Spetersvn_wc__db_pristine_get_future_path(const char **pristine_abspath,
142251881Speter                                    const char *wcroot_abspath,
143251881Speter                                    const svn_checksum_t *sha1_checksum,
144251881Speter                                    apr_pool_t *result_pool,
145251881Speter                                    apr_pool_t *scratch_pool)
146251881Speter{
147251881Speter  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot_abspath,
148251881Speter                             sha1_checksum,
149251881Speter                             result_pool, scratch_pool));
150251881Speter  return SVN_NO_ERROR;
151251881Speter}
152251881Speter
153251881Speter/* Set *CONTENTS to a readable stream from which the pristine text
154251881Speter * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the
155251881Speter * pristine store of WCROOT.  If SIZE is not null, set *SIZE to the size
156251881Speter * in bytes of that text. If that text is not in the pristine store,
157251881Speter * return an error.
158251881Speter *
159251881Speter * Even if the pristine text is removed from the store while it is being
160251881Speter * read, the stream will remain valid and readable until it is closed.
161251881Speter *
162251881Speter * Allocate the stream in RESULT_POOL.
163251881Speter *
164251881Speter * This function expects to be executed inside a SQLite txn.
165251881Speter *
166251881Speter * Implements 'notes/wc-ng/pristine-store' section A-3(d).
167251881Speter */
168251881Speterstatic svn_error_t *
169251881Speterpristine_read_txn(svn_stream_t **contents,
170251881Speter                  svn_filesize_t *size,
171251881Speter                  svn_wc__db_wcroot_t *wcroot,
172251881Speter                  const svn_checksum_t *sha1_checksum,
173251881Speter                  const char *pristine_abspath,
174251881Speter                  apr_pool_t *result_pool,
175251881Speter                  apr_pool_t *scratch_pool)
176251881Speter{
177251881Speter  svn_sqlite__stmt_t *stmt;
178251881Speter  svn_boolean_t have_row;
179251881Speter
180251881Speter  /* Check that this pristine text is present in the store.  (The presence
181251881Speter   * of the file is not sufficient.) */
182251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
183251881Speter                                    STMT_SELECT_PRISTINE_SIZE));
184251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
185251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
186251881Speter
187251881Speter  if (size)
188251881Speter    *size = svn_sqlite__column_int64(stmt, 0);
189251881Speter
190251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
191251881Speter  if (! have_row)
192251881Speter    {
193251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
194251881Speter                               _("Pristine text '%s' not present"),
195251881Speter                               svn_checksum_to_cstring_display(
196251881Speter                                 sha1_checksum, scratch_pool));
197251881Speter    }
198251881Speter
199251881Speter  /* Open the file as a readable stream.  It will remain readable even when
200299742Sdim   * deleted from disk; APR guarantees that on Windows as well as Unix.
201299742Sdim   *
202299742Sdim   * We also don't enable APR_BUFFERED on this file to maximize throughput
203299742Sdim   * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE buffers
204299742Sdim   * where needed in streams, there is no point in having another layer of
205299742Sdim   * buffers. */
206251881Speter  if (contents)
207299742Sdim    {
208299742Sdim      apr_file_t *file;
209299742Sdim      SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
210299742Sdim                               APR_OS_DEFAULT, result_pool));
211299742Sdim      *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
212299742Sdim    }
213299742Sdim
214251881Speter  return SVN_NO_ERROR;
215251881Speter}
216251881Speter
217251881Spetersvn_error_t *
218251881Spetersvn_wc__db_pristine_read(svn_stream_t **contents,
219251881Speter                         svn_filesize_t *size,
220251881Speter                         svn_wc__db_t *db,
221251881Speter                         const char *wri_abspath,
222251881Speter                         const svn_checksum_t *sha1_checksum,
223251881Speter                         apr_pool_t *result_pool,
224251881Speter                         apr_pool_t *scratch_pool)
225251881Speter{
226251881Speter  svn_wc__db_wcroot_t *wcroot;
227251881Speter  const char *local_relpath;
228251881Speter  const char *pristine_abspath;
229251881Speter
230251881Speter  SVN_ERR_ASSERT(contents != NULL);
231251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
232251881Speter
233251881Speter  /* Some 1.6-to-1.7 wc upgrades created rows without checksums and
234251881Speter     updating such a row passes NULL here. */
235251881Speter  if (!sha1_checksum)
236251881Speter    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
237251881Speter                             _("Can't read '%s' from pristine store "
238251881Speter                               "because no checksum supplied"),
239251881Speter                             svn_dirent_local_style(wri_abspath, scratch_pool));
240251881Speter
241251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
242251881Speter
243251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
244251881Speter                              wri_abspath, scratch_pool, scratch_pool));
245251881Speter  VERIFY_USABLE_WCROOT(wcroot);
246251881Speter
247251881Speter  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
248251881Speter                             sha1_checksum,
249251881Speter                             scratch_pool, scratch_pool));
250251881Speter  SVN_WC__DB_WITH_TXN(
251251881Speter    pristine_read_txn(contents, size,
252251881Speter                      wcroot, sha1_checksum, pristine_abspath,
253251881Speter                      result_pool, scratch_pool),
254251881Speter    wcroot);
255251881Speter
256251881Speter  return SVN_NO_ERROR;
257251881Speter}
258251881Speter
259251881Speter
260251881Speter/* Return the absolute path to the temporary directory for pristine text
261251881Speter   files within WCROOT. */
262251881Speterstatic char *
263251881Speterpristine_get_tempdir(svn_wc__db_wcroot_t *wcroot,
264251881Speter                     apr_pool_t *result_pool,
265251881Speter                     apr_pool_t *scratch_pool)
266251881Speter{
267251881Speter  return svn_dirent_join_many(result_pool, wcroot->abspath,
268251881Speter                              svn_wc_get_adm_dir(scratch_pool),
269299742Sdim                              PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL);
270251881Speter}
271251881Speter
272251881Speter/* Install the pristine text described by BATON into the pristine store of
273251881Speter * SDB.  If it is already stored then just delete the new file
274251881Speter * BATON->tempfile_abspath.
275251881Speter *
276251881Speter * This function expects to be executed inside a SQLite txn that has already
277251881Speter * acquired a 'RESERVED' lock.
278251881Speter *
279251881Speter * Implements 'notes/wc-ng/pristine-store' section A-3(a).
280251881Speter */
281251881Speterstatic svn_error_t *
282251881Speterpristine_install_txn(svn_sqlite__db_t *sdb,
283251881Speter                     /* The path to the source file that is to be moved into place. */
284299742Sdim                     svn_stream_t *install_stream,
285251881Speter                     /* The target path for the file (within the pristine store). */
286251881Speter                     const char *pristine_abspath,
287251881Speter                     /* The pristine text's SHA-1 checksum. */
288251881Speter                     const svn_checksum_t *sha1_checksum,
289251881Speter                     /* The pristine text's MD-5 checksum. */
290251881Speter                     const svn_checksum_t *md5_checksum,
291251881Speter                     apr_pool_t *scratch_pool)
292251881Speter{
293251881Speter  svn_sqlite__stmt_t *stmt;
294251881Speter  svn_boolean_t have_row;
295251881Speter
296251881Speter  /* If this pristine text is already present in the store, just keep it:
297251881Speter   * delete the new one and return. */
298251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
299251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
300251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
301251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
302299742Sdim
303251881Speter  if (have_row)
304251881Speter    {
305251881Speter#ifdef SVN_DEBUG
306251881Speter      /* Consistency checks.  Verify both files exist and match.
307251881Speter       * ### We could check much more. */
308251881Speter      {
309251881Speter        apr_finfo_t finfo1, finfo2;
310299742Sdim
311299742Sdim        SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE,
312299742Sdim                                             scratch_pool));
313299742Sdim
314251881Speter        SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE,
315251881Speter                            scratch_pool));
316251881Speter        if (finfo1.size != finfo2.size)
317251881Speter          {
318251881Speter            return svn_error_createf(
319251881Speter              SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
320251881Speter              _("New pristine text '%s' has different size: %ld versus %ld"),
321251881Speter              svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
322251881Speter              (long int)finfo1.size, (long int)finfo2.size);
323251881Speter          }
324251881Speter      }
325251881Speter#endif
326251881Speter
327251881Speter      /* Remove the temp file: it's already there */
328299742Sdim      SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
329251881Speter      return SVN_NO_ERROR;
330251881Speter    }
331251881Speter
332251881Speter  /* Move the file to its target location.  (If it is already there, it is
333251881Speter   * an orphan file and it doesn't matter if we overwrite it.) */
334299742Sdim  {
335299742Sdim    apr_finfo_t finfo;
336299742Sdim    SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, APR_FINFO_SIZE,
337299742Sdim                                         scratch_pool));
338299742Sdim    SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
339299742Sdim                                        TRUE, scratch_pool));
340251881Speter
341299742Sdim    SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
342299742Sdim                                      STMT_INSERT_PRISTINE));
343299742Sdim    SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
344299742Sdim    SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
345299742Sdim    SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
346299742Sdim    SVN_ERR(svn_sqlite__insert(NULL, stmt));
347251881Speter
348299742Sdim    SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool));
349299742Sdim  }
350251881Speter
351299742Sdim  return SVN_NO_ERROR;
352299742Sdim}
353251881Speter
354299742Sdimstruct svn_wc__db_install_data_t
355299742Sdim{
356299742Sdim  svn_wc__db_wcroot_t *wcroot;
357299742Sdim  svn_stream_t *inner_stream;
358299742Sdim};
359251881Speter
360299742Sdimsvn_error_t *
361299742Sdimsvn_wc__db_pristine_prepare_install(svn_stream_t **stream,
362299742Sdim                                    svn_wc__db_install_data_t **install_data,
363299742Sdim                                    svn_checksum_t **sha1_checksum,
364299742Sdim                                    svn_checksum_t **md5_checksum,
365299742Sdim                                    svn_wc__db_t *db,
366299742Sdim                                    const char *wri_abspath,
367299742Sdim                                    apr_pool_t *result_pool,
368299742Sdim                                    apr_pool_t *scratch_pool)
369299742Sdim{
370299742Sdim  svn_wc__db_wcroot_t *wcroot;
371299742Sdim  const char *local_relpath;
372299742Sdim  const char *temp_dir_abspath;
373251881Speter
374299742Sdim  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
375251881Speter
376299742Sdim  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
377299742Sdim                              wri_abspath, scratch_pool, scratch_pool));
378299742Sdim  VERIFY_USABLE_WCROOT(wcroot);
379299742Sdim
380299742Sdim  temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
381299742Sdim
382299742Sdim  *install_data = apr_pcalloc(result_pool, sizeof(**install_data));
383299742Sdim  (*install_data)->wcroot = wcroot;
384299742Sdim
385299742Sdim  SVN_ERR_W(svn_stream__create_for_install(stream,
386299742Sdim                                           temp_dir_abspath,
387299742Sdim                                           result_pool, scratch_pool),
388299742Sdim            _("Unable to create pristine install stream"));
389299742Sdim
390299742Sdim  (*install_data)->inner_stream = *stream;
391299742Sdim
392299742Sdim  if (md5_checksum)
393299742Sdim    *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
394299742Sdim                                      svn_checksum_md5, FALSE, result_pool);
395299742Sdim  if (sha1_checksum)
396299742Sdim    *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
397299742Sdim                                      svn_checksum_sha1, FALSE, result_pool);
398299742Sdim
399251881Speter  return SVN_NO_ERROR;
400251881Speter}
401251881Speter
402251881Spetersvn_error_t *
403299742Sdimsvn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data,
404251881Speter                            const svn_checksum_t *sha1_checksum,
405251881Speter                            const svn_checksum_t *md5_checksum,
406251881Speter                            apr_pool_t *scratch_pool)
407251881Speter{
408299742Sdim  svn_wc__db_wcroot_t *wcroot = install_data->wcroot;
409251881Speter  const char *pristine_abspath;
410251881Speter
411251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
412251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
413251881Speter  SVN_ERR_ASSERT(md5_checksum != NULL);
414251881Speter  SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
415251881Speter
416251881Speter  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
417251881Speter                             sha1_checksum,
418251881Speter                             scratch_pool, scratch_pool));
419251881Speter
420251881Speter  /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
421251881Speter   * at the disk, to ensure no concurrent pristine install/delete txn. */
422251881Speter  SVN_SQLITE__WITH_IMMEDIATE_TXN(
423251881Speter    pristine_install_txn(wcroot->sdb,
424299742Sdim                         install_data->inner_stream, pristine_abspath,
425251881Speter                         sha1_checksum, md5_checksum,
426251881Speter                         scratch_pool),
427251881Speter    wcroot->sdb);
428251881Speter
429251881Speter  return SVN_NO_ERROR;
430251881Speter}
431251881Speter
432299742Sdimsvn_error_t *
433299742Sdimsvn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data,
434299742Sdim                                  apr_pool_t *scratch_pool)
435299742Sdim{
436299742Sdim  return svn_error_trace(svn_stream__install_delete(install_data->inner_stream,
437299742Sdim                                                    scratch_pool));
438299742Sdim}
439251881Speter
440299742Sdim
441251881Spetersvn_error_t *
442251881Spetersvn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum,
443251881Speter                            svn_wc__db_t *db,
444251881Speter                            const char *wri_abspath,
445251881Speter                            const svn_checksum_t *sha1_checksum,
446251881Speter                            apr_pool_t *result_pool,
447251881Speter                            apr_pool_t *scratch_pool)
448251881Speter{
449251881Speter  svn_wc__db_wcroot_t *wcroot;
450251881Speter  const char *local_relpath;
451251881Speter  svn_sqlite__stmt_t *stmt;
452251881Speter  svn_boolean_t have_row;
453251881Speter
454251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
455251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
456251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
457251881Speter
458251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
459251881Speter                              wri_abspath, scratch_pool, scratch_pool));
460251881Speter  VERIFY_USABLE_WCROOT(wcroot);
461251881Speter
462251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
463251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
464251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
465251881Speter  if (!have_row)
466251881Speter    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
467251881Speter                             _("The pristine text with checksum '%s' was "
468251881Speter                               "not found"),
469251881Speter                             svn_checksum_to_cstring_display(sha1_checksum,
470251881Speter                                                             scratch_pool));
471251881Speter
472251881Speter  SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
473251881Speter  SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5);
474251881Speter
475251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
476251881Speter}
477251881Speter
478251881Speter
479251881Spetersvn_error_t *
480251881Spetersvn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum,
481251881Speter                             svn_wc__db_t *db,
482251881Speter                             const char *wri_abspath,
483251881Speter                             const svn_checksum_t *md5_checksum,
484251881Speter                             apr_pool_t *result_pool,
485251881Speter                             apr_pool_t *scratch_pool)
486251881Speter{
487251881Speter  svn_wc__db_wcroot_t *wcroot;
488251881Speter  const char *local_relpath;
489251881Speter  svn_sqlite__stmt_t *stmt;
490251881Speter  svn_boolean_t have_row;
491251881Speter
492251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
493251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
494251881Speter  SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);
495251881Speter
496251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
497251881Speter                              wri_abspath, scratch_pool, scratch_pool));
498251881Speter  VERIFY_USABLE_WCROOT(wcroot);
499251881Speter
500251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
501251881Speter                                    STMT_SELECT_PRISTINE_BY_MD5));
502251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, md5_checksum, scratch_pool));
503251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
504251881Speter  if (!have_row)
505251881Speter    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
506251881Speter                             _("The pristine text with MD5 checksum '%s' was "
507251881Speter                               "not found"),
508251881Speter                             svn_checksum_to_cstring_display(md5_checksum,
509251881Speter                                                             scratch_pool));
510251881Speter
511251881Speter  SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool));
512251881Speter  SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1);
513251881Speter
514251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
515251881Speter}
516251881Speter
517251881Speter/* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
518251881Speter   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */
519251881Speterstatic svn_error_t *
520251881Spetermaybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
521251881Speter                            svn_wc__db_wcroot_t *dst_wcroot,
522251881Speter                            const svn_checksum_t *checksum,
523251881Speter                            const svn_checksum_t *md5_checksum,
524251881Speter                            apr_int64_t size,
525251881Speter                            svn_cancel_func_t cancel_func,
526251881Speter                            void *cancel_baton,
527251881Speter                            apr_pool_t *scratch_pool)
528251881Speter{
529251881Speter  const char *pristine_abspath;
530251881Speter  svn_sqlite__stmt_t *stmt;
531251881Speter  svn_stream_t *src_stream;
532251881Speter  svn_stream_t *dst_stream;
533251881Speter  const char *tmp_abspath;
534251881Speter  const char *src_abspath;
535251881Speter  int affected_rows;
536251881Speter  svn_error_t *err;
537251881Speter
538251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
539251881Speter                                    STMT_INSERT_OR_IGNORE_PRISTINE));
540251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
541251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
542251881Speter  SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
543251881Speter
544251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
545251881Speter
546251881Speter  if (affected_rows == 0)
547251881Speter    return SVN_NO_ERROR;
548251881Speter
549251881Speter  SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
550251881Speter                                 pristine_get_tempdir(dst_wcroot,
551251881Speter                                                      scratch_pool,
552251881Speter                                                      scratch_pool),
553251881Speter                                 svn_io_file_del_on_pool_cleanup,
554251881Speter                                 scratch_pool, scratch_pool));
555251881Speter
556251881Speter  SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
557251881Speter                             scratch_pool, scratch_pool));
558251881Speter
559251881Speter  SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
560251881Speter                                   scratch_pool, scratch_pool));
561251881Speter
562251881Speter  /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
563251881Speter  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
564251881Speter                           cancel_func, cancel_baton,
565251881Speter                           scratch_pool));
566251881Speter
567251881Speter  SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
568251881Speter                             scratch_pool, scratch_pool));
569251881Speter
570251881Speter  /* Move the file to its target location.  (If it is already there, it is
571251881Speter   * an orphan file and it doesn't matter if we overwrite it.) */
572251881Speter  err = svn_io_file_rename(tmp_abspath, pristine_abspath, scratch_pool);
573251881Speter
574251881Speter  /* Maybe the directory doesn't exist yet? */
575251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
576251881Speter    {
577251881Speter      svn_error_t *err2;
578251881Speter
579251881Speter      err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
580251881Speter                                                scratch_pool),
581251881Speter                             APR_OS_DEFAULT, scratch_pool);
582251881Speter
583251881Speter      if (err2)
584251881Speter        /* Creating directory didn't work: Return all errors */
585251881Speter        return svn_error_trace(svn_error_compose_create(err, err2));
586251881Speter      else
587251881Speter        /* We could create a directory: retry install */
588251881Speter        svn_error_clear(err);
589251881Speter
590251881Speter      SVN_ERR(svn_io_file_rename(tmp_abspath, pristine_abspath, scratch_pool));
591251881Speter    }
592251881Speter  else
593251881Speter    SVN_ERR(err);
594251881Speter
595251881Speter  return SVN_NO_ERROR;
596251881Speter}
597251881Speter
598251881Speter/* Transaction implementation of svn_wc__db_pristine_transfer().
599251881Speter   We have a lock on DST_WCROOT.
600251881Speter */
601251881Speterstatic svn_error_t *
602251881Speterpristine_transfer_txn(svn_wc__db_wcroot_t *src_wcroot,
603251881Speter                       svn_wc__db_wcroot_t *dst_wcroot,
604251881Speter                       const char *src_relpath,
605251881Speter                       svn_cancel_func_t cancel_func,
606251881Speter                       void *cancel_baton,
607251881Speter                       apr_pool_t *scratch_pool)
608251881Speter{
609251881Speter  svn_sqlite__stmt_t *stmt;
610251881Speter  svn_boolean_t got_row;
611251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
612251881Speter
613251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
614251881Speter                                    STMT_SELECT_COPY_PRISTINES));
615251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
616251881Speter
617251881Speter  /* This obtains an sqlite read lock on src_wcroot */
618251881Speter  SVN_ERR(svn_sqlite__step(&got_row, stmt));
619251881Speter
620251881Speter  while (got_row)
621251881Speter    {
622251881Speter      const svn_checksum_t *checksum;
623251881Speter      const svn_checksum_t *md5_checksum;
624251881Speter      apr_int64_t size;
625251881Speter      svn_error_t *err;
626251881Speter
627251881Speter      svn_pool_clear(iterpool);
628251881Speter
629251881Speter      SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
630251881Speter      SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
631251881Speter      size = svn_sqlite__column_int64(stmt, 2);
632251881Speter
633251881Speter      err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
634251881Speter                                        checksum, md5_checksum, size,
635251881Speter                                        cancel_func, cancel_baton,
636251881Speter                                        iterpool);
637251881Speter
638251881Speter      if (err)
639251881Speter        return svn_error_trace(svn_error_compose_create(
640251881Speter                                    err,
641251881Speter                                    svn_sqlite__reset(stmt)));
642251881Speter
643251881Speter      SVN_ERR(svn_sqlite__step(&got_row, stmt));
644251881Speter    }
645251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
646251881Speter
647251881Speter  svn_pool_destroy(iterpool);
648251881Speter
649251881Speter  return SVN_NO_ERROR;
650251881Speter}
651251881Speter
652251881Spetersvn_error_t *
653251881Spetersvn_wc__db_pristine_transfer(svn_wc__db_t *db,
654251881Speter                             const char *src_local_abspath,
655251881Speter                             const char *dst_wri_abspath,
656251881Speter                             svn_cancel_func_t cancel_func,
657251881Speter                             void *cancel_baton,
658251881Speter                             apr_pool_t *scratch_pool)
659251881Speter{
660251881Speter  svn_wc__db_wcroot_t *src_wcroot, *dst_wcroot;
661251881Speter  const char *src_relpath, *dst_relpath;
662251881Speter
663251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&src_wcroot, &src_relpath,
664251881Speter                                                db, src_local_abspath,
665251881Speter                                                scratch_pool, scratch_pool));
666251881Speter  VERIFY_USABLE_WCROOT(src_wcroot);
667251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&dst_wcroot, &dst_relpath,
668251881Speter                                                db, dst_wri_abspath,
669251881Speter                                                scratch_pool, scratch_pool));
670251881Speter  VERIFY_USABLE_WCROOT(dst_wcroot);
671251881Speter
672251881Speter  if (src_wcroot == dst_wcroot
673251881Speter      || src_wcroot->sdb == dst_wcroot->sdb)
674251881Speter    {
675251881Speter      return SVN_NO_ERROR; /* Nothing to transfer */
676251881Speter    }
677251881Speter
678251881Speter  SVN_WC__DB_WITH_TXN(
679251881Speter    pristine_transfer_txn(src_wcroot, dst_wcroot, src_relpath,
680251881Speter                          cancel_func, cancel_baton, scratch_pool),
681251881Speter    dst_wcroot);
682251881Speter
683251881Speter  return SVN_NO_ERROR;
684251881Speter}
685251881Speter
686251881Speter
687251881Speter
688251881Speter
689251881Speter/* Remove the file at FILE_ABSPATH in such a way that we could re-create a
690251881Speter * new file of the same name at any time thereafter.
691251881Speter *
692251881Speter * On Windows, the file will not disappear immediately from the directory if
693251881Speter * it is still being read so the best thing to do is first rename it to a
694251881Speter * unique name. */
695251881Speterstatic svn_error_t *
696251881Speterremove_file(const char *file_abspath,
697251881Speter            svn_wc__db_wcroot_t *wcroot,
698251881Speter            svn_boolean_t ignore_enoent,
699251881Speter            apr_pool_t *scratch_pool)
700251881Speter{
701251881Speter#ifdef WIN32
702251881Speter  svn_error_t *err;
703251881Speter  const char *temp_abspath;
704251881Speter  const char *temp_dir_abspath
705251881Speter    = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
706251881Speter
707251881Speter  /* To rename the file to a unique name in the temp dir, first create a
708251881Speter   * uniquely named file in the temp dir and then overwrite it. */
709251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, &temp_abspath, temp_dir_abspath,
710251881Speter                                   svn_io_file_del_none,
711251881Speter                                   scratch_pool, scratch_pool));
712251881Speter  err = svn_io_file_rename(file_abspath, temp_abspath, scratch_pool);
713251881Speter  if (err && ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err))
714251881Speter    svn_error_clear(err);
715251881Speter  else
716251881Speter    SVN_ERR(err);
717251881Speter  file_abspath = temp_abspath;
718251881Speter#endif
719251881Speter
720251881Speter  SVN_ERR(svn_io_remove_file2(file_abspath, ignore_enoent, scratch_pool));
721251881Speter
722251881Speter  return SVN_NO_ERROR;
723251881Speter}
724251881Speter
725251881Speter/* If the pristine text referenced by SHA1_CHECKSUM in WCROOT/SDB, whose path
726251881Speter * within the pristine store is PRISTINE_ABSPATH, has a reference count of
727251881Speter * zero, delete it (both the database row and the disk file).
728251881Speter *
729251881Speter * This function expects to be executed inside a SQLite txn that has already
730251881Speter * acquired a 'RESERVED' lock.
731251881Speter */
732251881Speterstatic svn_error_t *
733251881Speterpristine_remove_if_unreferenced_txn(svn_sqlite__db_t *sdb,
734251881Speter                                    svn_wc__db_wcroot_t *wcroot,
735251881Speter                                    const svn_checksum_t *sha1_checksum,
736251881Speter                                    const char *pristine_abspath,
737251881Speter                                    apr_pool_t *scratch_pool)
738251881Speter{
739251881Speter  svn_sqlite__stmt_t *stmt;
740251881Speter  int affected_rows;
741251881Speter
742251881Speter  /* Remove the DB row, if refcount is 0. */
743251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
744251881Speter                                    STMT_DELETE_PRISTINE_IF_UNREFERENCED));
745251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
746251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
747251881Speter
748251881Speter  /* If we removed the DB row, then remove the file. */
749251881Speter  if (affected_rows > 0)
750251881Speter    {
751251881Speter      /* If the file is not present, something has gone wrong, but at this
752251881Speter       * point it no longer matters.  In a debug build, raise an error, but
753251881Speter       * in a release build, it is more helpful to ignore it and continue. */
754251881Speter#ifdef SVN_DEBUG
755251881Speter      svn_boolean_t ignore_enoent = FALSE;
756251881Speter#else
757251881Speter      svn_boolean_t ignore_enoent = TRUE;
758251881Speter#endif
759251881Speter
760251881Speter      SVN_ERR(remove_file(pristine_abspath, wcroot, ignore_enoent,
761251881Speter                          scratch_pool));
762251881Speter    }
763251881Speter
764251881Speter  return SVN_NO_ERROR;
765251881Speter}
766251881Speter
767251881Speter/* If the pristine text referenced by SHA1_CHECKSUM in WCROOT has a
768251881Speter * reference count of zero, delete it (both the database row and the disk
769251881Speter * file).
770251881Speter *
771251881Speter * Implements 'notes/wc-ng/pristine-store' section A-3(b). */
772251881Speterstatic svn_error_t *
773251881Speterpristine_remove_if_unreferenced(svn_wc__db_wcroot_t *wcroot,
774251881Speter                                const svn_checksum_t *sha1_checksum,
775251881Speter                                apr_pool_t *scratch_pool)
776251881Speter{
777251881Speter  const char *pristine_abspath;
778251881Speter
779251881Speter  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
780251881Speter                             sha1_checksum, scratch_pool, scratch_pool));
781251881Speter
782251881Speter  /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
783251881Speter   * at the disk, to ensure no concurrent pristine install/delete txn. */
784251881Speter  SVN_SQLITE__WITH_IMMEDIATE_TXN(
785251881Speter    pristine_remove_if_unreferenced_txn(
786251881Speter      wcroot->sdb, wcroot, sha1_checksum, pristine_abspath, scratch_pool),
787251881Speter    wcroot->sdb);
788251881Speter
789251881Speter  return SVN_NO_ERROR;
790251881Speter}
791251881Speter
792251881Spetersvn_error_t *
793251881Spetersvn_wc__db_pristine_remove(svn_wc__db_t *db,
794251881Speter                           const char *wri_abspath,
795251881Speter                           const svn_checksum_t *sha1_checksum,
796251881Speter                           apr_pool_t *scratch_pool)
797251881Speter{
798251881Speter  svn_wc__db_wcroot_t *wcroot;
799251881Speter  const char *local_relpath;
800251881Speter
801251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
802251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
803251881Speter  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
804251881Speter   * if the pristine text is not in the store. */
805251881Speter  if (sha1_checksum->kind != svn_checksum_sha1)
806251881Speter    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
807251881Speter                                         sha1_checksum,
808251881Speter                                         scratch_pool, scratch_pool));
809251881Speter  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
810251881Speter
811251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
812251881Speter                              wri_abspath, scratch_pool, scratch_pool));
813251881Speter  VERIFY_USABLE_WCROOT(wcroot);
814251881Speter
815251881Speter  /* If the work queue is not empty, don't delete any pristine text because
816251881Speter   * the work queue may contain a reference to it. */
817251881Speter  {
818251881Speter    svn_sqlite__stmt_t *stmt;
819251881Speter    svn_boolean_t have_row;
820251881Speter
821251881Speter    SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_LOOK_FOR_WORK));
822251881Speter    SVN_ERR(svn_sqlite__step(&have_row, stmt));
823251881Speter    SVN_ERR(svn_sqlite__reset(stmt));
824251881Speter
825251881Speter    if (have_row)
826251881Speter      return SVN_NO_ERROR;
827251881Speter  }
828251881Speter
829251881Speter  /* If not referenced, remove the PRISTINE table row and the file. */
830251881Speter  SVN_ERR(pristine_remove_if_unreferenced(wcroot, sha1_checksum, scratch_pool));
831251881Speter
832251881Speter  return SVN_NO_ERROR;
833251881Speter}
834251881Speter
835251881Speter
836299742Sdim/* Remove all unreferenced pristines in the WC DB in WCROOT.
837299742Sdim *
838299742Sdim * Look for pristine texts whose 'refcount' in the DB is zero, and remove
839299742Sdim * them from the 'pristine' table and from disk.
840299742Sdim *
841299742Sdim * TODO: At least check that any zero refcount is really correct, before
842299742Sdim *       using it.  See dev@ email thread "Pristine text missing - cleanup
843299742Sdim *       doesn't work", <http://svn.haxx.se/dev/archive-2013-04/0426.shtml>.
844299742Sdim *
845299742Sdim * TODO: Ideas for possible extra clean-up operations:
846299742Sdim *
847299742Sdim *       * Check and correct all the refcounts.  Identify any rows missing
848299742Sdim *         from the 'pristine' table.  (Create a temporary index for speed
849299742Sdim *         if necessary?)
850299742Sdim *
851299742Sdim *       * Check the checksums.  (Very expensive to check them all, so find
852299742Sdim *         a way to not check them all.)
853299742Sdim *
854299742Sdim *       * Check for pristine files missing from disk but referenced in the
855299742Sdim *         'pristine' table.
856299742Sdim *
857299742Sdim *       * Repair any pristine files missing from disk and/or rows missing
858299742Sdim *         from the 'pristine' table and/or bad checksums.  Generally
859299742Sdim *         requires contacting the server, so requires support at a higher
860299742Sdim *         level than this function.
861299742Sdim *
862299742Sdim *       * Identify any pristine text files on disk that are not referenced
863299742Sdim *         in the DB, and delete them.
864299742Sdim *
865299742Sdim * TODO: Provide feedback about any errors found and any corrections made.
866299742Sdim */
867251881Speterstatic svn_error_t *
868251881Speterpristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot,
869251881Speter                        apr_pool_t *scratch_pool)
870251881Speter{
871251881Speter  svn_sqlite__stmt_t *stmt;
872251881Speter  svn_error_t *err = NULL;
873299742Sdim  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
874251881Speter
875251881Speter  /* Find each unreferenced pristine in the DB and remove it. */
876251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
877251881Speter                                    STMT_SELECT_UNREFERENCED_PRISTINES));
878251881Speter  while (! err)
879251881Speter    {
880251881Speter      svn_boolean_t have_row;
881251881Speter      const svn_checksum_t *sha1_checksum;
882251881Speter
883299742Sdim      svn_pool_clear(iterpool);
884299742Sdim
885251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
886251881Speter      if (! have_row)
887251881Speter        break;
888251881Speter
889251881Speter      SVN_ERR(svn_sqlite__column_checksum(&sha1_checksum, stmt, 0,
890299742Sdim                                          iterpool));
891251881Speter      err = pristine_remove_if_unreferenced(wcroot, sha1_checksum,
892299742Sdim                                            iterpool);
893251881Speter    }
894251881Speter
895299742Sdim  svn_pool_destroy(iterpool);
896299742Sdim
897251881Speter  return svn_error_trace(
898251881Speter      svn_error_compose_create(err, svn_sqlite__reset(stmt)));
899251881Speter}
900251881Speter
901251881Spetersvn_error_t *
902251881Spetersvn_wc__db_pristine_cleanup(svn_wc__db_t *db,
903251881Speter                            const char *wri_abspath,
904251881Speter                            apr_pool_t *scratch_pool)
905251881Speter{
906251881Speter  svn_wc__db_wcroot_t *wcroot;
907251881Speter  const char *local_relpath;
908251881Speter
909251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
910251881Speter
911251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
912251881Speter                              wri_abspath, scratch_pool, scratch_pool));
913251881Speter  VERIFY_USABLE_WCROOT(wcroot);
914251881Speter
915251881Speter  SVN_ERR(pristine_cleanup_wcroot(wcroot, scratch_pool));
916251881Speter
917251881Speter  return SVN_NO_ERROR;
918251881Speter}
919251881Speter
920251881Speter
921251881Spetersvn_error_t *
922251881Spetersvn_wc__db_pristine_check(svn_boolean_t *present,
923251881Speter                          svn_wc__db_t *db,
924251881Speter                          const char *wri_abspath,
925251881Speter                          const svn_checksum_t *sha1_checksum,
926251881Speter                          apr_pool_t *scratch_pool)
927251881Speter{
928251881Speter  svn_wc__db_wcroot_t *wcroot;
929251881Speter  const char *local_relpath;
930251881Speter  svn_sqlite__stmt_t *stmt;
931251881Speter  svn_boolean_t have_row;
932251881Speter
933251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
934251881Speter  SVN_ERR_ASSERT(sha1_checksum != NULL);
935251881Speter
936251881Speter  if (sha1_checksum->kind != svn_checksum_sha1)
937251881Speter    {
938251881Speter      *present = FALSE;
939251881Speter      return SVN_NO_ERROR;
940251881Speter    }
941251881Speter
942251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
943251881Speter                              wri_abspath, scratch_pool, scratch_pool));
944251881Speter  VERIFY_USABLE_WCROOT(wcroot);
945251881Speter
946251881Speter  /* A filestat is much cheaper than a sqlite transaction especially on NFS,
947251881Speter     so first check if there is a pristine file and then if we are allowed
948251881Speter     to use it. */
949251881Speter  {
950251881Speter    const char *pristine_abspath;
951251881Speter    svn_node_kind_t kind_on_disk;
952251881Speter
953251881Speter    SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
954251881Speter                               sha1_checksum, scratch_pool, scratch_pool));
955251881Speter    SVN_ERR(svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool));
956251881Speter    if (kind_on_disk != svn_node_file)
957251881Speter      {
958251881Speter        *present = FALSE;
959251881Speter        return SVN_NO_ERROR;
960251881Speter      }
961251881Speter  }
962251881Speter
963251881Speter  /* Check that there is an entry in the PRISTINE table. */
964251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
965251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
966251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
967251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
968251881Speter
969251881Speter  *present = have_row;
970251881Speter  return SVN_NO_ERROR;
971251881Speter}
972