You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2014/02/02 03:23:32 UTC

svn commit: r1563542 - in /subversion/branches/fsfs-ucsnorm/subversion: include/svn_error_codes.h libsvn_fs_fs/temp_serializer.c

Author: brane
Date: Sun Feb  2 02:23:30 2014
New Revision: 1563542

URL: http://svn.apache.org/r1563542
Log:
On the fsfs-ucsnorm branch: Prevent modifications of nodes with paths
that contain name collisions.

* subversion/include/svn_error_codes.h
  (SVN_ERR_FS_NAME_COLLISION): New error code.
* subversion/libsvn_fs_fs/temp_serializer.c
  (get_entry_key): New; factored entry key deserialization from find_entry.
  (find_entry): Changed signature to return an svn_error_t*.
   Call get_entry_key instead of duplicating the deserialization in three
   (formerly two) places.
   If the entry was found, double-check that its key is unique.
  (svn_fs_fs__extract_dir_entry, svn_fs_fs__replace_dir_entry):
   Update calls to find_entry.

Modified:
    subversion/branches/fsfs-ucsnorm/subversion/include/svn_error_codes.h
    subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c

Modified: subversion/branches/fsfs-ucsnorm/subversion/include/svn_error_codes.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/include/svn_error_codes.h?rev=1563542&r1=1563541&r2=1563542&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/include/svn_error_codes.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/include/svn_error_codes.h Sun Feb  2 02:23:30 2014
@@ -846,6 +846,11 @@ SVN_ERROR_START
              SVN_ERR_FS_CATEGORY_START + 60,
              "Move without a suitable deletion")
 
+  /** @since New in 1.9. */
+  SVN_ERRDEF(SVN_ERR_FS_NAME_COLLISION,
+             SVN_ERR_FS_CATEGORY_START + 61,
+             "Normalized directory entry names are identical")
+
   /* repos errors */
 
   SVN_ERRDEF(SVN_ERR_REPOS_LOCKED,

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c?rev=1563542&r1=1563541&r2=1563542&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c Sun Feb  2 02:23:30 2014
@@ -767,13 +767,30 @@ svn_fs_fs__get_sharded_offset(void **out
   return SVN_NO_ERROR;
 }
 
-/* Utility function that returns the lowest index of the first entry in
- * *ENTRIES that points to a dir entry with a key equal or larger than KEY.
- * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
- * the number of valid entries in ENTRIES.
+/* Helper function for find_entry() that returns the key for ENTRIES[INDEX] */
+static const char *
+get_entry_key(svn_fs_fs__dirent_t **entries, apr_size_t index)
+{
+  const svn_fs_fs__dirent_t *entry =
+    svn_temp_deserializer__ptr(entries, (const void *const *)&entries[index]);
+  const char* entry_key =
+    svn_temp_deserializer__ptr(entry, (const void *const *)&entry->key);
+
+  /* use the name if it's identical to the key */
+  if (!entry_key)
+    entry_key = svn_temp_deserializer__ptr(
+        entry, (const void *const *)&entry->dirent.name);
+  return entry_key;
+}
+
+/* Utility function that sets *POSITION to the lowest index of the
+ * first entry in *ENTRIES that points to a dir entry with a key equal
+ * or larger than KEY.  If an exact match has been found, *FOUND will
+ * be set to TRUE. COUNT is the number of valid entries in ENTRIES.
  */
-static apr_size_t
-find_entry(svn_fs_fs__dirent_t **entries,
+static svn_error_t *
+find_entry(apr_size_t *position,
+           svn_fs_fs__dirent_t **entries,
            const char *key,
            apr_size_t count,
            svn_boolean_t *found)
@@ -785,15 +802,7 @@ find_entry(svn_fs_fs__dirent_t **entries
 
   for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
     {
-      const svn_fs_fs__dirent_t *entry =
-        svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
-      const char* entry_key =
-        svn_temp_deserializer__ptr(entry, (const void *const *)&entry->key);
-
-      /* use the name if it's identical to the key */
-      if (!entry_key)
-        entry_key = svn_temp_deserializer__ptr(
-            entry, (const void *const *)&entry->dirent.name);
+      const char *entry_key = get_entry_key(entries, middle);
 
       if (0 >= strcmp(entry_key, key))
         lower = middle + 1;
@@ -805,20 +814,40 @@ find_entry(svn_fs_fs__dirent_t **entries
   *found = FALSE;
   if (lower < count)
     {
-      const svn_fs_fs__dirent_t *entry =
-        svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
-      const char* entry_key =
-        svn_temp_deserializer__ptr(entry, (const void *const *)&entry->key);
+      const char *entry_key = get_entry_key(entries, lower);
+
+      if (0 == strcmp(entry_key, key))
+        {
+          *found = TRUE;
 
-      /* use the name if it's identical to the key */
-      if (!entry_key)
-        entry_key = svn_temp_deserializer__ptr(
-            entry, (const void *const *)&entry->dirent.name);
+          /* Check for name collisions in the directory list.
 
-      *found = (strcmp(entry_key, key) == 0);
+             A repository that was upgraded from FSFS v6 or earlier
+             and had not been properly verified and sanitized might
+             contain name collisions. When normalized lookups are
+             enabled, we must forbid all operations on colliding
+             names, or users might end up making modifications to the
+             wrong node.
+
+             XXX This check is really only needed when normalized
+                 lookups are enabled. If it turns out to be a
+                 performance problem, we can propagate the
+                 normalized-lookups flag all the way through the API
+                 to here. */
+          if (/* normalized_lookup && */ count > 1 && lower < count - 1)
+            {
+              entry_key = get_entry_key(entries, lower + 1);
+              if (0 == strcmp(entry_key, key))
+                return svn_error_createf(
+                    SVN_ERR_FS_NAME_COLLISION, NULL,
+                    _("A directory contains more than one entry named '%s'"),
+                    key);
+            }
+        }
     }
 
-  return lower;
+  *position = lower;
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -841,10 +870,9 @@ svn_fs_fs__extract_dir_entry(void **out,
     svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths);
 
   /* binary search for the desired entry by key */
-  apr_size_t pos = find_entry((svn_fs_fs__dirent_t **)entries,
-                              key,
-                              dir_data->count,
-                              &found);
+  apr_size_t pos;
+  SVN_ERR(find_entry(&pos, (svn_fs_fs__dirent_t **)entries,
+                     key, dir_data->count, &found));
 
   /* de-serialize that entry or return NULL, if no match has been found */
   *out = NULL;
@@ -954,7 +982,8 @@ svn_fs_fs__replace_dir_entry(void **data
                                (const void *const *)&dir_data->lengths);
 
   /* binary search for the desired entry by key */
-  pos = find_entry(entries, replace_baton->key, dir_data->count, &found);
+  SVN_ERR(find_entry(&pos, entries, replace_baton->key,
+                     dir_data->count, &found));
 
   /* handle entry removal (if found at all) */
   if (replace_baton->new_entry == NULL)