adm_ops.c revision 299742
1/*
2 * adm_ops.c: routines for affecting working copy administrative
3 *            information.  NOTE: this code doesn't know where the adm
4 *            info is actually stored.  Instead, generic handles to
5 *            adm data are requested via a reference to some PATH
6 *            (PATH being a regular, non-administrative directory or
7 *            file in the working copy).
8 *
9 * ====================================================================
10 *    Licensed to the Apache Software Foundation (ASF) under one
11 *    or more contributor license agreements.  See the NOTICE file
12 *    distributed with this work for additional information
13 *    regarding copyright ownership.  The ASF licenses this file
14 *    to you under the Apache License, Version 2.0 (the
15 *    "License"); you may not use this file except in compliance
16 *    with the License.  You may obtain a copy of the License at
17 *
18 *      http://www.apache.org/licenses/LICENSE-2.0
19 *
20 *    Unless required by applicable law or agreed to in writing,
21 *    software distributed under the License is distributed on an
22 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23 *    KIND, either express or implied.  See the License for the
24 *    specific language governing permissions and limitations
25 *    under the License.
26 * ====================================================================
27 */
28
29
30
31#include <string.h>
32#include <stdlib.h>
33
34#include <apr_pools.h>
35#include <apr_hash.h>
36#include <apr_time.h>
37#include <apr_errno.h>
38
39#include "svn_private_config.h"
40#include "svn_types.h"
41#include "svn_pools.h"
42#include "svn_string.h"
43#include "svn_error.h"
44#include "svn_dirent_uri.h"
45#include "svn_path.h"
46#include "svn_hash.h"
47#include "svn_wc.h"
48#include "svn_io.h"
49#include "svn_time.h"
50#include "svn_sorts.h"
51
52#include "wc.h"
53#include "adm_files.h"
54#include "conflicts.h"
55#include "workqueue.h"
56
57#include "private/svn_dep_compat.h"
58#include "private/svn_sorts_private.h"
59#include "private/svn_subr_private.h"
60
61
62struct svn_wc_committed_queue_t
63{
64  /* The pool in which ->queue is allocated. */
65  apr_pool_t *pool;
66  /* Mapping (const char *) wcroot_abspath to svn_wc__db_commit_queue_t * */
67  apr_hash_t *wc_queues;
68};
69
70typedef struct committed_queue_item_t
71{
72  const char *local_abspath;
73  svn_boolean_t recurse; /* Use legacy recursion */
74  svn_boolean_t committed; /* Process the node as committed */
75  svn_boolean_t remove_lock; /* Remove existing lock on node */
76  svn_boolean_t remove_changelist; /* Remove changelist on node */
77
78  /* The pristine text checksum. NULL if the old value should be kept
79     and for directories */
80  const svn_checksum_t *new_sha1_checksum;
81
82  apr_hash_t *new_dav_cache; /* New DAV cache for the node */
83} committed_queue_item_t;
84
85
86apr_pool_t *
87svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
88{
89  return queue->pool;
90}
91
92apr_hash_t *
93svn_wc__prop_array_to_hash(const apr_array_header_t *props,
94                           apr_pool_t *result_pool)
95{
96  int i;
97  apr_hash_t *prophash;
98
99  if (props == NULL || props->nelts == 0)
100    return NULL;
101
102  prophash = apr_hash_make(result_pool);
103
104  for (i = 0; i < props->nelts; i++)
105    {
106      const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *);
107      if (prop->value != NULL)
108        svn_hash_sets(prophash, prop->name, prop->value);
109    }
110
111  return prophash;
112}
113
114
115svn_wc_committed_queue_t *
116svn_wc_committed_queue_create(apr_pool_t *pool)
117{
118  svn_wc_committed_queue_t *q;
119
120  q = apr_palloc(pool, sizeof(*q));
121  q->pool = pool;
122  q->wc_queues = apr_hash_make(pool);
123
124  return q;
125}
126
127
128svn_error_t *
129svn_wc_queue_committed4(svn_wc_committed_queue_t *queue,
130                        svn_wc_context_t *wc_ctx,
131                        const char *local_abspath,
132                        svn_boolean_t recurse,
133                        svn_boolean_t is_committed,
134                        const apr_array_header_t *wcprop_changes,
135                        svn_boolean_t remove_lock,
136                        svn_boolean_t remove_changelist,
137                        const svn_checksum_t *sha1_checksum,
138                        apr_pool_t *scratch_pool)
139{
140  const char *wcroot_abspath;
141  svn_wc__db_commit_queue_t *db_queue;
142
143  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
144
145  /* Use the same pool as the one QUEUE was allocated in,
146     to prevent lifetime issues.  Intermediate operations
147     should use SCRATCH_POOL. */
148
149  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
150                                wc_ctx->db, local_abspath,
151                                scratch_pool, scratch_pool));
152
153  db_queue = svn_hash_gets(queue->wc_queues, wcroot_abspath);
154  if (! db_queue)
155    {
156      wcroot_abspath = apr_pstrdup(queue->pool, wcroot_abspath);
157
158      SVN_ERR(svn_wc__db_create_commit_queue(&db_queue,
159                                             wc_ctx->db, wcroot_abspath,
160                                             queue->pool, scratch_pool));
161
162      svn_hash_sets(queue->wc_queues, wcroot_abspath, db_queue);
163    }
164
165  return svn_error_trace(
166          svn_wc__db_commit_queue_add(db_queue, local_abspath, recurse,
167                                      is_committed, remove_lock,
168                                      remove_changelist, sha1_checksum,
169                                      svn_wc__prop_array_to_hash(wcprop_changes,
170                                                                 queue->pool),
171                                      queue->pool, scratch_pool));
172}
173
174
175svn_error_t *
176svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
177                                svn_wc_context_t *wc_ctx,
178                                svn_revnum_t new_revnum,
179                                const char *rev_date,
180                                const char *rev_author,
181                                svn_cancel_func_t cancel_func,
182                                void *cancel_baton,
183                                apr_pool_t *scratch_pool)
184{
185  apr_array_header_t *wcs;
186  int i;
187  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
188  apr_time_t new_date;
189
190  if (rev_date)
191    SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool));
192  else
193    new_date = 0;
194
195  /* Process the wc's in order of their paths. */
196  wcs = svn_sort__hash(queue->wc_queues, svn_sort_compare_items_as_paths,
197                       scratch_pool);
198  for (i = 0; i < wcs->nelts; i++)
199    {
200      const svn_sort__item_t *sort_item
201                                = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t);
202      svn_wc__db_commit_queue_t *db_queue = sort_item->value;
203
204      svn_pool_clear(iterpool);
205
206      SVN_ERR(svn_wc__db_process_commit_queue(wc_ctx->db, db_queue,
207                                              new_revnum, new_date, rev_author,
208                                              iterpool));
209    }
210
211  /* Make sure nothing happens if this function is called again.  */
212  apr_hash_clear(queue->wc_queues);
213
214  /* Ok; everything is committed now. Now we can start calling callbacks */
215  if (cancel_func)
216    SVN_ERR(cancel_func(cancel_baton));
217
218  for (i = 0; i < wcs->nelts; i++)
219    {
220      const svn_sort__item_t *sort_item
221          = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t);
222      const char *wcroot_abspath = sort_item->key;
223
224      svn_pool_clear(iterpool);
225
226      SVN_ERR(svn_wc__wq_run(wc_ctx->db, wcroot_abspath,
227                             cancel_func, cancel_baton,
228                             iterpool));
229    }
230
231  svn_pool_destroy(iterpool);
232
233  return SVN_NO_ERROR;
234}
235
236/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
237 * its parent directory in the WC.  It will have the regular properties
238 * provided in PROPS, or none if that is NULL.
239 *
240 * If the node is a file, set its on-disk executable and read-only bits to
241 * match its properties and lock state,
242 * ### only if it has an svn:executable or svn:needs-lock property.
243 * ### This is to match the previous behaviour of setting its props
244 *     afterwards by calling svn_wc_prop_set4(), but is not very clean.
245 *
246 * Sync the on-disk executable and read-only bits accordingly.
247 */
248static svn_error_t *
249add_from_disk(svn_wc__db_t *db,
250              const char *local_abspath,
251              svn_node_kind_t kind,
252              const apr_hash_t *props,
253              apr_pool_t *scratch_pool)
254{
255  if (kind == svn_node_file)
256    {
257      svn_skel_t *work_item = NULL;
258
259      if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE)
260                    || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK)))
261        SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
262                                                 scratch_pool, scratch_pool));
263
264      SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item,
265                                     scratch_pool));
266      if (work_item)
267        SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
268    }
269  else
270    {
271      SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL,
272                                          scratch_pool));
273    }
274
275  return SVN_NO_ERROR;
276}
277
278
279/* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of
280   LOCAL_ABSPATH.  REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not
281   wanted.  Check that the parent of LOCAL_ABSPATH is a versioned directory
282   in a state in which a new child node can be scheduled for addition;
283   return an error if not. */
284static svn_error_t *
285check_can_add_to_parent(const char **repos_root_url,
286                        const char **repos_uuid,
287                        svn_wc__db_t *db,
288                        const char *local_abspath,
289                        apr_pool_t *result_pool,
290                        apr_pool_t *scratch_pool)
291{
292  const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
293  svn_wc__db_status_t parent_status;
294  svn_node_kind_t parent_kind;
295  svn_error_t *err;
296
297  SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
298
299  err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
300                             NULL, repos_root_url, repos_uuid, NULL, NULL,
301                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
302                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
303                             NULL, NULL, NULL,
304                             db, parent_abspath, result_pool, scratch_pool);
305
306  if (err
307      || parent_status == svn_wc__db_status_not_present
308      || parent_status == svn_wc__db_status_excluded
309      || parent_status == svn_wc__db_status_server_excluded)
310    {
311      return
312        svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
313                          _("Can't find parent directory's node while"
314                            " trying to add '%s'"),
315                          svn_dirent_local_style(local_abspath,
316                                                 scratch_pool));
317    }
318  else if (parent_status == svn_wc__db_status_deleted)
319    {
320      return
321        svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
322                          _("Can't add '%s' to a parent directory"
323                            " scheduled for deletion"),
324                          svn_dirent_local_style(local_abspath,
325                                                 scratch_pool));
326    }
327  else if (parent_kind != svn_node_dir)
328    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
329                             _("Can't schedule an addition of '%s'"
330                               " below a not-directory node"),
331                             svn_dirent_local_style(local_abspath,
332                                                    scratch_pool));
333
334  /* If we haven't found the repository info yet, find it now. */
335  if ((repos_root_url && ! *repos_root_url)
336      || (repos_uuid && ! *repos_uuid))
337    {
338      if (parent_status == svn_wc__db_status_added)
339        SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
340                                         repos_root_url, repos_uuid, NULL,
341                                         NULL, NULL, NULL,
342                                         db, parent_abspath,
343                                         result_pool, scratch_pool));
344      else
345        SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL,
346                                         repos_root_url, repos_uuid, NULL,
347                                         NULL, NULL, NULL, NULL, NULL, NULL,
348                                         NULL, NULL, NULL,
349                                         db, parent_abspath,
350                                         result_pool, scratch_pool));
351    }
352
353  return SVN_NO_ERROR;
354}
355
356
357/* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for
358 * addition to its WC parent directory.
359 *
360 * Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether
361 * it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's
362 * a WC root.
363 *
364 * ### The checks here, and the outputs, are geared towards svn_wc_add4().
365 */
366static svn_error_t *
367check_can_add_node(svn_node_kind_t *kind_p,
368                   svn_boolean_t *db_row_exists_p,
369                   svn_boolean_t *is_wc_root_p,
370                   svn_wc__db_t *db,
371                   const char *local_abspath,
372                   const char *copyfrom_url,
373                   svn_revnum_t copyfrom_rev,
374                   apr_pool_t *scratch_pool)
375{
376  const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
377  svn_boolean_t is_wc_root;
378  svn_node_kind_t kind;
379  svn_boolean_t is_special;
380
381  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
382  SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url,
383                                                        scratch_pool)
384                                   && SVN_IS_VALID_REVNUM(copyfrom_rev)));
385
386  /* Check that the proposed node has an acceptable name. */
387  if (svn_wc_is_adm_dir(base_name, scratch_pool))
388    return svn_error_createf
389      (SVN_ERR_ENTRY_FORBIDDEN, NULL,
390       _("Can't create an entry with a reserved name while trying to add '%s'"),
391       svn_dirent_local_style(local_abspath, scratch_pool));
392
393  SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
394
395  /* Make sure something's there; set KIND and *KIND_P. */
396  SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
397                                    scratch_pool));
398  if (kind == svn_node_none)
399    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
400                             _("'%s' not found"),
401                             svn_dirent_local_style(local_abspath,
402                                                    scratch_pool));
403  if (kind == svn_node_unknown)
404    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
405                             _("Unsupported node kind for path '%s'"),
406                             svn_dirent_local_style(local_abspath,
407                                                    scratch_pool));
408  if (kind_p)
409    *kind_p = kind;
410
411  /* Determine whether a DB row for this node EXISTS, and whether it
412     IS_WC_ROOT.  If it exists, check that it is in an acceptable state for
413     adding the new node; if not, return an error. */
414  {
415    svn_wc__db_status_t status;
416    svn_boolean_t conflicted;
417    svn_boolean_t exists;
418    svn_error_t *err
419      = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
420                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
421                             NULL, NULL, NULL, NULL, NULL, NULL,
422                             &conflicted,
423                             NULL, NULL, NULL, NULL, NULL, NULL,
424                             db, local_abspath,
425                             scratch_pool, scratch_pool);
426
427    if (err)
428      {
429        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
430          return svn_error_trace(err);
431
432        svn_error_clear(err);
433        exists = FALSE;
434        is_wc_root = FALSE;
435      }
436    else
437      {
438        is_wc_root = FALSE;
439        exists = TRUE;
440
441        /* Note that the node may be in conflict even if it does not
442         * exist on disk (certain tree conflict scenarios). */
443        if (conflicted)
444          return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
445                                   _("'%s' is an existing item in conflict; "
446                                   "please mark the conflict as resolved "
447                                   "before adding a new item here"),
448                                   svn_dirent_local_style(local_abspath,
449                                                          scratch_pool));
450        switch (status)
451          {
452            case svn_wc__db_status_not_present:
453              break;
454            case svn_wc__db_status_deleted:
455              /* A working copy root should never have a WORKING_NODE */
456              SVN_ERR_ASSERT(!is_wc_root);
457              break;
458            case svn_wc__db_status_normal:
459              SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath,
460                                           scratch_pool));
461
462              if (is_wc_root && copyfrom_url)
463                {
464                  /* Integrate a sub working copy in a parent working copy
465                     (legacy behavior) */
466                  break;
467                }
468              else if (is_wc_root && is_special)
469                {
470                  /* Adding a symlink to a working copy root.
471                     (special_tests.py 23: externals as symlink targets) */
472                  break;
473                }
474              /* else: Fall through in default error */
475
476            default:
477              return svn_error_createf(
478                               SVN_ERR_ENTRY_EXISTS, NULL,
479                               _("'%s' is already under version control"),
480                               svn_dirent_local_style(local_abspath,
481                                                      scratch_pool));
482          }
483      } /* err */
484
485    if (db_row_exists_p)
486      *db_row_exists_p = exists;
487    if (is_wc_root_p)
488      *is_wc_root_p = is_wc_root;
489  }
490
491  return SVN_NO_ERROR;
492}
493
494
495/* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into
496 * a copied subtree in the outer working copy.
497 *
498 * LOCAL_ABSPATH must be the root of a nested working copy that has no
499 * local modifications.  The parent directory of LOCAL_ABSPATH must be a
500 * versioned directory in the outer WC, and must belong to the same
501 * repository as the nested WC.  The nested WC will be integrated into the
502 * parent's WC, and will no longer be a separate WC. */
503static svn_error_t *
504integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx,
505                            const char *local_abspath,
506                            apr_pool_t *scratch_pool)
507{
508  svn_wc__db_t *db = wc_ctx->db;
509  const char *moved_abspath;
510
511  /* Drop any references to the wc that is to be rewritten */
512  SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
513
514  /* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */
515  {
516    const char *tmpdir_abspath;
517    const char *moved_adm_abspath;
518    const char *adm_abspath;
519
520    SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
521                                           svn_dirent_dirname(local_abspath,
522                                                              scratch_pool),
523                                           scratch_pool, scratch_pool));
524    SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
525                                     svn_io_file_del_on_close,
526                                     scratch_pool, scratch_pool));
527    SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
528
529    adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool);
530    moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
531    SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
532  }
533
534  /* Copy entries from temporary location into the main db */
535  SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
536                       TRUE /* metadata_only */,
537                       NULL, NULL, NULL, NULL, scratch_pool));
538
539  /* Cleanup the temporary admin dir */
540  SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
541  SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
542                             scratch_pool));
543
544  /* The subdir is now part of our parent working copy. Our caller assumes
545     that we return the new node locked, so obtain a lock if we didn't
546     receive the lock via our depth infinity lock */
547  {
548    svn_boolean_t owns_lock;
549
550    SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
551                                        FALSE, scratch_pool));
552    if (!owns_lock)
553      SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
554                                       scratch_pool));
555  }
556
557  return SVN_NO_ERROR;
558}
559
560
561svn_error_t *
562svn_wc_add4(svn_wc_context_t *wc_ctx,
563            const char *local_abspath,
564            svn_depth_t depth,
565            const char *copyfrom_url,
566            svn_revnum_t copyfrom_rev,
567            svn_cancel_func_t cancel_func,
568            void *cancel_baton,
569            svn_wc_notify_func2_t notify_func,
570            void *notify_baton,
571            apr_pool_t *scratch_pool)
572{
573  svn_wc__db_t *db = wc_ctx->db;
574  svn_node_kind_t kind;
575  svn_boolean_t db_row_exists;
576  svn_boolean_t is_wc_root;
577  const char *repos_root_url;
578  const char *repos_uuid;
579
580  SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root,
581                             db, local_abspath, copyfrom_url, copyfrom_rev,
582                             scratch_pool));
583
584  /* Get REPOS_ROOT_URL and REPOS_UUID.  Check that the
585     parent is a versioned directory in an acceptable state. */
586  SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid,
587                                  db, local_abspath, scratch_pool,
588                                  scratch_pool));
589
590  /* If we're performing a repos-to-WC copy, check that the copyfrom
591     repository is the same as the parent dir's repository. */
592  if (copyfrom_url && !svn_uri__is_ancestor(repos_root_url, copyfrom_url))
593    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
594                             _("The URL '%s' has a different repository "
595                               "root than its parent"), copyfrom_url);
596
597  /* Verify that we can actually integrate the inner working copy */
598  if (is_wc_root)
599    {
600      const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid;
601      const char *inner_url;
602
603      SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath,
604                                       &inner_repos_root_url,
605                                       &inner_repos_uuid, NULL, NULL, NULL,
606                                       NULL, NULL, NULL, NULL, NULL, NULL,
607                                       NULL,
608                                       db, local_abspath,
609                                       scratch_pool, scratch_pool));
610
611      if (strcmp(inner_repos_uuid, repos_uuid)
612          || strcmp(repos_root_url, inner_repos_root_url))
613        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
614                                 _("Can't schedule the working copy at '%s' "
615                                   "from repository '%s' with uuid '%s' "
616                                   "for addition under a working copy from "
617                                   "repository '%s' with uuid '%s'."),
618                                 svn_dirent_local_style(local_abspath,
619                                                        scratch_pool),
620                                 inner_repos_root_url, inner_repos_uuid,
621                                 repos_root_url, repos_uuid);
622
623      inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
624                                              scratch_pool);
625
626      if (strcmp(copyfrom_url, inner_url))
627        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
628                                 _("Can't add '%s' with URL '%s', but with "
629                                   "the data from '%s'"),
630                                 svn_dirent_local_style(local_abspath,
631                                                        scratch_pool),
632                                 copyfrom_url, inner_url);
633    }
634
635  if (!copyfrom_url)  /* Case 2a: It's a simple add */
636    {
637      SVN_ERR(add_from_disk(db, local_abspath, kind, NULL,
638                            scratch_pool));
639      if (kind == svn_node_dir && !db_row_exists)
640        {
641          /* If using the legacy 1.6 interface the parent lock may not
642             be recursive and add is expected to lock the new dir.
643
644             ### Perhaps the lock should be created in the same
645             transaction that adds the node? */
646          svn_boolean_t owns_lock;
647
648          SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
649                                              FALSE, scratch_pool));
650          if (!owns_lock)
651            SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
652                                             scratch_pool));
653        }
654    }
655  else if (!is_wc_root)  /* Case 2b: It's a copy from the repository */
656    {
657      if (kind == svn_node_file)
658        {
659          /* This code should never be used, as it doesn't install proper
660             pristine and/or properties. But it was not an error in the old
661             version of this function.
662
663             ===> Use svn_wc_add_repos_file4() directly! */
664          svn_stream_t *content = svn_stream_empty(scratch_pool);
665
666          SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath,
667                                         content, NULL, NULL, NULL,
668                                         copyfrom_url, copyfrom_rev,
669                                         cancel_func, cancel_baton,
670                                         scratch_pool));
671        }
672      else
673        {
674          const char *repos_relpath =
675            svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
676
677          SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath,
678                                         apr_hash_make(scratch_pool),
679                                         copyfrom_rev, 0, NULL,
680                                         repos_relpath,
681                                         repos_root_url, repos_uuid,
682                                         copyfrom_rev,
683                                         NULL /* children */, depth,
684                                         FALSE /* is_move */,
685                                         NULL /* conflicts */,
686                                         NULL /* work items */,
687                                         scratch_pool));
688        }
689    }
690  else  /* Case 1: Integrating a separate WC into this one, in place */
691    {
692      SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
693                                          scratch_pool));
694    }
695
696  /* Report the addition to the caller. */
697  if (notify_func != NULL)
698    {
699      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
700                                                     svn_wc_notify_add,
701                                                     scratch_pool);
702      notify->kind = kind;
703      (*notify_func)(notify_baton, notify, scratch_pool);
704    }
705
706  return SVN_NO_ERROR;
707}
708
709
710svn_error_t *
711svn_wc_add_from_disk3(svn_wc_context_t *wc_ctx,
712                      const char *local_abspath,
713                      const apr_hash_t *props,
714                      svn_boolean_t skip_checks,
715                      svn_wc_notify_func2_t notify_func,
716                      void *notify_baton,
717                      apr_pool_t *scratch_pool)
718{
719  svn_node_kind_t kind;
720
721  SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath,
722                             NULL, SVN_INVALID_REVNUM, scratch_pool));
723  SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
724                                  scratch_pool, scratch_pool));
725
726  /* Canonicalize and check the props */
727  if (props)
728    {
729      apr_hash_t *new_props;
730
731      SVN_ERR(svn_wc__canonicalize_props(
732                &new_props,
733                local_abspath, kind, props, skip_checks,
734                scratch_pool, scratch_pool));
735      props = new_props;
736    }
737
738  /* Add to the DB and maybe update on-disk executable read-only bits */
739  SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
740                        scratch_pool));
741
742  /* Report the addition to the caller. */
743  if (notify_func != NULL)
744    {
745      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
746                                                     svn_wc_notify_add,
747                                                     scratch_pool);
748      notify->kind = kind;
749      notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
750      (*notify_func)(notify_baton, notify, scratch_pool);
751    }
752
753  return SVN_NO_ERROR;
754}
755
756/* Return a path where nothing exists on disk, within the admin directory
757   belonging to the WCROOT_ABSPATH directory.  */
758static const char *
759nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool)
760{
761  return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH,
762                           scratch_pool);
763}
764
765
766svn_error_t *
767svn_wc_get_pristine_copy_path(const char *path,
768                              const char **pristine_path,
769                              apr_pool_t *pool)
770{
771  svn_wc__db_t *db;
772  const char *local_abspath;
773  svn_error_t *err;
774
775  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
776
777  SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
778  /* DB is now open. This is seemingly a "light" function that a caller
779     may use repeatedly despite error return values. The rest of this
780     function should aggressively close DB, even in the error case.  */
781
782  err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath,
783                                       pool, pool);
784  if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
785    {
786      /* The node doesn't exist, so return a non-existent path located
787         in WCROOT/.svn/  */
788      const char *wcroot_abspath;
789
790      svn_error_clear(err);
791
792      err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath,
793                                  pool, pool);
794      if (err == NULL)
795        *pristine_path = nonexistent_path(wcroot_abspath, pool);
796    }
797
798   return svn_error_compose_create(err, svn_wc__db_close(db));
799}
800
801
802svn_error_t *
803svn_wc_get_pristine_contents2(svn_stream_t **contents,
804                              svn_wc_context_t *wc_ctx,
805                              const char *local_abspath,
806                              apr_pool_t *result_pool,
807                              apr_pool_t *scratch_pool)
808{
809  return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL,
810                                                       wc_ctx->db,
811                                                       local_abspath,
812                                                       result_pool,
813                                                       scratch_pool));
814}
815
816
817typedef struct get_pristine_lazyopen_baton_t
818{
819  svn_wc_context_t *wc_ctx;
820  const char *wri_abspath;
821  const svn_checksum_t *checksum;
822
823} get_pristine_lazyopen_baton_t;
824
825
826/* Implements svn_stream_lazyopen_func_t */
827static svn_error_t *
828get_pristine_lazyopen_func(svn_stream_t **stream,
829                           void *baton,
830                           apr_pool_t *result_pool,
831                           apr_pool_t *scratch_pool)
832{
833  get_pristine_lazyopen_baton_t *b = baton;
834  const svn_checksum_t *sha1_checksum;
835
836  /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
837     we'll use it to lookup the SHA1. */
838  if (b->checksum->kind == svn_checksum_sha1)
839    sha1_checksum = b->checksum;
840  else
841    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
842                                         b->wri_abspath, b->checksum,
843                                         scratch_pool, scratch_pool));
844
845  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
846                                   b->wri_abspath, sha1_checksum,
847                                   result_pool, scratch_pool));
848  return SVN_NO_ERROR;
849}
850
851svn_error_t *
852svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
853                                          svn_wc_context_t *wc_ctx,
854                                          const char *wri_abspath,
855                                          const svn_checksum_t *checksum,
856                                          apr_pool_t *result_pool,
857                                          apr_pool_t *scratch_pool)
858{
859  svn_boolean_t present;
860
861  *contents = NULL;
862
863  SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
864                                    checksum, scratch_pool));
865
866  if (present)
867    {
868      get_pristine_lazyopen_baton_t *gpl_baton;
869
870      gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
871      gpl_baton->wc_ctx = wc_ctx;
872      gpl_baton->wri_abspath = wri_abspath;
873      gpl_baton->checksum = checksum;
874
875      *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
876                                             gpl_baton, FALSE, result_pool);
877    }
878
879  return SVN_NO_ERROR;
880}
881
882
883
884svn_error_t *
885svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
886                 const char *local_abspath,
887                 const svn_lock_t *lock,
888                 apr_pool_t *scratch_pool)
889{
890  svn_wc__db_lock_t db_lock;
891  svn_error_t *err;
892  const svn_string_t *needs_lock;
893
894  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
895
896  SVN_ERR(svn_wc__write_check(wc_ctx->db,
897                              svn_dirent_dirname(local_abspath, scratch_pool),
898                              scratch_pool));
899
900  db_lock.token = lock->token;
901  db_lock.owner = lock->owner;
902  db_lock.comment = lock->comment;
903  db_lock.date = lock->creation_date;
904  err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool);
905  if (err)
906    {
907      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
908        return svn_error_trace(err);
909
910      /* Remap the error.  */
911      svn_error_clear(err);
912      return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
913                               _("'%s' is not under version control"),
914                               svn_dirent_local_style(local_abspath,
915                                                      scratch_pool));
916    }
917
918  /* if svn:needs-lock is present, then make the file read-write. */
919  err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
920                                 SVN_PROP_NEEDS_LOCK, scratch_pool,
921                                 scratch_pool);
922
923  if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
924    {
925      /* The node has non wc representation (e.g. deleted), so
926         we don't want to touch the in-wc file */
927      svn_error_clear(err);
928      return SVN_NO_ERROR;
929    }
930  SVN_ERR(err);
931
932  if (needs_lock)
933    SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
934
935  return SVN_NO_ERROR;
936}
937
938
939svn_error_t *
940svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
941                    const char *local_abspath,
942                    apr_pool_t *scratch_pool)
943{
944  svn_error_t *err;
945  svn_skel_t *work_item;
946
947  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
948
949  SVN_ERR(svn_wc__write_check(wc_ctx->db,
950                              svn_dirent_dirname(local_abspath, scratch_pool),
951                              scratch_pool));
952
953  SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item,
954                                           wc_ctx->db, local_abspath,
955                                           scratch_pool, scratch_pool));
956
957  err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, work_item,
958                               scratch_pool);
959  if (err)
960    {
961      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
962        return svn_error_trace(err);
963
964      /* Remap the error.  */
965      svn_error_clear(err);
966      return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
967                               _("'%s' is not under version control"),
968                               svn_dirent_local_style(local_abspath,
969                                                      scratch_pool));
970    }
971
972  return svn_error_trace(svn_wc__wq_run(wc_ctx->db, local_abspath,
973                                        NULL, NULL /* cancel*/,
974                                        scratch_pool));
975}
976
977
978svn_error_t *
979svn_wc_set_changelist2(svn_wc_context_t *wc_ctx,
980                       const char *local_abspath,
981                       const char *new_changelist,
982                       svn_depth_t depth,
983                       const apr_array_header_t *changelist_filter,
984                       svn_cancel_func_t cancel_func,
985                       void *cancel_baton,
986                       svn_wc_notify_func2_t notify_func,
987                       void *notify_baton,
988                       apr_pool_t *scratch_pool)
989{
990  /* Assert that we aren't being asked to set an empty changelist. */
991  SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0'));
992
993  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
994
995  SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath,
996                                       new_changelist, changelist_filter,
997                                       depth, notify_func, notify_baton,
998                                       cancel_func, cancel_baton,
999                                       scratch_pool));
1000
1001  return SVN_NO_ERROR;
1002}
1003
1004struct get_cl_fn_baton
1005{
1006  svn_wc__db_t *db;
1007  apr_hash_t *clhash;
1008  svn_changelist_receiver_t callback_func;
1009  void *callback_baton;
1010};
1011
1012
1013static svn_error_t *
1014get_node_changelist(const char *local_abspath,
1015                    svn_node_kind_t kind,
1016                    void *baton,
1017                    apr_pool_t *scratch_pool)
1018{
1019  struct get_cl_fn_baton *b = baton;
1020  const char *changelist;
1021
1022  SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1023                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1024                               NULL, NULL, NULL, NULL, NULL, &changelist,
1025                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1026                               b->db, local_abspath,
1027                               scratch_pool, scratch_pool));
1028  if (!b->clhash
1029      || (changelist && svn_hash_gets(b->clhash, changelist) != NULL))
1030    SVN_ERR(b->callback_func(b->callback_baton, local_abspath,
1031                             changelist, scratch_pool));
1032
1033  return SVN_NO_ERROR;
1034}
1035
1036
1037svn_error_t *
1038svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
1039                       const char *local_abspath,
1040                       svn_depth_t depth,
1041                       const apr_array_header_t *changelist_filter,
1042                       svn_changelist_receiver_t callback_func,
1043                       void *callback_baton,
1044                       svn_cancel_func_t cancel_func,
1045                       void *cancel_baton,
1046                       apr_pool_t *scratch_pool)
1047{
1048  struct get_cl_fn_baton gnb;
1049
1050  gnb.db = wc_ctx->db;
1051  gnb.clhash = NULL;
1052  gnb.callback_func = callback_func;
1053  gnb.callback_baton = callback_baton;
1054
1055  if (changelist_filter)
1056    SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter,
1057                                       scratch_pool));
1058
1059  return svn_error_trace(
1060    svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
1061                                   changelist_filter, get_node_changelist,
1062                                   &gnb, depth,
1063                                   cancel_func, cancel_baton,
1064                                   scratch_pool));
1065
1066}
1067
1068
1069svn_boolean_t
1070svn_wc__internal_changelist_match(svn_wc__db_t *db,
1071                                  const char *local_abspath,
1072                                  const apr_hash_t *clhash,
1073                                  apr_pool_t *scratch_pool)
1074{
1075  svn_error_t *err;
1076  const char *changelist;
1077
1078  if (clhash == NULL)
1079    return TRUE;
1080
1081  err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1082                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1083                             NULL, NULL, NULL, NULL, NULL, &changelist,
1084                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1085                             db, local_abspath, scratch_pool, scratch_pool);
1086  if (err)
1087    {
1088      svn_error_clear(err);
1089      return FALSE;
1090    }
1091
1092  return (changelist
1093            && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
1094}
1095
1096
1097svn_boolean_t
1098svn_wc__changelist_match(svn_wc_context_t *wc_ctx,
1099                         const char *local_abspath,
1100                         const apr_hash_t *clhash,
1101                         apr_pool_t *scratch_pool)
1102{
1103  return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash,
1104                                           scratch_pool);
1105}
1106