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 "svn_client.h"
25 #include "svn_dirent_uri.h"
28 #include "svn_pools.h"
30 #include "svn_sorts.h"
31 #include "svn_props.h"
35 #include "private/svn_fspath.h"
36 #include "private/svn_ra_private.h"
37 #include "private/svn_wc_private.h"
38 #include "svn_private_config.h"
40 /* Prototypes for referencing before declaration */
42 list_externals(apr_hash_t *externals,
44 apr_uint32_t dirent_fields,
45 svn_boolean_t fetch_locks,
46 svn_client_list_func2_t list_func,
48 svn_client_ctx_t *ctx,
49 apr_pool_t *scratch_pool);
52 list_internal(const char *path_or_url,
53 const svn_opt_revision_t *peg_revision,
54 const svn_opt_revision_t *revision,
56 apr_uint32_t dirent_fields,
57 svn_boolean_t fetch_locks,
58 svn_boolean_t include_externals,
59 const char *external_parent_url,
60 const char *external_target,
61 svn_client_list_func2_t list_func,
63 svn_client_ctx_t *ctx,
67 /* Get the directory entries of DIR at REV (relative to the root of
68 RA_SESSION), getting at least the fields specified by DIRENT_FIELDS.
69 Use the cancellation function/baton of CTX to check for cancellation.
71 If DEPTH is svn_depth_empty, return immediately. If DEPTH is
72 svn_depth_files, invoke LIST_FUNC on the file entries with BATON;
73 if svn_depth_immediates, invoke it on file and directory entries;
74 if svn_depth_infinity, invoke it on file and directory entries and
75 recurse into the directory entries with the same depth.
77 LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t
78 objects and FS_PATH is the absolute filesystem path of the RA session.
79 Use SCRATCH_POOL for temporary allocations.
81 If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
82 hash table whose keys are URLs of the directory which has externals
83 definitions, and whose values are the externals description text.
84 Allocate the hash's keys and values in RESULT_POOL.
86 EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
87 are listed, otherwise both are set to NULL by the caller.
90 get_dir_contents(apr_uint32_t dirent_fields,
93 svn_ra_session_t *ra_session,
97 svn_client_ctx_t *ctx,
98 apr_hash_t *externals,
99 const char *external_parent_url,
100 const char *external_target,
101 svn_client_list_func2_t list_func,
103 apr_pool_t *result_pool,
104 apr_pool_t *scratch_pool)
106 apr_hash_t *tmpdirents;
107 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
108 apr_array_header_t *array;
110 apr_hash_t *prop_hash = NULL;
111 const svn_string_t *prop_val = NULL;
114 if (depth == svn_depth_empty)
117 /* Get the directory's entries. If externals hash is non-NULL, get its
118 properties also. Ignore any not-authorized errors. */
119 err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
120 externals ? &prop_hash : NULL,
121 dir, rev, dirent_fields, scratch_pool);
123 if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
124 (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
126 svn_error_clear(err);
131 /* Filter out svn:externals from all properties hash. */
133 prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
138 SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
140 svn_hash_sets(externals,
141 svn_path_url_add_component2(url, dir, result_pool),
142 svn_string_dup(prop_val, result_pool));
145 if (ctx->cancel_func)
146 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
148 /* Sort the hash, so we can call the callback in a "deterministic" order. */
149 array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
151 for (i = 0; i < array->nelts; ++i)
153 svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
155 svn_dirent_t *the_ent = item->value;
158 svn_pool_clear(iterpool);
160 path = svn_relpath_join(dir, item->key, iterpool);
164 const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
165 lock = svn_hash_gets(locks, abs_path);
170 if (the_ent->kind == svn_node_file
171 || depth == svn_depth_immediates
172 || depth == svn_depth_infinity)
173 SVN_ERR(list_func(baton, path, the_ent, lock, fs_path,
174 external_parent_url, external_target, iterpool));
176 /* If externals is non-NULL, populate the externals hash table
177 recursively for all directory entries. */
178 if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
179 SVN_ERR(get_dir_contents(dirent_fields, path, rev,
180 ra_session, locks, fs_path, depth, ctx,
181 externals, external_parent_url,
182 external_target, list_func, baton,
183 result_pool, iterpool));
186 svn_pool_destroy(iterpool);
190 /* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */
191 /* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */
193 svn_client__ra_stat_compatible(svn_ra_session_t *ra_session,
195 svn_dirent_t **dirent_p,
196 apr_uint32_t dirent_fields,
197 svn_client_ctx_t *ctx,
202 err = svn_ra_stat(ra_session, "", rev, dirent_p, pool);
204 /* svnserve before 1.2 doesn't support the above, so fall back on
205 a less efficient method. */
206 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
208 const char *repos_root_url;
209 const char *session_url;
210 svn_node_kind_t kind;
211 svn_dirent_t *dirent;
213 svn_error_clear(err);
215 SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
216 SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
218 SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool));
220 if (kind != svn_node_none)
222 if (strcmp(session_url, repos_root_url) != 0)
224 svn_ra_session_t *parent_session;
225 apr_hash_t *parent_ents;
226 const char *parent_url, *base_name;
227 apr_pool_t *subpool = svn_pool_create(pool);
229 /* Open another session to the path's parent. This server
230 doesn't support svn_ra_reparent anyway, so don't try it. */
231 svn_uri_split(&parent_url, &base_name, session_url, subpool);
233 SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url,
237 /* Get all parent's entries, no props. */
238 SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
239 NULL, "", rev, dirent_fields, subpool));
241 /* Get the relevant entry. */
242 dirent = svn_hash_gets(parent_ents, base_name);
245 *dirent_p = svn_dirent_dup(dirent, pool);
249 svn_pool_destroy(subpool); /* Close RA session */
253 /* We can't get the directory entry for the repository root,
254 but we can still get the information we want.
255 The created-rev of the repository root must, by definition,
257 dirent = apr_palloc(pool, sizeof(*dirent));
259 dirent->size = SVN_INVALID_FILESIZE;
260 if (dirent_fields & SVN_DIRENT_HAS_PROPS)
263 SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props,
264 "", rev, 0 /* no dirent fields */,
266 dirent->has_props = (apr_hash_count(props) != 0);
268 dirent->created_rev = rev;
269 if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
274 SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props,
276 val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
278 SVN_ERR(svn_time_from_cstring(&dirent->time, val->data,
283 val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
284 dirent->last_author = val ? val->data : NULL;
299 /* List the file/directory entries for PATH_OR_URL at REVISION.
300 The actual node revision selected is determined by the path as
301 it exists in PEG_REVISION.
303 If DEPTH is svn_depth_infinity, then list all file and directory entries
304 recursively. Else if DEPTH is svn_depth_files, list all files under
305 PATH_OR_URL (if any), but not subdirectories. Else if DEPTH is
306 svn_depth_immediates, list all files and include immediate
307 subdirectories (at svn_depth_empty). Else if DEPTH is
308 svn_depth_empty, just list PATH_OR_URL with none of its entries.
310 DIRENT_FIELDS controls which fields in the svn_dirent_t's are
311 filled in. To have them totally filled in use SVN_DIRENT_ALL,
312 otherwise simply bitwise OR together the combination of SVN_DIRENT_*
313 fields you care about.
315 If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
317 If INCLUDE_EXTERNALS is TRUE, also list all external items
318 reached by recursion. DEPTH value passed to the original list target
319 applies for the externals also. EXTERNAL_PARENT_URL is url of the
320 directory which has the externals definitions. EXTERNAL_TARGET is the
321 target subdirectory of externals definitions.
323 Report directory entries by invoking LIST_FUNC/BATON.
324 Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external
325 items are listed, otherwise both are set to NULL.
327 Use authentication baton cached in CTX to authenticate against the
330 Use POOL for all allocations.
333 list_internal(const char *path_or_url,
334 const svn_opt_revision_t *peg_revision,
335 const svn_opt_revision_t *revision,
337 apr_uint32_t dirent_fields,
338 svn_boolean_t fetch_locks,
339 svn_boolean_t include_externals,
340 const char *external_parent_url,
341 const char *external_target,
342 svn_client_list_func2_t list_func,
344 svn_client_ctx_t *ctx,
347 svn_ra_session_t *ra_session;
348 svn_client__pathrev_t *loc;
349 svn_dirent_t *dirent;
353 apr_hash_t *externals;
355 if (include_externals)
356 externals = apr_hash_make(pool);
360 /* We use the kind field to determine if we should recurse, so we
362 dirent_fields |= SVN_DIRENT_KIND;
364 /* Get an RA plugin for this filesystem object. */
365 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
368 revision, ctx, pool));
370 fs_path = svn_client__pathrev_fspath(loc, pool);
372 SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent,
373 dirent_fields, ctx, pool));
375 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
376 _("URL '%s' non-existent in revision %ld"),
379 /* Maybe get all locks under url. */
382 /* IMPORTANT: If locks are stored in a more temporary pool, we need
383 to fix store_dirent below to duplicate the locks. */
384 err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool);
386 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
388 svn_error_clear(err);
392 return svn_error_trace(err);
397 /* Report the dirent for the target. */
398 SVN_ERR(list_func(baton, "", dirent, locks
399 ? (svn_hash_gets(locks, fs_path))
400 : NULL, fs_path, external_parent_url,
401 external_target, pool));
403 if (dirent->kind == svn_node_dir
404 && (depth == svn_depth_files
405 || depth == svn_depth_immediates
406 || depth == svn_depth_infinity))
407 SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
408 fs_path, depth, ctx, externals,
409 external_parent_url, external_target, list_func,
412 /* We handle externals after listing entries under path_or_url, so that
413 handling external items (and any errors therefrom) doesn't delay
414 the primary operation. */
415 if (include_externals && apr_hash_count(externals))
417 /* The 'externals' hash populated by get_dir_contents() is processed
419 SVN_ERR(list_externals(externals, depth, dirent_fields,
420 fetch_locks, list_func, baton,
428 wrap_list_error(const svn_client_ctx_t *ctx,
429 const char *target_abspath,
431 apr_pool_t *scratch_pool)
433 if (err && err->apr_err != SVN_ERR_CANCELLED)
435 if (ctx->notify_func2)
437 svn_wc_notify_t *notifier = svn_wc_create_notify(
439 svn_wc_notify_failed_external,
442 ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
444 svn_error_clear(err);
452 /* Walk through all the external items and list them. */
454 list_external_items(apr_array_header_t *external_items,
455 const char *externals_parent_url,
457 apr_uint32_t dirent_fields,
458 svn_boolean_t fetch_locks,
459 svn_client_list_func2_t list_func,
461 svn_client_ctx_t *ctx,
462 apr_pool_t *scratch_pool)
464 const char *externals_parent_repos_root_url;
465 apr_pool_t *iterpool;
468 SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
470 externals_parent_url, ctx,
471 scratch_pool, scratch_pool));
473 iterpool = svn_pool_create(scratch_pool);
475 for (i = 0; i < external_items->nelts; i++)
477 const char *resolved_url;
479 svn_wc_external_item2_t *item =
480 APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
482 svn_pool_clear(iterpool);
484 SVN_ERR(svn_wc__resolve_relative_external_url(
487 externals_parent_repos_root_url,
488 externals_parent_url,
489 iterpool, iterpool));
491 /* List the external */
492 SVN_ERR(wrap_list_error(ctx, item->target_dir,
493 list_internal(resolved_url,
496 depth, dirent_fields,
499 externals_parent_url,
501 list_func, baton, ctx,
506 svn_pool_destroy(iterpool);
511 /* List external items defined on each external in EXTERNALS, a const char *
512 externals_parent_url(url of the directory which has the externals
513 definitions) of all externals mapping to the svn_string_t * externals_desc
514 (externals description text). All other options are the same as those
515 passed to svn_client_list(). */
517 list_externals(apr_hash_t *externals,
519 apr_uint32_t dirent_fields,
520 svn_boolean_t fetch_locks,
521 svn_client_list_func2_t list_func,
523 svn_client_ctx_t *ctx,
524 apr_pool_t *scratch_pool)
526 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
527 apr_hash_index_t *hi;
529 for (hi = apr_hash_first(scratch_pool, externals);
531 hi = apr_hash_next(hi))
533 const char *externals_parent_url = svn__apr_hash_index_key(hi);
534 svn_string_t *externals_desc = svn__apr_hash_index_val(hi);
535 apr_array_header_t *external_items;
537 svn_pool_clear(iterpool);
539 SVN_ERR(svn_wc_parse_externals_description3(&external_items,
540 externals_parent_url,
541 externals_desc->data,
544 if (! external_items->nelts)
547 SVN_ERR(list_external_items(external_items, externals_parent_url, depth,
548 dirent_fields, fetch_locks, list_func,
549 baton, ctx, iterpool));
552 svn_pool_destroy(iterpool);
559 svn_client_list3(const char *path_or_url,
560 const svn_opt_revision_t *peg_revision,
561 const svn_opt_revision_t *revision,
563 apr_uint32_t dirent_fields,
564 svn_boolean_t fetch_locks,
565 svn_boolean_t include_externals,
566 svn_client_list_func2_t list_func,
568 svn_client_ctx_t *ctx,
572 return svn_error_trace(list_internal(path_or_url, peg_revision,
574 depth, dirent_fields,
577 NULL, NULL, list_func,