]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_client/list.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 "svn_client.h"
25 #include "svn_dirent_uri.h"
26 #include "svn_hash.h"
27 #include "svn_path.h"
28 #include "svn_pools.h"
29 #include "svn_time.h"
30 #include "svn_sorts.h"
31 #include "svn_props.h"
32
33 #include "client.h"
34
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"
39
40 /* Prototypes for referencing before declaration */
41 static svn_error_t *
42 list_externals(apr_hash_t *externals,
43                svn_depth_t depth,
44                apr_uint32_t dirent_fields,
45                svn_boolean_t fetch_locks,
46                svn_client_list_func2_t list_func,
47                void *baton,
48                svn_client_ctx_t *ctx,
49                apr_pool_t *scratch_pool);
50
51 static svn_error_t *
52 list_internal(const char *path_or_url,
53               const svn_opt_revision_t *peg_revision,
54               const svn_opt_revision_t *revision,
55               svn_depth_t depth,
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,
62               void *baton,
63               svn_client_ctx_t *ctx,
64               apr_pool_t *pool);
65
66
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.
70
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.
76
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.
80
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.
85
86    EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
87    are listed, otherwise both are set to NULL by the caller.
88 */
89 static svn_error_t *
90 get_dir_contents(apr_uint32_t dirent_fields,
91                  const char *dir,
92                  svn_revnum_t rev,
93                  svn_ra_session_t *ra_session,
94                  apr_hash_t *locks,
95                  const char *fs_path,
96                  svn_depth_t depth,
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,
102                  void *baton,
103                  apr_pool_t *result_pool,
104                  apr_pool_t *scratch_pool)
105 {
106   apr_hash_t *tmpdirents;
107   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
108   apr_array_header_t *array;
109   svn_error_t *err;
110   apr_hash_t *prop_hash = NULL;
111   const svn_string_t *prop_val = NULL;
112   int i;
113
114   if (depth == svn_depth_empty)
115     return SVN_NO_ERROR;
116
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);
122
123   if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
124               (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
125     {
126       svn_error_clear(err);
127       return SVN_NO_ERROR;
128     }
129   SVN_ERR(err);
130
131  /* Filter out svn:externals from all properties hash. */
132   if (prop_hash)
133     prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
134   if (prop_val)
135     {
136       const char *url;
137
138       SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
139
140       svn_hash_sets(externals,
141                     svn_path_url_add_component2(url, dir, result_pool),
142                     svn_string_dup(prop_val, result_pool));
143     }
144
145   if (ctx->cancel_func)
146     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
147
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,
150                          scratch_pool);
151   for (i = 0; i < array->nelts; ++i)
152     {
153       svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
154       const char *path;
155       svn_dirent_t *the_ent = item->value;
156       svn_lock_t *lock;
157
158       svn_pool_clear(iterpool);
159
160       path = svn_relpath_join(dir, item->key, iterpool);
161
162       if (locks)
163         {
164           const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
165           lock = svn_hash_gets(locks, abs_path);
166         }
167       else
168         lock = NULL;
169
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));
175
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));
184     }
185
186   svn_pool_destroy(iterpool);
187   return SVN_NO_ERROR;
188 }
189
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? */
192 svn_error_t *
193 svn_client__ra_stat_compatible(svn_ra_session_t *ra_session,
194                                svn_revnum_t rev,
195                                svn_dirent_t **dirent_p,
196                                apr_uint32_t dirent_fields,
197                                svn_client_ctx_t *ctx,
198                                apr_pool_t *pool)
199 {
200   svn_error_t *err;
201
202   err = svn_ra_stat(ra_session, "", rev, dirent_p, pool);
203
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)
207     {
208       const char *repos_root_url;
209       const char *session_url;
210       svn_node_kind_t kind;
211       svn_dirent_t *dirent;
212
213       svn_error_clear(err);
214
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));
217
218       SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool));
219
220       if (kind != svn_node_none)
221         {
222           if (strcmp(session_url, repos_root_url) != 0)
223             {
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);
228
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);
232
233               SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url,
234                                                   NULL, ctx,
235                                                   subpool, subpool));
236
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));
240
241               /* Get the relevant entry. */
242               dirent = svn_hash_gets(parent_ents, base_name);
243
244               if (dirent)
245                 *dirent_p = svn_dirent_dup(dirent, pool);
246               else
247                 *dirent_p = NULL;
248
249               svn_pool_destroy(subpool); /* Close RA session */
250             }
251           else
252             {
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,
256                  be rev. */
257               dirent = apr_palloc(pool, sizeof(*dirent));
258               dirent->kind = kind;
259               dirent->size = SVN_INVALID_FILESIZE;
260               if (dirent_fields & SVN_DIRENT_HAS_PROPS)
261                 {
262                   apr_hash_t *props;
263                   SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props,
264                                           "", rev, 0 /* no dirent fields */,
265                                           pool));
266                   dirent->has_props = (apr_hash_count(props) != 0);
267                 }
268               dirent->created_rev = rev;
269               if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
270                 {
271                   apr_hash_t *props;
272                   svn_string_t *val;
273
274                   SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props,
275                                               pool));
276                   val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
277                   if (val)
278                     SVN_ERR(svn_time_from_cstring(&dirent->time, val->data,
279                                                   pool));
280                   else
281                     dirent->time = 0;
282
283                   val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
284                   dirent->last_author = val ? val->data : NULL;
285                 }
286
287               *dirent_p = dirent;
288             }
289         }
290       else
291         *dirent_p = NULL;
292     }
293   else
294     SVN_ERR(err);
295
296   return SVN_NO_ERROR;
297 }
298
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.
302
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.
309
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.
314
315    If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
316
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.
322
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.
326
327    Use authentication baton cached in CTX to authenticate against the
328    repository.
329
330    Use POOL for all allocations.
331 */
332 static svn_error_t *
333 list_internal(const char *path_or_url,
334               const svn_opt_revision_t *peg_revision,
335               const svn_opt_revision_t *revision,
336               svn_depth_t depth,
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,
343               void *baton,
344               svn_client_ctx_t *ctx,
345               apr_pool_t *pool)
346 {
347   svn_ra_session_t *ra_session;
348   svn_client__pathrev_t *loc;
349   svn_dirent_t *dirent;
350   const char *fs_path;
351   svn_error_t *err;
352   apr_hash_t *locks;
353   apr_hash_t *externals;
354
355   if (include_externals)
356     externals = apr_hash_make(pool);
357   else
358     externals = NULL;
359
360   /* We use the kind field to determine if we should recurse, so we
361      always need it. */
362   dirent_fields |= SVN_DIRENT_KIND;
363
364   /* Get an RA plugin for this filesystem object. */
365   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
366                                             path_or_url, NULL,
367                                             peg_revision,
368                                             revision, ctx, pool));
369
370   fs_path = svn_client__pathrev_fspath(loc, pool);
371
372   SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent,
373                                          dirent_fields, ctx, pool));
374   if (! dirent)
375     return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
376                              _("URL '%s' non-existent in revision %ld"),
377                              loc->url, loc->rev);
378
379   /* Maybe get all locks under url. */
380   if (fetch_locks)
381     {
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);
385
386       if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
387         {
388           svn_error_clear(err);
389           locks = NULL;
390         }
391       else if (err)
392         return svn_error_trace(err);
393     }
394   else
395     locks = NULL;
396
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));
402
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,
410                              baton, pool, pool));
411
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))
416     {
417       /* The 'externals' hash populated by get_dir_contents() is processed
418          here. */
419       SVN_ERR(list_externals(externals, depth, dirent_fields,
420                              fetch_locks, list_func, baton,
421                              ctx, pool));
422     }
423
424   return SVN_NO_ERROR;
425 }
426
427 static svn_error_t *
428 wrap_list_error(const svn_client_ctx_t *ctx,
429                 const char *target_abspath,
430                 svn_error_t *err,
431                 apr_pool_t *scratch_pool)
432 {
433   if (err && err->apr_err != SVN_ERR_CANCELLED)
434     {
435       if (ctx->notify_func2)
436         {
437           svn_wc_notify_t *notifier = svn_wc_create_notify(
438                                             target_abspath,
439                                             svn_wc_notify_failed_external,
440                                             scratch_pool);
441           notifier->err = err;
442           ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
443         }
444       svn_error_clear(err);
445       return SVN_NO_ERROR;
446     }
447
448   return err;
449 }
450
451
452 /* Walk through all the external items and list them. */
453 static svn_error_t *
454 list_external_items(apr_array_header_t *external_items,
455                     const char *externals_parent_url,
456                     svn_depth_t depth,
457                     apr_uint32_t dirent_fields,
458                     svn_boolean_t fetch_locks,
459                     svn_client_list_func2_t list_func,
460                     void *baton,
461                     svn_client_ctx_t *ctx,
462                     apr_pool_t *scratch_pool)
463 {
464   const char *externals_parent_repos_root_url;
465   apr_pool_t *iterpool;
466   int i;
467
468   SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
469                                     NULL /* uuid */,
470                                     externals_parent_url, ctx,
471                                     scratch_pool, scratch_pool));
472
473   iterpool = svn_pool_create(scratch_pool);
474
475   for (i = 0; i < external_items->nelts; i++)
476     {
477       const char *resolved_url;
478
479       svn_wc_external_item2_t *item =
480           APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
481
482       svn_pool_clear(iterpool);
483
484       SVN_ERR(svn_wc__resolve_relative_external_url(
485                   &resolved_url,
486                   item,
487                   externals_parent_repos_root_url,
488                   externals_parent_url,
489                   iterpool, iterpool));
490
491       /* List the external */
492       SVN_ERR(wrap_list_error(ctx, item->target_dir,
493                               list_internal(resolved_url,
494                                             &item->peg_revision,
495                                             &item->revision,
496                                             depth, dirent_fields,
497                                             fetch_locks,
498                                             TRUE,
499                                             externals_parent_url,
500                                             item->target_dir,
501                                             list_func, baton, ctx,
502                                             iterpool),
503                               iterpool));
504
505     }
506   svn_pool_destroy(iterpool);
507
508   return SVN_NO_ERROR;
509 }
510
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(). */
516 static svn_error_t *
517 list_externals(apr_hash_t *externals,
518                svn_depth_t depth,
519                apr_uint32_t dirent_fields,
520                svn_boolean_t fetch_locks,
521                svn_client_list_func2_t list_func,
522                void *baton,
523                svn_client_ctx_t *ctx,
524                apr_pool_t *scratch_pool)
525 {
526   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
527   apr_hash_index_t *hi;
528
529   for (hi = apr_hash_first(scratch_pool, externals);
530        hi;
531        hi = apr_hash_next(hi))
532     {
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;
536
537       svn_pool_clear(iterpool);
538
539       SVN_ERR(svn_wc_parse_externals_description3(&external_items,
540                                                   externals_parent_url,
541                                                   externals_desc->data,
542                                                   FALSE, iterpool));
543
544       if (! external_items->nelts)
545         continue;
546
547       SVN_ERR(list_external_items(external_items, externals_parent_url, depth,
548                                   dirent_fields, fetch_locks, list_func,
549                                   baton, ctx, iterpool));
550
551     }
552   svn_pool_destroy(iterpool);
553
554   return SVN_NO_ERROR;
555 }
556
557
558 svn_error_t *
559 svn_client_list3(const char *path_or_url,
560                  const svn_opt_revision_t *peg_revision,
561                  const svn_opt_revision_t *revision,
562                  svn_depth_t depth,
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,
567                  void *baton,
568                  svn_client_ctx_t *ctx,
569                  apr_pool_t *pool)
570 {
571
572   return svn_error_trace(list_internal(path_or_url, peg_revision,
573                                        revision,
574                                        depth, dirent_fields,
575                                        fetch_locks,
576                                        include_externals,
577                                        NULL, NULL, list_func,
578                                        baton, ctx, pool));
579 }