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