]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/list.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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_sorts_private.h"
38 #include "private/svn_wc_private.h"
39 #include "svn_private_config.h"
40
41 /* Prototypes for referencing before declaration */
42 static svn_error_t *
43 list_externals(apr_hash_t *externals,
44                svn_depth_t depth,
45                apr_uint32_t dirent_fields,
46                svn_boolean_t fetch_locks,
47                svn_client_list_func2_t list_func,
48                void *baton,
49                svn_client_ctx_t *ctx,
50                apr_pool_t *scratch_pool);
51
52 static svn_error_t *
53 list_internal(const char *path_or_url,
54               const svn_opt_revision_t *peg_revision,
55               const svn_opt_revision_t *revision,
56               svn_depth_t depth,
57               apr_uint32_t dirent_fields,
58               svn_boolean_t fetch_locks,
59               svn_boolean_t include_externals,
60               const char *external_parent_url,
61               const char *external_target,
62               svn_client_list_func2_t list_func,
63               void *baton,
64               svn_client_ctx_t *ctx,
65               apr_pool_t *pool);
66
67
68 /* Get the directory entries of DIR at REV (relative to the root of
69    RA_SESSION), getting at least the fields specified by DIRENT_FIELDS.
70    Use the cancellation function/baton of CTX to check for cancellation.
71
72    If DEPTH is svn_depth_empty, return immediately.  If DEPTH is
73    svn_depth_files, invoke LIST_FUNC on the file entries with BATON;
74    if svn_depth_immediates, invoke it on file and directory entries;
75    if svn_depth_infinity, invoke it on file and directory entries and
76    recurse into the directory entries with the same depth.
77
78    LOCKS, if non-NULL, is a hash mapping const char * paths to svn_lock_t
79    objects and FS_PATH is the absolute filesystem path of the RA session.
80    Use SCRATCH_POOL for temporary allocations.
81
82    If the caller passes EXTERNALS as non-NULL, populate the EXTERNALS
83    hash table whose keys are URLs of the directory which has externals
84    definitions, and whose values are the externals description text.
85    Allocate the hash's keys and values in RESULT_POOL.
86
87    EXTERNAL_PARENT_URL and EXTERNAL_TARGET are set when external items
88    are listed, otherwise both are set to NULL by the caller.
89 */
90 static svn_error_t *
91 get_dir_contents(apr_uint32_t dirent_fields,
92                  const char *dir,
93                  svn_revnum_t rev,
94                  svn_ra_session_t *ra_session,
95                  apr_hash_t *locks,
96                  const char *fs_path,
97                  svn_depth_t depth,
98                  svn_client_ctx_t *ctx,
99                  apr_hash_t *externals,
100                  const char *external_parent_url,
101                  const char *external_target,
102                  svn_client_list_func2_t list_func,
103                  void *baton,
104                  apr_pool_t *result_pool,
105                  apr_pool_t *scratch_pool)
106 {
107   apr_hash_t *tmpdirents;
108   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
109   apr_array_header_t *array;
110   svn_error_t *err;
111   apr_hash_t *prop_hash = NULL;
112   const svn_string_t *prop_val = NULL;
113   int i;
114
115   if (depth == svn_depth_empty)
116     return SVN_NO_ERROR;
117
118   /* Get the directory's entries. If externals hash is non-NULL, get its
119      properties also. Ignore any not-authorized errors.  */
120   err = svn_ra_get_dir2(ra_session, &tmpdirents, NULL,
121                         externals ? &prop_hash : NULL,
122                         dir, rev, dirent_fields, scratch_pool);
123
124   if (err && ((err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED) ||
125               (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN)))
126     {
127       svn_error_clear(err);
128       return SVN_NO_ERROR;
129     }
130   SVN_ERR(err);
131
132  /* Locks will often be empty.  Prevent pointless lookups in that case. */
133  if (locks && apr_hash_count(locks) == 0)
134    locks = NULL;
135
136  /* Filter out svn:externals from all properties hash. */
137   if (prop_hash)
138     prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
139   if (prop_val)
140     {
141       const char *url;
142
143       SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
144
145       svn_hash_sets(externals,
146                     svn_path_url_add_component2(url, dir, result_pool),
147                     svn_string_dup(prop_val, result_pool));
148     }
149
150   if (ctx->cancel_func)
151     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
152
153   /* Sort the hash, so we can call the callback in a "deterministic" order. */
154   array = svn_sort__hash(tmpdirents, svn_sort_compare_items_lexically,
155                          scratch_pool);
156   for (i = 0; i < array->nelts; ++i)
157     {
158       svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
159       const char *path;
160       svn_dirent_t *the_ent = item->value;
161       svn_lock_t *lock;
162
163       svn_pool_clear(iterpool);
164
165       path = svn_relpath_join(dir, item->key, iterpool);
166
167       if (locks)
168         {
169           const char *abs_path = svn_fspath__join(fs_path, path, iterpool);
170           lock = svn_hash_gets(locks, abs_path);
171         }
172       else
173         lock = NULL;
174
175       if (the_ent->kind == svn_node_file
176           || depth == svn_depth_immediates
177           || depth == svn_depth_infinity)
178         SVN_ERR(list_func(baton, path, the_ent, lock, fs_path,
179                           external_parent_url, external_target, iterpool));
180
181       /* If externals is non-NULL, populate the externals hash table
182          recursively for all directory entries. */
183       if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
184         SVN_ERR(get_dir_contents(dirent_fields, path, rev,
185                                  ra_session, locks, fs_path, depth, ctx,
186                                  externals, external_parent_url,
187                                  external_target, list_func, baton,
188                                  result_pool, iterpool));
189     }
190
191   svn_pool_destroy(iterpool);
192   return SVN_NO_ERROR;
193 }
194
195
196 /* List the file/directory entries for PATH_OR_URL at REVISION.
197    The actual node revision selected is determined by the path as
198    it exists in PEG_REVISION.
199
200    If DEPTH is svn_depth_infinity, then list all file and directory entries
201    recursively.  Else if DEPTH is svn_depth_files, list all files under
202    PATH_OR_URL (if any), but not subdirectories.  Else if DEPTH is
203    svn_depth_immediates, list all files and include immediate
204    subdirectories (at svn_depth_empty).  Else if DEPTH is
205    svn_depth_empty, just list PATH_OR_URL with none of its entries.
206
207    DIRENT_FIELDS controls which fields in the svn_dirent_t's are
208    filled in.  To have them totally filled in use SVN_DIRENT_ALL,
209    otherwise simply bitwise OR together the combination of SVN_DIRENT_*
210    fields you care about.
211
212    If FETCH_LOCKS is TRUE, include locks when reporting directory entries.
213
214    If INCLUDE_EXTERNALS is TRUE, also list all external items
215    reached by recursion.  DEPTH value passed to the original list target
216    applies for the externals also.  EXTERNAL_PARENT_URL is url of the
217    directory which has the externals definitions.  EXTERNAL_TARGET is the
218    target subdirectory of externals definitions.
219
220    Report directory entries by invoking LIST_FUNC/BATON.
221    Pass EXTERNAL_PARENT_URL and EXTERNAL_TARGET to LIST_FUNC when external
222    items are listed, otherwise both are set to NULL.
223
224    Use authentication baton cached in CTX to authenticate against the
225    repository.
226
227    Use POOL for all allocations.
228 */
229 static svn_error_t *
230 list_internal(const char *path_or_url,
231               const svn_opt_revision_t *peg_revision,
232               const svn_opt_revision_t *revision,
233               svn_depth_t depth,
234               apr_uint32_t dirent_fields,
235               svn_boolean_t fetch_locks,
236               svn_boolean_t include_externals,
237               const char *external_parent_url,
238               const char *external_target,
239               svn_client_list_func2_t list_func,
240               void *baton,
241               svn_client_ctx_t *ctx,
242               apr_pool_t *pool)
243 {
244   svn_ra_session_t *ra_session;
245   svn_client__pathrev_t *loc;
246   svn_dirent_t *dirent;
247   const char *fs_path;
248   svn_error_t *err;
249   apr_hash_t *locks;
250   apr_hash_t *externals;
251
252   if (include_externals)
253     externals = apr_hash_make(pool);
254   else
255     externals = NULL;
256
257   /* We use the kind field to determine if we should recurse, so we
258      always need it. */
259   dirent_fields |= SVN_DIRENT_KIND;
260
261   /* Get an RA plugin for this filesystem object. */
262   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
263                                             path_or_url, NULL,
264                                             peg_revision,
265                                             revision, ctx, pool));
266
267   fs_path = svn_client__pathrev_fspath(loc, pool);
268
269   SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool));
270   if (! dirent)
271     return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
272                              _("URL '%s' non-existent in revision %ld"),
273                              loc->url, loc->rev);
274
275   /* Maybe get all locks under url. */
276   if (fetch_locks)
277     {
278       /* IMPORTANT: If locks are stored in a more temporary pool, we need
279          to fix store_dirent below to duplicate the locks. */
280       err = svn_ra_get_locks2(ra_session, &locks, "", depth, pool);
281
282       if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
283         {
284           svn_error_clear(err);
285           locks = NULL;
286         }
287       else if (err)
288         return svn_error_trace(err);
289     }
290   else
291     locks = NULL;
292
293   /* Report the dirent for the target. */
294   SVN_ERR(list_func(baton, "", dirent, locks
295                     ? (svn_hash_gets(locks, fs_path))
296                     : NULL, fs_path, external_parent_url,
297                     external_target, pool));
298
299   if (dirent->kind == svn_node_dir
300       && (depth == svn_depth_files
301           || depth == svn_depth_immediates
302           || depth == svn_depth_infinity))
303     SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
304                              fs_path, depth, ctx, externals,
305                              external_parent_url, external_target, list_func,
306                              baton, pool, pool));
307
308   /* We handle externals after listing entries under path_or_url, so that
309      handling external items (and any errors therefrom) doesn't delay
310      the primary operation. */
311   if (include_externals && apr_hash_count(externals))
312     {
313       /* The 'externals' hash populated by get_dir_contents() is processed
314          here. */
315       SVN_ERR(list_externals(externals, depth, dirent_fields,
316                              fetch_locks, list_func, baton,
317                              ctx, pool));
318     }
319
320   return SVN_NO_ERROR;
321 }
322
323 static svn_error_t *
324 wrap_list_error(const svn_client_ctx_t *ctx,
325                 const char *target_abspath,
326                 svn_error_t *err,
327                 apr_pool_t *scratch_pool)
328 {
329   if (err && err->apr_err != SVN_ERR_CANCELLED)
330     {
331       if (ctx->notify_func2)
332         {
333           svn_wc_notify_t *notifier = svn_wc_create_notify(
334                                             target_abspath,
335                                             svn_wc_notify_failed_external,
336                                             scratch_pool);
337           notifier->err = err;
338           ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool);
339         }
340       svn_error_clear(err);
341       return SVN_NO_ERROR;
342     }
343
344   return err;
345 }
346
347
348 /* Walk through all the external items and list them. */
349 static svn_error_t *
350 list_external_items(apr_array_header_t *external_items,
351                     const char *externals_parent_url,
352                     svn_depth_t depth,
353                     apr_uint32_t dirent_fields,
354                     svn_boolean_t fetch_locks,
355                     svn_client_list_func2_t list_func,
356                     void *baton,
357                     svn_client_ctx_t *ctx,
358                     apr_pool_t *scratch_pool)
359 {
360   const char *externals_parent_repos_root_url;
361   apr_pool_t *iterpool;
362   int i;
363
364   SVN_ERR(svn_client_get_repos_root(&externals_parent_repos_root_url,
365                                     NULL /* uuid */,
366                                     externals_parent_url, ctx,
367                                     scratch_pool, scratch_pool));
368
369   iterpool = svn_pool_create(scratch_pool);
370
371   for (i = 0; i < external_items->nelts; i++)
372     {
373       const char *resolved_url;
374
375       svn_wc_external_item2_t *item =
376           APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
377
378       svn_pool_clear(iterpool);
379
380       SVN_ERR(svn_wc__resolve_relative_external_url(
381                   &resolved_url,
382                   item,
383                   externals_parent_repos_root_url,
384                   externals_parent_url,
385                   iterpool, iterpool));
386
387       /* List the external */
388       SVN_ERR(wrap_list_error(ctx, item->target_dir,
389                               list_internal(resolved_url,
390                                             &item->peg_revision,
391                                             &item->revision,
392                                             depth, dirent_fields,
393                                             fetch_locks,
394                                             TRUE,
395                                             externals_parent_url,
396                                             item->target_dir,
397                                             list_func, baton, ctx,
398                                             iterpool),
399                               iterpool));
400
401     }
402   svn_pool_destroy(iterpool);
403
404   return SVN_NO_ERROR;
405 }
406
407 /* List external items defined on each external in EXTERNALS, a const char *
408    externals_parent_url(url of the directory which has the externals
409    definitions) of all externals mapping to the svn_string_t * externals_desc
410    (externals description text). All other options are the same as those
411    passed to svn_client_list(). */
412 static svn_error_t *
413 list_externals(apr_hash_t *externals,
414                svn_depth_t depth,
415                apr_uint32_t dirent_fields,
416                svn_boolean_t fetch_locks,
417                svn_client_list_func2_t list_func,
418                void *baton,
419                svn_client_ctx_t *ctx,
420                apr_pool_t *scratch_pool)
421 {
422   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
423   apr_hash_index_t *hi;
424
425   for (hi = apr_hash_first(scratch_pool, externals);
426        hi;
427        hi = apr_hash_next(hi))
428     {
429       const char *externals_parent_url = apr_hash_this_key(hi);
430       svn_string_t *externals_desc = apr_hash_this_val(hi);
431       apr_array_header_t *external_items;
432
433       svn_pool_clear(iterpool);
434
435       SVN_ERR(svn_wc_parse_externals_description3(&external_items,
436                                                   externals_parent_url,
437                                                   externals_desc->data,
438                                                   FALSE, iterpool));
439
440       if (! external_items->nelts)
441         continue;
442
443       SVN_ERR(list_external_items(external_items, externals_parent_url, depth,
444                                   dirent_fields, fetch_locks, list_func,
445                                   baton, ctx, iterpool));
446
447     }
448   svn_pool_destroy(iterpool);
449
450   return SVN_NO_ERROR;
451 }
452
453
454 svn_error_t *
455 svn_client_list3(const char *path_or_url,
456                  const svn_opt_revision_t *peg_revision,
457                  const svn_opt_revision_t *revision,
458                  svn_depth_t depth,
459                  apr_uint32_t dirent_fields,
460                  svn_boolean_t fetch_locks,
461                  svn_boolean_t include_externals,
462                  svn_client_list_func2_t list_func,
463                  void *baton,
464                  svn_client_ctx_t *ctx,
465                  apr_pool_t *pool)
466 {
467
468   return svn_error_trace(list_internal(path_or_url, peg_revision,
469                                        revision,
470                                        depth, dirent_fields,
471                                        fetch_locks,
472                                        include_externals,
473                                        NULL, NULL, list_func,
474                                        baton, ctx, pool));
475 }