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_types.h"
40 #include "svn_pools.h"
41 #include "svn_string.h"
42 #include "svn_error.h"
43 #include "svn_dirent_uri.h"
44 #include "svn_path.h"
45 #include "svn_hash.h"
46 #include "svn_wc.h"
47 #include "svn_io.h"
48 #include "svn_time.h"
49 #include "svn_sorts.h"
50
51 #include "wc.h"
52 #include "adm_files.h"
53 #include "conflicts.h"
54 #include "workqueue.h"
55
56 #include "svn_private_config.h"
57 #include "private/svn_subr_private.h"
58 #include "private/svn_dep_compat.h"
59
60
61 struct svn_wc_committed_queue_t
62 {
63 /* The pool in which ->queue is allocated. */
64 apr_pool_t *pool;
65 /* Mapping (const char *) local_abspath to (committed_queue_item_t *). */
66 apr_hash_t *queue;
67 /* Is any item in the queue marked as 'recursive'? */
68 svn_boolean_t have_recursive;
69 };
70
71 typedef struct committed_queue_item_t
72 {
73 const char *local_abspath;
74 svn_boolean_t recurse;
75 svn_boolean_t no_unlock;
76 svn_boolean_t keep_changelist;
77
78 /* The pristine text checksum. */
79 const svn_checksum_t *sha1_checksum;
80
81 apr_hash_t *new_dav_cache;
82 } committed_queue_item_t;
83
84
85 apr_pool_t *
svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t * queue)86 svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
87 {
88 return queue->pool;
89 }
90
91
92
93 /*** Finishing updates and commits. ***/
94
95 /* Queue work items that will finish a commit of the file or directory
96 * LOCAL_ABSPATH in DB:
97 * - queue the removal of any "revert-base" props and text files;
98 * - queue an update of the DB entry for this node
99 *
100 * ### The Pristine Store equivalent should be:
101 * - remember the old BASE_NODE and WORKING_NODE pristine text c'sums;
102 * - queue an update of the DB entry for this node (incl. updating the
103 * BASE_NODE c'sum and setting the WORKING_NODE c'sum to NULL);
104 * - queue deletion of the old pristine texts by the remembered checksums.
105 *
106 * CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must
107 * be provided if there is one, else NULL.
108 *
109 * STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of
110 * the node LOCAL_ABSPATH.
111 */
112 static svn_error_t *
process_committed_leaf(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t via_recurse,svn_wc__db_status_t status,svn_node_kind_t kind,svn_boolean_t prop_mods,const svn_checksum_t * old_checksum,svn_revnum_t new_revnum,apr_time_t new_changed_date,const char * new_changed_author,apr_hash_t * new_dav_cache,svn_boolean_t no_unlock,svn_boolean_t keep_changelist,const svn_checksum_t * checksum,apr_pool_t * scratch_pool)113 process_committed_leaf(svn_wc__db_t *db,
114 const char *local_abspath,
115 svn_boolean_t via_recurse,
116 svn_wc__db_status_t status,
117 svn_node_kind_t kind,
118 svn_boolean_t prop_mods,
119 const svn_checksum_t *old_checksum,
120 svn_revnum_t new_revnum,
121 apr_time_t new_changed_date,
122 const char *new_changed_author,
123 apr_hash_t *new_dav_cache,
124 svn_boolean_t no_unlock,
125 svn_boolean_t keep_changelist,
126 const svn_checksum_t *checksum,
127 apr_pool_t *scratch_pool)
128 {
129 svn_revnum_t new_changed_rev = new_revnum;
130 svn_skel_t *work_item = NULL;
131
132 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
133
134 {
135 const char *adm_abspath;
136
137 if (kind == svn_node_dir)
138 adm_abspath = local_abspath;
139 else
140 adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
141 SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
142 }
143
144 if (status == svn_wc__db_status_deleted)
145 {
146 return svn_error_trace(
147 svn_wc__db_base_remove(
148 db, local_abspath,
149 FALSE /* keep_as_working */,
150 FALSE /* queue_deletes */,
151 TRUE /* remove_locks */,
152 (! via_recurse)
153 ? new_revnum : SVN_INVALID_REVNUM,
154 NULL, NULL,
155 scratch_pool));
156 }
157 else if (status == svn_wc__db_status_not_present)
158 {
159 /* We are committing the leaf of a copy operation.
160 We leave the not-present marker to allow pulling in excluded
161 children of a copy.
162
163 The next update will remove the not-present marker. */
164
165 return SVN_NO_ERROR;
166 }
167
168 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
169 || status == svn_wc__db_status_incomplete
170 || status == svn_wc__db_status_added);
171
172 if (kind != svn_node_dir)
173 {
174 /* If we sent a delta (meaning: post-copy modification),
175 then this file will appear in the queue and so we should have
176 its checksum already. */
177 if (checksum == NULL)
178 {
179 /* It was copied and not modified. We must have a text
180 base for it. And the node should have a checksum. */
181 SVN_ERR_ASSERT(old_checksum != NULL);
182
183 checksum = old_checksum;
184
185 /* Is the node completely unmodified and are we recursing? */
186 if (via_recurse && !prop_mods)
187 {
188 /* If a copied node itself is not modified, but the op_root of
189 the copy is committed we have to make sure that changed_rev,
190 changed_date and changed_author don't change or the working
191 copy used for committing will show different last modified
192 information then a clean checkout of exactly the same
193 revisions. (Issue #3676) */
194
195 SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL,
196 NULL, &new_changed_rev,
197 &new_changed_date,
198 &new_changed_author, NULL, NULL,
199 NULL, NULL, NULL, NULL, NULL,
200 NULL, NULL, NULL, NULL,
201 NULL, NULL, NULL, NULL,
202 NULL, NULL, NULL,
203 db, local_abspath,
204 scratch_pool, scratch_pool));
205 }
206 }
207
208 SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
209 db, local_abspath,
210 prop_mods,
211 scratch_pool, scratch_pool));
212 }
213
214 /* The new text base will be found in the pristine store by its checksum. */
215 SVN_ERR(svn_wc__db_global_commit(db, local_abspath,
216 new_revnum, new_changed_rev,
217 new_changed_date, new_changed_author,
218 checksum,
219 NULL /* new_children */,
220 new_dav_cache,
221 keep_changelist,
222 no_unlock,
223 work_item,
224 scratch_pool));
225
226 return SVN_NO_ERROR;
227 }
228
229
230 svn_error_t *
svn_wc__process_committed_internal(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t recurse,svn_boolean_t top_of_recurse,svn_revnum_t new_revnum,apr_time_t new_date,const char * rev_author,apr_hash_t * new_dav_cache,svn_boolean_t no_unlock,svn_boolean_t keep_changelist,const svn_checksum_t * sha1_checksum,const svn_wc_committed_queue_t * queue,apr_pool_t * scratch_pool)231 svn_wc__process_committed_internal(svn_wc__db_t *db,
232 const char *local_abspath,
233 svn_boolean_t recurse,
234 svn_boolean_t top_of_recurse,
235 svn_revnum_t new_revnum,
236 apr_time_t new_date,
237 const char *rev_author,
238 apr_hash_t *new_dav_cache,
239 svn_boolean_t no_unlock,
240 svn_boolean_t keep_changelist,
241 const svn_checksum_t *sha1_checksum,
242 const svn_wc_committed_queue_t *queue,
243 apr_pool_t *scratch_pool)
244 {
245 svn_wc__db_status_t status;
246 svn_node_kind_t kind;
247 const svn_checksum_t *old_checksum;
248 svn_boolean_t prop_mods;
249
250 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
251 NULL, NULL, NULL, &old_checksum, NULL, NULL,
252 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
253 NULL, NULL, &prop_mods, NULL, NULL, NULL,
254 db, local_abspath,
255 scratch_pool, scratch_pool));
256
257 /* NOTE: be wary of making crazy semantic changes in this function, since
258 svn_wc_process_committed4() calls this. */
259
260 SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse,
261 status, kind, prop_mods, old_checksum,
262 new_revnum, new_date, rev_author,
263 new_dav_cache,
264 no_unlock, keep_changelist,
265 sha1_checksum,
266 scratch_pool));
267
268 /* Only check for recursion on nodes that have children */
269 if (kind != svn_node_file
270 || status == svn_wc__db_status_not_present
271 || status == svn_wc__db_status_excluded
272 || status == svn_wc__db_status_server_excluded
273 /* Node deleted -> then no longer a directory */
274 || status == svn_wc__db_status_deleted)
275 {
276 return SVN_NO_ERROR;
277 }
278
279 if (recurse)
280 {
281 const apr_array_header_t *children;
282 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
283 int i;
284
285 /* Read PATH's entries; this is the absolute path. */
286 SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
287 scratch_pool, iterpool));
288
289 /* Recursively loop over all children. */
290 for (i = 0; i < children->nelts; i++)
291 {
292 const char *name = APR_ARRAY_IDX(children, i, const char *);
293 const char *this_abspath;
294 const committed_queue_item_t *cqi;
295
296 svn_pool_clear(iterpool);
297
298 this_abspath = svn_dirent_join(local_abspath, name, iterpool);
299
300 sha1_checksum = NULL;
301 cqi = svn_hash_gets(queue->queue, this_abspath);
302
303 if (cqi != NULL)
304 sha1_checksum = cqi->sha1_checksum;
305
306 /* Recurse. Pass NULL for NEW_DAV_CACHE, because the
307 ones present in the current call are only applicable to
308 this one committed item. */
309 SVN_ERR(svn_wc__process_committed_internal(
310 db, this_abspath,
311 TRUE /* recurse */,
312 FALSE /* top_of_recurse */,
313 new_revnum, new_date,
314 rev_author,
315 NULL /* new_dav_cache */,
316 TRUE /* no_unlock */,
317 keep_changelist,
318 sha1_checksum,
319 queue,
320 iterpool));
321 }
322
323 svn_pool_destroy(iterpool);
324 }
325
326 return SVN_NO_ERROR;
327 }
328
329
330 apr_hash_t *
svn_wc__prop_array_to_hash(const apr_array_header_t * props,apr_pool_t * result_pool)331 svn_wc__prop_array_to_hash(const apr_array_header_t *props,
332 apr_pool_t *result_pool)
333 {
334 int i;
335 apr_hash_t *prophash;
336
337 if (props == NULL || props->nelts == 0)
338 return NULL;
339
340 prophash = apr_hash_make(result_pool);
341
342 for (i = 0; i < props->nelts; i++)
343 {
344 const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *);
345 if (prop->value != NULL)
346 svn_hash_sets(prophash, prop->name, prop->value);
347 }
348
349 return prophash;
350 }
351
352
353 svn_wc_committed_queue_t *
svn_wc_committed_queue_create(apr_pool_t * pool)354 svn_wc_committed_queue_create(apr_pool_t *pool)
355 {
356 svn_wc_committed_queue_t *q;
357
358 q = apr_palloc(pool, sizeof(*q));
359 q->pool = pool;
360 q->queue = apr_hash_make(pool);
361 q->have_recursive = FALSE;
362
363 return q;
364 }
365
366
367 svn_error_t *
svn_wc_queue_committed3(svn_wc_committed_queue_t * queue,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t recurse,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)368 svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
369 svn_wc_context_t *wc_ctx,
370 const char *local_abspath,
371 svn_boolean_t recurse,
372 const apr_array_header_t *wcprop_changes,
373 svn_boolean_t remove_lock,
374 svn_boolean_t remove_changelist,
375 const svn_checksum_t *sha1_checksum,
376 apr_pool_t *scratch_pool)
377 {
378 committed_queue_item_t *cqi;
379
380 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
381
382 queue->have_recursive |= recurse;
383
384 /* Use the same pool as the one QUEUE was allocated in,
385 to prevent lifetime issues. Intermediate operations
386 should use SCRATCH_POOL. */
387
388 /* Add to the array with paths and options */
389 cqi = apr_palloc(queue->pool, sizeof(*cqi));
390 cqi->local_abspath = local_abspath;
391 cqi->recurse = recurse;
392 cqi->no_unlock = !remove_lock;
393 cqi->keep_changelist = !remove_changelist;
394 cqi->sha1_checksum = sha1_checksum;
395 cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool);
396
397 svn_hash_sets(queue->queue, local_abspath, cqi);
398
399 return SVN_NO_ERROR;
400 }
401
402
403 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
404 processed recursively, return FALSE otherwise.
405
406 The algorithmic complexity of this search implementation is O(queue
407 length), but it's quite quick.
408 */
409 static svn_boolean_t
have_recursive_parent(apr_hash_t * queue,const committed_queue_item_t * item,apr_pool_t * scratch_pool)410 have_recursive_parent(apr_hash_t *queue,
411 const committed_queue_item_t *item,
412 apr_pool_t *scratch_pool)
413 {
414 apr_hash_index_t *hi;
415 const char *local_abspath = item->local_abspath;
416
417 for (hi = apr_hash_first(scratch_pool, queue); hi; hi = apr_hash_next(hi))
418 {
419 const committed_queue_item_t *qi = svn__apr_hash_index_val(hi);
420
421 if (qi == item)
422 continue;
423
424 if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath,
425 NULL))
426 return TRUE;
427 }
428
429 return FALSE;
430 }
431
432
433 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)434 svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
435 svn_wc_context_t *wc_ctx,
436 svn_revnum_t new_revnum,
437 const char *rev_date,
438 const char *rev_author,
439 svn_cancel_func_t cancel_func,
440 void *cancel_baton,
441 apr_pool_t *scratch_pool)
442 {
443 apr_array_header_t *sorted_queue;
444 int i;
445 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
446 apr_time_t new_date;
447 apr_hash_t *run_wqs = apr_hash_make(scratch_pool);
448 apr_hash_index_t *hi;
449
450 if (rev_date)
451 SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool));
452 else
453 new_date = 0;
454
455 /* Process the queued items in order of their paths. (The requirement is
456 * probably just that a directory must be processed before its children.) */
457 sorted_queue = svn_sort__hash(queue->queue, svn_sort_compare_items_as_paths,
458 scratch_pool);
459 for (i = 0; i < sorted_queue->nelts; i++)
460 {
461 const svn_sort__item_t *sort_item
462 = &APR_ARRAY_IDX(sorted_queue, i, svn_sort__item_t);
463 const committed_queue_item_t *cqi = sort_item->value;
464 const char *wcroot_abspath;
465
466 svn_pool_clear(iterpool);
467
468 /* Skip this item if it is a child of a recursive item, because it has
469 been (or will be) accounted for when that recursive item was (or
470 will be) processed. */
471 if (queue->have_recursive && have_recursive_parent(queue->queue, cqi,
472 iterpool))
473 continue;
474
475 SVN_ERR(svn_wc__process_committed_internal(
476 wc_ctx->db, cqi->local_abspath,
477 cqi->recurse,
478 TRUE /* top_of_recurse */,
479 new_revnum, new_date, rev_author,
480 cqi->new_dav_cache,
481 cqi->no_unlock,
482 cqi->keep_changelist,
483 cqi->sha1_checksum, queue,
484 iterpool));
485
486 /* Don't run the wq now, but remember that we must call it for this
487 working copy */
488 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
489 wc_ctx->db, cqi->local_abspath,
490 iterpool, iterpool));
491
492 if (! svn_hash_gets(run_wqs, wcroot_abspath))
493 {
494 wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath);
495 svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath);
496 }
497 }
498
499 /* Make sure nothing happens if this function is called again. */
500 apr_hash_clear(queue->queue);
501
502 /* Ok; everything is committed now. Now we can start calling callbacks */
503
504 if (cancel_func)
505 SVN_ERR(cancel_func(cancel_baton));
506
507 for (hi = apr_hash_first(scratch_pool, run_wqs);
508 hi;
509 hi = apr_hash_next(hi))
510 {
511 const char *wcroot_abspath = svn__apr_hash_index_key(hi);
512
513 svn_pool_clear(iterpool);
514
515 SVN_ERR(svn_wc__wq_run(wc_ctx->db, wcroot_abspath,
516 cancel_func, cancel_baton,
517 iterpool));
518 }
519
520 svn_pool_destroy(iterpool);
521
522 return SVN_NO_ERROR;
523 }
524
525 /* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
526 * its parent directory in the WC. It will have the regular properties
527 * provided in PROPS, or none if that is NULL.
528 *
529 * If the node is a file, set its on-disk executable and read-only bits to
530 * match its properties and lock state,
531 * ### only if it has an svn:executable or svn:needs-lock property.
532 * ### This is to match the previous behaviour of setting its props
533 * afterwards by calling svn_wc_prop_set4(), but is not very clean.
534 *
535 * Sync the on-disk executable and read-only bits accordingly.
536 */
537 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)538 add_from_disk(svn_wc__db_t *db,
539 const char *local_abspath,
540 svn_node_kind_t kind,
541 const apr_hash_t *props,
542 apr_pool_t *scratch_pool)
543 {
544 if (kind == svn_node_file)
545 {
546 svn_skel_t *work_item = NULL;
547
548 if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE)
549 || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK)))
550 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
551 scratch_pool, scratch_pool));
552
553 SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item,
554 scratch_pool));
555 if (work_item)
556 SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
557 }
558 else
559 {
560 SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL,
561 scratch_pool));
562 }
563
564 return SVN_NO_ERROR;
565 }
566
567
568 /* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of
569 LOCAL_ABSPATH. REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not
570 wanted. Check that the parent of LOCAL_ABSPATH is a versioned directory
571 in a state in which a new child node can be scheduled for addition;
572 return an error if not. */
573 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)574 check_can_add_to_parent(const char **repos_root_url,
575 const char **repos_uuid,
576 svn_wc__db_t *db,
577 const char *local_abspath,
578 apr_pool_t *result_pool,
579 apr_pool_t *scratch_pool)
580 {
581 const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
582 svn_wc__db_status_t parent_status;
583 svn_node_kind_t parent_kind;
584 svn_error_t *err;
585
586 SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
587
588 err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
589 NULL, repos_root_url, repos_uuid, NULL, NULL,
590 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
591 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
592 NULL, NULL, NULL,
593 db, parent_abspath, result_pool, scratch_pool);
594
595 if (err
596 || parent_status == svn_wc__db_status_not_present
597 || parent_status == svn_wc__db_status_excluded
598 || parent_status == svn_wc__db_status_server_excluded)
599 {
600 return
601 svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
602 _("Can't find parent directory's node while"
603 " trying to add '%s'"),
604 svn_dirent_local_style(local_abspath,
605 scratch_pool));
606 }
607 else if (parent_status == svn_wc__db_status_deleted)
608 {
609 return
610 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
611 _("Can't add '%s' to a parent directory"
612 " scheduled for deletion"),
613 svn_dirent_local_style(local_abspath,
614 scratch_pool));
615 }
616 else if (parent_kind != svn_node_dir)
617 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
618 _("Can't schedule an addition of '%s'"
619 " below a not-directory node"),
620 svn_dirent_local_style(local_abspath,
621 scratch_pool));
622
623 /* If we haven't found the repository info yet, find it now. */
624 if ((repos_root_url && ! *repos_root_url)
625 || (repos_uuid && ! *repos_uuid))
626 {
627 if (parent_status == svn_wc__db_status_added)
628 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
629 repos_root_url, repos_uuid, NULL,
630 NULL, NULL, NULL,
631 db, parent_abspath,
632 result_pool, scratch_pool));
633 else
634 SVN_ERR(svn_wc__db_scan_base_repos(NULL,
635 repos_root_url, repos_uuid,
636 db, parent_abspath,
637 result_pool, scratch_pool));
638 }
639
640 return SVN_NO_ERROR;
641 }
642
643
644 /* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for
645 * addition to its WC parent directory.
646 *
647 * Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether
648 * it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's
649 * a WC root.
650 *
651 * ### The checks here, and the outputs, are geared towards svn_wc_add4().
652 */
653 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)654 check_can_add_node(svn_node_kind_t *kind_p,
655 svn_boolean_t *db_row_exists_p,
656 svn_boolean_t *is_wc_root_p,
657 svn_wc__db_t *db,
658 const char *local_abspath,
659 const char *copyfrom_url,
660 svn_revnum_t copyfrom_rev,
661 apr_pool_t *scratch_pool)
662 {
663 const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
664 svn_boolean_t is_wc_root;
665 svn_node_kind_t kind;
666 svn_boolean_t is_special;
667
668 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
669 SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url,
670 scratch_pool)
671 && SVN_IS_VALID_REVNUM(copyfrom_rev)));
672
673 /* Check that the proposed node has an acceptable name. */
674 if (svn_wc_is_adm_dir(base_name, scratch_pool))
675 return svn_error_createf
676 (SVN_ERR_ENTRY_FORBIDDEN, NULL,
677 _("Can't create an entry with a reserved name while trying to add '%s'"),
678 svn_dirent_local_style(local_abspath, scratch_pool));
679
680 SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
681
682 /* Make sure something's there; set KIND and *KIND_P. */
683 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
684 scratch_pool));
685 if (kind == svn_node_none)
686 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
687 _("'%s' not found"),
688 svn_dirent_local_style(local_abspath,
689 scratch_pool));
690 if (kind == svn_node_unknown)
691 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
692 _("Unsupported node kind for path '%s'"),
693 svn_dirent_local_style(local_abspath,
694 scratch_pool));
695 if (kind_p)
696 *kind_p = kind;
697
698 /* Determine whether a DB row for this node EXISTS, and whether it
699 IS_WC_ROOT. If it exists, check that it is in an acceptable state for
700 adding the new node; if not, return an error. */
701 {
702 svn_wc__db_status_t status;
703 svn_boolean_t conflicted;
704 svn_boolean_t exists;
705 svn_error_t *err
706 = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
707 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
708 NULL, NULL, NULL, NULL, NULL, NULL,
709 &conflicted,
710 NULL, NULL, NULL, NULL, NULL, NULL,
711 db, local_abspath,
712 scratch_pool, scratch_pool);
713
714 if (err)
715 {
716 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
717 return svn_error_trace(err);
718
719 svn_error_clear(err);
720 exists = FALSE;
721 is_wc_root = FALSE;
722 }
723 else
724 {
725 is_wc_root = FALSE;
726 exists = TRUE;
727
728 /* Note that the node may be in conflict even if it does not
729 * exist on disk (certain tree conflict scenarios). */
730 if (conflicted)
731 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
732 _("'%s' is an existing item in conflict; "
733 "please mark the conflict as resolved "
734 "before adding a new item here"),
735 svn_dirent_local_style(local_abspath,
736 scratch_pool));
737 switch (status)
738 {
739 case svn_wc__db_status_not_present:
740 break;
741 case svn_wc__db_status_deleted:
742 /* A working copy root should never have a WORKING_NODE */
743 SVN_ERR_ASSERT(!is_wc_root);
744 break;
745 case svn_wc__db_status_normal:
746 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath,
747 scratch_pool));
748
749 if (is_wc_root && copyfrom_url)
750 {
751 /* Integrate a sub working copy in a parent working copy
752 (legacy behavior) */
753 break;
754 }
755 else if (is_wc_root && is_special)
756 {
757 /* Adding a symlink to a working copy root.
758 (special_tests.py 23: externals as symlink targets) */
759 break;
760 }
761 /* else: Fall through in default error */
762
763 default:
764 return svn_error_createf(
765 SVN_ERR_ENTRY_EXISTS, NULL,
766 _("'%s' is already under version control"),
767 svn_dirent_local_style(local_abspath,
768 scratch_pool));
769 }
770 } /* err */
771
772 if (db_row_exists_p)
773 *db_row_exists_p = exists;
774 if (is_wc_root_p)
775 *is_wc_root_p = is_wc_root;
776 }
777
778 return SVN_NO_ERROR;
779 }
780
781
782 /* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into
783 * a copied subtree in the outer working copy.
784 *
785 * LOCAL_ABSPATH must be the root of a nested working copy that has no
786 * local modifications. The parent directory of LOCAL_ABSPATH must be a
787 * versioned directory in the outer WC, and must belong to the same
788 * repository as the nested WC. The nested WC will be integrated into the
789 * parent's WC, and will no longer be a separate WC. */
790 static svn_error_t *
integrate_nested_wc_as_copy(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)791 integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx,
792 const char *local_abspath,
793 apr_pool_t *scratch_pool)
794 {
795 svn_wc__db_t *db = wc_ctx->db;
796 const char *moved_abspath;
797
798 /* Drop any references to the wc that is to be rewritten */
799 SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
800
801 /* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */
802 {
803 const char *tmpdir_abspath;
804 const char *moved_adm_abspath;
805 const char *adm_abspath;
806
807 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
808 svn_dirent_dirname(local_abspath,
809 scratch_pool),
810 scratch_pool, scratch_pool));
811 SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
812 svn_io_file_del_on_close,
813 scratch_pool, scratch_pool));
814 SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
815
816 adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool);
817 moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
818 SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
819 }
820
821 /* Copy entries from temporary location into the main db */
822 SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
823 TRUE /* metadata_only */,
824 NULL, NULL, NULL, NULL, scratch_pool));
825
826 /* Cleanup the temporary admin dir */
827 SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
828 SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
829 scratch_pool));
830
831 /* The subdir is now part of our parent working copy. Our caller assumes
832 that we return the new node locked, so obtain a lock if we didn't
833 receive the lock via our depth infinity lock */
834 {
835 svn_boolean_t owns_lock;
836
837 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
838 FALSE, scratch_pool));
839 if (!owns_lock)
840 SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
841 scratch_pool));
842 }
843
844 return SVN_NO_ERROR;
845 }
846
847
848 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)849 svn_wc_add4(svn_wc_context_t *wc_ctx,
850 const char *local_abspath,
851 svn_depth_t depth,
852 const char *copyfrom_url,
853 svn_revnum_t copyfrom_rev,
854 svn_cancel_func_t cancel_func,
855 void *cancel_baton,
856 svn_wc_notify_func2_t notify_func,
857 void *notify_baton,
858 apr_pool_t *scratch_pool)
859 {
860 svn_wc__db_t *db = wc_ctx->db;
861 svn_node_kind_t kind;
862 svn_boolean_t db_row_exists;
863 svn_boolean_t is_wc_root;
864 const char *repos_root_url;
865 const char *repos_uuid;
866
867 SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root,
868 db, local_abspath, copyfrom_url, copyfrom_rev,
869 scratch_pool));
870
871 /* Get REPOS_ROOT_URL and REPOS_UUID. Check that the
872 parent is a versioned directory in an acceptable state. */
873 SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid,
874 db, local_abspath, scratch_pool,
875 scratch_pool));
876
877 /* If we're performing a repos-to-WC copy, check that the copyfrom
878 repository is the same as the parent dir's repository. */
879 if (copyfrom_url && !svn_uri__is_ancestor(repos_root_url, copyfrom_url))
880 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
881 _("The URL '%s' has a different repository "
882 "root than its parent"), copyfrom_url);
883
884 /* Verify that we can actually integrate the inner working copy */
885 if (is_wc_root)
886 {
887 const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid;
888 const char *inner_url;
889
890 SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
891 &inner_repos_root_url,
892 &inner_repos_uuid,
893 db, local_abspath,
894 scratch_pool, scratch_pool));
895
896 if (strcmp(inner_repos_uuid, repos_uuid)
897 || strcmp(repos_root_url, inner_repos_root_url))
898 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
899 _("Can't schedule the working copy at '%s' "
900 "from repository '%s' with uuid '%s' "
901 "for addition under a working copy from "
902 "repository '%s' with uuid '%s'."),
903 svn_dirent_local_style(local_abspath,
904 scratch_pool),
905 inner_repos_root_url, inner_repos_uuid,
906 repos_root_url, repos_uuid);
907
908 inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
909 scratch_pool);
910
911 if (strcmp(copyfrom_url, inner_url))
912 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
913 _("Can't add '%s' with URL '%s', but with "
914 "the data from '%s'"),
915 svn_dirent_local_style(local_abspath,
916 scratch_pool),
917 copyfrom_url, inner_url);
918 }
919
920 if (!copyfrom_url) /* Case 2a: It's a simple add */
921 {
922 SVN_ERR(add_from_disk(db, local_abspath, kind, NULL,
923 scratch_pool));
924 if (kind == svn_node_dir && !db_row_exists)
925 {
926 /* If using the legacy 1.6 interface the parent lock may not
927 be recursive and add is expected to lock the new dir.
928
929 ### Perhaps the lock should be created in the same
930 transaction that adds the node? */
931 svn_boolean_t owns_lock;
932
933 SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
934 FALSE, scratch_pool));
935 if (!owns_lock)
936 SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
937 scratch_pool));
938 }
939 }
940 else if (!is_wc_root) /* Case 2b: It's a copy from the repository */
941 {
942 if (kind == svn_node_file)
943 {
944 /* This code should never be used, as it doesn't install proper
945 pristine and/or properties. But it was not an error in the old
946 version of this function.
947
948 ===> Use svn_wc_add_repos_file4() directly! */
949 svn_stream_t *content = svn_stream_empty(scratch_pool);
950
951 SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath,
952 content, NULL, NULL, NULL,
953 copyfrom_url, copyfrom_rev,
954 cancel_func, cancel_baton,
955 scratch_pool));
956 }
957 else
958 {
959 const char *repos_relpath =
960 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
961
962 SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath,
963 apr_hash_make(scratch_pool),
964 copyfrom_rev, 0, NULL,
965 repos_relpath,
966 repos_root_url, repos_uuid,
967 copyfrom_rev,
968 NULL /* children */, FALSE, depth,
969 NULL /* conflicts */,
970 NULL /* work items */,
971 scratch_pool));
972 }
973 }
974 else /* Case 1: Integrating a separate WC into this one, in place */
975 {
976 SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
977 scratch_pool));
978 }
979
980 /* Report the addition to the caller. */
981 if (notify_func != NULL)
982 {
983 svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
984 svn_wc_notify_add,
985 scratch_pool);
986 notify->kind = kind;
987 (*notify_func)(notify_baton, notify, scratch_pool);
988 }
989
990 return SVN_NO_ERROR;
991 }
992
993
994 svn_error_t *
svn_wc_add_from_disk2(svn_wc_context_t * wc_ctx,const char * local_abspath,const apr_hash_t * props,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)995 svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx,
996 const char *local_abspath,
997 const apr_hash_t *props,
998 svn_wc_notify_func2_t notify_func,
999 void *notify_baton,
1000 apr_pool_t *scratch_pool)
1001 {
1002 svn_node_kind_t kind;
1003
1004 SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath,
1005 NULL, SVN_INVALID_REVNUM, scratch_pool));
1006 SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
1007 scratch_pool, scratch_pool));
1008
1009 /* Canonicalize and check the props */
1010 if (props)
1011 {
1012 apr_hash_t *new_props;
1013
1014 SVN_ERR(svn_wc__canonicalize_props(
1015 &new_props,
1016 local_abspath, kind, props, FALSE /* skip_some_checks */,
1017 scratch_pool, scratch_pool));
1018 props = new_props;
1019 }
1020
1021 /* Add to the DB and maybe update on-disk executable read-only bits */
1022 SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
1023 scratch_pool));
1024
1025 /* Report the addition to the caller. */
1026 if (notify_func != NULL)
1027 {
1028 svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
1029 svn_wc_notify_add,
1030 scratch_pool);
1031 notify->kind = kind;
1032 notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
1033 (*notify_func)(notify_baton, notify, scratch_pool);
1034 }
1035
1036 return SVN_NO_ERROR;
1037 }
1038
1039 /* Return a path where nothing exists on disk, within the admin directory
1040 belonging to the WCROOT_ABSPATH directory. */
1041 static const char *
nonexistent_path(const char * wcroot_abspath,apr_pool_t * scratch_pool)1042 nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool)
1043 {
1044 return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH,
1045 scratch_pool);
1046 }
1047
1048
1049 svn_error_t *
svn_wc_get_pristine_copy_path(const char * path,const char ** pristine_path,apr_pool_t * pool)1050 svn_wc_get_pristine_copy_path(const char *path,
1051 const char **pristine_path,
1052 apr_pool_t *pool)
1053 {
1054 svn_wc__db_t *db;
1055 const char *local_abspath;
1056 svn_error_t *err;
1057
1058 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
1059
1060 SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
1061 /* DB is now open. This is seemingly a "light" function that a caller
1062 may use repeatedly despite error return values. The rest of this
1063 function should aggressively close DB, even in the error case. */
1064
1065 err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath,
1066 pool, pool);
1067 if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1068 {
1069 /* The node doesn't exist, so return a non-existent path located
1070 in WCROOT/.svn/ */
1071 const char *wcroot_abspath;
1072
1073 svn_error_clear(err);
1074
1075 err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath,
1076 pool, pool);
1077 if (err == NULL)
1078 *pristine_path = nonexistent_path(wcroot_abspath, pool);
1079 }
1080
1081 return svn_error_compose_create(err, svn_wc__db_close(db));
1082 }
1083
1084
1085 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)1086 svn_wc_get_pristine_contents2(svn_stream_t **contents,
1087 svn_wc_context_t *wc_ctx,
1088 const char *local_abspath,
1089 apr_pool_t *result_pool,
1090 apr_pool_t *scratch_pool)
1091 {
1092 return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL,
1093 wc_ctx->db,
1094 local_abspath,
1095 result_pool,
1096 scratch_pool));
1097 }
1098
1099
1100 typedef struct get_pristine_lazyopen_baton_t
1101 {
1102 svn_wc_context_t *wc_ctx;
1103 const char *wri_abspath;
1104 const svn_checksum_t *checksum;
1105
1106 } get_pristine_lazyopen_baton_t;
1107
1108
1109 /* Implements svn_stream_lazyopen_func_t */
1110 static svn_error_t *
get_pristine_lazyopen_func(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1111 get_pristine_lazyopen_func(svn_stream_t **stream,
1112 void *baton,
1113 apr_pool_t *result_pool,
1114 apr_pool_t *scratch_pool)
1115 {
1116 get_pristine_lazyopen_baton_t *b = baton;
1117 const svn_checksum_t *sha1_checksum;
1118
1119 /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
1120 we'll use it to lookup the SHA1. */
1121 if (b->checksum->kind == svn_checksum_sha1)
1122 sha1_checksum = b->checksum;
1123 else
1124 SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
1125 b->wri_abspath, b->checksum,
1126 scratch_pool, scratch_pool));
1127
1128 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
1129 b->wri_abspath, sha1_checksum,
1130 result_pool, scratch_pool));
1131 return SVN_NO_ERROR;
1132 }
1133
1134 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)1135 svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
1136 svn_wc_context_t *wc_ctx,
1137 const char *wri_abspath,
1138 const svn_checksum_t *checksum,
1139 apr_pool_t *result_pool,
1140 apr_pool_t *scratch_pool)
1141 {
1142 svn_boolean_t present;
1143
1144 *contents = NULL;
1145
1146 SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
1147 checksum, scratch_pool));
1148
1149 if (present)
1150 {
1151 get_pristine_lazyopen_baton_t *gpl_baton;
1152
1153 gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
1154 gpl_baton->wc_ctx = wc_ctx;
1155 gpl_baton->wri_abspath = wri_abspath;
1156 gpl_baton->checksum = checksum;
1157
1158 *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
1159 gpl_baton, FALSE, result_pool);
1160 }
1161
1162 return SVN_NO_ERROR;
1163 }
1164
1165
1166
1167 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)1168 svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
1169 const char *local_abspath,
1170 const svn_lock_t *lock,
1171 apr_pool_t *scratch_pool)
1172 {
1173 svn_wc__db_lock_t db_lock;
1174 svn_error_t *err;
1175 const svn_string_t *needs_lock;
1176
1177 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1178
1179 /* ### Enable after fixing callers */
1180 /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1181 svn_dirent_dirname(local_abspath, scratch_pool),
1182 scratch_pool));*/
1183
1184 db_lock.token = lock->token;
1185 db_lock.owner = lock->owner;
1186 db_lock.comment = lock->comment;
1187 db_lock.date = lock->creation_date;
1188 err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool);
1189 if (err)
1190 {
1191 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1192 return svn_error_trace(err);
1193
1194 /* Remap the error. */
1195 svn_error_clear(err);
1196 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1197 _("'%s' is not under version control"),
1198 svn_dirent_local_style(local_abspath,
1199 scratch_pool));
1200 }
1201
1202 /* if svn:needs-lock is present, then make the file read-write. */
1203 err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1204 SVN_PROP_NEEDS_LOCK, scratch_pool,
1205 scratch_pool);
1206
1207 if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1208 {
1209 /* The node has non wc representation (e.g. deleted), so
1210 we don't want to touch the in-wc file */
1211 svn_error_clear(err);
1212 return SVN_NO_ERROR;
1213 }
1214 SVN_ERR(err);
1215
1216 if (needs_lock)
1217 SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
1218
1219 return SVN_NO_ERROR;
1220 }
1221
1222
1223 svn_error_t *
svn_wc_remove_lock2(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)1224 svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
1225 const char *local_abspath,
1226 apr_pool_t *scratch_pool)
1227 {
1228 svn_error_t *err;
1229 const svn_string_t *needs_lock;
1230
1231 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1232
1233 /* ### Enable after fixing callers */
1234 /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1235 svn_dirent_dirname(local_abspath, scratch_pool),
1236 scratch_pool));*/
1237
1238 err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool);
1239 if (err)
1240 {
1241 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1242 return svn_error_trace(err);
1243
1244 /* Remap the error. */
1245 svn_error_clear(err);
1246 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1247 _("'%s' is not under version control"),
1248 svn_dirent_local_style(local_abspath,
1249 scratch_pool));
1250 }
1251
1252 /* if svn:needs-lock is present, then make the file read-only. */
1253 err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1254 SVN_PROP_NEEDS_LOCK, scratch_pool,
1255 scratch_pool);
1256 if (err)
1257 {
1258 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1259 return svn_error_trace(err);
1260
1261 svn_error_clear(err);
1262 return SVN_NO_ERROR; /* Node is shadowed and/or deleted,
1263 so we shouldn't apply its lock */
1264 }
1265
1266 if (needs_lock)
1267 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
1268
1269 return SVN_NO_ERROR;
1270 }
1271
1272
1273 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)1274 svn_wc_set_changelist2(svn_wc_context_t *wc_ctx,
1275 const char *local_abspath,
1276 const char *new_changelist,
1277 svn_depth_t depth,
1278 const apr_array_header_t *changelist_filter,
1279 svn_cancel_func_t cancel_func,
1280 void *cancel_baton,
1281 svn_wc_notify_func2_t notify_func,
1282 void *notify_baton,
1283 apr_pool_t *scratch_pool)
1284 {
1285 /* Assert that we aren't being asked to set an empty changelist. */
1286 SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0'));
1287
1288 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1289
1290 SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath,
1291 new_changelist, changelist_filter,
1292 depth, notify_func, notify_baton,
1293 cancel_func, cancel_baton,
1294 scratch_pool));
1295
1296 return SVN_NO_ERROR;
1297 }
1298
1299 struct get_cl_fn_baton
1300 {
1301 svn_wc__db_t *db;
1302 apr_hash_t *clhash;
1303 svn_changelist_receiver_t callback_func;
1304 void *callback_baton;
1305 };
1306
1307
1308 static svn_error_t *
get_node_changelist(const char * local_abspath,svn_node_kind_t kind,void * baton,apr_pool_t * scratch_pool)1309 get_node_changelist(const char *local_abspath,
1310 svn_node_kind_t kind,
1311 void *baton,
1312 apr_pool_t *scratch_pool)
1313 {
1314 struct get_cl_fn_baton *b = baton;
1315 const char *changelist;
1316
1317 SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1318 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1319 NULL, NULL, NULL, NULL, NULL, &changelist,
1320 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1321 b->db, local_abspath,
1322 scratch_pool, scratch_pool));
1323
1324 if (svn_wc__internal_changelist_match(b->db, local_abspath, b->clhash,
1325 scratch_pool))
1326 SVN_ERR(b->callback_func(b->callback_baton, local_abspath,
1327 changelist, scratch_pool));
1328
1329 return SVN_NO_ERROR;
1330 }
1331
1332
1333 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)1334 svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
1335 const char *local_abspath,
1336 svn_depth_t depth,
1337 const apr_array_header_t *changelist_filter,
1338 svn_changelist_receiver_t callback_func,
1339 void *callback_baton,
1340 svn_cancel_func_t cancel_func,
1341 void *cancel_baton,
1342 apr_pool_t *scratch_pool)
1343 {
1344 struct get_cl_fn_baton gnb;
1345
1346 gnb.db = wc_ctx->db;
1347 gnb.clhash = NULL;
1348 gnb.callback_func = callback_func;
1349 gnb.callback_baton = callback_baton;
1350
1351 if (changelist_filter)
1352 SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter,
1353 scratch_pool));
1354
1355 return svn_error_trace(
1356 svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
1357 changelist_filter, get_node_changelist,
1358 &gnb, depth,
1359 cancel_func, cancel_baton,
1360 scratch_pool));
1361
1362 }
1363
1364
1365 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)1366 svn_wc__internal_changelist_match(svn_wc__db_t *db,
1367 const char *local_abspath,
1368 const apr_hash_t *clhash,
1369 apr_pool_t *scratch_pool)
1370 {
1371 svn_error_t *err;
1372 const char *changelist;
1373
1374 if (clhash == NULL)
1375 return TRUE;
1376
1377 err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1378 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1379 NULL, NULL, NULL, NULL, NULL, &changelist,
1380 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1381 db, local_abspath, scratch_pool, scratch_pool);
1382 if (err)
1383 {
1384 svn_error_clear(err);
1385 return FALSE;
1386 }
1387
1388 return (changelist
1389 && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
1390 }
1391
1392
1393 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)1394 svn_wc__changelist_match(svn_wc_context_t *wc_ctx,
1395 const char *local_abspath,
1396 const apr_hash_t *clhash,
1397 apr_pool_t *scratch_pool)
1398 {
1399 return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash,
1400 scratch_pool);
1401 }
1402