]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_wc/adm_ops.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_wc / adm_ops.c
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 \f
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
62 struct 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
70 typedef 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
86 apr_pool_t *
87 svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
88 {
89   return queue->pool;
90 }
91
92 apr_hash_t *
93 svn_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
115 svn_wc_committed_queue_t *
116 svn_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
128 svn_error_t *
129 svn_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
175 svn_error_t *
176 svn_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  */
248 static svn_error_t *
249 add_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. */
284 static svn_error_t *
285 check_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  */
366 static svn_error_t *
367 check_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. */
503 static svn_error_t *
504 integrate_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
561 svn_error_t *
562 svn_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
710 svn_error_t *
711 svn_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 \f
756 /* Return a path where nothing exists on disk, within the admin directory
757    belonging to the WCROOT_ABSPATH directory.  */
758 static const char *
759 nonexistent_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
766 svn_error_t *
767 svn_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
802 svn_error_t *
803 svn_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
817 typedef 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 */
827 static svn_error_t *
828 get_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
851 svn_error_t *
852 svn_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
884 svn_error_t *
885 svn_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
939 svn_error_t *
940 svn_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
978 svn_error_t *
979 svn_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
1004 struct 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
1013 static svn_error_t *
1014 get_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
1037 svn_error_t *
1038 svn_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
1069 svn_boolean_t
1070 svn_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
1097 svn_boolean_t
1098 svn_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 }