list.c revision 299742
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 */
42static svn_error_t *
43list_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
52static svn_error_t *
53list_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*/
90static svn_error_t *
91get_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*/
229static svn_error_t *
230list_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
323static svn_error_t *
324wrap_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. */
349static svn_error_t *
350list_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(). */
412static svn_error_t *
413list_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
454svn_error_t *
455svn_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}
476