~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_repos/reporter.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 */
23
23
 
24
24
#include "svn_dirent_uri.h"
 
25
#include "svn_hash.h"
25
26
#include "svn_path.h"
26
27
#include "svn_types.h"
27
28
#include "svn_error.h"
32
33
#include "svn_props.h"
33
34
#include "repos.h"
34
35
#include "svn_private_config.h"
 
36
 
35
37
#include "private/svn_dep_compat.h"
36
38
#include "private/svn_fspath.h"
 
39
#include "private/svn_subr_private.h"
 
40
#include "private/svn_string_private.h"
37
41
 
38
42
#define NUM_CACHED_SOURCE_ROOTS 4
39
43
 
40
 
/* Theory of operation: we write report operations out to a temporary
41
 
   file as we receive them.  When the report is finished, we read the
 
44
/* Theory of operation: we write report operations out to a spill-buffer
 
45
   as we receive them.  When the report is finished, we read the
42
46
   operations back out again, using them to guide the progression of
43
47
   the delta between the source and target revs.
44
48
 
45
 
   Temporary file format: we use a simple ad-hoc format to store the
 
49
   Spill-buffer content format: we use a simple ad-hoc format to store the
46
50
   report operations.  Each report operation is the concatention of
47
51
   the following ("+/-" indicates the single character '+' or '-';
48
52
   <length> and <revnum> are written out as decimal strings):
100
104
   driven by the client as it describes its working copy revisions. */
101
105
typedef struct report_baton_t
102
106
{
103
 
  /* Parameters remembered from svn_repos_begin_report2 */
 
107
  /* Parameters remembered from svn_repos_begin_report3 */
104
108
  svn_repos_t *repos;
105
109
  const char *fs_base;         /* fspath corresponding to wc anchor */
106
110
  const char *s_operand;       /* anchor-relative wc target (may be empty) */
107
111
  svn_revnum_t t_rev;          /* Revnum which the edit will bring the wc to */
108
112
  const char *t_path;          /* FS path the edit will bring the wc to */
109
113
  svn_boolean_t text_deltas;   /* Whether to report text deltas */
 
114
  apr_size_t zero_copy_limit;  /* Max item size that will be sent using
 
115
                                  the zero-copy code path. */
110
116
 
111
117
  /* If the client requested a specific depth, record it here; if the
112
118
     client did not, then this is svn_depth_unknown, and the depth of
122
128
  svn_repos_authz_func_t authz_read_func;
123
129
  void *authz_read_baton;
124
130
 
125
 
  /* The temporary file in which we are stashing the report. */
126
 
  apr_file_t *tempfile;
 
131
  /* The spill-buffer holding the report. */
 
132
  svn_spillbuf_reader_t *reader;
127
133
 
128
134
  /* For the actual editor drive, we'll need a lookahead path info
129
135
     entry, a cache of FS roots, and a pool to store them. */
133
139
 
134
140
  /* Cache for revision properties. This is used to eliminate redundant
135
141
     revprop fetching. */
136
 
  apr_hash_t* revision_infos;
 
142
  apr_hash_t *revision_infos;
137
143
 
 
144
  /* This will not change. So, fetch it once and reuse it. */
 
145
  svn_string_t *repos_uuid;
138
146
  apr_pool_t *pool;
139
147
} report_baton_t;
140
148
 
158
166
/* --- READING PREVIOUSLY STORED REPORT INFORMATION --- */
159
167
 
160
168
static svn_error_t *
161
 
read_number(apr_uint64_t *num, apr_file_t *temp, apr_pool_t *pool)
 
169
read_number(apr_uint64_t *num, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
162
170
{
163
171
  char c;
164
172
 
165
173
  *num = 0;
166
174
  while (1)
167
175
    {
168
 
      SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
176
      SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
169
177
      if (c == ':')
170
178
        break;
171
179
      *num = *num * 10 + (c - '0');
174
182
}
175
183
 
176
184
static svn_error_t *
177
 
read_string(const char **str, apr_file_t *temp, apr_pool_t *pool)
 
185
read_string(const char **str, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
178
186
{
179
187
  apr_uint64_t len;
180
188
  apr_size_t size;
 
189
  apr_size_t amt;
181
190
  char *buf;
182
191
 
183
 
  SVN_ERR(read_number(&len, temp, pool));
 
192
  SVN_ERR(read_number(&len, reader, pool));
184
193
 
185
194
  /* Len can never be less than zero.  But could len be so large that
186
195
     len + 1 wraps around and we end up passing 0 to apr_palloc(),
201
210
 
202
211
  size = (apr_size_t)len;
203
212
  buf = apr_palloc(pool, size+1);
204
 
  SVN_ERR(svn_io_file_read_full2(temp, buf, size, NULL, NULL, pool));
 
213
  if (size > 0)
 
214
    {
 
215
      SVN_ERR(svn_spillbuf__reader_read(&amt, reader, buf, size, pool));
 
216
      SVN_ERR_ASSERT(amt == size);
 
217
    }
205
218
  buf[len] = 0;
206
219
  *str = buf;
207
220
  return SVN_NO_ERROR;
208
221
}
209
222
 
210
223
static svn_error_t *
211
 
read_rev(svn_revnum_t *rev, apr_file_t *temp, apr_pool_t *pool)
 
224
read_rev(svn_revnum_t *rev, svn_spillbuf_reader_t *reader, apr_pool_t *pool)
212
225
{
213
226
  char c;
214
227
  apr_uint64_t num;
215
228
 
216
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
229
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
217
230
  if (c == '+')
218
231
    {
219
 
      SVN_ERR(read_number(&num, temp, pool));
 
232
      SVN_ERR(read_number(&num, reader, pool));
220
233
      *rev = (svn_revnum_t) num;
221
234
    }
222
235
  else
225
238
}
226
239
 
227
240
/* Read a single character to set *DEPTH (having already read '+')
228
 
   from TEMP.  PATH is the path to which the depth applies, and is
 
241
   from READER.  PATH is the path to which the depth applies, and is
229
242
   used for error reporting only. */
230
243
static svn_error_t *
231
 
read_depth(svn_depth_t *depth, apr_file_t *temp, const char *path,
 
244
read_depth(svn_depth_t *depth, svn_spillbuf_reader_t *reader, const char *path,
232
245
           apr_pool_t *pool)
233
246
{
234
247
  char c;
235
248
 
236
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
249
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
237
250
  switch (c)
238
251
    {
239
252
    case 'X':
260
273
  return SVN_NO_ERROR;
261
274
}
262
275
 
263
 
/* Read a report operation *PI out of TEMP.  Set *PI to NULL if we
 
276
/* Read a report operation *PI out of READER.  Set *PI to NULL if we
264
277
   have reached the end of the report. */
265
278
static svn_error_t *
266
 
read_path_info(path_info_t **pi, apr_file_t *temp, apr_pool_t *pool)
 
279
read_path_info(path_info_t **pi,
 
280
               svn_spillbuf_reader_t *reader,
 
281
               apr_pool_t *pool)
267
282
{
268
283
  char c;
269
284
 
270
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
285
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
271
286
  if (c == '-')
272
287
    {
273
288
      *pi = NULL;
275
290
    }
276
291
 
277
292
  *pi = apr_palloc(pool, sizeof(**pi));
278
 
  SVN_ERR(read_string(&(*pi)->path, temp, pool));
279
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
293
  SVN_ERR(read_string(&(*pi)->path, reader, pool));
 
294
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
280
295
  if (c == '+')
281
 
    SVN_ERR(read_string(&(*pi)->link_path, temp, pool));
 
296
    SVN_ERR(read_string(&(*pi)->link_path, reader, pool));
282
297
  else
283
298
    (*pi)->link_path = NULL;
284
 
  SVN_ERR(read_rev(&(*pi)->rev, temp, pool));
285
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
299
  SVN_ERR(read_rev(&(*pi)->rev, reader, pool));
 
300
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
286
301
  if (c == '+')
287
 
    SVN_ERR(read_depth(&((*pi)->depth), temp, (*pi)->path, pool));
 
302
    SVN_ERR(read_depth(&((*pi)->depth), reader, (*pi)->path, pool));
288
303
  else
289
304
    (*pi)->depth = svn_depth_infinity;
290
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
305
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
291
306
  (*pi)->start_empty = (c == '+');
292
 
  SVN_ERR(svn_io_file_getc(&c, temp, pool));
 
307
  SVN_ERR(svn_spillbuf__reader_getc(&c, reader, pool));
293
308
  if (c == '+')
294
 
    SVN_ERR(read_string(&(*pi)->lock_token, temp, pool));
 
309
    SVN_ERR(read_string(&(*pi)->lock_token, reader, pool));
295
310
  else
296
311
    (*pi)->lock_token = NULL;
297
312
  (*pi)->pool = pool;
306
321
          (!*prefix || pi->path[plen] == '/'));
307
322
}
308
323
 
309
 
/* Fetch the next pathinfo from B->tempfile for a descendant of
 
324
/* Fetch the next pathinfo from B->reader for a descendant of
310
325
   PREFIX.  If the next pathinfo is for an immediate child of PREFIX,
311
326
   set *ENTRY to the path component of the report information and
312
327
   *INFO to the path information for that entry.  If the next pathinfo
353
368
          *entry = relpath;
354
369
          *info = b->lookahead;
355
370
          subpool = svn_pool_create(b->pool);
356
 
          SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
 
371
          SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
357
372
        }
358
373
    }
359
374
  return SVN_NO_ERROR;
371
386
    {
372
387
      svn_pool_destroy(b->lookahead->pool);
373
388
      subpool = svn_pool_create(b->pool);
374
 
      SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
 
389
      SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
375
390
    }
376
391
  return SVN_NO_ERROR;
377
392
}
433
448
change_dir_prop(report_baton_t *b, void *dir_baton, const char *name,
434
449
                const svn_string_t *value, apr_pool_t *pool)
435
450
{
436
 
  return b->editor->change_dir_prop(dir_baton, name, value, pool);
 
451
  return svn_error_trace(b->editor->change_dir_prop(dir_baton, name, value,
 
452
                                                    pool));
437
453
}
438
454
 
439
455
/* Call the file property-setting function of B->editor to set the
442
458
change_file_prop(report_baton_t *b, void *file_baton, const char *name,
443
459
                 const svn_string_t *value, apr_pool_t *pool)
444
460
{
445
 
  return b->editor->change_file_prop(file_baton, name, value, pool);
 
461
  return svn_error_trace(b->editor->change_file_prop(file_baton, name, value,
 
462
                                                     pool));
446
463
}
447
464
 
448
465
/* For the report B, return the relevant revprop data of revision REV in
470
487
                                       scratch_pool));
471
488
 
472
489
      /* Extract the committed-date. */
473
 
      cdate = apr_hash_get(r_props, SVN_PROP_REVISION_DATE,
474
 
                           APR_HASH_KEY_STRING);
 
490
      cdate = svn_hash_gets(r_props, SVN_PROP_REVISION_DATE);
475
491
 
476
492
      /* Extract the last-author. */
477
 
      author = apr_hash_get(r_props, SVN_PROP_REVISION_AUTHOR,
478
 
                            APR_HASH_KEY_STRING);
 
493
      author = svn_hash_gets(r_props, SVN_PROP_REVISION_AUTHOR);
479
494
 
480
495
      /* Create a result object */
481
496
      info = apr_palloc(b->pool, sizeof(*info));
504
519
                void *object, apr_pool_t *pool)
505
520
{
506
521
  svn_fs_root_t *s_root;
507
 
  apr_hash_t *s_props, *t_props;
 
522
  apr_hash_t *s_props = NULL, *t_props;
508
523
  apr_array_header_t *prop_diffs;
509
524
  int i;
510
525
  svn_revnum_t crev;
511
 
  const char *uuid;
512
 
  svn_string_t *cr_str;
513
 
  revision_info_t* revision_info;
 
526
  revision_info_t *revision_info;
514
527
  svn_boolean_t changed;
515
528
  const svn_prop_t *pc;
516
529
  svn_lock_t *lock;
 
530
  apr_hash_index_t *hi;
517
531
 
518
532
  /* Fetch the created-rev and send entry props. */
519
533
  SVN_ERR(svn_fs_node_created_rev(&crev, b->t_root, t_path, pool));
520
534
  if (SVN_IS_VALID_REVNUM(crev))
521
535
    {
 
536
      /* convert committed-rev to  string */
 
537
      char buf[SVN_INT64_BUFFER_SIZE];
 
538
      svn_string_t cr_str;
 
539
      cr_str.data = buf;
 
540
      cr_str.len = svn__i64toa(buf, crev);
 
541
 
522
542
      /* Transmit the committed-rev. */
523
 
      cr_str = svn_string_createf(pool, "%ld", crev);
524
543
      SVN_ERR(change_fn(b, object,
525
 
                        SVN_PROP_ENTRY_COMMITTED_REV, cr_str, pool));
 
544
                        SVN_PROP_ENTRY_COMMITTED_REV, &cr_str, pool));
526
545
 
527
546
      SVN_ERR(get_revision_info(b, crev, &revision_info, pool));
528
547
 
537
556
                          revision_info->author, pool));
538
557
 
539
558
      /* Transmit the UUID. */
540
 
      SVN_ERR(svn_fs_get_uuid(b->repos->fs, &uuid, pool));
541
559
      SVN_ERR(change_fn(b, object, SVN_PROP_ENTRY_UUID,
542
 
                        svn_string_create(uuid, pool), pool));
 
560
                        b->repos_uuid, pool));
543
561
    }
544
562
 
545
563
  /* Update lock properties. */
566
584
      /* If so, go ahead and get the source path's properties. */
567
585
      SVN_ERR(svn_fs_node_proplist(&s_props, s_root, s_path, pool));
568
586
    }
569
 
  else
570
 
    s_props = apr_hash_make(pool);
571
587
 
572
588
  /* Get the target path's properties */
573
589
  SVN_ERR(svn_fs_node_proplist(&t_props, b->t_root, t_path, pool));
574
590
 
575
 
  /* Now transmit the differences. */
576
 
  SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool));
577
 
  for (i = 0; i < prop_diffs->nelts; i++)
578
 
    {
579
 
      pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
580
 
      SVN_ERR(change_fn(b, object, pc->name, pc->value, pool));
581
 
    }
582
 
 
583
 
  return SVN_NO_ERROR;
584
 
}
 
591
  if (s_props && apr_hash_count(s_props))
 
592
    {
 
593
      /* Now transmit the differences. */
 
594
      SVN_ERR(svn_prop_diffs(&prop_diffs, t_props, s_props, pool));
 
595
      for (i = 0; i < prop_diffs->nelts; i++)
 
596
        {
 
597
          pc = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t);
 
598
          SVN_ERR(change_fn(b, object, pc->name, pc->value, pool));
 
599
        }
 
600
    }
 
601
  else if (apr_hash_count(t_props))
 
602
    {
 
603
      /* So source, i.e. all new.  Transmit all target props. */
 
604
      for (hi = apr_hash_first(pool, t_props); hi; hi = apr_hash_next(hi))
 
605
        {
 
606
          const void *key;
 
607
          void *val;
 
608
 
 
609
          apr_hash_this(hi, &key, NULL, &val);
 
610
          SVN_ERR(change_fn(b, object, key, val, pool));
 
611
        }
 
612
    }
 
613
 
 
614
  return SVN_NO_ERROR;
 
615
}
 
616
 
 
617
/* Baton type to be passed into send_zero_copy_delta.
 
618
 */
 
619
typedef struct zero_copy_baton_t
 
620
{
 
621
  /* don't process data larger than this limit */
 
622
  apr_size_t zero_copy_limit;
 
623
 
 
624
  /* window handler and baton to send the data to */
 
625
  svn_txdelta_window_handler_t dhandler;
 
626
  void *dbaton;
 
627
 
 
628
  /* return value: will be set to TRUE, if the data was processed. */
 
629
  svn_boolean_t zero_copy_succeeded;
 
630
} zero_copy_baton_t;
 
631
 
 
632
/* Implement svn_fs_process_contents_func_t.  If LEN is smaller than the
 
633
 * limit given in *BATON, send the CONTENTS as an delta windows to the
 
634
 * handler given in BATON and set the ZERO_COPY_SUCCEEDED flag in that
 
635
 * BATON.  Otherwise, reset it to FALSE.
 
636
 * Use POOL for temporary allocations.
 
637
 */
 
638
static svn_error_t *
 
639
send_zero_copy_delta(const unsigned char *contents,
 
640
                     apr_size_t len,
 
641
                     void *baton,
 
642
                     apr_pool_t *pool)
 
643
{
 
644
  zero_copy_baton_t *zero_copy_baton = baton;
 
645
 
 
646
  /* if the item is too large, the caller must revert to traditional
 
647
     streaming code. */
 
648
  if (len > zero_copy_baton->zero_copy_limit)
 
649
    {
 
650
      zero_copy_baton->zero_copy_succeeded = FALSE;
 
651
      return SVN_NO_ERROR;
 
652
    }
 
653
 
 
654
  SVN_ERR(svn_txdelta_send_contents(contents, len,
 
655
                                    zero_copy_baton->dhandler,
 
656
                                    zero_copy_baton->dbaton, pool));
 
657
 
 
658
  /* all fine now */
 
659
  zero_copy_baton->zero_copy_succeeded = TRUE;
 
660
  return SVN_NO_ERROR;
 
661
}
 
662
 
585
663
 
586
664
/* Make the appropriate edits on FILE_BATON to change its contents and
587
665
   properties from those in S_REV/S_PATH to those in B->t_root/T_PATH,
608
686
    {
609
687
      SVN_ERR(get_source_root(b, &s_root, s_rev));
610
688
 
611
 
      /* Is this delta calculation worth our time?  If we are ignoring
612
 
         ancestry, then our editor implementor isn't concerned by the
613
 
         theoretical differences between "has contents which have not
614
 
         changed with respect to" and "has the same actual contents
615
 
         as".  We'll do everything we can to avoid transmitting even
616
 
         an empty text-delta in that case.  */
617
 
      if (b->ignore_ancestry)
618
 
        SVN_ERR(svn_repos__compare_files(&changed, b->t_root, t_path,
619
 
                                         s_root, s_path, pool));
620
 
      else
621
 
        SVN_ERR(svn_fs_contents_changed(&changed, b->t_root, t_path, s_root,
622
 
                                        s_path, pool));
 
689
      /* We're not interested in the theoretical difference between "has
 
690
         contents which have not changed with respect to" and "has the same
 
691
         actual contents as" when sending text-deltas.  If we know the
 
692
         delta is an empty one, we avoiding sending it in either case. */
 
693
      SVN_ERR(svn_repos__compare_files(&changed, b->t_root, t_path,
 
694
                                       s_root, s_path, pool));
 
695
 
623
696
      if (!changed)
624
697
        return SVN_NO_ERROR;
625
698
 
631
704
  /* Send the delta stream if desired, or just a NULL window if not. */
632
705
  SVN_ERR(b->editor->apply_textdelta(file_baton, s_hex_digest, pool,
633
706
                                     &dhandler, &dbaton));
634
 
  if (b->text_deltas)
 
707
 
 
708
  if (dhandler != svn_delta_noop_window_handler)
635
709
    {
636
 
      SVN_ERR(svn_fs_get_file_delta_stream(&dstream, s_root, s_path,
637
 
                                           b->t_root, t_path, pool));
638
 
      return svn_txdelta_send_txstream(dstream, dhandler, dbaton, pool);
 
710
      if (b->text_deltas)
 
711
        {
 
712
          /* if we send deltas against empty streams, we may use our
 
713
             zero-copy code. */
 
714
          if (b->zero_copy_limit > 0 && s_path == NULL)
 
715
            {
 
716
              zero_copy_baton_t baton;
 
717
              svn_boolean_t called = FALSE;
 
718
 
 
719
              baton.zero_copy_limit = b->zero_copy_limit;
 
720
              baton.dhandler = dhandler;
 
721
              baton.dbaton = dbaton;
 
722
              baton.zero_copy_succeeded = FALSE;
 
723
              SVN_ERR(svn_fs_try_process_file_contents(&called,
 
724
                                                       b->t_root, t_path,
 
725
                                                       send_zero_copy_delta,
 
726
                                                       &baton, pool));
 
727
 
 
728
              /* data has been available and small enough,
 
729
                 i.e. been processed? */
 
730
              if (called && baton.zero_copy_succeeded)
 
731
                return SVN_NO_ERROR;
 
732
            }
 
733
 
 
734
          SVN_ERR(svn_fs_get_file_delta_stream(&dstream, s_root, s_path,
 
735
                                               b->t_root, t_path, pool));
 
736
          SVN_ERR(svn_txdelta_send_txstream(dstream, dhandler, dbaton, pool));
 
737
        }
 
738
      else
 
739
        SVN_ERR(dhandler(NULL, dbaton));
639
740
    }
640
 
  else
641
 
    return dhandler(NULL, dbaton);
 
741
 
 
742
  return SVN_NO_ERROR;
642
743
}
643
744
 
644
745
/* Determine if the user is authorized to view B->t_root/PATH. */
647
748
           apr_pool_t *pool)
648
749
{
649
750
  if (b->authz_read_func)
650
 
    return b->authz_read_func(allowed, b->t_root, path,
651
 
                              b->authz_read_baton, pool);
 
751
    return svn_error_trace(b->authz_read_func(allowed, b->t_root, path,
 
752
                                              b->authz_read_baton, pool));
652
753
  *allowed = TRUE;
653
754
  return SVN_NO_ERROR;
654
755
}
774
875
        }
775
876
    }
776
877
 
777
 
  return b->editor->add_file(path, parent_baton,
778
 
                             *copyfrom_path, *copyfrom_rev,
779
 
                             pool, new_file_baton);
 
878
  return svn_error_trace(b->editor->add_file(path, parent_baton,
 
879
                                             *copyfrom_path, *copyfrom_rev,
 
880
                                             pool, new_file_baton));
780
881
}
781
882
 
782
883
 
889
990
      SVN_ERR(svn_repos_deleted_rev(svn_fs_root_fs(b->t_root), t_path,
890
991
                                    s_rev, b->t_rev, &deleted_rev,
891
992
                                    pool));
 
993
 
 
994
      if (!SVN_IS_VALID_REVNUM(deleted_rev))
 
995
        {
 
996
          /* Two possibilities: either the thing doesn't exist in S_REV; or
 
997
             it wasn't deleted between S_REV and B->T_REV.  In the first case,
 
998
             I think we should leave DELETED_REV as SVN_INVALID_REVNUM, but
 
999
             in the second, it should be set to B->T_REV-1 for the call to
 
1000
             delete_entry() below. */
 
1001
          svn_node_kind_t kind;
 
1002
 
 
1003
          SVN_ERR(svn_fs_check_path(&kind, b->t_root, t_path, pool));
 
1004
          if (kind != svn_node_none)
 
1005
            deleted_rev = b->t_rev - 1;
 
1006
        }
 
1007
 
892
1008
      SVN_ERR(b->editor->delete_entry(e_path, deleted_rev, dir_baton,
893
1009
                                      pool));
894
1010
      s_path = NULL;
896
1012
 
897
1013
  /* If there's no target, we have nothing more to do. */
898
1014
  if (!t_entry)
899
 
    return skip_path_info(b, e_path);
 
1015
    return svn_error_trace(skip_path_info(b, e_path));
900
1016
 
901
1017
  /* Check if the user is authorized to find out about the target. */
902
1018
  SVN_ERR(check_auth(b, &allowed, t_path, pool));
906
1022
        SVN_ERR(b->editor->absent_directory(e_path, dir_baton, pool));
907
1023
      else
908
1024
        SVN_ERR(b->editor->absent_file(e_path, dir_baton, pool));
909
 
      return skip_path_info(b, e_path);
 
1025
      return svn_error_trace(skip_path_info(b, e_path));
910
1026
    }
911
1027
 
912
1028
  if (t_entry->kind == svn_node_dir)
922
1038
      SVN_ERR(delta_dirs(b, s_rev, s_path, t_path, new_baton, e_path,
923
1039
                         info ? info->start_empty : FALSE,
924
1040
                         wc_depth, requested_depth, pool));
925
 
      return b->editor->close_directory(new_baton, pool);
 
1041
      return svn_error_trace(b->editor->close_directory(new_baton, pool));
926
1042
    }
927
1043
  else
928
1044
    {
954
1070
      SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, b->t_root,
955
1071
                                   t_path, TRUE, pool));
956
1072
      hex_digest = svn_checksum_to_cstring(checksum, pool);
957
 
      return b->editor->close_file(new_baton, hex_digest, pool);
 
1073
      return svn_error_trace(b->editor->close_file(new_baton, hex_digest,
 
1074
                                                   pool));
958
1075
    }
959
1076
}
960
1077
 
1072
1189
                 item is a delete, remove the entry from the source hash,
1073
1190
                 but don't update the entry yet. */
1074
1191
              if (s_entries)
1075
 
                apr_hash_set(s_entries, name, APR_HASH_KEY_STRING, NULL);
 
1192
                svn_hash_sets(s_entries, name, NULL);
1076
1193
              continue;
1077
1194
            }
1078
1195
 
1079
1196
          e_fullpath = svn_relpath_join(e_path, name, subpool);
1080
1197
          t_fullpath = svn_fspath__join(t_path, name, subpool);
1081
 
          t_entry = apr_hash_get(t_entries, name, APR_HASH_KEY_STRING);
 
1198
          t_entry = svn_hash_gets(t_entries, name);
1082
1199
          s_fullpath = s_path ? svn_fspath__join(s_path, name, subpool) : NULL;
1083
1200
          s_entry = s_entries ?
1084
 
            apr_hash_get(s_entries, name, APR_HASH_KEY_STRING) : NULL;
 
1201
            svn_hash_gets(s_entries, name) : NULL;
1085
1202
 
1086
1203
          /* The only special cases here are
1087
1204
 
1102
1219
                                 DEPTH_BELOW_HERE(requested_depth), subpool));
1103
1220
 
1104
1221
          /* Don't revisit this name in the target or source entries. */
1105
 
          apr_hash_set(t_entries, name, APR_HASH_KEY_STRING, NULL);
 
1222
          svn_hash_sets(t_entries, name, NULL);
1106
1223
          if (s_entries
1107
1224
              /* Keep the entry for later process if it is reported as
1108
1225
                 excluded and got deleted in repos. */
1109
1226
              && (! info || info->depth != svn_depth_exclude || t_entry))
1110
 
            apr_hash_set(s_entries, name, APR_HASH_KEY_STRING, NULL);
 
1227
            svn_hash_sets(s_entries, name, NULL);
1111
1228
 
1112
1229
          /* pathinfo entries live in their own subpools due to lookahead,
1113
1230
             so we need to clear each one out as we finish with it. */
1128
1245
              svn_pool_clear(subpool);
1129
1246
              s_entry = svn__apr_hash_index_val(hi);
1130
1247
 
1131
 
              if (apr_hash_get(t_entries, s_entry->name,
1132
 
                               APR_HASH_KEY_STRING) == NULL)
 
1248
              if (svn_hash_gets(t_entries, s_entry->name) == NULL)
1133
1249
                {
1134
1250
                  svn_revnum_t deleted_rev;
1135
1251
 
1188
1304
              /* Look for an entry with the same name
1189
1305
                 in the source dirents. */
1190
1306
              s_entry = s_entries ?
1191
 
                  apr_hash_get(s_entries, t_entry->name, APR_HASH_KEY_STRING)
 
1307
                  svn_hash_gets(s_entries, t_entry->name)
1192
1308
                  : NULL;
1193
1309
              s_fullpath = s_entry ?
1194
1310
                  svn_fspath__join(s_path, t_entry->name, subpool) : NULL;
1270
1386
                         t_entry, root_baton, b->s_operand, info,
1271
1387
                         info->depth, b->requested_depth, pool));
1272
1388
 
1273
 
  return b->editor->close_directory(root_baton, pool);
 
1389
  return svn_error_trace(b->editor->close_directory(root_baton, pool));
1274
1390
}
1275
1391
 
1276
1392
/* Initialize the baton fields for editor-driving, and drive the editor. */
1277
1393
static svn_error_t *
1278
1394
finish_report(report_baton_t *b, apr_pool_t *pool)
1279
1395
{
1280
 
  apr_off_t offset;
1281
1396
  path_info_t *info;
1282
1397
  apr_pool_t *subpool;
1283
1398
  svn_revnum_t s_rev;
1286
1401
  /* Save our pool to manage the lookahead and fs_root cache with. */
1287
1402
  b->pool = pool;
1288
1403
 
1289
 
  /* Add an end marker and rewind the temporary file. */
1290
 
  SVN_ERR(svn_io_file_write_full(b->tempfile, "-", 1, NULL, pool));
1291
 
  offset = 0;
1292
 
  SVN_ERR(svn_io_file_seek(b->tempfile, APR_SET, &offset, pool));
 
1404
  /* Add the end marker. */
 
1405
  SVN_ERR(svn_spillbuf__reader_write(b->reader, "-", 1, pool));
1293
1406
 
1294
1407
  /* Read the first pathinfo from the report and verify that it is a top-level
1295
1408
     set_path entry. */
1296
 
  SVN_ERR(read_path_info(&info, b->tempfile, pool));
 
1409
  SVN_ERR(read_path_info(&info, b->reader, pool));
1297
1410
  if (!info || strcmp(info->path, b->s_operand) != 0
1298
1411
      || info->link_path || !SVN_IS_VALID_REVNUM(info->rev))
1299
1412
    return svn_error_create(SVN_ERR_REPOS_BAD_REVISION_REPORT, NULL,
1302
1415
 
1303
1416
  /* Initialize the lookahead pathinfo. */
1304
1417
  subpool = svn_pool_create(pool);
1305
 
  SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
 
1418
  SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
1306
1419
 
1307
1420
  if (b->lookahead && strcmp(b->lookahead->path, b->s_operand) == 0)
1308
1421
    {
1320
1433
          b->lookahead->depth = info->depth;
1321
1434
        }
1322
1435
      info = b->lookahead;
1323
 
      SVN_ERR(read_path_info(&b->lookahead, b->tempfile, subpool));
 
1436
      SVN_ERR(read_path_info(&b->lookahead, b->reader, subpool));
1324
1437
    }
1325
1438
 
1326
1439
  /* Open the target root and initialize the source root cache. */
1329
1442
    b->s_roots[i] = NULL;
1330
1443
 
1331
1444
  {
1332
 
    svn_error_t *err = drive(b, s_rev, info, pool);
 
1445
    svn_error_t *err = svn_error_trace(drive(b, s_rev, info, pool));
 
1446
 
1333
1447
    if (err == SVN_NO_ERROR)
1334
1448
      return svn_error_trace(b->editor->close_edit(b->edit_baton, pool));
1335
1449
 
1342
1456
 
1343
1457
/* --- COLLECTING THE REPORT INFORMATION --- */
1344
1458
 
1345
 
/* Record a report operation into the temporary file.  Return an error
 
1459
/* Record a report operation into the spill buffer.  Return an error
1346
1460
   if DEPTH is svn_depth_unknown. */
1347
1461
static svn_error_t *
1348
1462
write_path_info(report_baton_t *b, const char *path, const char *lpath,
1381
1495
  rep = apr_psprintf(pool, "+%" APR_SIZE_T_FMT ":%s%s%s%s%c%s",
1382
1496
                     strlen(path), path, lrep, rrep, drep,
1383
1497
                     start_empty ? '+' : '-', ltrep);
1384
 
  return svn_io_file_write_full(b->tempfile, rep, strlen(rep), NULL, pool);
 
1498
  return svn_error_trace(
 
1499
            svn_spillbuf__reader_write(b->reader, rep, strlen(rep), pool));
1385
1500
}
1386
1501
 
1387
1502
svn_error_t *
1389
1504
                    svn_depth_t depth, svn_boolean_t start_empty,
1390
1505
                    const char *lock_token, apr_pool_t *pool)
1391
1506
{
1392
 
  return write_path_info(baton, path, NULL, rev, depth, start_empty,
1393
 
                         lock_token, pool);
 
1507
  return svn_error_trace(
 
1508
            write_path_info(baton, path, NULL, rev, depth, start_empty,
 
1509
                            lock_token, pool));
1394
1510
}
1395
1511
 
1396
1512
svn_error_t *
1403
1519
    return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
1404
1520
                            _("Depth 'exclude' not supported for link"));
1405
1521
 
1406
 
  return write_path_info(baton, path, link_path, rev, depth,
1407
 
                         start_empty, lock_token, pool);
 
1522
  return svn_error_trace(
 
1523
            write_path_info(baton, path, link_path, rev, depth,
 
1524
                            start_empty, lock_token, pool));
1408
1525
}
1409
1526
 
1410
1527
svn_error_t *
1412
1529
{
1413
1530
  /* We pass svn_depth_infinity because deletion of a path always
1414
1531
     deletes everything underneath it. */
1415
 
  return write_path_info(baton, path, NULL, SVN_INVALID_REVNUM,
1416
 
                         svn_depth_infinity, FALSE, NULL, pool);
 
1532
  return svn_error_trace(
 
1533
            write_path_info(baton, path, NULL, SVN_INVALID_REVNUM,
 
1534
                            svn_depth_infinity, FALSE, NULL, pool));
1417
1535
}
1418
1536
 
1419
1537
svn_error_t *
1420
1538
svn_repos_finish_report(void *baton, apr_pool_t *pool)
1421
1539
{
1422
1540
  report_baton_t *b = baton;
1423
 
  svn_error_t *finish_err, *close_err;
1424
 
 
1425
 
  finish_err = finish_report(b, pool);
1426
 
  close_err = svn_io_file_close(b->tempfile, pool);
1427
 
 
1428
 
  return svn_error_trace(svn_error_compose_create(finish_err, close_err));
 
1541
 
 
1542
  return svn_error_trace(finish_report(b, pool));
1429
1543
}
1430
1544
 
1431
1545
svn_error_t *
1432
1546
svn_repos_abort_report(void *baton, apr_pool_t *pool)
1433
1547
{
1434
 
  report_baton_t *b = baton;
1435
 
 
1436
 
  return svn_error_trace(svn_io_file_close(b->tempfile, pool));
 
1548
  return SVN_NO_ERROR;
1437
1549
}
1438
1550
 
1439
1551
/* --- BEGINNING THE REPORT --- */
1440
1552
 
1441
1553
 
1442
1554
svn_error_t *
1443
 
svn_repos_begin_report2(void **report_baton,
 
1555
svn_repos_begin_report3(void **report_baton,
1444
1556
                        svn_revnum_t revnum,
1445
1557
                        svn_repos_t *repos,
1446
1558
                        const char *fs_base,
1454
1566
                        void *edit_baton,
1455
1567
                        svn_repos_authz_func_t authz_read_func,
1456
1568
                        void *authz_read_baton,
 
1569
                        apr_size_t zero_copy_limit,
1457
1570
                        apr_pool_t *pool)
1458
1571
{
1459
1572
  report_baton_t *b;
 
1573
  const char *uuid;
1460
1574
 
1461
1575
  if (depth == svn_depth_exclude)
1462
1576
    return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL,
1463
1577
                            _("Request depth 'exclude' not supported"));
1464
1578
 
 
1579
  SVN_ERR(svn_fs_get_uuid(repos->fs, &uuid, pool));
 
1580
 
1465
1581
  /* Build a reporter baton.  Copy strings in case the caller doesn't
1466
1582
     keep track of them. */
1467
1583
  b = apr_palloc(pool, sizeof(*b));
1472
1588
  b->t_path = switch_path ? svn_fspath__canonicalize(switch_path, pool)
1473
1589
                          : svn_fspath__join(b->fs_base, s_operand, pool);
1474
1590
  b->text_deltas = text_deltas;
 
1591
  b->zero_copy_limit = zero_copy_limit;
1475
1592
  b->requested_depth = depth;
1476
1593
  b->ignore_ancestry = ignore_ancestry;
1477
1594
  b->send_copyfrom_args = send_copyfrom_args;
1482
1599
  b->authz_read_baton = authz_read_baton;
1483
1600
  b->revision_infos = apr_hash_make(pool);
1484
1601
  b->pool = pool;
1485
 
 
1486
 
  SVN_ERR(svn_io_open_unique_file3(&b->tempfile, NULL, NULL,
1487
 
                                   svn_io_file_del_on_pool_cleanup,
1488
 
                                   pool, pool));
 
1602
  b->reader = svn_spillbuf__reader_create(1000 /* blocksize */,
 
1603
                                          1000000 /* maxsize */,
 
1604
                                          pool);
 
1605
  b->repos_uuid = svn_string_create(uuid, pool);
1489
1606
 
1490
1607
  /* Hand reporter back to client. */
1491
1608
  *report_baton = b;