1 /*
2 * node.c: routines for getting information about nodes in the working copy.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* A note about these functions:
25
26 We aren't really sure yet which bits of data libsvn_client needs about
27 nodes. In wc-1, we just grab the entry, and then use whatever we want
28 from it. Such a pattern is Bad.
29
30 This file is intended to hold functions which retrieve specific bits of
31 information about a node, and will hopefully give us a better idea about
32 what data libsvn_client needs, and how to best provide that data in 1.7
33 final. As such, these functions should only be called from outside
34 libsvn_wc; any internal callers are encouraged to use the appropriate
35 information fetching function, such as svn_wc__db_read_info().
36 */
37
38 #include <apr_pools.h>
39 #include <apr_time.h>
40
41 #include "svn_pools.h"
42 #include "svn_dirent_uri.h"
43 #include "svn_path.h"
44 #include "svn_hash.h"
45 #include "svn_types.h"
46
47 #include "wc.h"
48 #include "props.h"
49 #include "entries.h"
50 #include "wc_db.h"
51
52 #include "svn_private_config.h"
53 #include "private/svn_wc_private.h"
54
55
56 /* Set *CHILDREN_ABSPATHS to a new array of the full paths formed by joining
57 * each name in REL_CHILDREN onto DIR_ABSPATH.
58 *
59 * Allocate the output array and its elements in RESULT_POOL. */
60 static void
make_absolute(const apr_array_header_t ** children_abspaths,const char * dir_abspath,const apr_array_header_t * rel_children,apr_pool_t * result_pool)61 make_absolute(const apr_array_header_t **children_abspaths,
62 const char *dir_abspath,
63 const apr_array_header_t *rel_children,
64 apr_pool_t *result_pool)
65 {
66 apr_array_header_t *children;
67 int i;
68
69 children = apr_array_make(result_pool, rel_children->nelts,
70 sizeof(const char *));
71 for (i = 0; i < rel_children->nelts; i++)
72 {
73 const char *name = APR_ARRAY_IDX(rel_children, i, const char *);
74 APR_ARRAY_PUSH(children, const char *) =
75 svn_dirent_join(dir_abspath, name,
76 result_pool);
77 }
78
79 *children_abspaths = children;
80 }
81
82
83 svn_error_t *
svn_wc__node_get_children_of_working_node(const apr_array_header_t ** children,svn_wc_context_t * wc_ctx,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)84 svn_wc__node_get_children_of_working_node(const apr_array_header_t **children,
85 svn_wc_context_t *wc_ctx,
86 const char *dir_abspath,
87 apr_pool_t *result_pool,
88 apr_pool_t *scratch_pool)
89 {
90 const apr_array_header_t *child_names;
91
92 SVN_ERR(svn_wc__db_read_children_of_working_node(&child_names,
93 wc_ctx->db, dir_abspath,
94 scratch_pool, scratch_pool));
95 make_absolute(children, dir_abspath, child_names, result_pool);
96 return SVN_NO_ERROR;
97 }
98
99 svn_error_t *
svn_wc__node_get_not_present_children(const apr_array_header_t ** children,svn_wc_context_t * wc_ctx,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)100 svn_wc__node_get_not_present_children(const apr_array_header_t **children,
101 svn_wc_context_t *wc_ctx,
102 const char *dir_abspath,
103 apr_pool_t *result_pool,
104 apr_pool_t *scratch_pool)
105 {
106 const apr_array_header_t *child_names;
107
108 SVN_ERR(svn_wc__db_base_read_not_present_children(
109 &child_names,
110 wc_ctx->db, dir_abspath,
111 scratch_pool, scratch_pool));
112 make_absolute(children, dir_abspath, child_names, result_pool);
113 return SVN_NO_ERROR;
114 }
115
116
117 svn_error_t *
svn_wc__node_get_repos_info(svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)118 svn_wc__node_get_repos_info(svn_revnum_t *revision,
119 const char **repos_relpath,
120 const char **repos_root_url,
121 const char **repos_uuid,
122 svn_wc_context_t *wc_ctx,
123 const char *local_abspath,
124 apr_pool_t *result_pool,
125 apr_pool_t *scratch_pool)
126 {
127 return svn_error_trace(
128 svn_wc__db_read_repos_info(revision,
129 repos_relpath,
130 repos_root_url,
131 repos_uuid,
132 wc_ctx->db, local_abspath,
133 result_pool, scratch_pool));
134 }
135
136 /* Convert DB_KIND into the appropriate NODE_KIND value.
137 * If SHOW_HIDDEN is TRUE, report the node kind as found in the DB
138 * even if DB_STATUS indicates that the node is hidden.
139 * Else, return svn_node_none for such nodes.
140 *
141 * ### This is a bit ugly. We should consider promoting svn_kind_t
142 * ### to the de-facto node kind type instead of converting between them
143 * ### in non-backwards compat code.
144 * ### See also comments at the definition of svn_kind_t.
145 *
146 * ### In reality, the previous comment is out of date, as there is
147 * ### now only one enumeration for node kinds, and that is
148 * ### svn_node_kind_t (svn_kind_t was merged with that). But it's
149 * ### still ugly.
150 */
151 static svn_error_t *
convert_db_kind_to_node_kind(svn_node_kind_t * node_kind,svn_node_kind_t db_kind,svn_wc__db_status_t db_status,svn_boolean_t show_hidden)152 convert_db_kind_to_node_kind(svn_node_kind_t *node_kind,
153 svn_node_kind_t db_kind,
154 svn_wc__db_status_t db_status,
155 svn_boolean_t show_hidden)
156 {
157 *node_kind = db_kind;
158
159 /* Make sure hidden nodes return svn_node_none. */
160 if (! show_hidden)
161 switch (db_status)
162 {
163 case svn_wc__db_status_not_present:
164 case svn_wc__db_status_server_excluded:
165 case svn_wc__db_status_excluded:
166 *node_kind = svn_node_none;
167
168 default:
169 break;
170 }
171
172 return SVN_NO_ERROR;
173 }
174
175 svn_error_t *
svn_wc_read_kind2(svn_node_kind_t * kind,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t show_deleted,svn_boolean_t show_hidden,apr_pool_t * scratch_pool)176 svn_wc_read_kind2(svn_node_kind_t *kind,
177 svn_wc_context_t *wc_ctx,
178 const char *local_abspath,
179 svn_boolean_t show_deleted,
180 svn_boolean_t show_hidden,
181 apr_pool_t *scratch_pool)
182 {
183 svn_node_kind_t db_kind;
184
185 SVN_ERR(svn_wc__db_read_kind(&db_kind,
186 wc_ctx->db, local_abspath,
187 TRUE,
188 show_deleted,
189 show_hidden,
190 scratch_pool));
191
192 if (db_kind == svn_node_dir)
193 *kind = svn_node_dir;
194 else if (db_kind == svn_node_file || db_kind == svn_node_symlink)
195 *kind = svn_node_file;
196 else
197 *kind = svn_node_none;
198
199 return SVN_NO_ERROR;
200 }
201
202 svn_error_t *
svn_wc__node_get_changed_info(svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)203 svn_wc__node_get_changed_info(svn_revnum_t *changed_rev,
204 apr_time_t *changed_date,
205 const char **changed_author,
206 svn_wc_context_t *wc_ctx,
207 const char *local_abspath,
208 apr_pool_t *result_pool,
209 apr_pool_t *scratch_pool)
210 {
211 return svn_error_trace(
212 svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, changed_rev,
213 changed_date, changed_author, NULL, NULL, NULL,
214 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
215 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
216 wc_ctx->db, local_abspath, result_pool,
217 scratch_pool));
218 }
219
220 svn_error_t *
svn_wc__node_get_url(const char ** url,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)221 svn_wc__node_get_url(const char **url,
222 svn_wc_context_t *wc_ctx,
223 const char *local_abspath,
224 apr_pool_t *result_pool,
225 apr_pool_t *scratch_pool)
226 {
227 const char *repos_root_url;
228 const char *repos_relpath;
229
230 SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, &repos_root_url,
231 NULL,
232 wc_ctx->db, local_abspath,
233 scratch_pool, scratch_pool));
234
235 *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
236 result_pool);
237
238 return SVN_NO_ERROR;
239 }
240
241 /* A recursive node-walker, helper for svn_wc__internal_walk_children().
242 *
243 * Call WALK_CALLBACK with WALK_BATON on all children (recursively) of
244 * DIR_ABSPATH in DB, but not on DIR_ABSPATH itself. DIR_ABSPATH must be a
245 * versioned directory. If SHOW_HIDDEN is true, visit hidden nodes, else
246 * ignore them. Restrict the depth of the walk to DEPTH.
247 *
248 * ### Is it possible for a subdirectory to be hidden and known to be a
249 * directory? If so, and if show_hidden is true, this will try to
250 * recurse into it. */
251 static svn_error_t *
walker_helper(svn_wc__db_t * db,const char * dir_abspath,svn_boolean_t show_hidden,const apr_hash_t * changelist_filter,svn_wc__node_found_func_t walk_callback,void * walk_baton,svn_depth_t depth,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)252 walker_helper(svn_wc__db_t *db,
253 const char *dir_abspath,
254 svn_boolean_t show_hidden,
255 const apr_hash_t *changelist_filter,
256 svn_wc__node_found_func_t walk_callback,
257 void *walk_baton,
258 svn_depth_t depth,
259 svn_cancel_func_t cancel_func,
260 void *cancel_baton,
261 apr_pool_t *scratch_pool)
262 {
263 apr_pool_t *iterpool;
264 const apr_array_header_t *items;
265 int i;
266
267 if (depth == svn_depth_empty)
268 return SVN_NO_ERROR;
269
270 iterpool = svn_pool_create(scratch_pool);
271
272 SVN_ERR(svn_wc__db_read_children_walker_info(&items, db,
273 dir_abspath, scratch_pool,
274 iterpool));
275
276 for (i = 0; i < items->nelts; i++)
277 {
278 struct svn_wc__db_walker_info_t *wi =
279 APR_ARRAY_IDX(items, i, struct svn_wc__db_walker_info_t *);
280 const char *child_name = wi->name;
281 svn_node_kind_t child_kind = wi->kind;
282 svn_wc__db_status_t child_status = wi->status;
283 const char *child_abspath;
284
285 svn_pool_clear(iterpool);
286
287 /* See if someone wants to cancel this operation. */
288 if (cancel_func)
289 SVN_ERR(cancel_func(cancel_baton));
290
291 child_abspath = svn_dirent_join(dir_abspath, child_name, iterpool);
292
293 if (!show_hidden)
294 switch (child_status)
295 {
296 case svn_wc__db_status_not_present:
297 case svn_wc__db_status_server_excluded:
298 case svn_wc__db_status_excluded:
299 continue;
300 default:
301 break;
302 }
303
304 /* Return the child, if appropriate. */
305 if ( (child_kind == svn_node_file
306 || depth >= svn_depth_immediates)
307 && svn_wc__internal_changelist_match(db, child_abspath,
308 changelist_filter,
309 scratch_pool) )
310 {
311 svn_node_kind_t kind;
312
313 SVN_ERR(convert_db_kind_to_node_kind(&kind, child_kind,
314 child_status, show_hidden));
315 /* ### We might want to pass child_status as well because at least
316 * ### one callee is asking for it.
317 * ### But is it OK to use an svn_wc__db type in this API?
318 * ### Not yet, we need to get the node walker
319 * ### libsvn_wc-internal first. -hkw */
320 SVN_ERR(walk_callback(child_abspath, kind, walk_baton, iterpool));
321 }
322
323 /* Recurse into this directory, if appropriate. */
324 if (child_kind == svn_node_dir
325 && depth >= svn_depth_immediates)
326 {
327 svn_depth_t depth_below_here = depth;
328
329 if (depth == svn_depth_immediates)
330 depth_below_here = svn_depth_empty;
331
332 SVN_ERR(walker_helper(db, child_abspath, show_hidden,
333 changelist_filter,
334 walk_callback, walk_baton,
335 depth_below_here,
336 cancel_func, cancel_baton,
337 iterpool));
338 }
339 }
340
341 svn_pool_destroy(iterpool);
342
343 return SVN_NO_ERROR;
344 }
345
346
347 svn_error_t *
svn_wc__internal_walk_children(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t show_hidden,const apr_array_header_t * changelist_filter,svn_wc__node_found_func_t walk_callback,void * walk_baton,svn_depth_t walk_depth,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)348 svn_wc__internal_walk_children(svn_wc__db_t *db,
349 const char *local_abspath,
350 svn_boolean_t show_hidden,
351 const apr_array_header_t *changelist_filter,
352 svn_wc__node_found_func_t walk_callback,
353 void *walk_baton,
354 svn_depth_t walk_depth,
355 svn_cancel_func_t cancel_func,
356 void *cancel_baton,
357 apr_pool_t *scratch_pool)
358 {
359 svn_node_kind_t db_kind;
360 svn_node_kind_t kind;
361 svn_wc__db_status_t status;
362 apr_hash_t *changelist_hash = NULL;
363 const char *changelist = NULL;
364
365 SVN_ERR_ASSERT(walk_depth >= svn_depth_empty
366 && walk_depth <= svn_depth_infinity);
367
368 if (changelist_filter && changelist_filter->nelts)
369 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
370 scratch_pool));
371
372 /* Check if the node exists before the first callback */
373 SVN_ERR(svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
374 NULL, NULL, NULL, NULL, NULL, NULL,
375 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
376 changelist_hash ? &changelist : NULL,
377 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
378 db, local_abspath, scratch_pool, scratch_pool));
379
380 SVN_ERR(convert_db_kind_to_node_kind(&kind, db_kind, status, show_hidden));
381
382 if (!changelist_hash
383 || (changelist && svn_hash_gets(changelist_hash, changelist)))
384 {
385 SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool));
386 }
387
388 if (db_kind == svn_node_file
389 || status == svn_wc__db_status_not_present
390 || status == svn_wc__db_status_excluded
391 || status == svn_wc__db_status_server_excluded)
392 return SVN_NO_ERROR;
393
394 if (db_kind == svn_node_dir)
395 {
396 return svn_error_trace(
397 walker_helper(db, local_abspath, show_hidden, changelist_hash,
398 walk_callback, walk_baton,
399 walk_depth, cancel_func, cancel_baton, scratch_pool));
400 }
401
402 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
403 _("'%s' has an unrecognized node kind"),
404 svn_dirent_local_style(local_abspath,
405 scratch_pool));
406 }
407
408 svn_error_t *
svn_wc__node_is_not_present(svn_boolean_t * is_not_present,svn_boolean_t * is_excluded,svn_boolean_t * is_server_excluded,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t base_only,apr_pool_t * scratch_pool)409 svn_wc__node_is_not_present(svn_boolean_t *is_not_present,
410 svn_boolean_t *is_excluded,
411 svn_boolean_t *is_server_excluded,
412 svn_wc_context_t *wc_ctx,
413 const char *local_abspath,
414 svn_boolean_t base_only,
415 apr_pool_t *scratch_pool)
416 {
417 svn_wc__db_status_t status;
418
419 if (base_only)
420 {
421 SVN_ERR(svn_wc__db_base_get_info(&status,
422 NULL, NULL, NULL, NULL, NULL, NULL,
423 NULL, NULL, NULL, NULL, NULL, NULL,
424 NULL, NULL, NULL,
425 wc_ctx->db, local_abspath,
426 scratch_pool, scratch_pool));
427 }
428 else
429 {
430 SVN_ERR(svn_wc__db_read_info(&status,
431 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
432 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
433 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
434 NULL, NULL, NULL, NULL, NULL,
435 wc_ctx->db, local_abspath,
436 scratch_pool, scratch_pool));
437 }
438
439 if (is_not_present)
440 *is_not_present = (status == svn_wc__db_status_not_present);
441
442 if (is_excluded)
443 *is_excluded = (status == svn_wc__db_status_excluded);
444
445 if (is_server_excluded)
446 *is_server_excluded = (status == svn_wc__db_status_server_excluded);
447
448 return SVN_NO_ERROR;
449 }
450
451 svn_error_t *
svn_wc__node_is_added(svn_boolean_t * is_added,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)452 svn_wc__node_is_added(svn_boolean_t *is_added,
453 svn_wc_context_t *wc_ctx,
454 const char *local_abspath,
455 apr_pool_t *scratch_pool)
456 {
457 svn_wc__db_status_t status;
458
459 SVN_ERR(svn_wc__db_read_info(&status,
460 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
461 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
462 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
463 NULL, NULL, NULL, NULL, NULL,
464 wc_ctx->db, local_abspath,
465 scratch_pool, scratch_pool));
466 *is_added = (status == svn_wc__db_status_added);
467
468 return SVN_NO_ERROR;
469 }
470
471 svn_error_t *
svn_wc__node_has_working(svn_boolean_t * has_working,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)472 svn_wc__node_has_working(svn_boolean_t *has_working,
473 svn_wc_context_t *wc_ctx,
474 const char *local_abspath,
475 apr_pool_t *scratch_pool)
476 {
477 svn_wc__db_status_t status;
478
479 SVN_ERR(svn_wc__db_read_info(&status,
480 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
481 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
482 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
483 NULL, NULL, NULL, NULL, has_working,
484 wc_ctx->db, local_abspath,
485 scratch_pool, scratch_pool));
486
487 return SVN_NO_ERROR;
488 }
489
490
491 svn_error_t *
svn_wc__node_get_base(svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,const char ** lock_token,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t ignore_enoent,apr_pool_t * result_pool,apr_pool_t * scratch_pool)492 svn_wc__node_get_base(svn_node_kind_t *kind,
493 svn_revnum_t *revision,
494 const char **repos_relpath,
495 const char **repos_root_url,
496 const char **repos_uuid,
497 const char **lock_token,
498 svn_wc_context_t *wc_ctx,
499 const char *local_abspath,
500 svn_boolean_t ignore_enoent,
501 apr_pool_t *result_pool,
502 apr_pool_t *scratch_pool)
503 {
504 svn_error_t *err;
505 svn_wc__db_status_t status;
506 svn_wc__db_lock_t *lock;
507 svn_node_kind_t db_kind;
508
509 err = svn_wc__db_base_get_info(&status, &db_kind, revision, repos_relpath,
510 repos_root_url, repos_uuid, NULL,
511 NULL, NULL, NULL, NULL, NULL,
512 lock_token ? &lock : NULL,
513 NULL, NULL, NULL,
514 wc_ctx->db, local_abspath,
515 result_pool, scratch_pool);
516
517 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
518 return svn_error_trace(err);
519 else if (err
520 || (status != svn_wc__db_status_normal
521 && status != svn_wc__db_status_incomplete))
522 {
523 if (!ignore_enoent)
524 {
525 if (err)
526 return svn_error_trace(err);
527 else
528 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
529 _("The node '%s' was not found."),
530 svn_dirent_local_style(local_abspath,
531 scratch_pool));
532 }
533 svn_error_clear(err);
534
535 if (kind)
536 *kind = svn_node_unknown;
537 if (revision)
538 *revision = SVN_INVALID_REVNUM;
539 if (repos_relpath)
540 *repos_relpath = NULL;
541 if (repos_root_url)
542 *repos_root_url = NULL;
543 if (repos_uuid)
544 *repos_uuid = NULL;
545 if (lock_token)
546 *lock_token = NULL;
547 return SVN_NO_ERROR;
548 }
549
550 if (kind)
551 *kind = db_kind;
552 if (lock_token)
553 *lock_token = lock ? lock->token : NULL;
554
555 SVN_ERR_ASSERT(!revision || SVN_IS_VALID_REVNUM(*revision));
556 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
557 SVN_ERR_ASSERT(!repos_root_url || *repos_root_url);
558 SVN_ERR_ASSERT(!repos_uuid || *repos_uuid);
559 return SVN_NO_ERROR;
560 }
561
562 svn_error_t *
svn_wc__node_get_pre_ng_status_data(svn_revnum_t * revision,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)563 svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision,
564 svn_revnum_t *changed_rev,
565 apr_time_t *changed_date,
566 const char **changed_author,
567 svn_wc_context_t *wc_ctx,
568 const char *local_abspath,
569 apr_pool_t *result_pool,
570 apr_pool_t *scratch_pool)
571 {
572 svn_wc__db_status_t status;
573 svn_boolean_t have_base, have_more_work, have_work;
574
575 SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL, NULL, NULL,
576 changed_rev, changed_date, changed_author,
577 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
578 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
579 &have_base, &have_more_work, &have_work,
580 wc_ctx->db, local_abspath,
581 result_pool, scratch_pool));
582
583 if (!have_work
584 || ((!changed_rev || SVN_IS_VALID_REVNUM(*changed_rev))
585 && (!revision || SVN_IS_VALID_REVNUM(*revision)))
586 || ((status != svn_wc__db_status_added)
587 && (status != svn_wc__db_status_deleted)))
588 {
589 return SVN_NO_ERROR; /* We got everything we need */
590 }
591
592 if (have_base && !have_more_work)
593 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
594 changed_rev, changed_date, changed_author,
595 NULL, NULL, NULL,
596 NULL, NULL, NULL, NULL,
597 wc_ctx->db, local_abspath,
598 result_pool, scratch_pool));
599 else if (status == svn_wc__db_status_deleted)
600 /* Check the information below a WORKING delete */
601 SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, changed_rev,
602 changed_date, changed_author, NULL,
603 NULL, NULL, NULL, NULL,
604 wc_ctx->db, local_abspath,
605 result_pool, scratch_pool));
606
607 return SVN_NO_ERROR;
608 }
609
610 svn_error_t *
svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)611 svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t *wc_ctx,
612 const char *local_abspath,
613 apr_pool_t *scratch_pool)
614 {
615 return svn_error_trace(svn_wc__db_base_clear_dav_cache_recursive(
616 wc_ctx->db, local_abspath, scratch_pool));
617 }
618
619
620 svn_error_t *
svn_wc__node_get_lock_tokens_recursive(apr_hash_t ** lock_tokens,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)621 svn_wc__node_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
622 svn_wc_context_t *wc_ctx,
623 const char *local_abspath,
624 apr_pool_t *result_pool,
625 apr_pool_t *scratch_pool)
626 {
627 return svn_error_trace(svn_wc__db_base_get_lock_tokens_recursive(
628 lock_tokens, wc_ctx->db, local_abspath,
629 result_pool, scratch_pool));
630 }
631
632 svn_error_t *
svn_wc__get_excluded_subtrees(apr_hash_t ** server_excluded_subtrees,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)633 svn_wc__get_excluded_subtrees(apr_hash_t **server_excluded_subtrees,
634 svn_wc_context_t *wc_ctx,
635 const char *local_abspath,
636 apr_pool_t *result_pool,
637 apr_pool_t *scratch_pool)
638 {
639 return svn_error_trace(
640 svn_wc__db_get_excluded_subtrees(server_excluded_subtrees,
641 wc_ctx->db,
642 local_abspath,
643 result_pool,
644 scratch_pool));
645 }
646
647 svn_error_t *
svn_wc__internal_get_origin(svn_boolean_t * is_copy,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_depth_t * depth,const char ** copy_root_abspath,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t scan_deleted,apr_pool_t * result_pool,apr_pool_t * scratch_pool)648 svn_wc__internal_get_origin(svn_boolean_t *is_copy,
649 svn_revnum_t *revision,
650 const char **repos_relpath,
651 const char **repos_root_url,
652 const char **repos_uuid,
653 svn_depth_t *depth,
654 const char **copy_root_abspath,
655 svn_wc__db_t *db,
656 const char *local_abspath,
657 svn_boolean_t scan_deleted,
658 apr_pool_t *result_pool,
659 apr_pool_t *scratch_pool)
660 {
661 const char *original_repos_relpath;
662 const char *original_repos_root_url;
663 const char *original_repos_uuid;
664 svn_revnum_t original_revision;
665 svn_wc__db_status_t status;
666 svn_boolean_t have_more_work;
667 svn_boolean_t op_root;
668
669 const char *tmp_repos_relpath;
670
671 if (copy_root_abspath)
672 *copy_root_abspath = NULL;
673 if (!repos_relpath)
674 repos_relpath = &tmp_repos_relpath;
675
676 SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath,
677 repos_root_url, repos_uuid, NULL, NULL, NULL,
678 depth, NULL, NULL,
679 &original_repos_relpath,
680 &original_repos_root_url,
681 &original_repos_uuid, &original_revision,
682 NULL, NULL, NULL, NULL, NULL, &op_root, NULL,
683 NULL, NULL, &have_more_work, is_copy,
684 db, local_abspath, result_pool, scratch_pool));
685
686 if (*repos_relpath)
687 {
688 return SVN_NO_ERROR; /* Returned BASE information */
689 }
690
691 if (status == svn_wc__db_status_deleted && !scan_deleted)
692 {
693 if (is_copy)
694 *is_copy = FALSE; /* Deletes are stored in working; default to FALSE */
695
696 return SVN_NO_ERROR; /* No info */
697 }
698
699 if (original_repos_relpath)
700 {
701 /* We an have a copy */
702 *repos_relpath = original_repos_relpath;
703 if (revision)
704 *revision = original_revision;
705 if (repos_root_url)
706 *repos_root_url = original_repos_root_url;
707 if (repos_uuid)
708 *repos_uuid = original_repos_uuid;
709
710 if (copy_root_abspath == NULL)
711 return SVN_NO_ERROR;
712 else if (op_root)
713 {
714 *copy_root_abspath = apr_pstrdup(result_pool, local_abspath);
715 return SVN_NO_ERROR;
716 }
717 }
718
719 {
720 svn_boolean_t scan_working = FALSE;
721
722 if (status == svn_wc__db_status_added
723 || (status == svn_wc__db_status_deleted && have_more_work))
724 scan_working = TRUE;
725
726 if (scan_working)
727 {
728 const char *op_root_abspath;
729
730 SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath, NULL,
731 NULL, NULL, &original_repos_relpath,
732 repos_root_url,
733 repos_uuid, revision,
734 db, local_abspath,
735 result_pool, scratch_pool));
736
737 if (status == svn_wc__db_status_added)
738 {
739 if (is_copy)
740 *is_copy = FALSE;
741 return SVN_NO_ERROR; /* Local addition */
742 }
743
744 /* We don't know how the following error condition can be fulfilled
745 * but we have seen that happening in the wild. Better to create
746 * an error than a SEGFAULT. */
747 if (status == svn_wc__db_status_incomplete && !original_repos_relpath)
748 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
749 _("Incomplete copy information on path '%s'."),
750 svn_dirent_local_style(local_abspath,
751 scratch_pool));
752
753 *repos_relpath = svn_relpath_join(
754 original_repos_relpath,
755 svn_dirent_skip_ancestor(op_root_abspath,
756 local_abspath),
757 result_pool);
758 if (copy_root_abspath)
759 *copy_root_abspath = op_root_abspath;
760 }
761 else /* Deleted, excluded, not-present, server-excluded, ... */
762 {
763 if (is_copy)
764 *is_copy = FALSE;
765
766 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, repos_relpath,
767 repos_root_url, repos_uuid, NULL,
768 NULL, NULL, NULL, NULL, NULL, NULL,
769 NULL, NULL, NULL,
770 db, local_abspath,
771 result_pool, scratch_pool));
772 }
773
774 return SVN_NO_ERROR;
775 }
776 }
777
778 svn_error_t *
svn_wc__node_get_origin(svn_boolean_t * is_copy,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_depth_t * depth,const char ** copy_root_abspath,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t scan_deleted,apr_pool_t * result_pool,apr_pool_t * scratch_pool)779 svn_wc__node_get_origin(svn_boolean_t *is_copy,
780 svn_revnum_t *revision,
781 const char **repos_relpath,
782 const char **repos_root_url,
783 const char **repos_uuid,
784 svn_depth_t *depth,
785 const char **copy_root_abspath,
786 svn_wc_context_t *wc_ctx,
787 const char *local_abspath,
788 svn_boolean_t scan_deleted,
789 apr_pool_t *result_pool,
790 apr_pool_t *scratch_pool)
791 {
792 return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision,
793 repos_relpath, repos_root_url, repos_uuid,
794 depth, copy_root_abspath,
795 wc_ctx->db, local_abspath, scan_deleted,
796 result_pool, scratch_pool));
797 }
798
799 svn_error_t *
svn_wc__node_get_commit_status(svn_boolean_t * added,svn_boolean_t * deleted,svn_boolean_t * is_replace_root,svn_boolean_t * is_op_root,svn_revnum_t * revision,svn_revnum_t * original_revision,const char ** original_repos_relpath,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)800 svn_wc__node_get_commit_status(svn_boolean_t *added,
801 svn_boolean_t *deleted,
802 svn_boolean_t *is_replace_root,
803 svn_boolean_t *is_op_root,
804 svn_revnum_t *revision,
805 svn_revnum_t *original_revision,
806 const char **original_repos_relpath,
807 svn_wc_context_t *wc_ctx,
808 const char *local_abspath,
809 apr_pool_t *result_pool,
810 apr_pool_t *scratch_pool)
811 {
812 svn_wc__db_status_t status;
813 svn_boolean_t have_base;
814 svn_boolean_t have_more_work;
815 svn_boolean_t op_root;
816
817 /* ### All of this should be handled inside a single read transaction */
818 SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL,
819 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
820 original_repos_relpath, NULL, NULL,
821 original_revision, NULL, NULL, NULL,
822 NULL, NULL,
823 &op_root, NULL, NULL,
824 &have_base, &have_more_work, NULL,
825 wc_ctx->db, local_abspath,
826 result_pool, scratch_pool));
827
828 if (added)
829 *added = (status == svn_wc__db_status_added);
830 if (deleted)
831 *deleted = (status == svn_wc__db_status_deleted);
832 if (is_op_root)
833 *is_op_root = op_root;
834
835 if (is_replace_root)
836 {
837 if (status == svn_wc__db_status_added
838 && op_root
839 && (have_base || have_more_work))
840 SVN_ERR(svn_wc__db_node_check_replace(is_replace_root, NULL, NULL,
841 wc_ctx->db, local_abspath,
842 scratch_pool));
843 else
844 *is_replace_root = FALSE;
845 }
846
847 /* Retrieve some information from BASE which is needed for replacing
848 and/or deleting BASE nodes. */
849 if (have_base
850 && !have_more_work
851 && op_root
852 && (revision && !SVN_IS_VALID_REVNUM(*revision)))
853 {
854 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
855 NULL, NULL, NULL, NULL, NULL, NULL,
856 NULL, NULL, NULL, NULL,
857 wc_ctx->db, local_abspath,
858 scratch_pool, scratch_pool));
859 }
860
861 return SVN_NO_ERROR;
862 }
863
864 svn_error_t *
svn_wc__node_get_md5_from_sha1(const svn_checksum_t ** md5_checksum,svn_wc_context_t * wc_ctx,const char * wri_abspath,const svn_checksum_t * sha1_checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)865 svn_wc__node_get_md5_from_sha1(const svn_checksum_t **md5_checksum,
866 svn_wc_context_t *wc_ctx,
867 const char *wri_abspath,
868 const svn_checksum_t *sha1_checksum,
869 apr_pool_t *result_pool,
870 apr_pool_t *scratch_pool)
871 {
872 return svn_error_trace(svn_wc__db_pristine_get_md5(md5_checksum,
873 wc_ctx->db,
874 wri_abspath,
875 sha1_checksum,
876 result_pool,
877 scratch_pool));
878 }
879
880 svn_error_t *
svn_wc__get_not_present_descendants(const apr_array_header_t ** descendants,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)881 svn_wc__get_not_present_descendants(const apr_array_header_t **descendants,
882 svn_wc_context_t *wc_ctx,
883 const char *local_abspath,
884 apr_pool_t *result_pool,
885 apr_pool_t *scratch_pool)
886 {
887 return svn_error_trace(
888 svn_wc__db_get_not_present_descendants(descendants,
889 wc_ctx->db,
890 local_abspath,
891 result_pool,
892 scratch_pool));
893 }
894
895 svn_error_t *
svn_wc__rename_wc(svn_wc_context_t * wc_ctx,const char * from_abspath,const char * dst_abspath,apr_pool_t * scratch_pool)896 svn_wc__rename_wc(svn_wc_context_t *wc_ctx,
897 const char *from_abspath,
898 const char *dst_abspath,
899 apr_pool_t *scratch_pool)
900 {
901 const char *wcroot_abspath;
902 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, wc_ctx->db, from_abspath,
903 scratch_pool, scratch_pool));
904
905 if (! strcmp(from_abspath, wcroot_abspath))
906 {
907 SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, wcroot_abspath, scratch_pool));
908
909 SVN_ERR(svn_io_file_rename(from_abspath, dst_abspath, scratch_pool));
910 }
911 else
912 return svn_error_createf(
913 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
914 _("'%s' is not the root of the working copy '%s'"),
915 svn_dirent_local_style(from_abspath, scratch_pool),
916 svn_dirent_local_style(wcroot_abspath, scratch_pool));
917
918 return SVN_NO_ERROR;
919 }
920
921 svn_error_t *
svn_wc__check_for_obstructions(svn_wc_notify_state_t * obstruction_state,svn_node_kind_t * kind,svn_boolean_t * deleted,svn_boolean_t * excluded,svn_depth_t * parent_depth,svn_wc_context_t * wc_ctx,const char * local_abspath,svn_boolean_t no_wcroot_check,apr_pool_t * scratch_pool)922 svn_wc__check_for_obstructions(svn_wc_notify_state_t *obstruction_state,
923 svn_node_kind_t *kind,
924 svn_boolean_t *deleted,
925 svn_boolean_t *excluded,
926 svn_depth_t *parent_depth,
927 svn_wc_context_t *wc_ctx,
928 const char *local_abspath,
929 svn_boolean_t no_wcroot_check,
930 apr_pool_t *scratch_pool)
931 {
932 svn_wc__db_status_t status;
933 svn_node_kind_t db_kind;
934 svn_node_kind_t disk_kind;
935 svn_error_t *err;
936
937 *obstruction_state = svn_wc_notify_state_inapplicable;
938 if (kind)
939 *kind = svn_node_none;
940 if (deleted)
941 *deleted = FALSE;
942 if (excluded)
943 *excluded = FALSE;
944 if (parent_depth)
945 *parent_depth = svn_depth_unknown;
946
947 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
948
949 err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL,
950 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
951 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
952 NULL, NULL, NULL, NULL, NULL,
953 wc_ctx->db, local_abspath,
954 scratch_pool, scratch_pool);
955
956 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
957 {
958 svn_error_clear(err);
959
960 if (disk_kind != svn_node_none)
961 {
962 /* Nothing in the DB, but something on disk */
963 *obstruction_state = svn_wc_notify_state_obstructed;
964 return SVN_NO_ERROR;
965 }
966
967 err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
968 NULL, NULL, NULL, parent_depth, NULL, NULL,
969 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
970 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
971 NULL,
972 wc_ctx->db, svn_dirent_dirname(local_abspath,
973 scratch_pool),
974 scratch_pool, scratch_pool);
975
976 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
977 {
978 svn_error_clear(err);
979 /* No versioned parent; we can't add a node here */
980 *obstruction_state = svn_wc_notify_state_obstructed;
981 return SVN_NO_ERROR;
982 }
983 else
984 SVN_ERR(err);
985
986 if (db_kind != svn_node_dir
987 || (status != svn_wc__db_status_normal
988 && status != svn_wc__db_status_added))
989 {
990 /* The parent doesn't allow nodes to be added below it */
991 *obstruction_state = svn_wc_notify_state_obstructed;
992 }
993
994 return SVN_NO_ERROR;
995 }
996 else
997 SVN_ERR(err);
998
999 /* Check for obstructing working copies */
1000 if (!no_wcroot_check
1001 && db_kind == svn_node_dir
1002 && status == svn_wc__db_status_normal)
1003 {
1004 svn_boolean_t is_root;
1005 SVN_ERR(svn_wc__db_is_wcroot(&is_root, wc_ctx->db, local_abspath,
1006 scratch_pool));
1007
1008 if (is_root)
1009 {
1010 /* Callers should handle this as unversioned */
1011 *obstruction_state = svn_wc_notify_state_obstructed;
1012 return SVN_NO_ERROR;
1013 }
1014 }
1015
1016 if (kind)
1017 SVN_ERR(convert_db_kind_to_node_kind(kind, db_kind, status, FALSE));
1018
1019 switch (status)
1020 {
1021 case svn_wc__db_status_deleted:
1022 if (deleted)
1023 *deleted = TRUE;
1024 /* Fall through to svn_wc__db_status_not_present */
1025 case svn_wc__db_status_not_present:
1026 if (disk_kind != svn_node_none)
1027 *obstruction_state = svn_wc_notify_state_obstructed;
1028 break;
1029
1030 case svn_wc__db_status_excluded:
1031 case svn_wc__db_status_server_excluded:
1032 if (excluded)
1033 *excluded = TRUE;
1034 /* fall through */
1035 case svn_wc__db_status_incomplete:
1036 *obstruction_state = svn_wc_notify_state_missing;
1037 break;
1038
1039 case svn_wc__db_status_added:
1040 case svn_wc__db_status_normal:
1041 if (disk_kind == svn_node_none)
1042 *obstruction_state = svn_wc_notify_state_missing;
1043 else
1044 {
1045 svn_node_kind_t expected_kind;
1046
1047 SVN_ERR(convert_db_kind_to_node_kind(&expected_kind, db_kind,
1048 status, FALSE));
1049
1050 if (disk_kind != expected_kind)
1051 *obstruction_state = svn_wc_notify_state_obstructed;
1052 }
1053 break;
1054 default:
1055 SVN_ERR_MALFUNCTION();
1056 }
1057
1058 return SVN_NO_ERROR;
1059 }
1060
1061
1062 svn_error_t *
svn_wc__node_was_moved_away(const char ** moved_to_abspath,const char ** op_root_abspath,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1063 svn_wc__node_was_moved_away(const char **moved_to_abspath,
1064 const char **op_root_abspath,
1065 svn_wc_context_t *wc_ctx,
1066 const char *local_abspath,
1067 apr_pool_t *result_pool,
1068 apr_pool_t *scratch_pool)
1069 {
1070 svn_wc__db_status_t status;
1071
1072 if (moved_to_abspath)
1073 *moved_to_abspath = NULL;
1074 if (op_root_abspath)
1075 *op_root_abspath = NULL;
1076
1077 SVN_ERR(svn_wc__db_read_info(&status,
1078 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1079 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1080 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1081 NULL, NULL, NULL, NULL, NULL,
1082 wc_ctx->db, local_abspath,
1083 scratch_pool, scratch_pool));
1084
1085 if (status == svn_wc__db_status_deleted)
1086 SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL,
1087 op_root_abspath, wc_ctx->db,
1088 local_abspath,
1089 result_pool, scratch_pool));
1090
1091 return SVN_NO_ERROR;
1092 }
1093
1094
1095 svn_error_t *
svn_wc__node_was_moved_here(const char ** moved_from_abspath,const char ** delete_op_root_abspath,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1096 svn_wc__node_was_moved_here(const char **moved_from_abspath,
1097 const char **delete_op_root_abspath,
1098 svn_wc_context_t *wc_ctx,
1099 const char *local_abspath,
1100 apr_pool_t *result_pool,
1101 apr_pool_t *scratch_pool)
1102 {
1103 svn_error_t *err;
1104
1105 if (moved_from_abspath)
1106 *moved_from_abspath = NULL;
1107 if (delete_op_root_abspath)
1108 *delete_op_root_abspath = NULL;
1109
1110 err = svn_wc__db_scan_moved(moved_from_abspath, NULL, NULL,
1111 delete_op_root_abspath,
1112 wc_ctx->db, local_abspath,
1113 result_pool, scratch_pool);
1114
1115 if (err)
1116 {
1117 /* Return error for not added nodes */
1118 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1119 return svn_error_trace(err);
1120
1121 /* Path not moved here */
1122 svn_error_clear(err);
1123 return SVN_NO_ERROR;
1124 }
1125
1126 return SVN_NO_ERROR;
1127 }
1128