]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_client/list.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_client / list.c
1 /*
2  * list.c:  list local and remote directory entries.
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 #include <apr_fnmatch.h>
25
26 #include "svn_client.h"
27 #include "svn_dirent_uri.h"
28 #include "svn_hash.h"
29 #include "svn_path.h"
30 #include "svn_pools.h"
31 #include "svn_time.h"
32 #include "svn_sorts.h"
33 #include "svn_props.h"
34
35 #include "client.h"
36
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"
43
44 /* Prototypes for referencing before declaration */
45 static svn_error_t *
46 list_externals(apr_hash_t *externals,
47                const apr_array_header_t *patterns,
48                svn_depth_t depth,
49                apr_uint32_t dirent_fields,
50                svn_boolean_t fetch_locks,
51                svn_client_list_func2_t list_func,
52                void *baton,
53                svn_client_ctx_t *ctx,
54                apr_pool_t *scratch_pool);
55
56 static svn_error_t *
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,
61               svn_depth_t depth,
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,
68               void *baton,
69               svn_client_ctx_t *ctx,
70               apr_pool_t *pool);
71
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. */
75 static svn_boolean_t
76 match_patterns(const char *s,
77                const apr_array_header_t *patterns,
78                svn_membuf_t *scratch_buffer)
79 {
80   return patterns
81        ? svn_utf__fuzzy_glob_match(s, patterns, scratch_buffer)
82        : TRUE;
83 }
84
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.
88
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.
94
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
97    be reported.
98
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.
102
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.
107
108    EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
109    are listed, otherwise both are set to NULL by the caller.
110
111    Use SCRATCH_BUFFER for temporary string contents.
112 */
113 static svn_error_t *
114 get_dir_contents(apr_uint32_t dirent_fields,
115                  const char *dir,
116                  svn_revnum_t rev,
117                  svn_ra_session_t *ra_session,
118                  apr_hash_t *locks,
119                  const char *fs_path,
120                  const apr_array_header_t *patterns,
121                  svn_depth_t depth,
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,
127                  void *baton,
128                  svn_membuf_t *scratch_buffer,
129                  apr_pool_t *result_pool,
130                  apr_pool_t *scratch_pool)
131 {
132   apr_hash_t *tmpdirents;
133   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
134   apr_array_header_t *array;
135   svn_error_t *err;
136   apr_hash_t *prop_hash = NULL;
137   const svn_string_t *prop_val = NULL;
138   int i;
139
140   if (depth == svn_depth_empty)
141     return SVN_NO_ERROR;
142
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);
148
149   if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
150               (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
151     {
152       svn_error_clear(err);
153       return SVN_NO_ERROR;
154     }
155   SVN_ERR(err);
156
157  /* Locks will often be empty.  Prevent pointless lookups in that case. */
158  if (locks && apr_hash_count(locks) == 0)
159    locks = NULL;
160
161  /* Filter out svn:externals from all properties hash. */
162   if (prop_hash)
163     prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
164   if (prop_val)
165     {
166       const char *url;
167
168       SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
169
170       svn_hash_sets(externals,
171                     svn_path_url_add_component2(url, dir, result_pool),
172                     svn_string_dup(prop_val, result_pool));
173     }
174
175   if (ctx->cancel_func)
176     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
177
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,
180                          scratch_pool);
181   for (i = 0; i < array->nelts; ++i)
182     {
183       svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
184       const char *path;
185       svn_dirent_t *the_ent = item->value;
186       svn_lock_t *lock;
187
188       svn_pool_clear(iterpool);
189
190       path = svn_relpath_join(dir, item->key, iterpool);
191
192       if (locks)
193         {
194           const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
195           lock = svn_hash_gets(locks, abs_path);
196         }
197       else
198         lock = NULL;
199
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));
206
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));
215     }
216
217   svn_pool_destroy(iterpool);
218   return SVN_NO_ERROR;
219 }
220
221 /* Baton type to be used with list_receiver. */
222 typedef struct receiver_baton_t
223 {
224   /* Wrapped callback function to invoke. */
225   svn_client_list_func2_t list_func;
226
227   /* Baton to be used with LIST_FUNC. */
228   void *list_baton;
229
230   /* Client context providing cancellation support. */
231   svn_client_ctx_t *ctx;
232
233   /* All locks found for the whole tree; pick yours. */
234   apr_hash_t *locks;
235
236   /* Start path of the operation. */
237   const char *fs_base_path;
238 } receiver_baton_t;
239
240 /* Implement svn_ra_dirent_receiver_t.
241    The BATON type must be a receiver_baton_t. */
242 static svn_error_t *
243 list_receiver(const char *rel_path,
244               svn_dirent_t *dirent,
245               void *baton,
246               apr_pool_t *pool)
247 {
248   receiver_baton_t *b = baton;
249   const svn_lock_t *lock = NULL;
250
251   /* We only report the path relative to the start path. */
252   rel_path = svn_dirent_skip_ancestor(b->fs_base_path, rel_path);
253
254   if (b->locks)
255     {
256       const char *abs_path = svn_dirent_join(b->fs_base_path, rel_path, pool);
257       lock = svn_hash_gets(b->locks, abs_path);
258     }
259
260   if (b->ctx->cancel_func)
261     SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
262
263   SVN_ERR(b->list_func(b->list_baton, rel_path, dirent, lock,
264                        b->fs_base_path, NULL, NULL, pool));
265
266   return SVN_NO_ERROR;
267 }
268
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.
272
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.
279
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
282    be reported.
283
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.
288
289    If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
290
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.
296
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.
300
301    Use authentication baton cached in CTX to authenticate against the
302    repository.
303
304    Use POOL for all allocations.
305 */
306 static svn_error_t *
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,
311               svn_depth_t depth,
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,
318               void *baton,
319               svn_client_ctx_t *ctx,
320               apr_pool_t *pool)
321 {
322   svn_ra_session_t *ra_session;
323   svn_client__pathrev_t *loc;
324   svn_dirent_t *dirent;
325   const char *fs_path;
326   svn_error_t *err;
327   apr_hash_t *locks;
328   apr_hash_t *externals;
329   svn_membuf_t scratch_buffer;
330
331   if (include_externals)
332     externals = apr_hash_make(pool);
333   else
334     externals = NULL;
335
336   /* We use the kind field to determine if we should recurse, so we
337      always need it. */
338   dirent_fields |= SVN_DIRENT_KIND;
339
340   /* Get an RA plugin for this filesystem object. */
341   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
342                                             path_or_url, NULL,
343                                             peg_revision,
344                                             revision, ctx, pool));
345
346   fs_path = svn_client__pathrev_fspath(loc, pool);
347
348   /* Maybe get all locks under url. */
349   if (fetch_locks)
350     {
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);
354
355       if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
356         {
357           svn_error_clear(err);
358           locks = NULL;
359         }
360       else if (err)
361         return svn_error_trace(err);
362     }
363   else
364     locks = NULL;
365
366   /* Try to use the efficient and fully authz-filtered code path. */
367   if (!include_externals)
368     {
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;
375
376       err = svn_ra_list(ra_session, "", loc->rev, patterns, depth,
377                         dirent_fields, list_receiver, &receiver_baton, pool);
378
379       if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
380         svn_error_clear(err);
381       else
382         return svn_error_trace(err);
383     }
384
385   /* Stat for the file / directory node itself. */
386   SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool));
387   if (! dirent)
388     return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
389                              _("URL '%s' non-existent in revision %ld"),
390                              loc->url, loc->rev);
391
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);
395
396   /* Report the dirent for the target. */
397   if (match_patterns(svn_dirent_basename(fs_path, pool), patterns,
398                      &scratch_buffer))
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));
403
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));
412
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))
417     {
418       /* The 'externals' hash populated by get_dir_contents() is processed
419          here. */
420       SVN_ERR(list_externals(externals, patterns, depth, dirent_fields,
421                              fetch_locks, list_func, baton,
422                              ctx, pool));
423     }
424
425   return SVN_NO_ERROR;
426 }
427
428 static svn_error_t *
429 wrap_list_error(const svn_client_ctx_t *ctx,
430                 const char *target_abspath,
431                 svn_error_t *err,
432                 apr_pool_t *scratch_pool)
433 {
434   if (err && err->apr_err != SVN_ERR_CANCELLED)
435     {
436       if (ctx->notify_func2)
437         {
438           svn_wc_notify_t *notifier = svn_wc_create_notify(
439                                             target_abspath,
440                                             svn_wc_notify_failed_external,
441                                             scratch_pool);
442           notifier->err = err;
443           ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
444         }
445       svn_error_clear(err);
446       return SVN_NO_ERROR;
447     }
448
449   return err;
450 }
451
452
453 /* Walk through all the external items and list them. */
454 static svn_error_t *
455 list_external_items(apr_array_header_t *external_items,
456                     const char *externals_parent_url,
457                     const apr_array_header_t *patterns,
458                     svn_depth_t depth,
459                     apr_uint32_t dirent_fields,
460                     svn_boolean_t fetch_locks,
461                     svn_client_list_func2_t list_func,
462                     void *baton,
463                     svn_client_ctx_t *ctx,
464                     apr_pool_t *scratch_pool)
465 {
466   const char *externals_parent_repos_root_url;
467   apr_pool_t *iterpool;
468   int i;
469
470   SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
471                                     NULL /* uuid */,
472                                     externals_parent_url, ctx,
473                                     scratch_pool, scratch_pool));
474
475   iterpool = svn_pool_create(scratch_pool);
476
477   for (i = 0; i < external_items->nelts; i++)
478     {
479       const char *resolved_url;
480
481       svn_wc_external_item2_t *item =
482           APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
483
484       svn_pool_clear(iterpool);
485
486       SVN_ERR(svn_wc__resolve_relative_external_url(
487                   &resolved_url,
488                   item,
489                   externals_parent_repos_root_url,
490                   externals_parent_url,
491                   iterpool, iterpool));
492
493       /* List the external */
494       SVN_ERR(wrap_list_error(ctx, item->target_dir,
495                               list_internal(resolved_url,
496                                             &item->peg_revision,
497                                             &item->revision,
498                                             patterns,
499                                             depth, dirent_fields,
500                                             fetch_locks,
501                                             TRUE,
502                                             externals_parent_url,
503                                             item->target_dir,
504                                             list_func, baton, ctx,
505                                             iterpool),
506                               iterpool));
507
508     }
509   svn_pool_destroy(iterpool);
510
511   return SVN_NO_ERROR;
512 }
513
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(). */
519 static svn_error_t *
520 list_externals(apr_hash_t *externals,
521                const apr_array_header_t *patterns,
522                svn_depth_t depth,
523                apr_uint32_t dirent_fields,
524                svn_boolean_t fetch_locks,
525                svn_client_list_func2_t list_func,
526                void *baton,
527                svn_client_ctx_t *ctx,
528                apr_pool_t *scratch_pool)
529 {
530   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
531   apr_hash_index_t *hi;
532
533   for (hi = apr_hash_first(scratch_pool, externals);
534        hi;
535        hi = apr_hash_next(hi))
536     {
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;
540
541       svn_pool_clear(iterpool);
542
543       SVN_ERR(svn_wc_parse_externals_description3(&external_items,
544                                                   externals_parent_url,
545                                                   externals_desc->data,
546                                                   FALSE, iterpool));
547
548       if (! external_items->nelts)
549         continue;
550
551       SVN_ERR(list_external_items(external_items, externals_parent_url,
552                                   patterns, depth, dirent_fields,
553                                   fetch_locks, list_func, baton, ctx,
554                                   iterpool));
555
556     }
557   svn_pool_destroy(iterpool);
558
559   return SVN_NO_ERROR;
560 }
561
562
563 svn_error_t *
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,
568                  svn_depth_t depth,
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,
573                  void *baton,
574                  svn_client_ctx_t *ctx,
575                  apr_pool_t *scratch_pool)
576 {
577
578   return svn_error_trace(list_internal(path_or_url, peg_revision,
579                                        revision, patterns,
580                                        depth, dirent_fields,
581                                        fetch_locks,
582                                        include_externals,
583                                        NULL, NULL, list_func,
584                                        baton, ctx, scratch_pool));
585 }