1251881Speter/* 2251881Speter * adm_crawler.c: report local WC mods to an Editor. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter#include <string.h> 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter#include <apr_file_io.h> 31251881Speter#include <apr_hash.h> 32251881Speter 33251881Speter#include "svn_hash.h" 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_pools.h" 36251881Speter#include "svn_wc.h" 37251881Speter#include "svn_io.h" 38251881Speter#include "svn_delta.h" 39251881Speter#include "svn_dirent_uri.h" 40251881Speter#include "svn_path.h" 41251881Speter 42251881Speter#include "private/svn_wc_private.h" 43251881Speter 44251881Speter#include "wc.h" 45251881Speter#include "adm_files.h" 46251881Speter#include "translate.h" 47251881Speter#include "workqueue.h" 48251881Speter#include "conflicts.h" 49251881Speter 50251881Speter#include "svn_private_config.h" 51251881Speter 52251881Speter 53251881Speter/* Helper for report_revisions_and_depths(). 54251881Speter 55251881Speter Perform an atomic restoration of the file LOCAL_ABSPATH; that is, copy 56251881Speter the file's text-base to the administrative tmp area, and then move 57251881Speter that file to LOCAL_ABSPATH with possible translations/expansions. If 58251881Speter USE_COMMIT_TIMES is set, then set working file's timestamp to 59251881Speter last-commit-time. Either way, set entry-timestamp to match that of 60251881Speter the working file when all is finished. 61251881Speter 62251881Speter If MARK_RESOLVED_TEXT_CONFLICT is TRUE, mark as resolved any existing 63251881Speter text conflict on LOCAL_ABSPATH. 64251881Speter 65251881Speter Not that a valid access baton with a write lock to the directory of 66251881Speter LOCAL_ABSPATH must be available in DB.*/ 67251881Speterstatic svn_error_t * 68251881Speterrestore_file(svn_wc__db_t *db, 69251881Speter const char *local_abspath, 70251881Speter svn_boolean_t use_commit_times, 71251881Speter svn_boolean_t mark_resolved_text_conflict, 72299742Sdim svn_cancel_func_t cancel_func, 73299742Sdim void *cancel_baton, 74251881Speter apr_pool_t *scratch_pool) 75251881Speter{ 76251881Speter svn_skel_t *work_item; 77251881Speter 78251881Speter SVN_ERR(svn_wc__wq_build_file_install(&work_item, 79251881Speter db, local_abspath, 80251881Speter NULL /* source_abspath */, 81251881Speter use_commit_times, 82251881Speter TRUE /* record_fileinfo */, 83251881Speter scratch_pool, scratch_pool)); 84251881Speter /* ### we need an existing path for wq_add. not entirely WRI_ABSPATH yet */ 85251881Speter SVN_ERR(svn_wc__db_wq_add(db, 86251881Speter svn_dirent_dirname(local_abspath, scratch_pool), 87251881Speter work_item, scratch_pool)); 88251881Speter 89251881Speter /* Run the work item immediately. */ 90251881Speter SVN_ERR(svn_wc__wq_run(db, local_abspath, 91299742Sdim cancel_func, cancel_baton, 92251881Speter scratch_pool)); 93251881Speter 94251881Speter /* Remove any text conflict */ 95251881Speter if (mark_resolved_text_conflict) 96299742Sdim SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, 97299742Sdim cancel_func, cancel_baton, 98299742Sdim scratch_pool)); 99251881Speter 100251881Speter return SVN_NO_ERROR; 101251881Speter} 102251881Speter 103251881Spetersvn_error_t * 104251881Spetersvn_wc_restore(svn_wc_context_t *wc_ctx, 105251881Speter const char *local_abspath, 106251881Speter svn_boolean_t use_commit_times, 107251881Speter apr_pool_t *scratch_pool) 108251881Speter{ 109299742Sdim /* ### If ever revved: Add cancel func. */ 110251881Speter svn_wc__db_status_t status; 111251881Speter svn_node_kind_t kind; 112251881Speter svn_node_kind_t disk_kind; 113251881Speter const svn_checksum_t *checksum; 114251881Speter 115251881Speter SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); 116251881Speter 117251881Speter if (disk_kind != svn_node_none) 118251881Speter return svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL, 119251881Speter _("The existing node '%s' can not be restored."), 120251881Speter svn_dirent_local_style(local_abspath, 121251881Speter scratch_pool)); 122251881Speter 123251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 124251881Speter NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, 125251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 126251881Speter NULL, NULL, NULL, NULL, 127251881Speter wc_ctx->db, local_abspath, 128251881Speter scratch_pool, scratch_pool)); 129251881Speter 130251881Speter if (status != svn_wc__db_status_normal 131251881Speter && !((status == svn_wc__db_status_added 132251881Speter || status == svn_wc__db_status_incomplete) 133251881Speter && (kind == svn_node_dir 134251881Speter || (kind == svn_node_file && checksum != NULL) 135251881Speter /* || (kind == svn_node_symlink && target)*/))) 136251881Speter { 137251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 138251881Speter _("The node '%s' can not be restored."), 139251881Speter svn_dirent_local_style(local_abspath, 140251881Speter scratch_pool)); 141251881Speter } 142251881Speter 143251881Speter if (kind == svn_node_file || kind == svn_node_symlink) 144251881Speter SVN_ERR(restore_file(wc_ctx->db, local_abspath, use_commit_times, 145251881Speter FALSE /*mark_resolved_text_conflict*/, 146299742Sdim NULL, NULL /* cancel func, baton */, 147251881Speter scratch_pool)); 148251881Speter else 149251881Speter SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); 150251881Speter 151251881Speter return SVN_NO_ERROR; 152251881Speter} 153251881Speter 154299742Sdim/* Try to restore LOCAL_ABSPATH of node type KIND and if successful, 155251881Speter notify that the node is restored. Use DB for accessing the working copy. 156251881Speter If USE_COMMIT_TIMES is set, then set working file's timestamp to 157251881Speter last-commit-time. 158251881Speter 159251881Speter This function does all temporary allocations in SCRATCH_POOL 160251881Speter */ 161251881Speterstatic svn_error_t * 162251881Speterrestore_node(svn_wc__db_t *db, 163251881Speter const char *local_abspath, 164251881Speter svn_node_kind_t kind, 165299742Sdim svn_boolean_t mark_resolved_text_conflict, 166251881Speter svn_boolean_t use_commit_times, 167299742Sdim svn_cancel_func_t cancel_func, 168299742Sdim void *cancel_baton, 169251881Speter svn_wc_notify_func2_t notify_func, 170251881Speter void *notify_baton, 171251881Speter apr_pool_t *scratch_pool) 172251881Speter{ 173251881Speter if (kind == svn_node_file || kind == svn_node_symlink) 174251881Speter { 175251881Speter /* Recreate file from text-base; mark any text conflict as resolved */ 176251881Speter SVN_ERR(restore_file(db, local_abspath, use_commit_times, 177299742Sdim mark_resolved_text_conflict, 178299742Sdim cancel_func, cancel_baton, 179251881Speter scratch_pool)); 180251881Speter } 181251881Speter else if (kind == svn_node_dir) 182251881Speter { 183251881Speter /* Recreating a directory is just a mkdir */ 184251881Speter SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); 185251881Speter } 186251881Speter 187251881Speter /* ... report the restoration to the caller. */ 188251881Speter if (notify_func != NULL) 189251881Speter { 190251881Speter svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, 191251881Speter svn_wc_notify_restore, 192251881Speter scratch_pool); 193251881Speter notify->kind = svn_node_file; 194251881Speter (*notify_func)(notify_baton, notify, scratch_pool); 195251881Speter } 196251881Speter 197251881Speter return SVN_NO_ERROR; 198251881Speter} 199251881Speter 200251881Speter/* The recursive crawler that describes a mixed-revision working 201251881Speter copy to an RA layer. Used to initiate updates. 202251881Speter 203251881Speter This is a depth-first recursive walk of the children of DIR_ABSPATH 204251881Speter (not including DIR_ABSPATH itself) using DB. Look at each node and 205251881Speter check if its revision is different than DIR_REV. If so, report this 206251881Speter fact to REPORTER. If a node has a different URL than expected, or 207251881Speter a different depth than its parent, report that to REPORTER. 208251881Speter 209251881Speter Report DIR_ABSPATH to the reporter as REPORT_RELPATH. 210251881Speter 211251881Speter Alternatively, if REPORT_EVERYTHING is set, then report all 212251881Speter children unconditionally. 213251881Speter 214251881Speter DEPTH is actually the *requested* depth for the update-like 215251881Speter operation for which we are reporting working copy state. However, 216251881Speter certain requested depths affect the depth of the report crawl. For 217251881Speter example, if the requested depth is svn_depth_empty, there's no 218251881Speter point descending into subdirs, no matter what their depths. So: 219251881Speter 220251881Speter If DEPTH is svn_depth_empty, don't report any files and don't 221251881Speter descend into any subdirs. If svn_depth_files, report files but 222251881Speter still don't descend into subdirs. If svn_depth_immediates, report 223251881Speter files, and report subdirs themselves but not their entries. If 224251881Speter svn_depth_infinity or svn_depth_unknown, report everything all the 225251881Speter way down. (That last sentence might sound counterintuitive, but 226251881Speter since you can't go deeper than the local ambient depth anyway, 227251881Speter requesting svn_depth_infinity really means "as deep as the various 228251881Speter parts of this working copy go". Of course, the information that 229251881Speter comes back from the server will be different for svn_depth_unknown 230251881Speter than for svn_depth_infinity.) 231251881Speter 232251881Speter DIR_REPOS_RELPATH, DIR_REPOS_ROOT and DIR_DEPTH are the repository 233251881Speter relative path, the repository root and depth stored on the directory, 234251881Speter passed here to avoid another database query. 235251881Speter 236251881Speter DEPTH_COMPATIBILITY_TRICK means the same thing here as it does 237251881Speter in svn_wc_crawl_revisions5(). 238251881Speter 239251881Speter If RESTORE_FILES is set, then unexpectedly missing working files 240251881Speter will be restored from text-base and NOTIFY_FUNC/NOTIFY_BATON 241251881Speter will be called to report the restoration. USE_COMMIT_TIMES is 242251881Speter passed to restore_file() helper. */ 243251881Speterstatic svn_error_t * 244251881Speterreport_revisions_and_depths(svn_wc__db_t *db, 245251881Speter const char *dir_abspath, 246251881Speter const char *report_relpath, 247251881Speter svn_revnum_t dir_rev, 248251881Speter const char *dir_repos_relpath, 249251881Speter const char *dir_repos_root, 250251881Speter svn_depth_t dir_depth, 251251881Speter const svn_ra_reporter3_t *reporter, 252251881Speter void *report_baton, 253251881Speter svn_boolean_t restore_files, 254251881Speter svn_depth_t depth, 255251881Speter svn_boolean_t honor_depth_exclude, 256251881Speter svn_boolean_t depth_compatibility_trick, 257251881Speter svn_boolean_t report_everything, 258251881Speter svn_boolean_t use_commit_times, 259251881Speter svn_cancel_func_t cancel_func, 260251881Speter void *cancel_baton, 261251881Speter svn_wc_notify_func2_t notify_func, 262251881Speter void *notify_baton, 263251881Speter apr_pool_t *scratch_pool) 264251881Speter{ 265251881Speter apr_hash_t *base_children; 266251881Speter apr_hash_t *dirents; 267251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 268251881Speter apr_hash_index_t *hi; 269251881Speter svn_error_t *err; 270251881Speter 271251881Speter 272251881Speter /* Get both the SVN Entries and the actual on-disk entries. Also 273251881Speter notice that we're picking up hidden entries too (read_children never 274251881Speter hides children). */ 275251881Speter SVN_ERR(svn_wc__db_base_get_children_info(&base_children, db, dir_abspath, 276251881Speter scratch_pool, iterpool)); 277251881Speter 278251881Speter if (restore_files) 279251881Speter { 280251881Speter err = svn_io_get_dirents3(&dirents, dir_abspath, TRUE, 281251881Speter scratch_pool, scratch_pool); 282251881Speter 283251881Speter if (err && (APR_STATUS_IS_ENOENT(err->apr_err) 284251881Speter || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 285251881Speter { 286251881Speter svn_error_clear(err); 287251881Speter /* There is no directory, and if we could create the directory 288251881Speter we would have already created it when walking the parent 289251881Speter directory */ 290251881Speter restore_files = FALSE; 291251881Speter dirents = NULL; 292251881Speter } 293251881Speter else 294251881Speter SVN_ERR(err); 295251881Speter } 296251881Speter else 297251881Speter dirents = NULL; 298251881Speter 299251881Speter /*** Do the real reporting and recursing. ***/ 300251881Speter 301251881Speter /* Looping over current directory's BASE children: */ 302251881Speter for (hi = apr_hash_first(scratch_pool, base_children); 303251881Speter hi != NULL; 304251881Speter hi = apr_hash_next(hi)) 305251881Speter { 306299742Sdim const char *child = apr_hash_this_key(hi); 307251881Speter const char *this_report_relpath; 308251881Speter const char *this_abspath; 309251881Speter svn_boolean_t this_switched = FALSE; 310299742Sdim struct svn_wc__db_base_info_t *ths = apr_hash_this_val(hi); 311251881Speter 312251881Speter if (cancel_func) 313251881Speter SVN_ERR(cancel_func(cancel_baton)); 314251881Speter 315251881Speter /* Clear the iteration subpool here because the loop has a bunch 316251881Speter of 'continue' jump statements. */ 317251881Speter svn_pool_clear(iterpool); 318251881Speter 319251881Speter /* Compute the paths and URLs we need. */ 320251881Speter this_report_relpath = svn_relpath_join(report_relpath, child, iterpool); 321251881Speter this_abspath = svn_dirent_join(dir_abspath, child, iterpool); 322251881Speter 323251881Speter /*** File Externals **/ 324251881Speter if (ths->update_root) 325251881Speter { 326251881Speter /* File externals are ... special. We ignore them. */; 327251881Speter continue; 328251881Speter } 329251881Speter 330251881Speter /* First check for exclusion */ 331251881Speter if (ths->status == svn_wc__db_status_excluded) 332251881Speter { 333251881Speter if (honor_depth_exclude) 334251881Speter { 335251881Speter /* Report the excluded path, no matter whether report_everything 336251881Speter flag is set. Because the report_everything flag indicates 337251881Speter that the server will treat the wc as empty and thus push 338251881Speter full content of the files/subdirs. But we want to prevent the 339251881Speter server from pushing the full content of this_path at us. */ 340251881Speter 341251881Speter /* The server does not support link_path report on excluded 342251881Speter path. We explicitly prohibit this situation in 343251881Speter svn_wc_crop_tree(). */ 344251881Speter SVN_ERR(reporter->set_path(report_baton, 345251881Speter this_report_relpath, 346251881Speter dir_rev, 347251881Speter svn_depth_exclude, 348251881Speter FALSE, 349251881Speter NULL, 350251881Speter iterpool)); 351251881Speter } 352251881Speter else 353251881Speter { 354251881Speter /* We want to pull in the excluded target. So, report it as 355251881Speter deleted, and server will respond properly. */ 356251881Speter if (! report_everything) 357251881Speter SVN_ERR(reporter->delete_path(report_baton, 358251881Speter this_report_relpath, iterpool)); 359251881Speter } 360251881Speter continue; 361251881Speter } 362251881Speter 363251881Speter /*** The Big Tests: ***/ 364251881Speter if (ths->status == svn_wc__db_status_server_excluded 365251881Speter || ths->status == svn_wc__db_status_not_present) 366251881Speter { 367251881Speter /* If the entry is 'absent' or 'not-present', make sure the server 368251881Speter knows it's gone... 369251881Speter ...unless we're reporting everything, in which case we're 370251881Speter going to report it missing later anyway. 371251881Speter 372251881Speter This instructs the server to send it back to us, if it is 373251881Speter now available (an addition after a not-present state), or if 374251881Speter it is now authorized (change in authz for the absent item). */ 375251881Speter if (! report_everything) 376251881Speter SVN_ERR(reporter->delete_path(report_baton, this_report_relpath, 377251881Speter iterpool)); 378251881Speter continue; 379251881Speter } 380251881Speter 381251881Speter /* Is the entry NOT on the disk? We may be able to restore it. */ 382251881Speter if (restore_files 383251881Speter && svn_hash_gets(dirents, child) == NULL) 384251881Speter { 385251881Speter svn_wc__db_status_t wrk_status; 386251881Speter svn_node_kind_t wrk_kind; 387251881Speter const svn_checksum_t *checksum; 388299742Sdim svn_boolean_t conflicted; 389251881Speter 390251881Speter SVN_ERR(svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, 391251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 392251881Speter &checksum, NULL, NULL, NULL, NULL, NULL, 393299742Sdim NULL, NULL, NULL, NULL, &conflicted, 394251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 395251881Speter db, this_abspath, iterpool, iterpool)); 396251881Speter 397251881Speter if ((wrk_status == svn_wc__db_status_normal 398251881Speter || wrk_status == svn_wc__db_status_added 399251881Speter || wrk_status == svn_wc__db_status_incomplete) 400251881Speter && (wrk_kind == svn_node_dir || checksum)) 401251881Speter { 402251881Speter svn_node_kind_t dirent_kind; 403251881Speter 404251881Speter /* It is possible on a case insensitive system that the 405251881Speter entry is not really missing, but just cased incorrectly. 406251881Speter In this case we can't overwrite it with the pristine 407251881Speter version */ 408251881Speter SVN_ERR(svn_io_check_path(this_abspath, &dirent_kind, iterpool)); 409251881Speter 410251881Speter if (dirent_kind == svn_node_none) 411251881Speter { 412251881Speter SVN_ERR(restore_node(db, this_abspath, wrk_kind, 413299742Sdim conflicted, use_commit_times, 414299742Sdim cancel_func, cancel_baton, 415299742Sdim notify_func, notify_baton, iterpool)); 416251881Speter } 417251881Speter } 418251881Speter } 419251881Speter 420251881Speter /* And finally prepare for reporting */ 421251881Speter if (!ths->repos_relpath) 422251881Speter { 423251881Speter ths->repos_relpath = svn_relpath_join(dir_repos_relpath, child, 424251881Speter iterpool); 425251881Speter } 426251881Speter else 427251881Speter { 428251881Speter const char *childname 429251881Speter = svn_relpath_skip_ancestor(dir_repos_relpath, ths->repos_relpath); 430251881Speter 431251881Speter if (childname == NULL || strcmp(childname, child) != 0) 432251881Speter { 433251881Speter this_switched = TRUE; 434251881Speter } 435251881Speter } 436251881Speter 437251881Speter /* Tweak THIS_DEPTH to a useful value. */ 438251881Speter if (ths->depth == svn_depth_unknown) 439251881Speter ths->depth = svn_depth_infinity; 440251881Speter 441251881Speter /*** Files ***/ 442251881Speter if (ths->kind == svn_node_file 443251881Speter || ths->kind == svn_node_symlink) 444251881Speter { 445251881Speter if (report_everything) 446251881Speter { 447251881Speter /* Report the file unconditionally, one way or another. */ 448251881Speter if (this_switched) 449251881Speter SVN_ERR(reporter->link_path(report_baton, 450251881Speter this_report_relpath, 451251881Speter svn_path_url_add_component2( 452251881Speter dir_repos_root, 453251881Speter ths->repos_relpath, iterpool), 454251881Speter ths->revnum, 455251881Speter ths->depth, 456251881Speter FALSE, 457251881Speter ths->lock ? ths->lock->token : NULL, 458251881Speter iterpool)); 459251881Speter else 460251881Speter SVN_ERR(reporter->set_path(report_baton, 461251881Speter this_report_relpath, 462251881Speter ths->revnum, 463251881Speter ths->depth, 464251881Speter FALSE, 465251881Speter ths->lock ? ths->lock->token : NULL, 466251881Speter iterpool)); 467251881Speter } 468251881Speter 469251881Speter /* Possibly report a disjoint URL ... */ 470251881Speter else if (this_switched) 471251881Speter SVN_ERR(reporter->link_path(report_baton, 472251881Speter this_report_relpath, 473251881Speter svn_path_url_add_component2( 474251881Speter dir_repos_root, 475251881Speter ths->repos_relpath, iterpool), 476251881Speter ths->revnum, 477251881Speter ths->depth, 478251881Speter FALSE, 479251881Speter ths->lock ? ths->lock->token : NULL, 480251881Speter iterpool)); 481251881Speter /* ... or perhaps just a differing revision or lock token, 482251881Speter or the mere presence of the file in a depth-empty dir. */ 483251881Speter else if (ths->revnum != dir_rev 484251881Speter || ths->lock 485251881Speter || dir_depth == svn_depth_empty) 486251881Speter SVN_ERR(reporter->set_path(report_baton, 487251881Speter this_report_relpath, 488251881Speter ths->revnum, 489251881Speter ths->depth, 490251881Speter FALSE, 491251881Speter ths->lock ? ths->lock->token : NULL, 492251881Speter iterpool)); 493251881Speter } /* end file case */ 494251881Speter 495251881Speter /*** Directories (in recursive mode) ***/ 496251881Speter else if (ths->kind == svn_node_dir 497251881Speter && (depth > svn_depth_files 498251881Speter || depth == svn_depth_unknown)) 499251881Speter { 500251881Speter svn_boolean_t is_incomplete; 501251881Speter svn_boolean_t start_empty; 502251881Speter svn_depth_t report_depth = ths->depth; 503251881Speter 504251881Speter is_incomplete = (ths->status == svn_wc__db_status_incomplete); 505251881Speter start_empty = is_incomplete; 506251881Speter 507251881Speter if (!SVN_DEPTH_IS_RECURSIVE(depth)) 508251881Speter report_depth = svn_depth_empty; 509251881Speter 510251881Speter /* When a <= 1.6 working copy is upgraded without some of its 511251881Speter subdirectories we miss some information in the database. If we 512251881Speter report the revision as -1, the update editor will receive an 513251881Speter add_directory() while it still knows the directory. 514251881Speter 515251881Speter This would raise strange tree conflicts and probably assertions 516251881Speter as it would a BASE vs BASE conflict */ 517251881Speter if (is_incomplete && !SVN_IS_VALID_REVNUM(ths->revnum)) 518251881Speter ths->revnum = dir_rev; 519251881Speter 520251881Speter if (depth_compatibility_trick 521251881Speter && ths->depth <= svn_depth_files 522251881Speter && depth > ths->depth) 523251881Speter { 524251881Speter start_empty = TRUE; 525251881Speter } 526251881Speter 527251881Speter if (report_everything) 528251881Speter { 529251881Speter /* Report the dir unconditionally, one way or another... */ 530251881Speter if (this_switched) 531251881Speter SVN_ERR(reporter->link_path(report_baton, 532251881Speter this_report_relpath, 533251881Speter svn_path_url_add_component2( 534251881Speter dir_repos_root, 535251881Speter ths->repos_relpath, iterpool), 536251881Speter ths->revnum, 537251881Speter report_depth, 538251881Speter start_empty, 539251881Speter ths->lock ? ths->lock->token 540251881Speter : NULL, 541251881Speter iterpool)); 542251881Speter else 543251881Speter SVN_ERR(reporter->set_path(report_baton, 544251881Speter this_report_relpath, 545251881Speter ths->revnum, 546251881Speter report_depth, 547251881Speter start_empty, 548251881Speter ths->lock ? ths->lock->token : NULL, 549251881Speter iterpool)); 550251881Speter } 551251881Speter else if (this_switched) 552251881Speter { 553251881Speter /* ...or possibly report a disjoint URL ... */ 554251881Speter SVN_ERR(reporter->link_path(report_baton, 555251881Speter this_report_relpath, 556251881Speter svn_path_url_add_component2( 557251881Speter dir_repos_root, 558251881Speter ths->repos_relpath, iterpool), 559251881Speter ths->revnum, 560251881Speter report_depth, 561251881Speter start_empty, 562251881Speter ths->lock ? ths->lock->token : NULL, 563251881Speter iterpool)); 564251881Speter } 565251881Speter else if (ths->revnum != dir_rev 566251881Speter || ths->lock 567251881Speter || is_incomplete 568251881Speter || dir_depth == svn_depth_empty 569251881Speter || dir_depth == svn_depth_files 570251881Speter || (dir_depth == svn_depth_immediates 571251881Speter && ths->depth != svn_depth_empty) 572251881Speter || (ths->depth < svn_depth_infinity 573251881Speter && SVN_DEPTH_IS_RECURSIVE(depth))) 574251881Speter { 575251881Speter /* ... or perhaps just a differing revision, lock token, 576251881Speter incomplete subdir, the mere presence of the directory 577251881Speter in a depth-empty or depth-files dir, or if the parent 578251881Speter dir is at depth-immediates but the child is not at 579251881Speter depth-empty. Also describe shallow subdirs if we are 580251881Speter trying to set depth to infinity. */ 581251881Speter SVN_ERR(reporter->set_path(report_baton, 582251881Speter this_report_relpath, 583251881Speter ths->revnum, 584251881Speter report_depth, 585251881Speter start_empty, 586251881Speter ths->lock ? ths->lock->token : NULL, 587251881Speter iterpool)); 588251881Speter } 589251881Speter 590251881Speter /* Finally, recurse if necessary and appropriate. */ 591251881Speter if (SVN_DEPTH_IS_RECURSIVE(depth)) 592251881Speter { 593251881Speter const char *repos_relpath = ths->repos_relpath; 594251881Speter 595251881Speter if (repos_relpath == NULL) 596251881Speter { 597251881Speter repos_relpath = svn_relpath_join(dir_repos_relpath, child, 598251881Speter iterpool); 599251881Speter } 600251881Speter 601251881Speter SVN_ERR(report_revisions_and_depths(db, 602251881Speter this_abspath, 603251881Speter this_report_relpath, 604251881Speter ths->revnum, 605251881Speter repos_relpath, 606251881Speter dir_repos_root, 607251881Speter ths->depth, 608251881Speter reporter, report_baton, 609251881Speter restore_files, depth, 610251881Speter honor_depth_exclude, 611251881Speter depth_compatibility_trick, 612251881Speter start_empty, 613251881Speter use_commit_times, 614251881Speter cancel_func, cancel_baton, 615251881Speter notify_func, notify_baton, 616251881Speter iterpool)); 617251881Speter } 618251881Speter } /* end directory case */ 619251881Speter } /* end main entries loop */ 620251881Speter 621251881Speter /* We're done examining this dir's entries, so free everything. */ 622251881Speter svn_pool_destroy(iterpool); 623251881Speter 624251881Speter return SVN_NO_ERROR; 625251881Speter} 626251881Speter 627251881Speter 628251881Speter/*------------------------------------------------------------------*/ 629251881Speter/*** Public Interfaces ***/ 630251881Speter 631251881Speter 632251881Spetersvn_error_t * 633251881Spetersvn_wc_crawl_revisions5(svn_wc_context_t *wc_ctx, 634251881Speter const char *local_abspath, 635251881Speter const svn_ra_reporter3_t *reporter, 636251881Speter void *report_baton, 637251881Speter svn_boolean_t restore_files, 638251881Speter svn_depth_t depth, 639251881Speter svn_boolean_t honor_depth_exclude, 640251881Speter svn_boolean_t depth_compatibility_trick, 641251881Speter svn_boolean_t use_commit_times, 642251881Speter svn_cancel_func_t cancel_func, 643251881Speter void *cancel_baton, 644251881Speter svn_wc_notify_func2_t notify_func, 645251881Speter void *notify_baton, 646251881Speter apr_pool_t *scratch_pool) 647251881Speter{ 648251881Speter svn_wc__db_t *db = wc_ctx->db; 649251881Speter svn_error_t *fserr, *err; 650251881Speter svn_revnum_t target_rev = SVN_INVALID_REVNUM; 651251881Speter svn_boolean_t start_empty; 652251881Speter svn_wc__db_status_t status; 653251881Speter svn_node_kind_t target_kind; 654251881Speter const char *repos_relpath, *repos_root_url; 655251881Speter svn_depth_t target_depth; 656251881Speter svn_wc__db_lock_t *target_lock; 657251881Speter svn_node_kind_t disk_kind; 658251881Speter svn_depth_t report_depth; 659251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 660251881Speter 661251881Speter /* Get the base rev, which is the first revnum that entries will be 662251881Speter compared to, and some other WC info about the target. */ 663251881Speter err = svn_wc__db_base_get_info(&status, &target_kind, &target_rev, 664251881Speter &repos_relpath, &repos_root_url, 665251881Speter NULL, NULL, NULL, NULL, &target_depth, 666251881Speter NULL, NULL, &target_lock, 667251881Speter NULL, NULL, NULL, 668251881Speter db, local_abspath, scratch_pool, 669251881Speter scratch_pool); 670251881Speter 671251881Speter if (err 672251881Speter || (status != svn_wc__db_status_normal 673251881Speter && status != svn_wc__db_status_incomplete)) 674251881Speter { 675251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 676251881Speter return svn_error_trace(err); 677251881Speter 678251881Speter svn_error_clear(err); 679251881Speter 680251881Speter /* We don't know about this node, so all we have to do is tell 681251881Speter the reporter that we don't know this node. 682251881Speter 683251881Speter But first we have to start the report by sending some basic 684251881Speter information for the root. */ 685251881Speter 686251881Speter if (depth == svn_depth_unknown) 687251881Speter depth = svn_depth_infinity; 688251881Speter 689251881Speter SVN_ERR(reporter->set_path(report_baton, "", 0, depth, FALSE, 690251881Speter NULL, scratch_pool)); 691251881Speter SVN_ERR(reporter->delete_path(report_baton, "", scratch_pool)); 692251881Speter 693251881Speter /* Finish the report, which causes the update editor to be 694251881Speter driven. */ 695251881Speter SVN_ERR(reporter->finish_report(report_baton, scratch_pool)); 696251881Speter 697251881Speter return SVN_NO_ERROR; 698251881Speter } 699251881Speter 700251881Speter if (target_depth == svn_depth_unknown) 701251881Speter target_depth = svn_depth_infinity; 702251881Speter 703251881Speter start_empty = (status == svn_wc__db_status_incomplete); 704251881Speter if (depth_compatibility_trick 705251881Speter && target_depth <= svn_depth_immediates 706251881Speter && depth > target_depth) 707251881Speter { 708251881Speter start_empty = TRUE; 709251881Speter } 710251881Speter 711251881Speter if (restore_files) 712251881Speter SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); 713251881Speter else 714251881Speter disk_kind = svn_node_unknown; 715251881Speter 716251881Speter /* Determine if there is a missing node that should be restored */ 717251881Speter if (restore_files 718251881Speter && disk_kind == svn_node_none) 719251881Speter { 720251881Speter svn_wc__db_status_t wrk_status; 721251881Speter svn_node_kind_t wrk_kind; 722251881Speter const svn_checksum_t *checksum; 723299742Sdim svn_boolean_t conflicted; 724251881Speter 725251881Speter err = svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, NULL, 726251881Speter NULL, NULL, NULL, NULL, NULL, &checksum, NULL, 727251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 728299742Sdim NULL, &conflicted, NULL, NULL, NULL, NULL, 729299742Sdim NULL, NULL, 730251881Speter db, local_abspath, 731251881Speter scratch_pool, scratch_pool); 732251881Speter 733251881Speter 734251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 735251881Speter { 736251881Speter svn_error_clear(err); 737251881Speter wrk_status = svn_wc__db_status_not_present; 738251881Speter wrk_kind = svn_node_file; 739251881Speter } 740251881Speter else 741251881Speter SVN_ERR(err); 742251881Speter 743251881Speter if ((wrk_status == svn_wc__db_status_normal 744251881Speter || wrk_status == svn_wc__db_status_added 745251881Speter || wrk_status == svn_wc__db_status_incomplete) 746251881Speter && (wrk_kind == svn_node_dir || checksum)) 747251881Speter { 748251881Speter SVN_ERR(restore_node(wc_ctx->db, local_abspath, 749299742Sdim wrk_kind, conflicted, use_commit_times, 750299742Sdim cancel_func, cancel_baton, 751251881Speter notify_func, notify_baton, 752251881Speter scratch_pool)); 753251881Speter } 754251881Speter } 755251881Speter 756251881Speter { 757251881Speter report_depth = target_depth; 758251881Speter 759251881Speter if (honor_depth_exclude 760251881Speter && depth != svn_depth_unknown 761251881Speter && depth < target_depth) 762251881Speter report_depth = depth; 763251881Speter 764251881Speter /* The first call to the reporter merely informs it that the 765251881Speter top-level directory being updated is at BASE_REV. Its PATH 766251881Speter argument is ignored. */ 767251881Speter SVN_ERR(reporter->set_path(report_baton, "", target_rev, report_depth, 768251881Speter start_empty, NULL, scratch_pool)); 769251881Speter } 770251881Speter if (target_kind == svn_node_dir) 771251881Speter { 772251881Speter if (depth != svn_depth_empty) 773251881Speter { 774251881Speter /* Recursively crawl ROOT_DIRECTORY and report differing 775251881Speter revisions. */ 776251881Speter err = report_revisions_and_depths(wc_ctx->db, 777251881Speter local_abspath, 778251881Speter "", 779251881Speter target_rev, 780251881Speter repos_relpath, 781251881Speter repos_root_url, 782251881Speter report_depth, 783251881Speter reporter, report_baton, 784251881Speter restore_files, depth, 785251881Speter honor_depth_exclude, 786251881Speter depth_compatibility_trick, 787251881Speter start_empty, 788251881Speter use_commit_times, 789251881Speter cancel_func, cancel_baton, 790251881Speter notify_func, notify_baton, 791251881Speter scratch_pool); 792251881Speter if (err) 793251881Speter goto abort_report; 794251881Speter } 795251881Speter } 796251881Speter 797251881Speter else if (target_kind == svn_node_file || target_kind == svn_node_symlink) 798251881Speter { 799251881Speter const char *parent_abspath, *base; 800251881Speter svn_wc__db_status_t parent_status; 801251881Speter const char *parent_repos_relpath; 802251881Speter 803251881Speter svn_dirent_split(&parent_abspath, &base, local_abspath, 804251881Speter scratch_pool); 805251881Speter 806251881Speter /* We can assume a file is in the same repository as its parent 807251881Speter directory, so we only look at the relpath. */ 808251881Speter err = svn_wc__db_base_get_info(&parent_status, NULL, NULL, 809251881Speter &parent_repos_relpath, NULL, NULL, NULL, 810251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 811251881Speter NULL, NULL, NULL, 812251881Speter db, parent_abspath, 813251881Speter scratch_pool, scratch_pool); 814251881Speter 815251881Speter if (err) 816251881Speter goto abort_report; 817251881Speter 818251881Speter if (strcmp(repos_relpath, 819251881Speter svn_relpath_join(parent_repos_relpath, base, 820251881Speter scratch_pool)) != 0) 821251881Speter { 822251881Speter /* This file is disjoint with respect to its parent 823251881Speter directory. Since we are looking at the actual target of 824251881Speter the report (not some file in a subdirectory of a target 825251881Speter directory), and that target is a file, we need to pass an 826251881Speter empty string to link_path. */ 827251881Speter err = reporter->link_path(report_baton, 828251881Speter "", 829251881Speter svn_path_url_add_component2( 830251881Speter repos_root_url, 831251881Speter repos_relpath, 832251881Speter scratch_pool), 833251881Speter target_rev, 834251881Speter svn_depth_infinity, 835251881Speter FALSE, 836251881Speter target_lock ? target_lock->token : NULL, 837251881Speter scratch_pool); 838251881Speter if (err) 839251881Speter goto abort_report; 840251881Speter } 841251881Speter else if (target_lock) 842251881Speter { 843251881Speter /* If this entry is a file node, we just want to report that 844251881Speter node's revision. Since we are looking at the actual target 845251881Speter of the report (not some file in a subdirectory of a target 846251881Speter directory), and that target is a file, we need to pass an 847251881Speter empty string to set_path. */ 848251881Speter err = reporter->set_path(report_baton, "", target_rev, 849251881Speter svn_depth_infinity, 850251881Speter FALSE, 851251881Speter target_lock ? target_lock->token : NULL, 852251881Speter scratch_pool); 853251881Speter if (err) 854251881Speter goto abort_report; 855251881Speter } 856251881Speter } 857251881Speter 858251881Speter /* Finish the report, which causes the update editor to be driven. */ 859251881Speter return svn_error_trace(reporter->finish_report(report_baton, scratch_pool)); 860251881Speter 861251881Speter abort_report: 862251881Speter /* Clean up the fs transaction. */ 863251881Speter if ((fserr = reporter->abort_report(report_baton, scratch_pool))) 864251881Speter { 865251881Speter fserr = svn_error_quick_wrap(fserr, _("Error aborting report")); 866251881Speter svn_error_compose(err, fserr); 867251881Speter } 868251881Speter return svn_error_trace(err); 869251881Speter} 870251881Speter 871251881Speter/*** Copying stream ***/ 872251881Speter 873251881Speter/* A copying stream is a bit like the unix tee utility: 874251881Speter * 875251881Speter * It reads the SOURCE when asked for data and while returning it, 876251881Speter * also writes the same data to TARGET. 877251881Speter */ 878251881Speterstruct copying_stream_baton 879251881Speter{ 880251881Speter /* Stream to read input from. */ 881251881Speter svn_stream_t *source; 882251881Speter 883251881Speter /* Stream to write all data read to. */ 884251881Speter svn_stream_t *target; 885251881Speter}; 886251881Speter 887251881Speter 888251881Speter/* */ 889251881Speterstatic svn_error_t * 890251881Speterread_handler_copy(void *baton, char *buffer, apr_size_t *len) 891251881Speter{ 892251881Speter struct copying_stream_baton *btn = baton; 893251881Speter 894299742Sdim SVN_ERR(svn_stream_read_full(btn->source, buffer, len)); 895251881Speter 896251881Speter return svn_stream_write(btn->target, buffer, len); 897251881Speter} 898251881Speter 899251881Speter/* */ 900251881Speterstatic svn_error_t * 901251881Speterclose_handler_copy(void *baton) 902251881Speter{ 903251881Speter struct copying_stream_baton *btn = baton; 904251881Speter 905251881Speter SVN_ERR(svn_stream_close(btn->target)); 906251881Speter return svn_stream_close(btn->source); 907251881Speter} 908251881Speter 909251881Speter 910251881Speter/* Return a stream - allocated in POOL - which reads its input 911251881Speter * from SOURCE and, while returning that to the caller, at the 912251881Speter * same time writes that to TARGET. 913251881Speter */ 914251881Speterstatic svn_stream_t * 915251881Spetercopying_stream(svn_stream_t *source, 916251881Speter svn_stream_t *target, 917251881Speter apr_pool_t *pool) 918251881Speter{ 919251881Speter struct copying_stream_baton *baton; 920251881Speter svn_stream_t *stream; 921251881Speter 922251881Speter baton = apr_palloc(pool, sizeof (*baton)); 923251881Speter baton->source = source; 924251881Speter baton->target = target; 925251881Speter 926251881Speter stream = svn_stream_create(baton, pool); 927299742Sdim svn_stream_set_read2(stream, NULL /* only full read support */, 928299742Sdim read_handler_copy); 929251881Speter svn_stream_set_close(stream, close_handler_copy); 930251881Speter 931251881Speter return stream; 932251881Speter} 933251881Speter 934251881Speter 935251881Speter/* Set *STREAM to a stream from which the caller can read the pristine text 936251881Speter * of the working version of the file at LOCAL_ABSPATH. If the working 937251881Speter * version of LOCAL_ABSPATH has no pristine text because it is locally 938251881Speter * added, set *STREAM to an empty stream. If the working version of 939251881Speter * LOCAL_ABSPATH is not a file, return an error. 940251881Speter * 941251881Speter * Set *EXPECTED_MD5_CHECKSUM to the recorded MD5 checksum. 942251881Speter * 943251881Speter * Arrange for the actual checksum of the text to be calculated and written 944251881Speter * into *ACTUAL_MD5_CHECKSUM when the stream is read. 945251881Speter */ 946251881Speterstatic svn_error_t * 947251881Speterread_and_checksum_pristine_text(svn_stream_t **stream, 948251881Speter const svn_checksum_t **expected_md5_checksum, 949251881Speter svn_checksum_t **actual_md5_checksum, 950251881Speter svn_wc__db_t *db, 951251881Speter const char *local_abspath, 952251881Speter apr_pool_t *result_pool, 953251881Speter apr_pool_t *scratch_pool) 954251881Speter{ 955251881Speter svn_stream_t *base_stream; 956251881Speter 957251881Speter SVN_ERR(svn_wc__get_pristine_contents(&base_stream, NULL, db, local_abspath, 958251881Speter result_pool, scratch_pool)); 959251881Speter if (base_stream == NULL) 960251881Speter { 961251881Speter base_stream = svn_stream_empty(result_pool); 962251881Speter *expected_md5_checksum = NULL; 963251881Speter *actual_md5_checksum = NULL; 964251881Speter } 965251881Speter else 966251881Speter { 967251881Speter const svn_checksum_t *expected_md5; 968251881Speter 969251881Speter SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, 970251881Speter NULL, NULL, NULL, NULL, &expected_md5, 971251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 972251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 973251881Speter NULL, NULL, NULL, NULL, 974251881Speter db, local_abspath, 975251881Speter result_pool, scratch_pool)); 976251881Speter if (expected_md5 == NULL) 977251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 978251881Speter _("Pristine checksum for file '%s' is missing"), 979251881Speter svn_dirent_local_style(local_abspath, 980251881Speter scratch_pool)); 981251881Speter if (expected_md5->kind != svn_checksum_md5) 982251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&expected_md5, db, local_abspath, 983251881Speter expected_md5, 984251881Speter result_pool, scratch_pool)); 985251881Speter *expected_md5_checksum = expected_md5; 986251881Speter 987251881Speter /* Arrange to set ACTUAL_MD5_CHECKSUM to the MD5 of what is *actually* 988251881Speter found when the base stream is read. */ 989251881Speter base_stream = svn_stream_checksummed2(base_stream, actual_md5_checksum, 990251881Speter NULL, svn_checksum_md5, TRUE, 991251881Speter result_pool); 992251881Speter } 993251881Speter 994251881Speter *stream = base_stream; 995251881Speter return SVN_NO_ERROR; 996251881Speter} 997251881Speter 998251881Speter 999251881Spetersvn_error_t * 1000251881Spetersvn_wc__internal_transmit_text_deltas(const char **tempfile, 1001251881Speter const svn_checksum_t **new_text_base_md5_checksum, 1002251881Speter const svn_checksum_t **new_text_base_sha1_checksum, 1003251881Speter svn_wc__db_t *db, 1004251881Speter const char *local_abspath, 1005251881Speter svn_boolean_t fulltext, 1006251881Speter const svn_delta_editor_t *editor, 1007251881Speter void *file_baton, 1008251881Speter apr_pool_t *result_pool, 1009251881Speter apr_pool_t *scratch_pool) 1010251881Speter{ 1011251881Speter svn_txdelta_window_handler_t handler; 1012251881Speter void *wh_baton; 1013251881Speter const svn_checksum_t *expected_md5_checksum; /* recorded MD5 of BASE_S. */ 1014251881Speter svn_checksum_t *verify_checksum; /* calc'd MD5 of BASE_STREAM */ 1015251881Speter svn_checksum_t *local_md5_checksum; /* calc'd MD5 of LOCAL_STREAM */ 1016251881Speter svn_checksum_t *local_sha1_checksum; /* calc'd SHA1 of LOCAL_STREAM */ 1017299742Sdim svn_wc__db_install_data_t *install_data = NULL; 1018251881Speter svn_error_t *err; 1019299742Sdim svn_error_t *err2; 1020251881Speter svn_stream_t *base_stream; /* delta source */ 1021251881Speter svn_stream_t *local_stream; /* delta target: LOCAL_ABSPATH transl. to NF */ 1022251881Speter 1023251881Speter /* Translated input */ 1024251881Speter SVN_ERR(svn_wc__internal_translated_stream(&local_stream, db, 1025251881Speter local_abspath, local_abspath, 1026251881Speter SVN_WC_TRANSLATE_TO_NF, 1027251881Speter scratch_pool, scratch_pool)); 1028251881Speter 1029251881Speter /* If the caller wants a copy of the working file translated to 1030251881Speter * repository-normal form, make the copy by tee-ing the stream and set 1031251881Speter * *TEMPFILE to the path to it. This is only needed for the 1.6 API, 1032251881Speter * 1.7 doesn't set TEMPFILE. Even when using the 1.6 API this file 1033251881Speter * is not used by the functions that would have used it when using 1034251881Speter * the 1.6 code. It's possible that 3rd party users (if there are any) 1035251881Speter * might expect this file to be a text-base. */ 1036251881Speter if (tempfile) 1037251881Speter { 1038251881Speter svn_stream_t *tempstream; 1039251881Speter 1040251881Speter /* It can't be the same location as in 1.6 because the admin directory 1041251881Speter no longer exists. */ 1042251881Speter SVN_ERR(svn_stream_open_unique(&tempstream, tempfile, 1043251881Speter NULL, svn_io_file_del_none, 1044251881Speter result_pool, scratch_pool)); 1045251881Speter 1046251881Speter /* Wrap the translated stream with a new stream that writes the 1047251881Speter translated contents into the new text base file as we read from it. 1048251881Speter Note that the new text base file will be closed when the new stream 1049251881Speter is closed. */ 1050251881Speter local_stream = copying_stream(local_stream, tempstream, scratch_pool); 1051251881Speter } 1052251881Speter if (new_text_base_sha1_checksum) 1053251881Speter { 1054251881Speter svn_stream_t *new_pristine_stream; 1055251881Speter 1056299742Sdim SVN_ERR(svn_wc__db_pristine_prepare_install(&new_pristine_stream, 1057299742Sdim &install_data, 1058299742Sdim &local_sha1_checksum, NULL, 1059299742Sdim db, local_abspath, 1060299742Sdim scratch_pool, scratch_pool)); 1061251881Speter local_stream = copying_stream(local_stream, new_pristine_stream, 1062251881Speter scratch_pool); 1063251881Speter } 1064251881Speter 1065251881Speter /* If sending a full text is requested, or if there is no pristine text 1066251881Speter * (e.g. the node is locally added), then set BASE_STREAM to an empty 1067251881Speter * stream and leave EXPECTED_MD5_CHECKSUM and VERIFY_CHECKSUM as NULL. 1068251881Speter * 1069251881Speter * Otherwise, set BASE_STREAM to a stream providing the base (source) text 1070251881Speter * for the delta, set EXPECTED_MD5_CHECKSUM to its stored MD5 checksum, 1071251881Speter * and arrange for its VERIFY_CHECKSUM to be calculated later. */ 1072251881Speter if (! fulltext) 1073251881Speter { 1074251881Speter /* We will be computing a delta against the pristine contents */ 1075251881Speter /* We need the expected checksum to be an MD-5 checksum rather than a 1076251881Speter * SHA-1 because we want to pass it to apply_textdelta(). */ 1077251881Speter SVN_ERR(read_and_checksum_pristine_text(&base_stream, 1078251881Speter &expected_md5_checksum, 1079251881Speter &verify_checksum, 1080251881Speter db, local_abspath, 1081251881Speter scratch_pool, scratch_pool)); 1082251881Speter } 1083251881Speter else 1084251881Speter { 1085251881Speter /* Send a fulltext. */ 1086251881Speter base_stream = svn_stream_empty(scratch_pool); 1087251881Speter expected_md5_checksum = NULL; 1088251881Speter verify_checksum = NULL; 1089251881Speter } 1090251881Speter 1091251881Speter /* Tell the editor that we're about to apply a textdelta to the 1092251881Speter file baton; the editor returns to us a window consumer and baton. */ 1093251881Speter { 1094251881Speter /* apply_textdelta() is working against a base with this checksum */ 1095251881Speter const char *base_digest_hex = NULL; 1096251881Speter 1097251881Speter if (expected_md5_checksum) 1098251881Speter /* ### Why '..._display()'? expected_md5_checksum should never be all- 1099251881Speter * zero, but if it is, we would want to pass NULL not an all-zero 1100251881Speter * digest to apply_textdelta(), wouldn't we? */ 1101251881Speter base_digest_hex = svn_checksum_to_cstring_display(expected_md5_checksum, 1102251881Speter scratch_pool); 1103251881Speter 1104251881Speter SVN_ERR(editor->apply_textdelta(file_baton, base_digest_hex, scratch_pool, 1105251881Speter &handler, &wh_baton)); 1106251881Speter } 1107251881Speter 1108251881Speter /* Run diff processing, throwing windows at the handler. */ 1109251881Speter err = svn_txdelta_run(base_stream, local_stream, 1110251881Speter handler, wh_baton, 1111251881Speter svn_checksum_md5, &local_md5_checksum, 1112251881Speter NULL, NULL, 1113251881Speter scratch_pool, scratch_pool); 1114251881Speter 1115251881Speter /* Close the two streams to force writing the digest */ 1116299742Sdim err2 = svn_stream_close(base_stream); 1117299742Sdim if (err2) 1118299742Sdim { 1119299742Sdim /* Set verify_checksum to NULL if svn_stream_close() returns error 1120299742Sdim because checksum will be uninitialized in this case. */ 1121299742Sdim verify_checksum = NULL; 1122299742Sdim err = svn_error_compose_create(err, err2); 1123299742Sdim } 1124299742Sdim 1125251881Speter err = svn_error_compose_create(err, svn_stream_close(local_stream)); 1126251881Speter 1127251881Speter /* If we have an error, it may be caused by a corrupt text base, 1128251881Speter so check the checksum. */ 1129251881Speter if (expected_md5_checksum && verify_checksum 1130251881Speter && !svn_checksum_match(expected_md5_checksum, verify_checksum)) 1131251881Speter { 1132251881Speter /* The entry checksum does not match the actual text 1133251881Speter base checksum. Extreme badness. Of course, 1134251881Speter theoretically we could just switch to 1135251881Speter fulltext transmission here, and everything would 1136251881Speter work fine; after all, we're going to replace the 1137251881Speter text base with a new one in a moment anyway, and 1138251881Speter we'd fix the checksum then. But it's better to 1139251881Speter error out. People should know that their text 1140251881Speter bases are getting corrupted, so they can 1141251881Speter investigate. Other commands could be affected, 1142251881Speter too, such as `svn diff'. */ 1143251881Speter 1144251881Speter if (tempfile) 1145251881Speter err = svn_error_compose_create( 1146251881Speter err, 1147251881Speter svn_io_remove_file2(*tempfile, TRUE, scratch_pool)); 1148251881Speter 1149251881Speter err = svn_error_compose_create( 1150251881Speter svn_checksum_mismatch_err(expected_md5_checksum, verify_checksum, 1151251881Speter scratch_pool, 1152251881Speter _("Checksum mismatch for text base of '%s'"), 1153251881Speter svn_dirent_local_style(local_abspath, 1154251881Speter scratch_pool)), 1155251881Speter err); 1156251881Speter 1157251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT_TEXT_BASE, err, NULL); 1158251881Speter } 1159251881Speter 1160251881Speter /* Now, handle that delta transmission error if any, so we can stop 1161251881Speter thinking about it after this point. */ 1162251881Speter SVN_ERR_W(err, apr_psprintf(scratch_pool, 1163251881Speter _("While preparing '%s' for commit"), 1164251881Speter svn_dirent_local_style(local_abspath, 1165251881Speter scratch_pool))); 1166251881Speter 1167251881Speter if (new_text_base_md5_checksum) 1168251881Speter *new_text_base_md5_checksum = svn_checksum_dup(local_md5_checksum, 1169251881Speter result_pool); 1170251881Speter if (new_text_base_sha1_checksum) 1171251881Speter { 1172299742Sdim SVN_ERR(svn_wc__db_pristine_install(install_data, 1173251881Speter local_sha1_checksum, 1174251881Speter local_md5_checksum, 1175251881Speter scratch_pool)); 1176251881Speter *new_text_base_sha1_checksum = svn_checksum_dup(local_sha1_checksum, 1177251881Speter result_pool); 1178251881Speter } 1179251881Speter 1180251881Speter /* Close the file baton, and get outta here. */ 1181251881Speter return svn_error_trace( 1182251881Speter editor->close_file(file_baton, 1183251881Speter svn_checksum_to_cstring(local_md5_checksum, 1184251881Speter scratch_pool), 1185251881Speter scratch_pool)); 1186251881Speter} 1187251881Speter 1188251881Spetersvn_error_t * 1189251881Spetersvn_wc_transmit_text_deltas3(const svn_checksum_t **new_text_base_md5_checksum, 1190251881Speter const svn_checksum_t **new_text_base_sha1_checksum, 1191251881Speter svn_wc_context_t *wc_ctx, 1192251881Speter const char *local_abspath, 1193251881Speter svn_boolean_t fulltext, 1194251881Speter const svn_delta_editor_t *editor, 1195251881Speter void *file_baton, 1196251881Speter apr_pool_t *result_pool, 1197251881Speter apr_pool_t *scratch_pool) 1198251881Speter{ 1199251881Speter return svn_wc__internal_transmit_text_deltas(NULL, 1200251881Speter new_text_base_md5_checksum, 1201251881Speter new_text_base_sha1_checksum, 1202251881Speter wc_ctx->db, local_abspath, 1203251881Speter fulltext, editor, 1204251881Speter file_baton, result_pool, 1205251881Speter scratch_pool); 1206251881Speter} 1207251881Speter 1208251881Spetersvn_error_t * 1209251881Spetersvn_wc__internal_transmit_prop_deltas(svn_wc__db_t *db, 1210251881Speter const char *local_abspath, 1211251881Speter const svn_delta_editor_t *editor, 1212251881Speter void *baton, 1213251881Speter apr_pool_t *scratch_pool) 1214251881Speter{ 1215251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1216251881Speter int i; 1217251881Speter apr_array_header_t *propmods; 1218251881Speter svn_node_kind_t kind; 1219251881Speter 1220251881Speter SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, 1221251881Speter FALSE /* allow_missing */, 1222251881Speter FALSE /* show_deleted */, 1223251881Speter FALSE /* show_hidden */, 1224251881Speter iterpool)); 1225251881Speter 1226251881Speter if (kind == svn_node_none) 1227251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1228251881Speter _("The node '%s' was not found."), 1229251881Speter svn_dirent_local_style(local_abspath, iterpool)); 1230251881Speter 1231251881Speter /* Get an array of local changes by comparing the hashes. */ 1232251881Speter SVN_ERR(svn_wc__internal_propdiff(&propmods, NULL, db, local_abspath, 1233251881Speter scratch_pool, iterpool)); 1234251881Speter 1235251881Speter /* Apply each local change to the baton */ 1236251881Speter for (i = 0; i < propmods->nelts; i++) 1237251881Speter { 1238251881Speter const svn_prop_t *p = &APR_ARRAY_IDX(propmods, i, svn_prop_t); 1239251881Speter 1240251881Speter svn_pool_clear(iterpool); 1241251881Speter 1242251881Speter if (kind == svn_node_file) 1243251881Speter SVN_ERR(editor->change_file_prop(baton, p->name, p->value, 1244251881Speter iterpool)); 1245251881Speter else 1246251881Speter SVN_ERR(editor->change_dir_prop(baton, p->name, p->value, 1247251881Speter iterpool)); 1248251881Speter } 1249251881Speter 1250251881Speter svn_pool_destroy(iterpool); 1251251881Speter return SVN_NO_ERROR; 1252251881Speter} 1253251881Speter 1254251881Spetersvn_error_t * 1255251881Spetersvn_wc_transmit_prop_deltas2(svn_wc_context_t *wc_ctx, 1256251881Speter const char *local_abspath, 1257251881Speter const svn_delta_editor_t *editor, 1258251881Speter void *baton, 1259251881Speter apr_pool_t *scratch_pool) 1260251881Speter{ 1261251881Speter return svn_wc__internal_transmit_prop_deltas(wc_ctx->db, local_abspath, 1262251881Speter editor, baton, scratch_pool); 1263251881Speter} 1264