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