1/*
2 * add.c:  wrappers around wc add/mkdir functionality.
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/* ==================================================================== */
25
26
27
28/*** Includes. ***/
29
30#include <string.h>
31#include <apr_lib.h>
32#include <apr_fnmatch.h>
33#include "svn_wc.h"
34#include "svn_client.h"
35#include "svn_string.h"
36#include "svn_pools.h"
37#include "svn_error.h"
38#include "svn_dirent_uri.h"
39#include "svn_path.h"
40#include "svn_io.h"
41#include "svn_config.h"
42#include "svn_props.h"
43#include "svn_hash.h"
44#include "svn_sorts.h"
45#include "client.h"
46#include "svn_ctype.h"
47
48#include "private/svn_client_private.h"
49#include "private/svn_wc_private.h"
50#include "private/svn_ra_private.h"
51#include "private/svn_sorts_private.h"
52#include "private/svn_magic.h"
53
54#include "svn_private_config.h"
55
56
57
58/*** Code. ***/
59
60/* Remove leading and trailing white space from a C string, in place. */
61static void
62trim_string(char **pstr)
63{
64  char *str = *pstr;
65  size_t i;
66
67  while (svn_ctype_isspace(*str))
68    str++;
69  *pstr = str;
70  i = strlen(str);
71  while ((i > 0) && svn_ctype_isspace(str[i-1]))
72    i--;
73  str[i] = '\0';
74}
75
76/* Remove leading and trailing single- or double quotes from a C string,
77 * in place. */
78static void
79unquote_string(char **pstr)
80{
81  char *str = *pstr;
82  size_t i = strlen(str);
83
84  if (i > 0 && ((*str == '"' && str[i - 1] == '"') ||
85                (*str == '\'' && str[i - 1] == '\'')))
86    {
87      str[i - 1] = '\0';
88      str++;
89    }
90  *pstr = str;
91}
92
93/* Split PROPERTY and store each individual value in PROPS.
94   Allocates from POOL. */
95static void
96split_props(apr_array_header_t **props,
97            const char *property,
98            apr_pool_t *pool)
99{
100  apr_array_header_t *temp_props;
101  char *new_prop;
102  int i = 0;
103  int j = 0;
104
105  temp_props = apr_array_make(pool, 4, sizeof(char *));
106  new_prop = apr_palloc(pool, strlen(property)+1);
107
108  for (i = 0; property[i] != '\0'; i++)
109    {
110      if (property[i] != ';')
111        {
112          new_prop[j] = property[i];
113          j++;
114        }
115      else if (property[i] == ';')
116        {
117          /* ";;" becomes ";" */
118          if (property[i+1] == ';')
119            {
120              new_prop[j] = ';';
121              j++;
122              i++;
123            }
124          else
125            {
126              new_prop[j] = '\0';
127              APR_ARRAY_PUSH(temp_props, char *) = new_prop;
128              new_prop += j + 1;
129              j = 0;
130            }
131        }
132    }
133  new_prop[j] = '\0';
134  APR_ARRAY_PUSH(temp_props, char *) = new_prop;
135  *props = temp_props;
136}
137
138/* PROPVALS is a hash mapping char * property names to const char * property
139   values.  PROPERTIES can be empty but not NULL.
140
141   If FILENAME doesn't match the filename pattern PATTERN case insensitively,
142   the do nothing.  Otherwise for each 'name':'value' pair in PROPVALS, add
143   a new entry mappying 'name' to a svn_string_t * wrapping the 'value' in
144   PROPERTIES.  The svn_string_t is allocated in the pool used to allocate
145   PROPERTIES, but the char *'s from PROPVALS are re-used in PROPERTIES.
146   If PROPVALS contains a 'svn:mime-type' mapping, then set *MIMETYPE to
147   the mapped value.  Likewise if PROPVALS contains a mapping for
148   svn:executable, then set *HAVE_EXECUTABLE to TRUE.
149
150   Use SCRATCH_POOL for temporary allocations.
151*/
152static void
153get_auto_props_for_pattern(apr_hash_t *properties,
154                           const char **mimetype,
155                           svn_boolean_t *have_executable,
156                           const char *filename,
157                           const char *pattern,
158                           apr_hash_t *propvals,
159                           apr_pool_t *scratch_pool)
160{
161  apr_hash_index_t *hi;
162
163  /* check if filename matches and return if it doesn't */
164  if (apr_fnmatch(pattern, filename,
165                  APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
166    return;
167
168  for (hi = apr_hash_first(scratch_pool, propvals);
169       hi != NULL;
170       hi = apr_hash_next(hi))
171    {
172      const char *propname = apr_hash_this_key(hi);
173      const char *propval = apr_hash_this_val(hi);
174      svn_string_t *propval_str =
175        svn_string_create_empty(apr_hash_pool_get(properties));
176
177      propval_str->data = propval;
178      propval_str->len = strlen(propval);
179
180      svn_hash_sets(properties, propname, propval_str);
181      if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0)
182        *mimetype = propval;
183      else if (strcmp(propname, SVN_PROP_EXECUTABLE) == 0)
184        *have_executable = TRUE;
185    }
186}
187
188svn_error_t *
189svn_client__get_paths_auto_props(apr_hash_t **properties,
190                                 const char **mimetype,
191                                 const char *path,
192                                 svn_magic__cookie_t *magic_cookie,
193                                 apr_hash_t *autoprops,
194                                 svn_client_ctx_t *ctx,
195                                 apr_pool_t *result_pool,
196                                 apr_pool_t *scratch_pool)
197{
198  apr_hash_index_t *hi;
199  svn_boolean_t have_executable = FALSE;
200
201  *properties = apr_hash_make(result_pool);
202  *mimetype = NULL;
203
204  if (autoprops)
205    {
206      for (hi = apr_hash_first(scratch_pool, autoprops);
207           hi != NULL;
208           hi = apr_hash_next(hi))
209        {
210          const char *pattern = apr_hash_this_key(hi);
211          apr_hash_t *propvals = apr_hash_this_val(hi);
212
213          get_auto_props_for_pattern(*properties, mimetype, &have_executable,
214                                     svn_dirent_basename(path, scratch_pool),
215                                     pattern, propvals, scratch_pool);
216        }
217    }
218
219  /* if mimetype has not been set check the file */
220  if (! *mimetype)
221    {
222      SVN_ERR(svn_io_detect_mimetype2(mimetype, path, ctx->mimetypes_map,
223                                      result_pool));
224
225      /* If we got no mime-type, or if it is "application/octet-stream",
226       * try to get the mime-type from libmagic. */
227      if (magic_cookie &&
228          (!*mimetype ||
229           strcmp(*mimetype, "application/octet-stream") == 0))
230        {
231          const char *magic_mimetype;
232
233         /* Since libmagic usually treats UTF-16 files as "text/plain",
234          * svn_magic__detect_binary_mimetype() will return NULL for such
235          * files. This is fine for now since we currently don't support
236          * UTF-16-encoded text files (issue #2194).
237          * Once we do support UTF-16 this code path will fail to detect
238          * them as text unless the svn_io_detect_mimetype2() call above
239          * returns "text/plain" for them. */
240          SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype,
241                                                    path, magic_cookie,
242                                                    result_pool,
243                                                    scratch_pool));
244          if (magic_mimetype)
245            *mimetype = magic_mimetype;
246        }
247
248      if (*mimetype)
249        apr_hash_set(*properties, SVN_PROP_MIME_TYPE,
250                     strlen(SVN_PROP_MIME_TYPE),
251                     svn_string_create(*mimetype, result_pool));
252    }
253
254  /* if executable has not been set check the file */
255  if (! have_executable)
256    {
257      svn_boolean_t executable = FALSE;
258      SVN_ERR(svn_io_is_file_executable(&executable, path, scratch_pool));
259      if (executable)
260        apr_hash_set(*properties, SVN_PROP_EXECUTABLE,
261                     strlen(SVN_PROP_EXECUTABLE),
262                     svn_string_create_empty(result_pool));
263    }
264
265  return SVN_NO_ERROR;
266}
267
268/* Only call this if the on-disk node kind is a file. */
269static svn_error_t *
270add_file(const char *local_abspath,
271         svn_magic__cookie_t *magic_cookie,
272         apr_hash_t *autoprops,
273         svn_boolean_t no_autoprops,
274         svn_client_ctx_t *ctx,
275         apr_pool_t *pool)
276{
277  apr_hash_t *properties;
278  const char *mimetype;
279  svn_node_kind_t kind;
280  svn_boolean_t is_special;
281
282  /* Check to see if this is a special file. */
283  SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, pool));
284
285  /* Determine the properties that the file should have */
286  if (is_special)
287    {
288      mimetype = NULL;
289      properties = apr_hash_make(pool);
290      svn_hash_sets(properties, SVN_PROP_SPECIAL,
291                    svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
292    }
293  else
294    {
295      apr_hash_t *file_autoprops = NULL;
296
297      /* Get automatic properties */
298      /* If we are setting autoprops grab the inherited svn:auto-props and
299         config file auto-props for this file if we haven't already got them
300         when iterating over the file's unversioned parents. */
301      if (!no_autoprops)
302        {
303          if (autoprops == NULL)
304            SVN_ERR(svn_client__get_all_auto_props(
305              &file_autoprops, svn_dirent_dirname(local_abspath,pool),
306              ctx, pool, pool));
307          else
308            file_autoprops = autoprops;
309        }
310
311      /* This may fail on write-only files:
312         we open them to estimate file type. */
313      SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
314                                               local_abspath, magic_cookie,
315                                               file_autoprops, ctx, pool,
316                                               pool));
317    }
318
319  /* Add the file */
320  SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, properties,
321                                FALSE /* skip checks */,
322                                ctx->notify_func2, ctx->notify_baton2, pool));
323
324  return SVN_NO_ERROR;
325}
326
327/* Schedule directory DIR_ABSPATH, and some of the tree under it, for
328 * addition.  DEPTH is the depth at this point in the descent (it may
329 * be changed for recursive calls).
330 *
331 * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for
332 * addition, add will fail and return an error unless FORCE is TRUE.
333 *
334 * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files
335 * if necessary.
336 *
337 * If not NULL, CONFIG_AUTOPROPS is a hash representing the config file and
338 * svn:auto-props autoprops which apply to DIR_ABSPATH.  It maps
339 * const char * file patterns to another hash which maps const char *
340 * property names to const char *property values.  If CONFIG_AUTOPROPS is
341 * NULL and the config file and svn:auto-props autoprops are required by this
342 * function, then such will be obtained.
343 *
344 * If IGNORES is not NULL, then it is an array of const char * ignore patterns
345 * that apply to any children of DIR_ABSPATH.  If REFRESH_IGNORES is TRUE, then
346 * the passed in value of IGNORES (if any) is itself ignored and this function
347 * will gather all ignore patterns applicable to DIR_ABSPATH itself (allocated in
348 * RESULT_POOL).  Any recursive calls to this function get the refreshed ignore
349 * patterns.  If IGNORES is NULL and REFRESH_IGNORES is FALSE, then all children of DIR_ABSPATH
350 * are unconditionally added.
351 *
352 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow
353 * the user to cancel the operation.
354 *
355 * Use SCRATCH_POOL for temporary allocations.
356 */
357static svn_error_t *
358add_dir_recursive(const char *dir_abspath,
359                  svn_depth_t depth,
360                  svn_boolean_t force,
361                  svn_boolean_t no_autoprops,
362                  svn_magic__cookie_t *magic_cookie,
363                  apr_hash_t *config_autoprops,
364                  svn_boolean_t refresh_ignores,
365                  apr_array_header_t *ignores,
366                  svn_client_ctx_t *ctx,
367                  apr_pool_t *result_pool,
368                  apr_pool_t *scratch_pool)
369{
370  svn_error_t *err;
371  apr_pool_t *iterpool;
372  apr_hash_t *dirents;
373  apr_hash_index_t *hi;
374  svn_boolean_t entry_exists = FALSE;
375
376  /* Check cancellation; note that this catches recursive calls too. */
377  if (ctx->cancel_func)
378    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
379
380  iterpool = svn_pool_create(scratch_pool);
381
382  /* Add this directory to revision control. */
383  err = svn_wc_add_from_disk3(ctx->wc_ctx, dir_abspath, NULL /*props*/,
384                              FALSE /* skip checks */,
385                              ctx->notify_func2, ctx->notify_baton2,
386                              iterpool);
387  if (err)
388    {
389      if (err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
390        {
391          svn_error_clear(err);
392          entry_exists = TRUE;
393        }
394      else if (err)
395        {
396          return svn_error_trace(err);
397        }
398    }
399
400  /* Fetch ignores after adding to handle ignores on the directory itself
401     and ancestors via the single db optimization in libsvn_wc */
402  if (refresh_ignores)
403    SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath,
404                                ctx->config, result_pool, iterpool));
405
406  /* If DIR_ABSPATH is the root of an unversioned subtree then get the
407     following "autoprops":
408
409       1) Explicit and inherited svn:auto-props properties on
410          DIR_ABSPATH
411       2) auto-props from the CTX->CONFIG hash
412
413     Since this set of autoprops applies to all unversioned children of
414     DIR_ABSPATH, we will pass these along to any recursive calls to
415     add_dir_recursive() and calls to add_file() below.  Thus sparing
416     these callees from looking up the same information. */
417  if (!entry_exists && config_autoprops == NULL)
418    {
419      SVN_ERR(svn_client__get_all_auto_props(&config_autoprops, dir_abspath,
420                                             ctx, scratch_pool, iterpool));
421    }
422
423  SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool,
424                              iterpool));
425
426  /* Read the directory entries one by one and add those things to
427     version control. */
428  for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
429    {
430      const char *name = apr_hash_this_key(hi);
431      svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
432      const char *abspath;
433
434      svn_pool_clear(iterpool);
435
436      /* Check cancellation so you can cancel during an
437       * add of a directory with lots of files. */
438      if (ctx->cancel_func)
439        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
440
441      /* Skip over SVN admin directories. */
442      if (svn_wc_is_adm_dir(name, iterpool))
443        continue;
444
445      if (ignores
446          && svn_wc_match_ignore_list(name, ignores, iterpool))
447        continue;
448
449      /* Construct the full path of the entry. */
450      abspath = svn_dirent_join(dir_abspath, name, iterpool);
451
452      /* Recurse on directories; add files; ignore the rest. */
453      if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
454        {
455          svn_depth_t depth_below_here = depth;
456          if (depth == svn_depth_immediates)
457            depth_below_here = svn_depth_empty;
458
459          /* When DIR_ABSPATH is the root of an unversioned subtree then
460             it and all of its children have the same set of ignores.  So
461             save any recursive calls the extra work of finding the same
462             set of ignores. */
463          if (refresh_ignores && !entry_exists)
464            refresh_ignores = FALSE;
465
466          SVN_ERR(add_dir_recursive(abspath, depth_below_here,
467                                    force, no_autoprops,
468                                    magic_cookie, config_autoprops,
469                                    refresh_ignores, ignores, ctx,
470                                    result_pool, iterpool));
471        }
472      else if ((dirent->kind == svn_node_file || dirent->special)
473               && depth >= svn_depth_files)
474        {
475          err = add_file(abspath, magic_cookie, config_autoprops,
476                         no_autoprops, ctx, iterpool);
477          if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
478            svn_error_clear(err);
479          else
480            SVN_ERR(err);
481        }
482    }
483
484  /* Destroy the per-iteration pool. */
485  svn_pool_destroy(iterpool);
486
487  return SVN_NO_ERROR;
488}
489
490/* This structure is used as baton for collecting the config entries
491   in the auto-props section and any inherited svn:auto-props
492   properties.
493*/
494typedef struct collect_auto_props_baton_t
495{
496  /* the hash table for storing the property name/value pairs */
497  apr_hash_t *autoprops;
498
499  /* a pool used for allocating memory */
500  apr_pool_t *result_pool;
501} collect_auto_props_baton_t;
502
503/* Implements svn_config_enumerator2_t callback.
504
505   For one auto-props config entry (NAME, VALUE), stash a copy of
506   NAME and VALUE, allocated in BATON->POOL, in BATON->AUTOPROP.
507   BATON must point to an collect_auto_props_baton_t.
508*/
509static svn_boolean_t
510all_auto_props_collector(const char *name,
511                         const char *value,
512                         void *baton,
513                         apr_pool_t *pool)
514{
515  collect_auto_props_baton_t *autoprops_baton = baton;
516  apr_array_header_t *autoprops;
517  int i;
518
519  /* nothing to do here without a value */
520  if (*value == 0)
521    return TRUE;
522
523  split_props(&autoprops, value, pool);
524
525  for (i = 0; i < autoprops->nelts; i ++)
526    {
527      size_t len;
528      const char *this_value;
529      char *property = APR_ARRAY_IDX(autoprops, i, char *);
530      char *equal_sign = strchr(property, '=');
531
532      if (equal_sign)
533        {
534          *equal_sign = '\0';
535          equal_sign++;
536          trim_string(&equal_sign);
537          unquote_string(&equal_sign);
538          this_value = equal_sign;
539        }
540      else
541        {
542          this_value = "";
543        }
544      trim_string(&property);
545      len = strlen(property);
546
547      if (len > 0)
548        {
549          apr_hash_t *pattern_hash = svn_hash_gets(autoprops_baton->autoprops,
550                                                   name);
551          svn_string_t *propval;
552
553          /* Force reserved boolean property values to '*'. */
554          if (svn_prop_is_boolean(property))
555            {
556              /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL */
557              propval = svn_string_create("*", autoprops_baton->result_pool);
558            }
559          else
560            {
561              propval = svn_string_create(this_value,
562                                          autoprops_baton->result_pool);
563            }
564
565          if (!pattern_hash)
566            {
567              pattern_hash = apr_hash_make(autoprops_baton->result_pool);
568              svn_hash_sets(autoprops_baton->autoprops,
569                            apr_pstrdup(autoprops_baton->result_pool, name),
570                            pattern_hash);
571            }
572          svn_hash_sets(pattern_hash,
573                        apr_pstrdup(autoprops_baton->result_pool, property),
574                        propval->data);
575        }
576    }
577  return TRUE;
578}
579
580/* Go up the directory tree from LOCAL_ABSPATH, looking for a versioned
581 * directory.  If found, return its path in *EXISTING_PARENT_ABSPATH.
582 * Otherwise, return SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
583static svn_error_t *
584find_existing_parent(const char **existing_parent_abspath,
585                     svn_client_ctx_t *ctx,
586                     const char *local_abspath,
587                     apr_pool_t *result_pool,
588                     apr_pool_t *scratch_pool)
589{
590  svn_node_kind_t kind;
591  const char *parent_abspath;
592  svn_wc_context_t *wc_ctx = ctx->wc_ctx;
593
594  SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, local_abspath,
595                            FALSE, FALSE, scratch_pool));
596
597  if (kind == svn_node_dir)
598    {
599      *existing_parent_abspath = apr_pstrdup(result_pool, local_abspath);
600      return SVN_NO_ERROR;
601    }
602
603  if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
604    return svn_error_create(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);
605
606  if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, scratch_pool),
607                        scratch_pool))
608    return svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, NULL,
609                             _("'%s' ends in a reserved name"),
610                             svn_dirent_local_style(local_abspath,
611                                                    scratch_pool));
612
613  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
614
615  if (ctx->cancel_func)
616    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
617
618  SVN_ERR(find_existing_parent(existing_parent_abspath, ctx, parent_abspath,
619                               result_pool, scratch_pool));
620
621  return SVN_NO_ERROR;
622}
623
624svn_error_t *
625svn_client__get_all_auto_props(apr_hash_t **autoprops,
626                               const char *path_or_url,
627                               svn_client_ctx_t *ctx,
628                               apr_pool_t *result_pool,
629                               apr_pool_t *scratch_pool)
630{
631  int i;
632  apr_array_header_t *inherited_config_auto_props;
633  apr_hash_t *props;
634  svn_opt_revision_t rev;
635  svn_string_t *config_auto_prop;
636  svn_boolean_t use_autoprops;
637  collect_auto_props_baton_t autoprops_baton;
638  svn_error_t *err = NULL;
639  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
640  svn_boolean_t target_is_url = svn_path_is_url(path_or_url);
641  svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config,
642                                                  SVN_CONFIG_CATEGORY_CONFIG)
643                                  : NULL;
644  *autoprops = apr_hash_make(result_pool);
645  autoprops_baton.result_pool = result_pool;
646  autoprops_baton.autoprops = *autoprops;
647
648
649  /* Are "traditional" auto-props enabled?  If so grab them from the
650    config.  This is our starting set auto-props, which may be overriden
651    by svn:auto-props. */
652  SVN_ERR(svn_config_get_bool(cfg, &use_autoprops,
653                              SVN_CONFIG_SECTION_MISCELLANY,
654                              SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE));
655  if (use_autoprops)
656    svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS,
657                          all_auto_props_collector, &autoprops_baton,
658                          scratch_pool);
659
660  /* Convert the config file setting (if any) into a hash mapping file
661     patterns to as hash of prop-->val mappings. */
662  if (svn_path_is_url(path_or_url))
663    rev.kind = svn_opt_revision_head;
664  else
665    rev.kind = svn_opt_revision_working;
666
667  /* If PATH_OR_URL is a WC path, then it might be unversioned, in which case
668     we find it's nearest versioned parent. */
669  do
670    {
671      err = svn_client_propget5(&props, &inherited_config_auto_props,
672                                SVN_PROP_INHERITABLE_AUTO_PROPS, path_or_url,
673                                &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
674                                scratch_pool, iterpool);
675      if (err)
676        {
677          if (target_is_url || err->apr_err != SVN_ERR_UNVERSIONED_RESOURCE)
678            return svn_error_trace(err);
679
680          svn_error_clear(err);
681          err = NULL;
682          SVN_ERR(find_existing_parent(&path_or_url, ctx, path_or_url,
683                                       scratch_pool, iterpool));
684        }
685      else
686        {
687          break;
688        }
689    }
690  while (err == NULL);
691
692  /* Stash any explicit PROPS for PARENT_PATH into the inherited props array,
693     since these are actually inherited props for LOCAL_ABSPATH. */
694  config_auto_prop = svn_hash_gets(props, path_or_url);
695
696  if (config_auto_prop)
697    {
698      svn_prop_inherited_item_t *new_iprop =
699        apr_palloc(scratch_pool, sizeof(*new_iprop));
700      new_iprop->path_or_url = path_or_url;
701      new_iprop->prop_hash = apr_hash_make(scratch_pool);
702      svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS,
703                    config_auto_prop);
704      APR_ARRAY_PUSH(inherited_config_auto_props,
705                     svn_prop_inherited_item_t *) = new_iprop;
706    }
707
708  for (i = 0; i < inherited_config_auto_props->nelts; i++)
709    {
710      svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
711        inherited_config_auto_props, i, svn_prop_inherited_item_t *);
712      const svn_string_t *propval =
713        svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS);
714
715        {
716          const char *ch = propval->data;
717          svn_stringbuf_t *config_auto_prop_pattern;
718          svn_stringbuf_t *config_auto_prop_val;
719
720          svn_pool_clear(iterpool);
721
722          config_auto_prop_pattern = svn_stringbuf_create_empty(iterpool);
723          config_auto_prop_val = svn_stringbuf_create_empty(iterpool);
724
725          /* Parse svn:auto-props value. */
726          while (*ch != '\0')
727            {
728              svn_stringbuf_setempty(config_auto_prop_pattern);
729              svn_stringbuf_setempty(config_auto_prop_val);
730
731              /* Parse the file pattern. */
732              while (*ch != '\0' && *ch != '=' && *ch != '\n')
733                {
734                  svn_stringbuf_appendbyte(config_auto_prop_pattern, *ch);
735                  ch++;
736                }
737
738              svn_stringbuf_strip_whitespace(config_auto_prop_pattern);
739
740              /* Parse the auto-prop group. */
741              while (*ch != '\0' && *ch != '\n')
742                {
743                  svn_stringbuf_appendbyte(config_auto_prop_val, *ch);
744                  ch++;
745                }
746
747              /* Strip leading '=' and whitespace from auto-prop group. */
748              if (config_auto_prop_val->data[0] == '=')
749                svn_stringbuf_remove(config_auto_prop_val, 0, 1);
750              svn_stringbuf_strip_whitespace(config_auto_prop_val);
751
752              all_auto_props_collector(config_auto_prop_pattern->data,
753                                       config_auto_prop_val->data,
754                                       &autoprops_baton,
755                                       scratch_pool);
756
757              /* Skip to next line if any. */
758              while (*ch != '\0' && *ch != '\n')
759                ch++;
760              if (*ch == '\n')
761                ch++;
762            }
763        }
764    }
765
766  svn_pool_destroy(iterpool);
767
768  return SVN_NO_ERROR;
769}
770
771/* The main logic of the public svn_client_add5.
772 *
773 * EXISTING_PARENT_ABSPATH is the absolute path to the first existing
774 * parent directory of local_abspath. If not NULL, all missing parents
775 * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */
776static svn_error_t *
777add(const char *local_abspath,
778    svn_depth_t depth,
779    svn_boolean_t force,
780    svn_boolean_t no_ignore,
781    svn_boolean_t no_autoprops,
782    const char *existing_parent_abspath,
783    svn_client_ctx_t *ctx,
784    apr_pool_t *scratch_pool)
785{
786  svn_node_kind_t kind;
787  svn_error_t *err;
788  svn_magic__cookie_t *magic_cookie;
789  apr_array_header_t *ignores = NULL;
790
791  SVN_ERR(svn_magic__init(&magic_cookie, ctx->config, scratch_pool));
792
793  if (existing_parent_abspath)
794    {
795      const char *parent_abspath;
796      const char *child_relpath;
797      apr_array_header_t *components;
798      int i;
799      apr_pool_t *iterpool;
800
801      parent_abspath = existing_parent_abspath;
802      child_relpath = svn_dirent_is_child(existing_parent_abspath,
803                                          local_abspath, NULL);
804      components = svn_path_decompose(child_relpath, scratch_pool);
805      iterpool = svn_pool_create(scratch_pool);
806      for (i = 0; i < components->nelts - 1; i++)
807        {
808          const char *component;
809          svn_node_kind_t disk_kind;
810
811          svn_pool_clear(iterpool);
812
813          if (ctx->cancel_func)
814            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
815
816          component = APR_ARRAY_IDX(components, i, const char *);
817          parent_abspath = svn_dirent_join(parent_abspath, component,
818                                           scratch_pool);
819          SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool));
820          if (disk_kind != svn_node_none && disk_kind != svn_node_dir)
821            return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL,
822                                     _("'%s' prevents creating parent of '%s'"),
823                                     parent_abspath, local_abspath);
824
825          SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool));
826          SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, parent_abspath,
827                                        NULL /*props*/,
828                                        FALSE /* skip checks */,
829                                        ctx->notify_func2, ctx->notify_baton2,
830                                        scratch_pool));
831        }
832      svn_pool_destroy(iterpool);
833    }
834
835  SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
836  if (kind == svn_node_dir)
837    {
838      /* We use add_dir_recursive for all directory targets
839         and pass depth along no matter what it is, so that the
840         target's depth will be set correctly. */
841      err = add_dir_recursive(local_abspath, depth, force,
842                              no_autoprops, magic_cookie, NULL,
843                              !no_ignore, ignores, ctx,
844                              scratch_pool, scratch_pool);
845    }
846  else if (kind == svn_node_file)
847    err = add_file(local_abspath, magic_cookie, NULL,
848                   no_autoprops, ctx, scratch_pool);
849  else if (kind == svn_node_none)
850    {
851      svn_boolean_t tree_conflicted;
852
853      /* Provide a meaningful error message if the node does not exist
854       * on disk but is a tree conflict victim. */
855      err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
856                                 ctx->wc_ctx, local_abspath,
857                                 scratch_pool);
858      if (err)
859        svn_error_clear(err);
860      else if (tree_conflicted)
861        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
862                                 _("'%s' is an existing item in conflict; "
863                                   "please mark the conflict as resolved "
864                                   "before adding a new item here"),
865                                 svn_dirent_local_style(local_abspath,
866                                                        scratch_pool));
867
868      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
869                               _("'%s' not found"),
870                               svn_dirent_local_style(local_abspath,
871                                                      scratch_pool));
872    }
873  else
874    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
875                             _("Unsupported node kind for path '%s'"),
876                             svn_dirent_local_style(local_abspath,
877                                                    scratch_pool));
878
879  /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set.  */
880  if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
881    {
882      svn_error_clear(err);
883      err = SVN_NO_ERROR;
884    }
885  return svn_error_trace(err);
886}
887
888
889
890svn_error_t *
891svn_client_add5(const char *path,
892                svn_depth_t depth,
893                svn_boolean_t force,
894                svn_boolean_t no_ignore,
895                svn_boolean_t no_autoprops,
896                svn_boolean_t add_parents,
897                svn_client_ctx_t *ctx,
898                apr_pool_t *scratch_pool)
899{
900  const char *parent_abspath;
901  const char *local_abspath;
902  const char *existing_parent_abspath;
903  svn_boolean_t is_wc_root;
904  svn_error_t *err;
905
906  if (svn_path_is_url(path))
907    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
908                             _("'%s' is not a local path"), path);
909
910  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
911
912  /* See if we're being asked to add a wc-root.  That's typically not
913     okay, unless we're in "force" mode.  svn_wc__is_wcroot()
914     will return TRUE even if LOCAL_ABSPATH is a *symlink* to a working
915     copy root, which is a scenario we want to treat differently.  */
916  err = svn_wc__is_wcroot(&is_wc_root, ctx->wc_ctx, local_abspath,
917                          scratch_pool);
918  if (err)
919    {
920      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
921          && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
922        {
923          return svn_error_trace(err);
924        }
925
926      svn_error_clear(err);
927      err = NULL; /* SVN_NO_ERROR */
928      is_wc_root = FALSE;
929    }
930  if (is_wc_root)
931    {
932#ifdef HAVE_SYMLINK
933      svn_node_kind_t disk_kind;
934      svn_boolean_t is_special;
935
936      SVN_ERR(svn_io_check_special_path(local_abspath, &disk_kind, &is_special,
937                                        scratch_pool));
938
939      /* A symlink can be an unversioned target and a wcroot. Lets try to add
940         the symlink, which can't be a wcroot. */
941      if (is_special)
942        is_wc_root = FALSE;
943      else
944#endif
945        {
946          if (! force)
947            return svn_error_createf(
948                                 SVN_ERR_ENTRY_EXISTS, NULL,
949                                 _("'%s' is already under version control"),
950                                 svn_dirent_local_style(local_abspath,
951                                                        scratch_pool));
952        }
953    }
954
955  if (is_wc_root)
956    parent_abspath = local_abspath; /* We will only add children */
957  else
958    parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
959
960  existing_parent_abspath = NULL;
961  if (add_parents && !is_wc_root)
962    {
963      apr_pool_t *subpool;
964      const char *existing_parent_abspath2;
965
966      subpool = svn_pool_create(scratch_pool);
967      SVN_ERR(find_existing_parent(&existing_parent_abspath2, ctx,
968                                   parent_abspath, scratch_pool, subpool));
969      if (strcmp(existing_parent_abspath2, parent_abspath) != 0)
970        existing_parent_abspath = existing_parent_abspath2;
971      svn_pool_destroy(subpool);
972    }
973
974  SVN_WC__CALL_WITH_WRITE_LOCK(
975    add(local_abspath, depth, force, no_ignore, no_autoprops,
976        existing_parent_abspath, ctx, scratch_pool),
977    ctx->wc_ctx, (existing_parent_abspath ? existing_parent_abspath
978                                          : parent_abspath),
979    FALSE /* lock_anchor */, scratch_pool);
980  return SVN_NO_ERROR;
981}
982
983
984static svn_error_t *
985path_driver_cb_func(void **dir_baton,
986                    const svn_delta_editor_t *editor,
987                    void *edit_baton,
988                    void *parent_baton,
989                    void *callback_baton,
990                    const char *path,
991                    apr_pool_t *pool)
992{
993  SVN_ERR(svn_path_check_valid(path, pool));
994  return editor->add_directory(path, parent_baton, NULL,
995                               SVN_INVALID_REVNUM, pool, dir_baton);
996}
997
998/* Append URL, and all it's non-existent parent directories, to TARGETS.
999   Use TEMPPOOL for temporary allocations and POOL for any additions to
1000   TARGETS. */
1001static svn_error_t *
1002add_url_parents(svn_ra_session_t *ra_session,
1003                const char *url,
1004                apr_array_header_t *targets,
1005                apr_pool_t *temppool,
1006                apr_pool_t *pool)
1007{
1008  svn_node_kind_t kind;
1009  const char *parent_url = svn_uri_dirname(url, pool);
1010
1011  SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
1012  SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
1013                            temppool));
1014
1015  if (kind == svn_node_none)
1016    SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
1017
1018  APR_ARRAY_PUSH(targets, const char *) = url;
1019
1020  return SVN_NO_ERROR;
1021}
1022
1023static svn_error_t *
1024mkdir_urls(const apr_array_header_t *urls,
1025           svn_boolean_t make_parents,
1026           const apr_hash_t *revprop_table,
1027           svn_commit_callback2_t commit_callback,
1028           void *commit_baton,
1029           svn_client_ctx_t *ctx,
1030           apr_pool_t *pool)
1031{
1032  svn_ra_session_t *ra_session = NULL;
1033  const svn_delta_editor_t *editor;
1034  void *edit_baton;
1035  const char *log_msg;
1036  apr_array_header_t *targets;
1037  apr_hash_t *targets_hash;
1038  apr_hash_t *commit_revprops;
1039  svn_error_t *err;
1040  const char *common;
1041  int i;
1042
1043  /* Find any non-existent parent directories */
1044  if (make_parents)
1045    {
1046      apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
1047                                                    sizeof(const char *));
1048      const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
1049      apr_pool_t *iterpool = svn_pool_create(pool);
1050
1051      SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL,
1052                                          ctx, pool, iterpool));
1053
1054      for (i = 0; i < urls->nelts; i++)
1055        {
1056          const char *url = APR_ARRAY_IDX(urls, i, const char *);
1057
1058          svn_pool_clear(iterpool);
1059          SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
1060        }
1061
1062      svn_pool_destroy(iterpool);
1063
1064      urls = all_urls;
1065    }
1066
1067  /* Condense our list of mkdir targets. */
1068  SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE,
1069                                   pool, pool));
1070
1071  /*Remove duplicate targets introduced by make_parents with more targets. */
1072  SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
1073  SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));
1074
1075  if (! targets->nelts)
1076    {
1077      const char *bname;
1078      svn_uri_split(&common, &bname, common, pool);
1079      APR_ARRAY_PUSH(targets, const char *) = bname;
1080
1081      if (*bname == '\0')
1082        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1083                                 _("There is no valid URI above '%s'"),
1084                                 common);
1085    }
1086  else
1087    {
1088      svn_boolean_t resplit = FALSE;
1089
1090      /* We can't "mkdir" the root of an editor drive, so if one of
1091         our targets is the empty string, we need to back everything
1092         up by a path component. */
1093      for (i = 0; i < targets->nelts; i++)
1094        {
1095          const char *path = APR_ARRAY_IDX(targets, i, const char *);
1096          if (! *path)
1097            {
1098              resplit = TRUE;
1099              break;
1100            }
1101        }
1102      if (resplit)
1103        {
1104          const char *bname;
1105
1106          svn_uri_split(&common, &bname, common, pool);
1107
1108          if (*bname == '\0')
1109             return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1110                                      _("There is no valid URI above '%s'"),
1111                                      common);
1112
1113          for (i = 0; i < targets->nelts; i++)
1114            {
1115              const char *path = APR_ARRAY_IDX(targets, i, const char *);
1116              path = svn_relpath_join(bname, path, pool);
1117              APR_ARRAY_IDX(targets, i, const char *) = path;
1118            }
1119        }
1120    }
1121
1122  svn_sort__array(targets, svn_sort_compare_paths);
1123
1124  /* ### This reparent may be problematic in limited-authz-to-common-parent
1125     ### scenarios (compare issue #3242).  See also issue #3649. */
1126  if (ra_session)
1127    SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1128
1129  /* Create new commit items and add them to the array. */
1130  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1131    {
1132      svn_client_commit_item3_t *item;
1133      const char *tmp_file;
1134      apr_array_header_t *commit_items
1135        = apr_array_make(pool, targets->nelts, sizeof(item));
1136
1137      for (i = 0; i < targets->nelts; i++)
1138        {
1139          const char *path = APR_ARRAY_IDX(targets, i, const char *);
1140
1141          item = svn_client_commit_item3_create(pool);
1142          item->url = svn_path_url_add_component2(common, path, pool);
1143          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1144          APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1145        }
1146
1147      SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
1148                                      ctx, pool));
1149
1150      if (! log_msg)
1151        return SVN_NO_ERROR;
1152    }
1153  else
1154    log_msg = "";
1155
1156  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1157                                           log_msg, ctx, pool));
1158
1159  /* Open an RA session for the URL. Note that we don't have a local
1160     directory, nor a place to put temp files. */
1161  if (!ra_session)
1162    SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx,
1163                                        pool, pool));
1164  else
1165    SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1166
1167
1168  /* Fetch RA commit editor */
1169  SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
1170                        svn_client__get_shim_callbacks(ctx->wc_ctx, NULL,
1171                                                       pool)));
1172  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1173                                    commit_revprops,
1174                                    commit_callback,
1175                                    commit_baton,
1176                                    NULL, TRUE, /* No lock tokens */
1177                                    pool));
1178
1179  /* Call the path-based editor driver. */
1180  err = svn_error_trace(
1181        svn_delta_path_driver3(editor, edit_baton, targets, TRUE,
1182                               path_driver_cb_func, NULL, pool));
1183
1184  if (err)
1185    {
1186      /* At least try to abort the edit (and fs txn) before throwing err. */
1187      return svn_error_compose_create(
1188                err,
1189                svn_error_trace(editor->abort_edit(edit_baton, pool)));
1190    }
1191
1192  if (ctx->notify_func2)
1193    {
1194      svn_wc_notify_t *notify;
1195      notify = svn_wc_create_notify_url(common,
1196                                        svn_wc_notify_commit_finalizing,
1197                                        pool);
1198      ctx->notify_func2(ctx->notify_baton2, notify, pool);
1199    }
1200
1201  /* Close the edit. */
1202  return svn_error_trace(editor->close_edit(edit_baton, pool));
1203}
1204
1205
1206
1207svn_error_t *
1208svn_client__make_local_parents(const char *local_abspath,
1209                               svn_boolean_t make_parents,
1210                               svn_client_ctx_t *ctx,
1211                               apr_pool_t *scratch_pool)
1212{
1213  svn_error_t *err;
1214  svn_node_kind_t orig_kind;
1215  SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool));
1216  if (make_parents)
1217    SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
1218  else
1219    SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
1220
1221  err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE,
1222                        make_parents, ctx, scratch_pool);
1223
1224  /* If we created a new directory, but couldn't add it to version
1225     control, then delete it. */
1226  if (err && (orig_kind == svn_node_none))
1227    {
1228      err = svn_error_compose_create(err,
1229                                     svn_io_remove_dir2(local_abspath, FALSE,
1230                                                        NULL, NULL,
1231                                                        scratch_pool));
1232    }
1233
1234  return svn_error_trace(err);
1235}
1236
1237
1238svn_error_t *
1239svn_client_mkdir4(const apr_array_header_t *paths,
1240                  svn_boolean_t make_parents,
1241                  const apr_hash_t *revprop_table,
1242                  svn_commit_callback2_t commit_callback,
1243                  void *commit_baton,
1244                  svn_client_ctx_t *ctx,
1245                  apr_pool_t *pool)
1246{
1247  if (! paths->nelts)
1248    return SVN_NO_ERROR;
1249
1250  SVN_ERR(svn_client__assert_homogeneous_target_type(paths));
1251
1252  if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
1253    {
1254      SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback,
1255                         commit_baton, ctx, pool));
1256    }
1257  else
1258    {
1259      /* This is a regular "mkdir" + "svn add" */
1260      apr_pool_t *iterpool = svn_pool_create(pool);
1261      int i;
1262
1263      for (i = 0; i < paths->nelts; i++)
1264        {
1265          const char *path = APR_ARRAY_IDX(paths, i, const char *);
1266
1267          svn_pool_clear(iterpool);
1268
1269          /* See if the user wants us to stop. */
1270          if (ctx->cancel_func)
1271            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1272
1273          SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
1274
1275          SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
1276                                                 iterpool));
1277        }
1278      svn_pool_destroy(iterpool);
1279    }
1280
1281  return SVN_NO_ERROR;
1282}
1283