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