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
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 *
svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t * queue)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 *
svn_wc__prop_array_to_hash(const apr_array_header_t * props,apr_pool_t * result_pool)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 *
svn_wc_committed_queue_create(apr_pool_t * pool)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 *
svn_wc_queue_committed4(svn_wc_committed_queue_t * queue,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t recurse,svn_boolean_t is_committed,const apr_array_header_t * wcprop_changes,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * sha1_checksum,apr_pool_t * scratch_pool)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 *
svn_wc_process_committed_queue2(svn_wc_committed_queue_t * queue,svn_wc_context_t * wc_ctx,svn_revnum_t new_revnum,const char * rev_date,const char * rev_author,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)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 *
add_from_disk(svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t kind,const apr_hash_t * props,apr_pool_t * scratch_pool)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 *
check_can_add_to_parent(const char ** repos_root_url,const char ** repos_uuid,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
check_can_add_node(svn_node_kind_t * kind_p,svn_boolean_t * db_row_exists_p,svn_boolean_t * is_wc_root_p,svn_wc__db_t * db,const char * local_abspath,const char * copyfrom_url,svn_revnum_t copyfrom_rev,apr_pool_t * scratch_pool)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 *
integrate_nested_wc_as_copy(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)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 *
svn_wc_add4(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_depth_t depth,const char * copyfrom_url,svn_revnum_t copyfrom_rev,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)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 *
svn_wc_add_from_disk3(svn_wc_context_t * wc_ctx,const char * local_abspath,const apr_hash_t * props,svn_boolean_t skip_checks,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)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
756 /* Return a path where nothing exists on disk, within the admin directory
757 belonging to the WCROOT_ABSPATH directory. */
758 static const char *
nonexistent_path(const char * wcroot_abspath,apr_pool_t * scratch_pool)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 *
svn_wc_get_pristine_copy_path(const char * path,const char ** pristine_path,apr_pool_t * pool)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 *
svn_wc_get_pristine_contents2(svn_stream_t ** contents,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
get_pristine_lazyopen_func(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
svn_wc__get_pristine_contents_by_checksum(svn_stream_t ** contents,svn_wc_context_t * wc_ctx,const char * wri_abspath,const svn_checksum_t * checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
svn_wc_add_lock2(svn_wc_context_t * wc_ctx,const char * local_abspath,const svn_lock_t * lock,apr_pool_t * scratch_pool)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 *
svn_wc_remove_lock2(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)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 *
svn_wc_set_changelist2(svn_wc_context_t * wc_ctx,const char * local_abspath,const char * new_changelist,svn_depth_t depth,const apr_array_header_t * changelist_filter,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)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 *
get_node_changelist(const char * local_abspath,svn_node_kind_t kind,void * baton,apr_pool_t * scratch_pool)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 *
svn_wc_get_changelists(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_depth_t depth,const apr_array_header_t * changelist_filter,svn_changelist_receiver_t callback_func,void * callback_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)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
svn_wc__internal_changelist_match(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * clhash,apr_pool_t * scratch_pool)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
svn_wc__changelist_match(svn_wc_context_t * wc_ctx,const char * local_abspath,const apr_hash_t * clhash,apr_pool_t * scratch_pool)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 }
1106