2 * list.c: list local and remote directory entries.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 #include <apr_fnmatch.h>
26 #include "svn_client.h"
27 #include "svn_dirent_uri.h"
30 #include "svn_pools.h"
32 #include "svn_sorts.h"
33 #include "svn_props.h"
37 #include "private/svn_fspath.h"
38 #include "private/svn_ra_private.h"
39 #include "private/svn_sorts_private.h"
40 #include "private/svn_utf_private.h"
41 #include "private/svn_wc_private.h"
42 #include "svn_private_config.h"
44 /* Prototypes for referencing before declaration */
46 list_externals(apr_hash_t *externals,
47 const apr_array_header_t *patterns,
49 apr_uint32_t dirent_fields,
50 svn_boolean_t fetch_locks,
51 svn_client_list_func2_t list_func,
53 svn_client_ctx_t *ctx,
54 apr_pool_t *scratch_pool);
57 list_internal(const char *path_or_url,
58 const svn_opt_revision_t *peg_revision,
59 const svn_opt_revision_t *revision,
60 const apr_array_header_t *patterns,
62 apr_uint32_t dirent_fields,
63 svn_boolean_t fetch_locks,
64 svn_boolean_t include_externals,
65 const char *external_parent_url,
66 const char *external_target,
67 svn_client_list_func2_t list_func,
69 svn_client_ctx_t *ctx,
72 /* Return TRUE if S matches any of the const char * in PATTERNS.
73 * Note that any S will match if PATTERNS is empty.
74 * Use SCRATCH_BUFFER for temporary string contents. */
76 match_patterns(const char *s,
77 const apr_array_header_t *patterns,
78 svn_membuf_t *scratch_buffer)
81 ? svn_utf__fuzzy_glob_match(s, patterns, scratch_buffer)
85 /* Get the directory entries of DIR at REV (relative to the root of
86 RA_SESSION), getting at least the fields specified by DIRENT_FIELDS.
87 Use the cancellation function/baton of CTX to check for cancellation.
89 If DEPTH is svn_depth_empty, return immediately. If DEPTH is
90 svn_depth_files, invoke LIST_FUNC on the file entries with BATON;
91 if svn_depth_immediates, invoke it on file and directory entries;
92 if svn_depth_infinity, invoke it on file and directory entries and
93 recurse into the directory entries with the same depth.
95 If PATTERNS is not empty, the last path segments must match at least
96 one of const char * patterns in it or the respective dirent will not
99 LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t
100 objects and FS_PATH is the absolute filesystem path of the RA session.
101 Use SCRATCH_POOL for temporary allocations.
103 If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
104 hash table whose keys are URLs of the directory which has externals
105 definitions, and whose values are the externals description text.
106 Allocate the hash's keys and values in RESULT_POOL.
108 EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
109 are listed, otherwise both are set to NULL by the caller.
111 Use SCRATCH_BUFFER for temporary string contents.
114 get_dir_contents(apr_uint32_t dirent_fields,
117 svn_ra_session_t *ra_session,
120 const apr_array_header_t *patterns,
122 svn_client_ctx_t *ctx,
123 apr_hash_t *externals,
124 const char *external_parent_url,
125 const char *external_target,
126 svn_client_list_func2_t list_func,
128 svn_membuf_t *scratch_buffer,
129 apr_pool_t *result_pool,
130 apr_pool_t *scratch_pool)
132 apr_hash_t *tmpdirents;
133 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
134 apr_array_header_t *array;
136 apr_hash_t *prop_hash = NULL;
137 const svn_string_t *prop_val = NULL;
140 if (depth == svn_depth_empty)
143 /* Get the directory's entries. If externals hash is non-NULL, get its
144 properties also. Ignore any not-authorized errors. */
145 err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
146 externals ? &prop_hash : NULL,
147 dir, rev, dirent_fields, scratch_pool);
149 if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
150 (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
152 svn_error_clear(err);
157 /* Locks will often be empty. Prevent pointless lookups in that case. */
158 if (locks && apr_hash_count(locks) == 0)
161 /* Filter out svn:externals from all properties hash. */
163 prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
168 SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
170 svn_hash_sets(externals,
171 svn_path_url_add_component2(url, dir, result_pool),
172 svn_string_dup(prop_val, result_pool));
175 if (ctx->cancel_func)
176 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
178 /* Sort the hash, so we can call the callback in a "deterministic" order. */
179 array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
181 for (i = 0; i < array->nelts; ++i)
183 svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
185 svn_dirent_t *the_ent = item->value;
188 svn_pool_clear(iterpool);
190 path = svn_relpath_join(dir, item->key, iterpool);
194 const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
195 lock = svn_hash_gets(locks, abs_path);
200 if (the_ent->kind == svn_node_file
201 || depth == svn_depth_immediates
202 || depth == svn_depth_infinity)
203 if (match_patterns(item->key, patterns, scratch_buffer))
204 SVN_ERR(list_func(baton, path, the_ent, lock, fs_path,
205 external_parent_url, external_target, iterpool));
207 /* If externals is non-NULL, populate the externals hash table
208 recursively for all directory entries. */
209 if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
210 SVN_ERR(get_dir_contents(dirent_fields, path, rev, ra_session,
211 locks, fs_path, patterns, depth, ctx,
212 externals, external_parent_url,
213 external_target, list_func, baton,
214 scratch_buffer, result_pool, iterpool));
217 svn_pool_destroy(iterpool);
221 /* Baton type to be used with list_receiver. */
222 typedef struct receiver_baton_t
224 /* Wrapped callback function to invoke. */
225 svn_client_list_func2_t list_func;
227 /* Baton to be used with LIST_FUNC. */
230 /* Client context providing cancellation support. */
231 svn_client_ctx_t *ctx;
233 /* All locks found for the whole tree; pick yours. */
236 /* Start path of the operation. */
237 const char *fs_base_path;
240 /* Implement svn_ra_dirent_receiver_t.
241 The BATON type must be a receiver_baton_t. */
243 list_receiver(const char *rel_path,
244 svn_dirent_t *dirent,
248 receiver_baton_t *b = baton;
249 const svn_lock_t *lock = NULL;
251 /* We only report the path relative to the start path. */
252 rel_path = svn_dirent_skip_ancestor(b->fs_base_path, rel_path);
256 const char *abs_path = svn_dirent_join(b->fs_base_path, rel_path, pool);
257 lock = svn_hash_gets(b->locks, abs_path);
260 if (b->ctx->cancel_func)
261 SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
263 SVN_ERR(b->list_func(b->list_baton, rel_path, dirent, lock,
264 b->fs_base_path, NULL, NULL, pool));
269 /* List the file/directory entries for PATH_OR_URL at REVISION.
270 The actual node revision selected is determined by the path as
271 it exists in PEG_REVISION.
273 If DEPTH is svn_depth_infinity, then list all file and directory entries
274 recursively. Else if DEPTH is svn_depth_files, list all files under
275 PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is
276 svn_depth_immediates, list all files and include immediate
277 subdirectories (at svn_depth_empty). Else if DEPTH is
278 svn_depth_empty, just list PATH_OR_URL with none of its entries.
280 If PATTERNS is not NULL, the last path segments must match at least
281 one of const char * patterns in it or the respective dirent will not
284 DIRENT_FIELDS controls which fields in the svn_dirent_t's are
285 filled in. To have them totally filled in use SVN_DIRENT_ALL,
286 otherwise simply bitwise OR together the combination of SVN_DIRENT_*
287 fields you care about.
289 If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
291 If INCLUDE_EXTERNALS is TRUE, also list all external items
292 reached by recursion. DEPTH value passed to the original list target
293 applies for the externals also. EXTERNAL_PARENT_URL is url of the
294 directory which has the externals definitions. EXTERNAL_TARGET is the
295 target subdirectory of externals definitions.
297 Report directory entries by invoking LIST_FUNC/BATON.
298 Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external
299 items are listed, otherwise both are set to NULL.
301 Use authentication baton cached in CTX to authenticate against the
304 Use POOL for all allocations.
307 list_internal(const char *path_or_url,
308 const svn_opt_revision_t *peg_revision,
309 const svn_opt_revision_t *revision,
310 const apr_array_header_t *patterns,
312 apr_uint32_t dirent_fields,
313 svn_boolean_t fetch_locks,
314 svn_boolean_t include_externals,
315 const char *external_parent_url,
316 const char *external_target,
317 svn_client_list_func2_t list_func,
319 svn_client_ctx_t *ctx,
322 svn_ra_session_t *ra_session;
323 svn_client__pathrev_t *loc;
324 svn_dirent_t *dirent;
328 apr_hash_t *externals;
329 svn_membuf_t scratch_buffer;
331 if (include_externals)
332 externals = apr_hash_make(pool);
336 /* We use the kind field to determine if we should recurse, so we
338 dirent_fields |= SVN_DIRENT_KIND;
340 /* Get an RA plugin for this filesystem object. */
341 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
344 revision, ctx, pool));
346 fs_path = svn_client__pathrev_fspath(loc, pool);
348 /* Maybe get all locks under url. */
351 /* IMPORTANT: If locks are stored in a more temporary pool, we need
352 to fix store_dirent below to duplicate the locks. */
353 err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool);
355 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
357 svn_error_clear(err);
361 return svn_error_trace(err);
366 /* Try to use the efficient and fully authz-filtered code path. */
367 if (!include_externals)
369 receiver_baton_t receiver_baton;
370 receiver_baton.list_baton = baton;
371 receiver_baton.ctx = ctx;
372 receiver_baton.list_func = list_func;
373 receiver_baton.locks = locks;
374 receiver_baton.fs_base_path = fs_path;
376 err = svn_ra_list(ra_session, "", loc->rev, patterns, depth,
377 dirent_fields, list_receiver, &receiver_baton, pool);
379 if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
380 svn_error_clear(err);
382 return svn_error_trace(err);
385 /* Stat for the file / directory node itself. */
386 SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool));
388 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
389 _("URL '%s' non-existent in revision %ld"),
392 /* We need a scratch buffer for temporary string data.
393 * Create one with a reasonable initial size. */
394 svn_membuf__create(&scratch_buffer, 256, pool);
396 /* Report the dirent for the target. */
397 if (match_patterns(svn_dirent_basename(fs_path, pool), patterns,
399 SVN_ERR(list_func(baton, "", dirent, locks
400 ? (svn_hash_gets(locks, fs_path))
401 : NULL, fs_path, external_parent_url,
402 external_target, pool));
404 if (dirent->kind == svn_node_dir
405 && (depth == svn_depth_files
406 || depth == svn_depth_immediates
407 || depth == svn_depth_infinity))
408 SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
409 fs_path, patterns, depth, ctx, externals,
410 external_parent_url, external_target, list_func,
411 baton, &scratch_buffer, pool, pool));
413 /* We handle externals after listing entries under path_or_url, so that
414 handling external items (and any errors therefrom) doesn't delay
415 the primary operation. */
416 if (include_externals && apr_hash_count(externals))
418 /* The 'externals' hash populated by get_dir_contents() is processed
420 SVN_ERR(list_externals(externals, patterns, depth, dirent_fields,
421 fetch_locks, list_func, baton,
429 wrap_list_error(const svn_client_ctx_t *ctx,
430 const char *target_abspath,
432 apr_pool_t *scratch_pool)
434 if (err && err->apr_err != SVN_ERR_CANCELLED)
436 if (ctx->notify_func2)
438 svn_wc_notify_t *notifier = svn_wc_create_notify(
440 svn_wc_notify_failed_external,
443 ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
445 svn_error_clear(err);
453 /* Walk through all the external items and list them. */
455 list_external_items(apr_array_header_t *external_items,
456 const char *externals_parent_url,
457 const apr_array_header_t *patterns,
459 apr_uint32_t dirent_fields,
460 svn_boolean_t fetch_locks,
461 svn_client_list_func2_t list_func,
463 svn_client_ctx_t *ctx,
464 apr_pool_t *scratch_pool)
466 const char *externals_parent_repos_root_url;
467 apr_pool_t *iterpool;
470 SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
472 externals_parent_url, ctx,
473 scratch_pool, scratch_pool));
475 iterpool = svn_pool_create(scratch_pool);
477 for (i = 0; i < external_items->nelts; i++)
479 const char *resolved_url;
481 svn_wc_external_item2_t *item =
482 APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
484 svn_pool_clear(iterpool);
486 SVN_ERR(svn_wc__resolve_relative_external_url(
489 externals_parent_repos_root_url,
490 externals_parent_url,
491 iterpool, iterpool));
493 /* List the external */
494 SVN_ERR(wrap_list_error(ctx, item->target_dir,
495 list_internal(resolved_url,
499 depth, dirent_fields,
502 externals_parent_url,
504 list_func, baton, ctx,
509 svn_pool_destroy(iterpool);
514 /* List external items defined on each external in EXTERNALS, a const char *
515 externals_parent_url(url of the directory which has the externals
516 definitions) of all externals mapping to the svn_string_t * externals_desc
517 (externals description text). All other options are the same as those
518 passed to svn_client_list(). */
520 list_externals(apr_hash_t *externals,
521 const apr_array_header_t *patterns,
523 apr_uint32_t dirent_fields,
524 svn_boolean_t fetch_locks,
525 svn_client_list_func2_t list_func,
527 svn_client_ctx_t *ctx,
528 apr_pool_t *scratch_pool)
530 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
531 apr_hash_index_t *hi;
533 for (hi = apr_hash_first(scratch_pool, externals);
535 hi = apr_hash_next(hi))
537 const char *externals_parent_url = apr_hash_this_key(hi);
538 svn_string_t *externals_desc = apr_hash_this_val(hi);
539 apr_array_header_t *external_items;
541 svn_pool_clear(iterpool);
543 SVN_ERR(svn_wc_parse_externals_description3(&external_items,
544 externals_parent_url,
545 externals_desc->data,
548 if (! external_items->nelts)
551 SVN_ERR(list_external_items(external_items, externals_parent_url,
552 patterns, depth, dirent_fields,
553 fetch_locks, list_func, baton, ctx,
557 svn_pool_destroy(iterpool);
564 svn_client_list4(const char *path_or_url,
565 const svn_opt_revision_t *peg_revision,
566 const svn_opt_revision_t *revision,
567 const apr_array_header_t *patterns,
569 apr_uint32_t dirent_fields,
570 svn_boolean_t fetch_locks,
571 svn_boolean_t include_externals,
572 svn_client_list_func2_t list_func,
574 svn_client_ctx_t *ctx,
575 apr_pool_t *scratch_pool)
578 return svn_error_trace(list_internal(path_or_url, peg_revision,
580 depth, dirent_fields,
583 NULL, NULL, list_func,
584 baton, ctx, scratch_pool));