You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC

svn commit: r997472 [14/41] - in /subversion/branches/py-tests-as-modules: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/ subversion/bindings/javahl/native/ subversi...

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.c Wed Sep 15 19:32:26 2010
@@ -44,9 +44,11 @@
 #include "svn_hash.h"
 #include "svn_props.h"
 #include "svn_sorts.h"
+#include "svn_string.h"
 #include "svn_time.h"
 #include "svn_mergeinfo.h"
 #include "svn_config.h"
+#include "svn_ctype.h"
 
 #include "fs.h"
 #include "err.h"
@@ -150,6 +152,9 @@ read_min_unpacked_rev(svn_revnum_t *min_
 static svn_error_t *
 update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool);
 
+static svn_error_t *
+update_min_unpacked_revprop(svn_fs_t *fs, apr_pool_t *pool);
+
 /* Pathname helper functions */
 
 /* Return TRUE is REV is packed in FS, FALSE otherwise. */
@@ -250,6 +255,14 @@ svn_fs_fs__path_rev_absolute(const char 
                              svn_revnum_t rev,
                              apr_pool_t *pool)
 {
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT)
+    {
+      *path = path_rev(fs, rev, pool);
+      return SVN_NO_ERROR;
+    }
+
   if (! is_packed_rev(fs, rev))
     {
       svn_node_kind_t kind;
@@ -317,6 +330,7 @@ path_revprops(svn_fs_t *fs, svn_revnum_t
 static APR_INLINE const char *
 path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
 {
+  SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
   return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
                               apr_pstrcat(pool, txn_id, PATH_EXT_TXN, NULL),
                               NULL);
@@ -558,7 +572,8 @@ get_lock_on_filesystem(const char *lock_
    BATON and that subpool, destroy the subpool (releasing the write
    lock) and return what BODY returned. */
 static svn_error_t *
-with_some_lock(svn_error_t *(*body)(void *baton,
+with_some_lock(svn_fs_t *fs,
+               svn_error_t *(*body)(void *baton,
                                     apr_pool_t *pool),
                void *baton,
                const char *lock_filename,
@@ -585,7 +600,14 @@ with_some_lock(svn_error_t *(*body)(void
   err = get_lock_on_filesystem(lock_filename, subpool);
 
   if (!err)
-    err = body(baton, subpool);
+    {
+      fs_fs_data_t *ffd = fs->fsap_data;
+      if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
+        SVN_ERR(update_min_unpacked_rev(fs, pool));
+      if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
+        SVN_ERR(update_min_unpacked_revprop(fs, pool));
+      err = body(baton, subpool);
+    }
 
   svn_pool_destroy(subpool);
 
@@ -613,7 +635,7 @@ svn_fs_fs__with_write_lock(svn_fs_t *fs,
   apr_thread_mutex_t *mutex = ffsd->fs_write_lock;
 #endif
 
-  return with_some_lock(body, baton,
+  return with_some_lock(fs, body, baton,
                         path_lock(fs, pool),
 #if SVN_FS_FS__USE_LOCK_MUTEX
                         mutex,
@@ -636,7 +658,7 @@ with_txn_current_lock(svn_fs_t *fs,
   apr_thread_mutex_t *mutex = ffsd->txn_current_lock;
 #endif
 
-  return with_some_lock(body, baton,
+  return with_some_lock(fs, body, baton,
                         path_txn_current_lock(fs, pool),
 #if SVN_FS_FS__USE_LOCK_MUTEX
                         mutex,
@@ -885,21 +907,21 @@ get_file_offset(apr_off_t *offset_p, apr
 }
 
 
-/* Check that BUF, a buffer of text from format file PATH, contains
-   only digits, raising error SVN_ERR_BAD_VERSION_FILE_FORMAT if not.
+/* Check that BUF, a nul-terminated buffer of text from format file PATH,
+   contains only digits at OFFSET and beyond, raising an error if not.
 
    Uses POOL for temporary allocation. */
 static svn_error_t *
-check_format_file_buffer_numeric(const char *buf, const char *path,
-                                 apr_pool_t *pool)
+check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
+                                 const char *path, apr_pool_t *pool)
 {
   const char *p;
 
-  for (p = buf; *p; p++)
-    if (!apr_isdigit(*p))
+  for (p = buf + offset; *p; p++)
+    if (!svn_ctype_isdigit(*p))
       return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-        _("Format file '%s' contains an unexpected non-digit"),
-        svn_dirent_local_style(path, pool));
+        _("Format file '%s' contains unexpected non-digit '%c' within '%s'"),
+        svn_dirent_local_style(path, pool), *p, buf);
 
   return SVN_NO_ERROR;
 }
@@ -953,8 +975,8 @@ read_format(int *pformat, int *max_files
   SVN_ERR(err);
 
   /* Check that the first line contains only digits. */
-  SVN_ERR(check_format_file_buffer_numeric(buf, path, pool));
-  *pformat = atoi(buf);
+  SVN_ERR(check_format_file_buffer_numeric(buf, 0, path, pool));
+  SVN_ERR(svn_cstring_atoi(pformat, buf));
 
   /* Set the default values for anything that can be set via an option. */
   *max_files_per_dir = 0;
@@ -984,8 +1006,8 @@ read_format(int *pformat, int *max_files
           if (strncmp(buf+7, "sharded ", 8) == 0)
             {
               /* Check that the argument is numeric. */
-              SVN_ERR(check_format_file_buffer_numeric(buf+15, path, pool));
-              *max_files_per_dir = atoi(buf+15);
+              SVN_ERR(check_format_file_buffer_numeric(buf, 15, path, pool));
+              SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf + 15));
               continue;
             }
         }
@@ -1042,12 +1064,6 @@ write_format(const char *path, int forma
                                   contents->data, contents->len,
                                   svn_io_file_del_none, pool));
 
-#ifdef WIN32
-      /* make the destination writable, but only on Windows, because
-         Windows does not let us replace read-only files. */
-      SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
-#endif /* WIN32 */
-
       /* rename the temp file as the real destination */
       SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
     }
@@ -1173,6 +1189,8 @@ update_min_unpacked_rev(svn_fs_t *fs, ap
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
+  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
+
   return read_min_unpacked_rev(&ffd->min_unpacked_rev,
                                path_min_unpacked_rev(fs, pool),
                                pool);
@@ -1183,6 +1201,8 @@ update_min_unpacked_revprop(svn_fs_t *fs
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
+  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
+
   return read_min_unpacked_rev(&ffd->min_unpacked_revprop,
                                path_min_unpacked_revprop(fs, pool),
                                pool);
@@ -1268,11 +1288,32 @@ upgrade_body(void *baton, apr_pool_t *po
   fs_fs_data_t *ffd = fs->fsap_data;
   int format, max_files_per_dir;
   const char *format_path = path_format(fs, pool);
+  svn_node_kind_t kind;
 
   /* Read the FS format number and max-files-per-dir setting. */
   SVN_ERR(read_format(&format, &max_files_per_dir, format_path, pool));
 
-  /* If we're already up-to-date, there's nothing to be done here. */
+  /* If the config file does not exist, create one. */
+  SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
+                            &kind, pool));
+  switch (kind)
+    {
+    case svn_node_none:
+      SVN_ERR(write_config(fs, pool));
+      break;
+    case svn_node_file:
+      break;
+    default:
+      return svn_error_return(svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+                                                _("'%s' is not a regular file."
+                                                  " Please move it out of "
+                                                  "the way and try again"),
+                                                svn_dirent_join(fs->path,
+                                                                PATH_CONFIG,
+                                                                pool)));
+    }
+
+  /* If we're already up-to-date, there's nothing else to be done here. */
   if (format == SVN_FS_FS__FORMAT_NUMBER)
     return SVN_NO_ERROR;
 
@@ -1489,15 +1530,69 @@ svn_fs_fs__hotcopy(const char *src_path,
                       pool));
   SVN_ERR(check_format(format));
 
+  /* Try to copy the config.
+   *
+   * ### We try copying the config file before doing anything else,
+   * ### because higher layers will abort the hotcopy if we throw
+   * ### an error from this function, and that renders the hotcopy
+   * ### unusable anyway. */
+  if (format >= SVN_FS_FS__MIN_CONFIG_FILE)
+    {
+      svn_error_t *err;
+
+      err = svn_io_dir_file_copy(src_path, dst_path, PATH_CONFIG, pool);
+      if (err)
+        {
+          if (APR_STATUS_IS_ENOENT(err->apr_err))
+            {
+              /* 1.6.0 to 1.6.11 did not copy the configuration file during
+               * hotcopy. So if we're hotcopying a repository which has been
+               * created as a hotcopy itself, it's possible that fsfs.conf
+               * does not exist. Ask the user to re-create it.
+               *
+               * ### It would be nice to make this a non-fatal error,
+               * ### but this function does not get an svn_fs_t object
+               * ### so we have no way of just printing a warning via
+               * ### the fs->warning() callback. */
+
+              const char *msg;
+              const char *src_abspath;
+              const char *dst_abspath;
+              const char *config_relpath;
+              svn_error_t *err2;
+
+              config_relpath = svn_dirent_join(src_path, PATH_CONFIG, pool);
+              err2 = svn_dirent_get_absolute(&src_abspath, src_path, pool);
+              if (err2)
+                return svn_error_return(svn_error_compose_create(err, err2));
+              err2 = svn_dirent_get_absolute(&dst_abspath, dst_path, pool);
+              if (err2)
+                return svn_error_return(svn_error_compose_create(err, err2));
+              
+              /* ### hack: strip off the 'db/' directory from paths so
+               * ### they make sense to the user */
+              src_abspath = svn_dirent_dirname(src_abspath, pool);
+              dst_abspath = svn_dirent_dirname(dst_abspath, pool);
+
+              msg = apr_psprintf(pool,
+                                 _("Failed to create hotcopy at '%s'. "
+                                   "The file '%s' is missing from the source "
+                                   "repository. Please create this file, for "
+                                   "instance by running 'svnadmin upgrade %s'"),
+                                 dst_abspath, config_relpath, src_abspath);
+              return svn_error_return(svn_error_quick_wrap(err, msg));
+            }
+          else
+            return svn_error_return(err);
+        }
+    }
+
   /* Copy the 'current' file. */
   SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool));
 
   /* Copy the uuid. */
   SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool));
 
-  /* Copy the config. */
-  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CONFIG, pool));
-
   /* Copy the rep cache before copying the rev files to make sure all
      cached references will be present in the copy. */
   src_subdir = svn_dirent_join(src_path, REP_CACHE_DB_NAME, pool);
@@ -1568,8 +1663,8 @@ svn_fs_fs__hotcopy(const char *src_path,
             {
               SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
                                       iterpool));
-              SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
-                                           iterpool));
+              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
+                                        iterpool));
             }
         }
 
@@ -1630,8 +1725,8 @@ svn_fs_fs__hotcopy(const char *src_path,
             {
               SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
                                       iterpool));
-              SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
-                                           iterpool));
+              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
+                                        iterpool));
             }
         }
 
@@ -1851,18 +1946,20 @@ get_packed_offset(apr_off_t *rev_offset,
     {
       svn_stringbuf_t *sb;
       svn_boolean_t eof;
+      apr_int64_t val;
+      svn_error_t *err;
 
       svn_pool_clear(iterpool);
       SVN_ERR(svn_stream_readline(manifest_stream, &sb, "\n", &eof, iterpool));
       if (eof)
         break;
 
-      errno = 0; /* apr_atoi64() in APR-0.9 does not always set errno */
-      APR_ARRAY_PUSH(manifest, apr_off_t) =
-                apr_atoi64(svn_string_create_from_buf(sb, iterpool)->data);
-      if (errno == ERANGE)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                "Manifest offset too large");
+      err = svn_cstring_atoi64(&val, sb->data);
+      if (err)
+        return svn_error_return(
+                 svn_error_create(SVN_ERR_FS_CORRUPT, err,
+                                  _("Manifest offset too large")));
+      APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val;
     }
   svn_pool_destroy(iterpool);
 
@@ -1963,6 +2060,7 @@ read_rep_offsets(representation_t **rep_
 {
   representation_t *rep;
   char *str, *last_str;
+  apr_int64_t val;
 
   rep = apr_pcalloc(pool, sizeof(*rep));
   *rep_p = rep;
@@ -1986,21 +2084,24 @@ read_rep_offsets(representation_t **rep_
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Malformed text representation offset line in node-rev"));
 
-  rep->offset = apr_atoi64(str);
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  rep->offset = (apr_off_t)val;
 
   str = apr_strtok(NULL, " ", &last_str);
   if (str == NULL)
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Malformed text representation offset line in node-rev"));
 
-  rep->size = apr_atoi64(str);
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  rep->size = (svn_filesize_t)val;
 
   str = apr_strtok(NULL, " ", &last_str);
   if (str == NULL)
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Malformed text representation offset line in node-rev"));
 
-  rep->expanded_size = apr_atoi64(str);
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  rep->expanded_size = (svn_filesize_t)val;
 
   /* Read in the MD5 hash. */
   str = apr_strtok(NULL, " ", &last_str);
@@ -2116,7 +2217,10 @@ svn_fs_fs__read_noderev(node_revision_t 
 
   /* Read the 'count' field. */
   value = apr_hash_get(headers, HEADER_COUNT, APR_HASH_KEY_STRING);
-  noderev->predecessor_count = (value == NULL) ? 0 : atoi(value);
+  if (value)
+    SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, value));
+  else
+    noderev->predecessor_count = 0;
 
   /* Get the properties location. */
   value = apr_hash_get(headers, HEADER_PROPS, APR_HASH_KEY_STRING);
@@ -2207,7 +2311,10 @@ svn_fs_fs__read_noderev(node_revision_t 
 
   /* Get the mergeinfo count. */
   value = apr_hash_get(headers, HEADER_MINFO_CNT, APR_HASH_KEY_STRING);
-  noderev->mergeinfo_count = (value == NULL) ? 0 : apr_atoi64(value);
+  if (value)
+    SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, value));
+  else
+    noderev->mergeinfo_count = 0;
 
   /* Get whether *this* node has mergeinfo. */
   value = apr_hash_get(headers, HEADER_MINFO_HERE, APR_HASH_KEY_STRING);
@@ -2394,6 +2501,7 @@ read_rep_line(struct rep_args **rep_args
   apr_size_t limit;
   struct rep_args *rep_args;
   char *str, *last_str;
+  apr_int64_t val;
 
   limit = sizeof(buffer);
   SVN_ERR(svn_io_read_length_line(file, buffer, &limit, pool));
@@ -2421,24 +2529,30 @@ read_rep_line(struct rep_args **rep_args
 
   /* We have hopefully a DELTA vs. a non-empty base revision. */
   str = apr_strtok(buffer, " ", &last_str);
-  if (! str || (strcmp(str, REP_DELTA) != 0)) goto err;
+  if (! str || (strcmp(str, REP_DELTA) != 0))
+    goto error;
 
   str = apr_strtok(NULL, " ", &last_str);
-  if (! str) goto err;
+  if (! str)
+    goto error;
   rep_args->base_revision = SVN_STR_TO_REV(str);
 
   str = apr_strtok(NULL, " ", &last_str);
-  if (! str) goto err;
-  rep_args->base_offset = (apr_off_t) apr_atoi64(str);
+  if (! str)
+    goto error;
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  rep_args->base_offset = (apr_off_t)val;
 
   str = apr_strtok(NULL, " ", &last_str);
-  if (! str) goto err;
-  rep_args->base_length = (apr_size_t) apr_atoi64(str);
+  if (! str)
+    goto error;
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  rep_args->base_length = (apr_size_t)val;
 
   *rep_args_p = rep_args;
   return SVN_NO_ERROR;
 
- err:
+ error:
   return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                           _("Malformed representation header"));
 }
@@ -2501,6 +2615,7 @@ get_root_changes_offset(apr_off_t *root_
   apr_off_t rev_offset;
   char buf[64];
   int i, num_bytes;
+  const char *str;
   apr_size_t len;
   apr_seek_where_t seek_relative;
 
@@ -2555,7 +2670,8 @@ get_root_changes_offset(apr_off_t *root_
   /* Look for the next previous newline. */
   for (i = num_bytes - 2; i >= 0; i--)
     {
-      if (buf[i] == '\n') break;
+      if (buf[i] == '\n')
+        break;
     }
 
   if (i < 0)
@@ -2566,24 +2682,42 @@ get_root_changes_offset(apr_off_t *root_
     }
 
   i++;
-
-  if (root_offset)
-    *root_offset = rev_offset + apr_atoi64(&buf[i]);
+  str = &buf[i];
 
   /* find the next space */
   for ( ; i < (num_bytes - 2) ; i++)
-    if (buf[i] == ' ') break;
+    if (buf[i] == ' ')
+      break;
 
   if (i == (num_bytes - 2))
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Final line in revision file missing space"));
 
+  if (root_offset)
+    {
+      apr_int64_t val;
+
+      buf[i] = '\0';
+      SVN_ERR(svn_cstring_atoi64(&val, str));
+      *root_offset = rev_offset + (apr_off_t)val;
+    }
+
   i++;
+  str = &buf[i];
+
+  /* find the next newline */
+  for ( ; i < num_bytes; i++)
+    if (buf[i] == '\n')
+      break;
 
-  /* note that apr_atoi64() will stop reading as soon as it encounters
-     the final newline. */
   if (changes_offset)
-    *changes_offset = rev_offset + apr_atoi64(&buf[i]);
+    {
+      apr_int64_t val;
+
+      buf[i] = '\0';
+      SVN_ERR(svn_cstring_atoi64(&val, str));
+      *changes_offset = rev_offset + (apr_off_t)val;
+    }
 
   return SVN_NO_ERROR;
 }
@@ -2591,7 +2725,10 @@ get_root_changes_offset(apr_off_t *root_
 /* Move a file into place from OLD_FILENAME in the transactions
    directory to its final location NEW_FILENAME in the repository.  On
    Unix, match the permissions of the new file to the permissions of
-   PERMS_REFERENCE.  Temporary allocations are from POOL. */
+   PERMS_REFERENCE.  Temporary allocations are from POOL.
+   
+   This function almost duplicates svn_io_file_move(), but it tries to
+   guarantee a flush. */
 static svn_error_t *
 move_into_place(const char *old_filename,
                 const char *new_filename,
@@ -2600,7 +2737,7 @@ move_into_place(const char *old_filename
 {
   svn_error_t *err;
 
-  SVN_ERR(svn_fs_fs__dup_perms(old_filename, perms_reference, pool));
+  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
 
   /* Move the file into place. */
   err = svn_io_file_rename(old_filename, new_filename, pool);
@@ -2616,6 +2753,11 @@ move_into_place(const char *old_filename
       /* Flush the target of the copy to disk. */
       SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
                                APR_OS_DEFAULT, pool));
+      /* ### BH: Does this really guarantee a flush of the data written
+         ### via a completely different handle on all operating systems?
+         ###
+         ### Maybe we should perform the copy ourselves instead of making
+         ### apr do that and flush the real handle? */
       SVN_ERR(svn_io_file_flush_to_disk(file, pool));
       SVN_ERR(svn_io_file_close(file, pool));
     }
@@ -2819,9 +2961,11 @@ svn_fs_fs__revision_proplist(apr_hash_t 
                              apr_pool_t *pool)
 {
   svn_error_t *err;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
   err = revision_proplist(proplist_p, fs, rev, pool);
-  if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
+  if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION
+      && ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
     {
       /* If a pack is occurring simultaneously, the min-unpacked-revprop value
          could change, so reload it and then attempt to fetch these revprops
@@ -4584,6 +4728,13 @@ get_txn_proplist(apr_hash_t *proplist,
 {
   svn_stream_t *stream;
 
+  /* Check for issue #3696. (When we find and fix the cause, we can change
+   * this to an assertion.) */
+  if (txn_id == NULL)
+    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+                            _("Internal error: a null transaction id was "
+                              "passed to get_txn_proplist()"));
+
   /* Open the transaction properties file. */
   SVN_ERR(svn_stream_open_readonly(&stream, path_txn_props(fs, txn_id, pool),
                                    pool, pool));
@@ -5709,34 +5860,6 @@ write_final_changed_path_info(apr_off_t 
   return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-svn_fs_fs__dup_perms(const char *filename,
-                     const char *perms_reference,
-                     apr_pool_t *pool)
-{
-#ifndef WIN32
-  apr_status_t status;
-  apr_finfo_t finfo;
-  const char *filename_apr, *perms_reference_apr;
-
-  SVN_ERR(svn_path_cstring_from_utf8(&filename_apr, filename, pool));
-  SVN_ERR(svn_path_cstring_from_utf8(&perms_reference_apr, perms_reference,
-                                     pool));
-
-  status = apr_stat(&finfo, perms_reference_apr, APR_FINFO_PROT, pool);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't stat '%s'"),
-                              svn_dirent_local_style(perms_reference, pool));
-  status = apr_file_perms_set(filename_apr, finfo.protection);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't chmod '%s'"),
-                              svn_dirent_local_style(filename, pool));
-#endif
-  return SVN_NO_ERROR;
-}
-
-
 /* Atomically update the 'current' file to hold the specifed REV,
    NEXT_NODE_ID, and NEXT_COPY_ID.  (The two next-ID parameters are
    ignored and may be NULL if the FS format does not use them.)
@@ -5980,13 +6103,12 @@ commit_body(void *baton, apr_pool_t *poo
       const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
       err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
       if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
-        SVN_ERR(err);
+        return svn_error_return(err);
       svn_error_clear(err);
-      SVN_ERR(svn_fs_fs__dup_perms(new_dir,
-                                   svn_dirent_join(cb->fs->path,
-                                                   PATH_REVS_DIR,
-                                                   pool),
-                                   pool));
+      SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
+                                                PATH_REVS_DIR,
+                                                pool),
+                                new_dir, pool));
 
       if (ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT ||
           new_rev >= ffd->min_unpacked_revprop)
@@ -5996,11 +6118,10 @@ commit_body(void *baton, apr_pool_t *poo
           if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
             SVN_ERR(err);
           svn_error_clear(err);
-          SVN_ERR(svn_fs_fs__dup_perms(new_dir,
-                                       svn_dirent_join(cb->fs->path,
-                                                       PATH_REVPROPS_DIR,
-                                                       pool),
-                                       pool));
+          SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
+                                                    PATH_REVPROPS_DIR,
+                                                    pool),
+                                    new_dir, pool));
         }
     }
 
@@ -6305,11 +6426,12 @@ svn_fs_fs__reserve_copy_id(const char **
 static svn_error_t *
 write_revision_zero(svn_fs_t *fs)
 {
+  const char *path_revision_zero = path_rev(fs, 0, fs->pool);
   apr_hash_t *proplist;
   svn_string_t date;
 
   /* Write out a rev file for revision 0. */
-  SVN_ERR(svn_io_file_create(path_rev(fs, 0, fs->pool),
+  SVN_ERR(svn_io_file_create(path_revision_zero,
                              "PLAIN\nEND\nENDREP\n"
                              "id: 0.0.r0/17\n"
                              "type: dir\n"
@@ -6318,6 +6440,7 @@ write_revision_zero(svn_fs_t *fs)
                              "2d2977d1c96f487abe4a1e202dd03b4e\n"
                              "cpath: /\n"
                              "\n\n17 107\n", fs->pool));
+  SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool));
 
   /* Set a date on revision 0. */
   date.data = svn_time_to_cstring(apr_time_now(), fs->pool);
@@ -6555,7 +6678,7 @@ recover_find_max_ids(svn_fs_t *fs, svn_r
 {
   apr_hash_t *headers;
   char *value;
-  node_revision_t noderev;
+  representation_t *data_rep;
   struct rep_args *ra;
   struct recover_read_from_file_baton baton;
   svn_stream_t *stream;
@@ -6568,10 +6691,6 @@ recover_find_max_ids(svn_fs_t *fs, svn_r
                                                                pool),
                             pool));
 
-  /* We're going to populate a skeletal noderev - just the id and data_rep. */
-  value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
-  noderev.id = svn_fs_fs__id_parse(value, strlen(value), pool);
-
   /* Check that this is a directory.  It should be. */
   value = apr_hash_get(headers, HEADER_TYPE, APR_HASH_KEY_STRING);
   if (value == NULL || strcmp(value, KIND_DIR) != 0)
@@ -6582,18 +6701,18 @@ recover_find_max_ids(svn_fs_t *fs, svn_r
   value = apr_hash_get(headers, HEADER_TEXT, APR_HASH_KEY_STRING);
   if (!value)
     return SVN_NO_ERROR;
-  SVN_ERR(read_rep_offsets(&noderev.data_rep, value, NULL, FALSE, pool));
+  SVN_ERR(read_rep_offsets(&data_rep, value, NULL, FALSE, pool));
 
   /* If the directory's data representation wasn't changed in this revision,
      we've already scanned the directory's contents for noderevs, so we don't
      need to again.  This will occur if a property is changed on a directory
      without changing the directory's contents. */
-  if (noderev.data_rep->revision != rev)
+  if (data_rep->revision != rev)
     return SVN_NO_ERROR;
 
   /* We could use get_dir_contents(), but this is much cheaper.  It does
      rely on directory entries being stored as PLAIN reps, though. */
-  offset = noderev.data_rep->offset;
+  offset = data_rep->offset;
   SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
   SVN_ERR(read_rep_line(&ra, rev_file, pool));
   if (ra->is_delta)
@@ -6605,7 +6724,7 @@ recover_find_max_ids(svn_fs_t *fs, svn_r
      stored in the representation. */
   baton.file = rev_file;
   baton.pool = pool;
-  baton.remaining = noderev.data_rep->expanded_size;
+  baton.remaining = data_rep->expanded_size;
   stream = svn_stream_create(&baton, pool);
   svn_stream_set_read(stream, read_handler_recover);
 
@@ -6896,7 +7015,7 @@ svn_fs_fs__ensure_dir_exists(const char 
 
   /* We successfully created a new directory.  Dup the permissions
      from FS->path. */
-  return svn_fs_fs__dup_perms(path, fs->path, pool);
+  return svn_io_copy_perms(path, fs->path, pool);
 }
 
 /* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to
@@ -7047,7 +7166,7 @@ svn_fs_fs__list_transactions(apr_array_h
   txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
 
   /* Now find a listing of this directory. */
-  SVN_ERR(svn_io_get_dirents2(&dirents, txn_dir, pool));
+  SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));
 
   /* Loop through all the entries and return anything that ends with '.txn'. */
   for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
@@ -7419,7 +7538,9 @@ pack_shard(const char *revs_dir,
 
   SVN_ERR(svn_stream_close(manifest_stream));
   SVN_ERR(svn_stream_close(pack_stream));
-  SVN_ERR(svn_fs_fs__dup_perms(pack_file_dir, shard_path, pool));
+  SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool));
+  SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool));
+  SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, pool));
 
   /* Update the min-unpacked-rev file to reflect our newly packed shard.
    * (ffd->min_unpacked_rev will be updated by open_pack_or_rev_file().)

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs_fs.h Wed Sep 15 19:32:26 2010
@@ -401,12 +401,6 @@ svn_error_t *svn_fs_fs__move_into_place(
                                         const char *perms_reference,
                                         apr_pool_t *pool);
 
-/* Match the perms on FILENAME to the PERMS_REFERENCE file if we're
-   not on a win32 system.  On win32, this is a no-op. */
-svn_error_t *svn_fs_fs__dup_perms(const char *filename,
-                                  const char *perms_reference,
-                                  apr_pool_t *pool);
-
 /* Sets *PATH to the path of REV in FS, whether in a pack file or not.
    Allocate in POOL. */
 svn_error_t *

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/id.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/id.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/id.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/id.c Wed Sep 15 19:32:26 2010
@@ -275,6 +275,9 @@ svn_fs_fs__id_parse(const char *data,
 
   if (str[0] == 'r')
     {
+      apr_int64_t val;
+      svn_error_t *err;
+
       /* This is a revision type ID */
       pvt->txn_id = NULL;
 
@@ -286,7 +289,13 @@ svn_fs_fs__id_parse(const char *data,
       str = apr_strtok(NULL, "/", &last_str);
       if (str == NULL)
         return NULL;
-      pvt->offset = apr_atoi64(str);
+      err = svn_cstring_atoi64(&val, str);
+      if (err)
+        {
+          svn_error_clear(err);
+          return NULL;
+        }
+      pvt->offset = (apr_off_t)val;
     }
   else if (str[0] == 't')
     {

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.c Wed Sep 15 19:32:26 2010
@@ -192,7 +192,7 @@ write_digest_file(apr_hash_t *children,
           svn_stringbuf_appendbytes(children_list,
                                     svn__apr_hash_index_key(hi),
                                     svn__apr_hash_index_klen(hi));
-          svn_stringbuf_appendbytes(children_list, "\n", 1);
+          svn_stringbuf_appendbyte(children_list, '\n');
         }
       hash_store(hash, CHILDREN_KEY, sizeof(CHILDREN_KEY)-1,
                  children_list->data, children_list->len, pool);
@@ -213,7 +213,7 @@ write_digest_file(apr_hash_t *children,
   SVN_ERR(svn_stream_close(stream));
   SVN_ERR(svn_io_file_rename(tmp_path, digest_path, pool));
   SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, fs, 0, pool));
-  return svn_fs_fs__dup_perms(digest_path, rev_0_path, pool);
+  return svn_io_copy_perms(rev_0_path, digest_path, pool);
 }
 
 
@@ -325,7 +325,7 @@ set_lock(svn_fs_t *fs,
          apr_pool_t *pool)
 {
   svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool);
-  svn_stringbuf_t *last_child = svn_stringbuf_create("", pool);
+  const char *lock_digest_path = NULL;
   apr_pool_t *subpool;
 
   SVN_ERR_ASSERT(lock);
@@ -356,15 +356,16 @@ set_lock(svn_fs_t *fs,
         {
           this_lock = lock;
           lock = NULL;
-          svn_stringbuf_set(last_child, digest_file);
+          lock_digest_path = apr_pstrdup(pool, digest_file);
         }
       else
         {
           /* If we already have an entry for this path, we're done. */
-          if (apr_hash_get(this_children, last_child->data, last_child->len))
+          if (apr_hash_get(this_children, lock_digest_path,
+                           APR_HASH_KEY_STRING))
             break;
-          apr_hash_set(this_children, last_child->data,
-                       last_child->len, (void *)1);
+          apr_hash_set(this_children, lock_digest_path,
+                       APR_HASH_KEY_STRING, (void *)1);
         }
       SVN_ERR(write_digest_file(this_children, this_lock, fs,
                                 digest_path, subpool));
@@ -387,13 +388,13 @@ delete_lock(svn_fs_t *fs,
             apr_pool_t *pool)
 {
   svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool);
-  svn_stringbuf_t *child_to_kill = svn_stringbuf_create("", pool);
+  const char *child_to_kill = NULL;
   apr_pool_t *subpool;
 
   SVN_ERR_ASSERT(lock);
 
   /* Iterate in reverse, deleting the lock for LOCK->path, and then
-     pruning entries from its parents. */
+     deleting its entry as it appears in each of its parents. */
   subpool = svn_pool_create(pool);
   while (1729)
     {
@@ -411,31 +412,27 @@ delete_lock(svn_fs_t *fs,
       SVN_ERR(read_digest_file(&this_children, &this_lock, fs,
                                digest_path, subpool));
 
-      /* If we are supposed to drop the last entry from this path's
-         children list, do so. */
-      if (child_to_kill->len)
-        apr_hash_set(this_children, child_to_kill->data,
-                     child_to_kill->len, NULL);
-
       /* Delete the lock (first time through only). */
       if (lock)
         {
           this_lock = NULL;
           lock = NULL;
+          child_to_kill = apr_pstrdup(pool, digest_file);
         }
 
+      if (child_to_kill)
+        apr_hash_set(this_children, child_to_kill, APR_HASH_KEY_STRING, NULL);
+
       if (! (this_lock || apr_hash_count(this_children) != 0))
         {
           /* Special case:  no goodz, no file.  And remember to nix
              the entry for it in its parent. */
-          svn_stringbuf_set(child_to_kill, digest_file);
           SVN_ERR(svn_io_remove_file2(digest_path, FALSE, subpool));
         }
       else
         {
           SVN_ERR(write_digest_file(this_children, this_lock, fs,
                                     digest_path, subpool));
-          svn_stringbuf_setempty(child_to_kill);
         }
 
       /* Prep for next iteration, or bail if we're done. */
@@ -465,7 +462,7 @@ get_lock(svn_lock_t **lock_p,
 
   SVN_ERR(read_digest_file(NULL, &lock, fs, digest_path, pool));
   if (! lock)
-    return SVN_FS__ERR_NO_SUCH_LOCK(fs, path);
+    return SVN_FS__ERR_NO_SUCH_LOCK(fs, path, pool);
 
   /* Don't return an expired lock. */
   if (lock->expiration_date && (apr_time_now() > lock->expiration_date))
@@ -475,7 +472,7 @@ get_lock(svn_lock_t **lock_p,
       if (have_write_lock)
         SVN_ERR(delete_lock(fs, lock, pool));
       *lock_p = NULL;
-      return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token);
+      return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token, pool);
     }
 
   *lock_p = lock;
@@ -679,7 +676,7 @@ lock_body(void *baton, apr_pool_t *pool)
   SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool));
   SVN_ERR(svn_fs_fs__check_path(&kind, root, lb->path, pool));
   if (kind == svn_node_dir)
-    return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path);
+    return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path, pool);
 
   /* While our locking implementation easily supports the locking of
      nonexistent paths, we deliberately choose not to allow such madness. */
@@ -699,7 +696,7 @@ lock_body(void *baton, apr_pool_t *pool)
 
   /* We need to have a username attached to the fs. */
   if (!lb->fs->access_ctx || !lb->fs->access_ctx->username)
-    return SVN_FS__ERR_NO_USER(lb->fs);
+    return SVN_FS__ERR_NO_USER(lb->fs, pool);
 
   /* Is the caller attempting to lock an out-of-date working file? */
   if (SVN_IS_VALID_REVNUM(lb->current_rev))
@@ -745,7 +742,7 @@ lock_body(void *baton, apr_pool_t *pool)
       if (! lb->steal_lock)
         {
           /* Sorry, the path is already locked. */
-          return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock);
+          return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock, pool);
         }
       else
         {
@@ -800,16 +797,16 @@ unlock_body(void *baton, apr_pool_t *poo
     {
       /* Sanity check:  the incoming token should match lock->token. */
       if (strcmp(ub->token, lock->token) != 0)
-        return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path);
+        return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path, pool);
 
       /* There better be a username attached to the fs. */
       if (! (ub->fs->access_ctx && ub->fs->access_ctx->username))
-        return SVN_FS__ERR_NO_USER(ub->fs);
+        return SVN_FS__ERR_NO_USER(ub->fs, pool);
 
       /* And that username better be the same as the lock's owner. */
       if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0)
-        return SVN_FS__ERR_LOCK_OWNER_MISMATCH
-          (ub->fs, ub->fs->access_ctx->username, lock->owner);
+        return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
+           ub->fs, ub->fs->access_ctx->username, lock->owner, pool);
     }
 
   /* Remove lock and lock token files. */
@@ -901,20 +898,82 @@ svn_fs_fs__get_lock(svn_lock_t **lock_p,
 }
 
 
+/* Baton for get_locks_filter_func(). */
+typedef struct
+{
+  const char *path;
+  svn_depth_t requested_depth;
+  svn_fs_get_locks_callback_t get_locks_func;
+  void *get_locks_baton;
+
+} get_locks_filter_baton_t;
+
+
+/* A wrapper for the GET_LOCKS_FUNC passed to svn_fs_fs__get_locks()
+   which filters out locks on paths that aren't within
+   BATON->requested_depth of BATON->path before called
+   BATON->get_locks_func() with BATON->get_locks_baton.
+
+   NOTE: See issue #3660 for details about how the FSFS lock
+   management code is inconsistent.  Until that inconsistency is
+   resolved, we take this filtering approach rather than honoring
+   depth requests closer to the crawling code.  In other words, once
+   we decide how to resolve issue #3660, there might be a more
+   performant way to honor the depth passed to svn_fs_fs__get_locks().  */
+static svn_error_t *
+get_locks_filter_func(void *baton,
+                      svn_lock_t *lock,
+                      apr_pool_t *pool)
+{
+  get_locks_filter_baton_t *b = baton;
+
+  /* Filter out unwanted paths.  Since Subversion only allows
+     locks on files, we can treat depth=immediates the same as
+     depth=files for filtering purposes.  Meaning, we'll keep
+     this lock if:
+
+     a) its path is the very path we queried, or
+     b) we've asked for a fully recursive answer, or
+     c) we've asked for depth=files or depth=immediates, and this
+        lock is on an immediate child of our query path.
+  */
+  if ((strcmp(b->path, lock->path) == 0) 
+      || (b->requested_depth == svn_depth_infinity))
+    {
+      SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool));
+    }
+  else if ((b->requested_depth == svn_depth_files) ||
+           (b->requested_depth == svn_depth_immediates))
+    {
+      const char *rel_uri = svn_uri_is_child(b->path, lock->path, pool);
+      if (rel_uri && (svn_path_component_count(rel_uri) == 1))
+        SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool));
+    }
+
+  return SVN_NO_ERROR; 
+}
+
 svn_error_t *
 svn_fs_fs__get_locks(svn_fs_t *fs,
                      const char *path,
+                     svn_depth_t depth,
                      svn_fs_get_locks_callback_t get_locks_func,
                      void *get_locks_baton,
                      apr_pool_t *pool)
 {
   const char *digest_path;
+  get_locks_filter_baton_t glfb;
 
   SVN_ERR(svn_fs__check_fs(fs, TRUE));
   path = svn_fs__canonicalize_abspath(path, pool);
 
+  glfb.path = path;
+  glfb.requested_depth = depth; 
+  glfb.get_locks_func = get_locks_func;
+  glfb.get_locks_baton = get_locks_baton;
+
   /* Get the top digest path in our tree of interest, and then walk it. */
   digest_path = digest_path_from_path(fs, path, pool);
-  return walk_digest_files(fs, digest_path, get_locks_func,
-                           get_locks_baton, FALSE, pool);
+  return walk_digest_files(fs, digest_path, get_locks_filter_func, &glfb,
+                           FALSE, pool);
 }

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/lock.h Wed Sep 15 19:32:26 2010
@@ -60,6 +60,7 @@ svn_error_t *svn_fs_fs__get_lock(svn_loc
 
 svn_error_t *svn_fs_fs__get_locks(svn_fs_t *fs,
                                   const char *path,
+                                  svn_depth_t depth,
                                   svn_fs_get_locks_callback_t get_locks_func,
                                   void *get_locks_baton,
                                   apr_pool_t *pool);

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/structure
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/structure?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/structure (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/structure Wed Sep 15 19:32:26 2010
@@ -59,7 +59,7 @@ repository) is:
   format              File containing the format number of this filesystem
   fsfs.conf           Configuration file
   min-unpacked-rev    File containing the oldest revision not in a pack file
-  min-unpacked_revprop File containing the oldest revision of unpacked revprop
+  min-unpacked-revprop File containing the oldest revision of unpacked revprop
   rep-cache.db        SQLite database mapping rep checksums to locations
   revprops.db         SQLite database of the packed revision properties
 
@@ -102,10 +102,8 @@ When representation sharing is enabled, 
 representation checksum and location mappings using a SQLite database in
 "rep-cache.db".  The database has a single table, which stores the sha1
 hash text as the primary key, mapped to the representation revision, offset,
-size and expanded size.  A final field, reuse count, is currently used
-for distinguishing representations which are shared.  This file is not
-required, and may be removed at an abritrary time, with the subsequent
-loss of rep-sharing capabilities.
+size and expanded size.  This file is not required, and may be removed at an
+abritrary time, with the subsequent loss of rep-sharing capabilities.
 
 Filesystem formats
 ------------------
@@ -500,7 +498,8 @@ names are the first 3 characters of the 
 
 Also stored in the digest file for a given FS path are pointers to
 other digest files which contain information associated with other FS
-paths that are our path's immediate children.
+paths that are beneath our path (an immediate child thereof, or a
+grandchild, or a great-grandchild, ...).
 
 To answer the question, "Does path FOO have a lock associated with
 it?", one need only generate the MD5 digest of FOO's
@@ -515,4 +514,4 @@ To inquire about locks on children of th
 reference the same path as above, but look for a list of children in
 that file (instead of lock information).  Children are listed as MD5
 digests, too, so you would simply iterate over those digests and
-consult the files they reference, and so on, recursively.
+consult the files they reference for lock information.

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/tree.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/tree.c Wed Sep 15 19:32:26 2010
@@ -358,7 +358,7 @@ mutable_root_node(dag_node_t **node_p,
     return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
   else
     /* If it's not a transaction root, we can't change its contents.  */
-    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
+    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path, pool);
 }
 
 
@@ -692,7 +692,7 @@ open_path(parent_path_t **parent_path_p,
 
       /* The path isn't finished yet; we'd better be in a directory.  */
       if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
-        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
+        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far, pool),
                   apr_psprintf(pool, _("Failure opening '%s'"), path));
 
       rest = next;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra/compat.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra/compat.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra/compat.c Wed Sep 15 19:32:26 2010
@@ -663,24 +663,24 @@ svn_ra__file_revs_from_log(svn_ra_sessio
   svn_stream_t *last_stream;
   apr_pool_t *currpool, *lastpool;
 
+  /* Check to make sure we're dealing with a file. */
+  SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool));
+
+  if (kind == svn_node_dir)
+    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+                             _("'%s' is not a file"), path);
+
   SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool));
   SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
 
   /* Create the initial path, using the repos_url and session_url */
-  tmp = svn_path_is_child(repos_url, session_url, pool);
+  tmp = svn_uri_is_child(repos_url, session_url, pool);
   repos_abs_path = apr_palloc(pool, strlen(tmp) + 1);
   repos_abs_path[0] = '/';
   memcpy(repos_abs_path + 1, tmp, strlen(tmp));
 
-  /* Check to make sure we're dealing with a file. */
-  SVN_ERR(svn_ra_check_path(ra_session, "", end, &kind, pool));
-
-  if (kind == svn_node_dir)
-    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
-                             _("'%s' is not a file"), repos_abs_path);
-
   condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
-  APR_ARRAY_PUSH(condensed_targets, const char *) = "";
+  APR_ARRAY_PUSH(condensed_targets, const char *) = path;
 
   lmb.path = svn_path_uri_decode(repos_abs_path, pool);
   lmb.eldest = NULL;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra/deprecated.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra/deprecated.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra/deprecated.c Wed Sep 15 19:32:26 2010
@@ -30,6 +30,7 @@
 #include "svn_path.h"
 #include "svn_compat.h"
 #include "svn_props.h"
+#include "svn_pools.h"
 
 #include "ra_loader.h"
 
@@ -148,6 +149,18 @@ static svn_ra_reporter2_t reporter_3in2_
   abort_report
 };
 
+svn_error_t *svn_ra_open3(svn_ra_session_t **session_p,
+                          const char *repos_URL,
+                          const char *uuid,
+                          const svn_ra_callbacks2_t *callbacks,
+                          void *callback_baton,
+                          apr_hash_t *config,
+                          apr_pool_t *pool)
+{
+  return svn_ra_open4(session_p, NULL, repos_URL, uuid,
+                      callbacks, callback_baton, config, pool);
+}
+
 svn_error_t *svn_ra_open2(svn_ra_session_t **session_p,
                           const char *repos_URL,
                           const svn_ra_callbacks2_t *callbacks,

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.c Wed Sep 15 19:32:26 2010
@@ -268,7 +268,8 @@ svn_ra_create_callbacks(svn_ra_callbacks
   return SVN_NO_ERROR;
 }
 
-svn_error_t *svn_ra_open3(svn_ra_session_t **session_p,
+svn_error_t *svn_ra_open4(svn_ra_session_t **session_p,
+                          const char **corrected_url_p,
                           const char *repos_URL,
                           const char *uuid,
                           const svn_ra_callbacks2_t *callbacks,
@@ -276,6 +277,7 @@ svn_error_t *svn_ra_open3(svn_ra_session
                           apr_hash_t *config,
                           apr_pool_t *pool)
 {
+  apr_pool_t *sesspool = svn_pool_create(pool);
   svn_ra_session_t *session;
   const struct ra_lib_defn *defn;
   const svn_ra__vtable_t *vtable = NULL;
@@ -294,6 +296,10 @@ svn_error_t *svn_ra_open3(svn_ra_session
   svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP;
   const char *store_pp_plaintext
     = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT;
+  const char *corrected_url;
+
+  /* Initialize the return variable. */
+  *session_p = NULL;
 
   if (callbacks->auth_baton)
     {
@@ -357,7 +363,7 @@ svn_error_t *svn_ra_open3(svn_ra_session
 
           /* Find out where we're about to connect to, and
            * try to pick a server group based on the destination. */
-          apr_err = apr_uri_parse(pool, repos_URL, &repos_URI);
+          apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI);
           /* ### Should apr_uri_parse leave hostname NULL?  It doesn't
            * for "file:///" URLs, only for bogus URLs like "bogus".
            * If this is the right behavior for apr_uri_parse, maybe we
@@ -367,7 +373,8 @@ svn_error_t *svn_ra_open3(svn_ra_session
                                      _("Illegal repository URL '%s'"),
                                      repos_URL);
           server_group = svn_config_find_group(servers, repos_URI.hostname,
-                                               SVN_CONFIG_SECTION_GROUPS, pool);
+                                               SVN_CONFIG_SECTION_GROUPS,
+                                               sesspool);
 
           if (server_group)
             {
@@ -458,12 +465,12 @@ svn_error_t *svn_ra_open3(svn_ra_session
 
           if (! initfunc)
             SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
-                                   pool));
+                                   sesspool));
           if (! initfunc)
             /* Library not found. */
             continue;
 
-          SVN_ERR(initfunc(svn_ra_version(), &vtable, pool));
+          SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool));
 
           SVN_ERR(check_ra_version(vtable->get_version(), scheme));
 
@@ -477,25 +484,36 @@ svn_error_t *svn_ra_open3(svn_ra_session
                              repos_URL);
 
   /* Create the session object. */
-  session = apr_pcalloc(pool, sizeof(*session));
+  session = apr_pcalloc(sesspool, sizeof(*session));
   session->vtable = vtable;
-  session->pool = pool;
+  session->pool = sesspool;
 
   /* Ask the library to open the session. */
-  SVN_ERR_W(vtable->open_session(session, repos_URL, callbacks, callback_baton,
-                               config, pool),
+  SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL,
+                                 callbacks, callback_baton, config, sesspool),
             apr_psprintf(pool, "Unable to connect to a repository at URL '%s'",
                          repos_URL));
-
+  
+  /* If the session open stuff detected a server-provided URL
+     correction (a 301 or 302 redirect response during the initial
+     OPTIONS request), then kill the session so the caller can decide
+     what to do. */
+  if (corrected_url_p && corrected_url)
+    {
+      *corrected_url_p = apr_pstrdup(pool, corrected_url);
+      svn_pool_destroy(sesspool);
+      return SVN_NO_ERROR;
+    }
+  
   /* Check the UUID. */
   if (uuid)
     {
       const char *repository_uuid;
 
       SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
-
       if (strcmp(uuid, repository_uuid) != 0)
         {
+          svn_pool_destroy(sesspool);
           return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
                                    _("Repository UUID '%s' doesn't match "
                                      "expected UUID '%s'"),
@@ -623,6 +641,28 @@ svn_error_t *svn_ra_rev_prop(svn_ra_sess
   return session->vtable->rev_prop(session, rev, name, value, pool);
 }
 
+struct ccw_baton
+{
+  svn_commit_callback2_t original_callback;
+  void *original_baton;
+
+  svn_ra_session_t *session;
+};
+
+/* Wrapper which populates the repos_root field of the commit_info struct */
+static svn_error_t *
+commit_callback_wrapper(const svn_commit_info_t *commit_info,
+                        void *baton,
+                        apr_pool_t *pool)
+{
+  struct ccw_baton *ccwb = baton;
+  svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool);
+  
+  SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool));
+
+  return ccwb->original_callback(ci, ccwb->original_baton, pool);
+}
+
 svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
                                        const svn_delta_editor_t **editor,
                                        void **edit_baton,
@@ -633,10 +673,21 @@ svn_error_t *svn_ra_get_commit_editor3(s
                                        svn_boolean_t keep_locks,
                                        apr_pool_t *pool)
 {
+  /* Allocate this in a pool, since the callback will be called long after
+     this function as returned. */
+  struct ccw_baton *ccwb = apr_palloc(pool, sizeof(*ccwb));
+
+  ccwb->original_callback = callback;
+  ccwb->original_baton = callback_baton;
+  ccwb->session = session;
+
   return session->vtable->get_commit_editor(session, editor, edit_baton,
-                                            revprop_table, callback,
-                                            callback_baton, lock_tokens,
-                                            keep_locks, pool);
+                                            revprop_table,
+                                            callback
+                                                ? commit_callback_wrapper
+                                                : NULL,
+                                            callback ? ccwb : NULL,
+                                            lock_tokens, keep_locks, pool);
 }
 
 svn_error_t *svn_ra_get_file(svn_ra_session_t *session,
@@ -1014,13 +1065,26 @@ svn_error_t *svn_ra_get_lock(svn_ra_sess
   return session->vtable->get_lock(session, lock, path, pool);
 }
 
+svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session,
+                               apr_hash_t **locks,
+                               const char *path,
+                               svn_depth_t depth,
+                               apr_pool_t *pool)
+{
+  SVN_ERR_ASSERT(*path != '/');
+  SVN_ERR_ASSERT((depth == svn_depth_empty) ||
+                 (depth == svn_depth_files) ||
+                 (depth == svn_depth_immediates) ||
+                 (depth == svn_depth_infinity));
+  return session->vtable->get_locks(session, locks, path, depth, pool);
+}
+
 svn_error_t *svn_ra_get_locks(svn_ra_session_t *session,
                               apr_hash_t **locks,
                               const char *path,
                               apr_pool_t *pool)
 {
-  SVN_ERR_ASSERT(*path != '/');
-  return session->vtable->get_locks(session, locks, path, pool);
+  return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool);
 }
 
 svn_error_t *svn_ra_replay(svn_ra_session_t *session,

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra/ra_loader.h Wed Sep 15 19:32:26 2010
@@ -54,6 +54,7 @@ typedef struct svn_ra__vtable_t {
   /* All fields in SESSION, except priv, have been initialized by the
      time this is called.  SESSION->priv may be set by this function. */
   svn_error_t *(*open_session)(svn_ra_session_t *session,
+                               const char **corrected_url,
                                const char *repos_URL,
                                const svn_ra_callbacks2_t *callbacks,
                                void *callback_baton,
@@ -230,6 +231,7 @@ typedef struct svn_ra__vtable_t {
   svn_error_t *(*get_locks)(svn_ra_session_t *session,
                             apr_hash_t **locks,
                             const char *path,
+                            svn_depth_t depth,
                             apr_pool_t *pool);
   svn_error_t *(*replay)(svn_ra_session_t *session,
                          svn_revnum_t revision,

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra/wrapper_template.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra/wrapper_template.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra/wrapper_template.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra/wrapper_template.h Wed Sep 15 19:32:26 2010
@@ -71,12 +71,15 @@ static svn_error_t *compat_open(void **s
    * the alternative (creating a new ra_util library) would be massive
    * overkill for the time being.  Just be sure to keep the following
    * line and the code of svn_ra_create_callbacks in sync.  */
-  svn_ra_callbacks2_t *callbacks2 = apr_pcalloc(pool,
+  apr_pool_t *sesspool = svn_pool_create(pool);
+  svn_ra_callbacks2_t *callbacks2 = apr_pcalloc(sesspool,
                                                 sizeof(*callbacks2));
 
-  svn_ra_session_t *sess = apr_pcalloc(pool, sizeof(*sess));
+  svn_ra_session_t *sess = apr_pcalloc(sesspool, sizeof(*sess));
+  const char *session_url;
+
   sess->vtable = &VTBL;
-  sess->pool = pool;
+  sess->pool = sesspool;
 
   callbacks2->open_tmp_file = callbacks->open_tmp_file;
   callbacks2->auth_baton = callbacks->auth_baton;
@@ -87,8 +90,18 @@ static svn_error_t *compat_open(void **s
   callbacks2->progress_func = NULL;
   callbacks2->progress_baton = NULL;
 
-  SVN_ERR(VTBL.open_session(sess, repos_URL, callbacks2, callback_baton,
-                            config, pool));
+  SVN_ERR(VTBL.open_session(sess, &session_url, repos_URL,
+                            callbacks2, callback_baton, config, sesspool));
+
+  if (strcmp(repos_URL, session_url) != 0)
+    {
+      svn_pool_destroy(sesspool);
+      return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL,
+                               _("Session URL '%s' does not match requested "
+                                 " URL '%s', and redirection was disallowed."),
+                               session_url, repos_URL);
+    }
+
   *session_baton = sess;
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/ra_plugin.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/ra_plugin.c Wed Sep 15 19:32:26 2010
@@ -115,12 +115,12 @@ get_username(svn_ra_session_t *session,
   if (*sess->username)
     {
       SVN_ERR(svn_fs_create_access(&access_ctx, sess->username,
-                                   pool));
+                                   session->pool));
       SVN_ERR(svn_fs_set_access(sess->fs, access_ctx));
 
       /* Make sure this context is disassociated when the pool gets
          destroyed. */
-      apr_pool_cleanup_register(pool, sess->fs, cleanup_access,
+      apr_pool_cleanup_register(session->pool, sess->fs, cleanup_access,
                                 apr_pool_cleanup_null);
     }
 
@@ -341,14 +341,16 @@ deltify_etc(const svn_commit_info_t *com
             void *baton, apr_pool_t *pool)
 {
   struct deltify_etc_baton *db = baton;
-  svn_error_t *err1, *err2;
+  svn_error_t *err1 = SVN_NO_ERROR;
+  svn_error_t *err2;
   apr_hash_index_t *hi;
   apr_pool_t *iterpool;
 
   /* Invoke the original callback first, in case someone's waiting to
      know the revision number so they can go off and annotate an
      issue or something. */
-  err1 = (*db->callback)(commit_info, db->callback_baton, pool);
+  if (*db->callback)
+    err1 = (*db->callback)(commit_info, db->callback_baton, pool);
 
   /* Maybe unlock the paths. */
   if (db->lock_tokens)
@@ -435,6 +437,7 @@ ignore_warnings(void *baton,
 
 static svn_error_t *
 svn_ra_local__open(svn_ra_session_t *session,
+                   const char **corrected_url,
                    const char *repos_URL,
                    const svn_ra_callbacks2_t *callbacks,
                    void *callback_baton,
@@ -444,6 +447,10 @@ svn_ra_local__open(svn_ra_session_t *ses
   svn_ra_local__session_baton_t *sess;
   const char *fs_path;
 
+  /* We don't support redirections in ra-local. */
+  if (corrected_url)
+    *corrected_url = NULL;
+
   /* Allocate and stash the session_sess args we have already. */
   sess = apr_pcalloc(pool, sizeof(*sess));
   sess->callbacks = callbacks;
@@ -1323,6 +1330,7 @@ static svn_error_t *
 svn_ra_local__get_locks(svn_ra_session_t *session,
                         apr_hash_t **locks,
                         const char *path,
+                        svn_depth_t depth,
                         apr_pool_t *pool)
 {
   svn_ra_local__session_baton_t *sess = session->priv;
@@ -1330,8 +1338,8 @@ svn_ra_local__get_locks(svn_ra_session_t
 
   /* Kinda silly to call the repos wrapper, since we have no authz
      func to give it.  But heck, why not. */
-  return svn_repos_fs_get_locks(locks, sess->repos, abs_path,
-                                NULL, NULL, pool);
+  return svn_repos_fs_get_locks2(locks, sess->repos, abs_path, depth,
+                                 NULL, NULL, pool);
 }
 
 

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/split_url.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/split_url.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_local/split_url.c Wed Sep 15 19:32:26 2010
@@ -24,6 +24,7 @@
 #include "ra_local.h"
 #include <string.h>
 #include "svn_path.h"
+#include "svn_dirent_uri.h"
 #include "svn_private_config.h"
 
 
@@ -35,129 +36,23 @@ svn_ra_local__split_URL(svn_repos_t **re
                         apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
-  const char *repos_root;
-  const char *hostname, *path;
+  const char *repos_dirent;
+  const char *repos_root_dirent;
   svn_stringbuf_t *urlbuf;
 
-  /* Verify that the URL is well-formed (loosely) */
-
-  /* First, check for the "file://" prefix. */
-  if (strncmp(URL, "file://", 7) != 0)
-    return svn_error_createf
-      (SVN_ERR_RA_ILLEGAL_URL, NULL,
-       _("Local URL '%s' does not contain 'file://' prefix"), URL);
-
-  /* Then, skip what's between the "file://" prefix and the next
-     occurance of '/' -- this is the hostname, and we are considering
-     everything from that '/' until the end of the URL to be the
-     absolute path portion of the URL.
-     If we got just "file://", treat it the same as "file:///". */
-  hostname = URL + 7;
-  if (*hostname == '\0')
-    {
-      path = "/";
-      hostname = NULL;
-    }
-  else
-    {
-      path = strchr(hostname, '/');
-      if (! path)
-        return svn_error_createf
-          (SVN_ERR_RA_ILLEGAL_URL, NULL,
-           _("Local URL '%s' contains only a hostname, no path"), URL);
-
-      /* Treat localhost as an empty hostname. */
-      if (hostname != path)
-        {
-          hostname = svn_path_uri_decode(apr_pstrmemdup(pool, hostname,
-                                                        path - hostname), pool);
-          if (strncmp(hostname, "localhost", 9) == 0)
-            hostname = NULL;
-        }
-      else
-        hostname = NULL;
-    }
-
-  /* Duplicate the URL, starting at the top of the path.
-     At the same time, we URI-decode the path. */
-#if defined(WIN32) || defined(__CYGWIN__)
-  /* On Windows, we'll typically have to skip the leading / if the
-     path starts with a drive letter.  Like most Web browsers, We
-     support two variants of this scheme:
-
-         file:///X:/path    and
-         file:///X|/path
-
-    Note that, at least on WinNT and above,  file:////./X:/path  will
-    also work, so we must make sure the transformation doesn't break
-    that, and  file:///path  (that looks within the current drive
-    only) should also keep working.
-    If we got a non-empty hostname other than localhost, we convert this
-    into an UNC path.  In this case, we obviously don't strip the slash
-    even if the path looks like it starts with a drive letter.
-    Another thing to remember is that the form file:///\machine/share
-    was the only way to access UNC paths in svn before 1.2.  We
-    need to support that for compatibility with old working copies.
-  */
-  {
-    static const char valid_drive_letters[] =
-      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    /* Casting away const! */
-    char *dup_path = (char *)svn_path_uri_decode(path, pool);
-    if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1])
-        && (dup_path[2] == ':' || dup_path[2] == '|')
-        && (dup_path[3] == '/' || dup_path[3] == '\0'))
-      {
-        /* Skip the leading slash. */
-        ++dup_path;
-        /* We're using path below to calculate fs_path, so keep it in sync. */
-        ++path;
-        if (dup_path[1] == '|')
-          dup_path[1] = ':';
-
-        if (dup_path[3] == '\0')
-          {
-            /* A valid dirent for the driveroot must be like "C:/" instead of
-               just "C:" or svn_dirent_join() will use the current directory
-               on the drive instead */
-
-            char *new_path = apr_pcalloc(pool, 4);
-            new_path[0] = dup_path[0];
-            new_path[1] = ':';
-            new_path[2] = '/';
-            new_path[3] = '\0';
-          }
-      }
-    if (hostname)
-      /* We still know that the path starts with a slash. */
-      repos_root = apr_pstrcat(pool, "//", hostname, path, NULL);
-    else
-      repos_root = dup_path;
-  }
-#else
-  /* Currently, the only hostnames we are allowing on non-Win32 platforms
-     are the empty string and 'localhost'. */
-  if (hostname)
-    return svn_error_createf
-      (SVN_ERR_RA_ILLEGAL_URL, NULL,
-       _("Local URL '%s' contains unsupported hostname"), URL);
-
-  repos_root = svn_path_uri_decode(path, pool);
-#endif
+  SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool));
 
   /* Search for a repository in the full path. */
-  repos_root = svn_repos_find_root_path(repos_root, pool);
-  if (!repos_root)
-    return svn_error_createf
-      (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL,
-       _("Unable to open repository '%s'"), URL);
+  repos_root_dirent = svn_repos_find_root_path(repos_dirent, pool);
+  if (!repos_root_dirent)
+    return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL,
+                             _("Unable to open repository '%s'"), URL);
 
   /* Attempt to open a repository at URL. */
-  err = svn_repos_open(repos, repos_root, pool);
+  err = svn_repos_open(repos, repos_root_dirent, pool);
   if (err)
-    return svn_error_createf
-      (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
-       _("Unable to open repository '%s'"), URL);
+    return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err,
+                             _("Unable to open repository '%s'"), URL);
 
   /* Assert capabilities directly, since client == server. */
   {
@@ -166,26 +61,17 @@ svn_ra_local__split_URL(svn_repos_t **re
     SVN_ERR(svn_repos_remember_client_capabilities(*repos, caps));
   }
 
-  /* What remains of URL after being hacked at in the previous step is
-     REPOS_URL.  FS_PATH is what we've hacked off in the process.
-     Note that path is not encoded and what we gave to svn_root_find_root_path
-     may have been destroyed by that function.  So we have to decode it once
-     more.  But then, it is ours...
-     We want the suffix of path after the repos root part.  Note that
-     repos_root may contain //hostname, but path doesn't.  */
-  *fs_path = svn_path_uri_decode(path, pool)
-    + (strlen(repos_root)
-       - (hostname ? strlen(hostname) + 2 : 0));
-
-  /* Ensure that *FS_PATH has its leading slash. */
-  if (**fs_path != '/')
-    *fs_path = apr_pstrcat(pool, "/", *fs_path, NULL);
+  *fs_path = &repos_dirent[strlen(repos_root_dirent)];
+
+  if (**fs_path == '\0')
+    *fs_path = "/";
 
   /* Remove the path components in *fs_path from the original URL, to get
      the URL to the repository root. */
   urlbuf = svn_stringbuf_create(URL, pool);
   svn_path_remove_components(urlbuf,
-                             svn_path_component_count(*fs_path));
+                             svn_path_component_count(repos_dirent)
+                             - svn_path_component_count(repos_root_dirent));
   *repos_url = urlbuf->data;
 
   return SVN_NO_ERROR;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/commit.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/commit.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/commit.c Wed Sep 15 19:32:26 2010
@@ -416,8 +416,8 @@ static svn_error_t * do_checkout(commit_
      ### place result into res->wr_url and return it */
 
   /* create/prep the request */
-  request =
-    svn_ra_neon__request_create(cc->ras, "CHECKOUT", vsn_url, pool);
+  SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "CHECKOUT", vsn_url,
+                                      pool));
 
   /* ### store this into cc to avoid pool growth */
   body = apr_psprintf(request->pool,
@@ -807,9 +807,8 @@ static svn_error_t * commit_delete_entry
                                 APR_HASH_KEY_STRING)))
         apr_hash_set(child_tokens, path, APR_HASH_KEY_STRING, token);
 
-
-      request =
-        svn_ra_neon__request_create(parent->cc->ras, "DELETE", child, pool);
+      SVN_ERR(svn_ra_neon__request_create(&request, parent->cc->ras, "DELETE",
+                                          child, pool));
 
       err = svn_ra_neon__assemble_locktoken_body(&locks_list,
                                                  child_tokens, request->pool);
@@ -1274,7 +1273,8 @@ static svn_error_t * commit_close_file(v
       svn_error_t *err = SVN_NO_ERROR;
 
       /* create/prep the request */
-      request = svn_ra_neon__request_create(cc->ras, "PUT", url, pool);
+      SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "PUT", url,
+                                          pool));
 
       extra_headers = apr_hash_make(request->pool);
 
@@ -1355,9 +1355,8 @@ static svn_error_t * commit_close_edit(v
                                       cc->disable_merge_response,
                                       pool));
   SVN_ERR(delete_activity(edit_baton, pool));
-  SVN_ERR(svn_ra_neon__maybe_store_auth_info(cc->ras, pool));
 
-  if (commit_info->revision != SVN_INVALID_REVNUM)
+  if (cc->callback && commit_info->revision != SVN_INVALID_REVNUM)
     SVN_ERR(cc->callback(commit_info, cc->callback_baton, pool));
 
   return SVN_NO_ERROR;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/fetch.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/fetch.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/fetch.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/fetch.c Wed Sep 15 19:32:26 2010
@@ -394,7 +394,7 @@ static svn_error_t *custom_get_request(s
       delta_base = NULL;
     }
 
-  request = svn_ra_neon__request_create(ras, "GET", url, pool);
+  SVN_ERR(svn_ra_neon__request_create(&request, ras, "GET", url, pool));
 
   if (delta_base)
     {
@@ -1097,7 +1097,8 @@ svn_error_t *svn_ra_neon__get_latest_rev
      PROPFINDs. */
   if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(ras))
     {
-      SVN_ERR(svn_ra_neon__exchange_capabilities(ras, latest_revnum, pool));
+      SVN_ERR(svn_ra_neon__exchange_capabilities(ras, NULL, 
+                                                 latest_revnum, pool));
       if (! SVN_IS_VALID_REVNUM(*latest_revnum))
         return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
                                 _("The OPTIONS response did not include "
@@ -1111,7 +1112,6 @@ svn_error_t *svn_ra_neon__get_latest_rev
                                              ras, ras->root.path,
                                              SVN_INVALID_REVNUM, pool));
     }
-  SVN_ERR(svn_ra_neon__maybe_store_auth_info(ras, pool));
 
   return NULL;
 }
@@ -1713,8 +1713,10 @@ start_element(int *elem, void *userdata,
       if (! rb->receiving_all)
         break;
 
+      base_checksum = svn_xml_get_attr_value("base-checksum", atts);
+
       SVN_ERR((*rb->editor->apply_textdelta)(rb->file_baton,
-                                             NULL, /* ### base_checksum */
+                                             base_checksum,
                                              rb->file_pool,
                                              &(rb->whandler),
                                              &(rb->whandler_baton)));
@@ -2439,8 +2441,7 @@ static svn_error_t * reporter_finish_rep
          _("REPORT response handling failed to complete the editor drive"));
     }
 
-  /* store auth info if we can. */
-  return svn_ra_neon__maybe_store_auth_info(rb->ras, pool);
+  return SVN_NO_ERROR;
 }
 
 static const svn_ra_reporter3_t ra_neon_reporter = {

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/get_locks.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/get_locks.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/get_locks.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/get_locks.c Wed Sep 15 19:32:26 2010
@@ -85,7 +85,7 @@ static const svn_ra_neon__xml_elm_t getl
  * The get-locks-report xml request body is super-simple.
  * The server doesn't need anything but the URI in the REPORT request line.
  *
- *    <S:get-locks-report xmlns...>
+ *    <S:get-locks-report [depth=DEPTH] xmlns...>
  *    </S:get-locks-report>
  *
  * The get-locks-report xml response is just a list of svn_lock_t's
@@ -121,6 +121,8 @@ static const svn_ra_neon__xml_elm_t getl
 
 /* Context for parsing server's response. */
 typedef struct {
+  const char *path;                /* target of the report */
+  svn_depth_t requested_depth;     /* requested depth of the report */
   svn_lock_t *current_lock;        /* the lock being constructed */
   svn_stringbuf_t *cdata_accum;    /* a place to accumulate cdata */
   const char *encoding;            /* normally NULL, else the value of
@@ -235,8 +237,33 @@ getlocks_end_element(void *userdata, int
         SVN_ERR(svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
                                  _("Incomplete lock data returned")));
 
-      apr_hash_set(baton->lock_hash, baton->current_lock->path,
-                   APR_HASH_KEY_STRING, baton->current_lock);
+      /* Filter out unwanted paths.  Since Subversion only allows
+         locks on files, we can treat depth=immediates the same as
+         depth=files for filtering purposes.  Meaning, we'll keep
+         this lock if:
+
+         a) its path is the very path we queried, or
+         b) we've asked for a fully recursive answer, or
+         c) we've asked for depth=files or depth=immediates, and this
+            lock is on an immediate child of our query path.
+      */
+      if ((strcmp(baton->path, baton->current_lock->path) == 0)
+          || (baton->requested_depth == svn_depth_infinity))
+        {
+          apr_hash_set(baton->lock_hash, baton->current_lock->path,
+                       APR_HASH_KEY_STRING, baton->current_lock);
+        }
+      else if ((baton->requested_depth == svn_depth_files) ||
+               (baton->requested_depth == svn_depth_immediates))
+        {
+          const char *rel_uri = svn_uri_is_child(baton->path,
+                                                 baton->current_lock->path,
+                                                 baton->scratchpool);
+          if (rel_uri && (svn_path_component_count(rel_uri) == 1))
+            apr_hash_set(baton->lock_hash, baton->current_lock->path,
+                         APR_HASH_KEY_STRING, baton->current_lock);
+          svn_pool_clear(baton->scratchpool);
+        }
       break;
 
     case ELEM_lock_path:
@@ -330,20 +357,30 @@ getlocks_end_element(void *userdata, int
 }
 
 
-
 svn_error_t *
 svn_ra_neon__get_locks(svn_ra_session_t *session,
                        apr_hash_t **locks,
                        const char *path,
+                       svn_depth_t depth,
                        apr_pool_t *pool)
 {
   svn_ra_neon__session_t *ras = session->priv;
-  const char *body, *url;
+  const char *body, *url, *rel_path;
   svn_error_t *err;
   int status_code = 0;
   get_locks_baton_t baton;
 
+  /* We always run the report on the 'public' URL, which represents
+     HEAD anyway.  If the path doesn't exist in HEAD, then there can't
+     possibly be a lock, so we just return no locks. */
+  url = svn_path_url_add_component2(ras->url->data, path, pool);
+
+  SVN_ERR(svn_ra_neon__get_path_relative_to_root(session, &rel_path,
+                                                 url, pool));
+
   baton.lock_hash = apr_hash_make(pool);
+  baton.path = apr_pstrcat(pool, "/", rel_path, NULL);
+  baton.requested_depth = depth;
   baton.pool = pool;
   baton.scratchpool = svn_pool_create(pool);
   baton.current_lock = NULL;
@@ -353,14 +390,9 @@ svn_ra_neon__get_locks(svn_ra_session_t 
   body = apr_psprintf(pool,
                       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                       "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
-                      "xmlns:D=\"DAV:\">"
-                      "</S:get-locks-report>");
-
-
-  /* We always run the report on the 'public' URL, which represents
-     HEAD anyway.  If the path doesn't exist in HEAD, then there can't
-     possibly be a lock, so we just return no locks. */
-  url = svn_path_url_add_component(ras->url->data, path, pool);
+                      "xmlns:D=\"DAV:\" depth=\"%s\">"
+                      "</S:get-locks-report>",
+                      svn_depth_to_word(depth));
 
   err = svn_ra_neon__parsed_request(ras, "REPORT", url,
                                     body, NULL, NULL,

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/lock.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/lock.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/lock.c Wed Sep 15 19:32:26 2010
@@ -32,6 +32,7 @@
 #include "svn_ra.h"
 #include "../libsvn_ra/ra_loader.h"
 #include "svn_path.h"
+#include "svn_string.h"
 #include "svn_time.h"
 #include "svn_private_config.h"
 
@@ -209,8 +210,9 @@ lock_from_baton(svn_lock_t **lock,
         {
           if (strncmp("Second-", timeout_str, strlen("Second-")) == 0)
             {
-              int time_offset = atoi(&(timeout_str[7]));
-
+              int time_offset;
+              
+              SVN_ERR(svn_cstring_atoi(&time_offset, &(timeout_str[7])));
               lck->expiration_date = lck->creation_date
                 + apr_time_from_sec(time_offset);
             }
@@ -259,7 +261,7 @@ do_lock(svn_lock_t **lock,
                                _("Failed to parse URI '%s'"), url);
     }
 
-  req = svn_ra_neon__request_create(ras, "LOCK", uri.path, pool);
+  SVN_ERR(svn_ra_neon__request_create(&req, ras, "LOCK", uri.path, pool));
   ne_uri_free(&uri);
 
   lrb->pool = pool;
@@ -532,7 +534,7 @@ svn_ra_neon__get_lock_internal(svn_ra_ne
   url = apr_pstrdup(pool, uri.path);
   ne_uri_free(&uri);
 
-  req = svn_ra_neon__request_create(ras, "PROPFIND", url, pool);
+  SVN_ERR(svn_ra_neon__request_create(&req, ras, "PROPFIND", url, pool));
 
   lrb->pool = pool;
   lrb->xml_table = lock_elements;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/options.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/options.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/options.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_ra_neon/options.c Wed Sep 15 19:32:26 2010
@@ -144,6 +144,8 @@ parse_capabilities(ne_request *req,
   *youngest_rev = SVN_INVALID_REVNUM;
 
   /* Start out assuming all capabilities are unsupported. */
+  apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
+               APR_HASH_KEY_STRING, capability_no);
   apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
                APR_HASH_KEY_STRING, capability_no);
   apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
@@ -249,6 +251,7 @@ parse_capabilities(ne_request *req,
 
 svn_error_t *
 svn_ra_neon__exchange_capabilities(svn_ra_neon__session_t *ras,
+                                   const char **relocation_location,
                                    svn_revnum_t *youngest_rev,
                                    apr_pool_t *pool)
 {
@@ -262,8 +265,11 @@ svn_ra_neon__exchange_capabilities(svn_r
   oc.cdata = svn_stringbuf_create("", pool);
 
   *youngest_rev = SVN_INVALID_REVNUM;
+  if (relocation_location)
+    *relocation_location = NULL;
 
-  req = svn_ra_neon__request_create(ras, "OPTIONS", ras->url->data, pool);
+  SVN_ERR(svn_ra_neon__request_create(&req, ras, "OPTIONS", ras->url->data,
+                                      pool));
 
   /* ### Use a symbolic name somewhere for this MIME type? */
   ne_add_request_header(req->ne_req, "Content-Type", "text/xml");
@@ -280,9 +286,17 @@ svn_ra_neon__exchange_capabilities(svn_r
                                            "<D:options xmlns:D=\"DAV:\">"
                                            "<D:activity-collection-set/>"
                                            "</D:options>",
-                                           200, 0, pool)))
+                                           200,
+                                           relocation_location ? 301 : 0,
+                                           pool)))
     goto cleanup;
 
+  if (req->code == 301)
+    {
+      *relocation_location = svn_ra_neon__request_get_location(req, pool);
+      goto cleanup;
+    }
+
   /* Was there an XML parse error somewhere? */
   err = svn_ra_neon__check_parse_error("OPTIONS", parser, ras->url->data);
   if (err)
@@ -316,7 +330,8 @@ svn_ra_neon__get_activity_collection(con
 {
   svn_revnum_t ignored_revnum;
   if (! ras->act_coll)
-    SVN_ERR(svn_ra_neon__exchange_capabilities(ras, &ignored_revnum, pool));
+    SVN_ERR(svn_ra_neon__exchange_capabilities(ras, NULL, 
+                                               &ignored_revnum, pool));
   *activity_coll = svn_string_create(ras->act_coll, pool);
   return SVN_NO_ERROR;
 }
@@ -346,7 +361,8 @@ svn_ra_neon__has_capability(svn_ra_sessi
   if (cap_result == NULL)
     {
       svn_revnum_t ignored_revnum;
-      SVN_ERR(svn_ra_neon__exchange_capabilities(ras, &ignored_revnum, pool));
+      SVN_ERR(svn_ra_neon__exchange_capabilities(ras, NULL,
+                                                 &ignored_revnum, pool));
     }