You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ko...@apache.org on 2021/08/27 13:53:57 UTC

svn commit: r1892649 - in /subversion/branches/pristines-on-demand/subversion: libsvn_wc/textbase.c tests/cmdline/externals_tests.py

Author: kotkov
Date: Fri Aug 27 13:53:57 2021
New Revision: 1892649

URL: http://svn.apache.org/viewvc?rev=1892649&view=rev
Log:
On the pristines-on-demand branch: Use contents of the working file as
the text-base if there are no modifications.

This is a prerequisite step to making the pristine text-base files optional,
because we want to be able to only fetch the base contents when it's actually
required, and when it cannot be provided from what's already available.
In case of a file without modifications, the text-base is the contents of
the file itself, appropriately detranslated.

* subversion/libsvn_wc/textbase.c
  (): Include translate.h.
  (compare_and_verify): New helper function, based on the implementation of
   svn_wc__internal_file_modified_p().
  (check_file_modified): New helper function.
  (open_textbase): Ask wc.db for additional props, such as the file size
   and timestamp.  If the file is unmodified, use it as the text-base.

* subversion/tests/cmdline/externals_tests.py
  (update_modify_file_external,
   remap_file_external_with_prop_del,
   file_external_recorded_info): Mark these tests as XFAIL on Windows,
   because we face an existing issue where the `src_stream` not closed in
   externals.c:apply_textdelta() and the file handle is kept open up to the
   point when we try to rename over that file.  On Windows, renaming
   over an open file is not supported, at least without using certain file
   flags (FILE_FLAG_POSIX_SEMANTICS).  Since this is an existing issue,
   let's handle it separately.

Modified:
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c
    subversion/branches/pristines-on-demand/subversion/tests/cmdline/externals_tests.py

Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c?rev=1892649&r1=1892648&r2=1892649&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c (original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c Fri Aug 27 13:53:57 2021
@@ -25,9 +25,176 @@
 
 #include "textbase.h"
 #include "wc.h"
+#include "translate.h"
 #include "workqueue.h"
 
 static svn_error_t *
+compare_and_verify(svn_boolean_t *modified_p,
+                   svn_wc__db_t *db,
+                   const char *versioned_file_abspath,
+                   svn_filesize_t versioned_file_size,
+                   const svn_checksum_t *pristine_checksum,
+                   svn_boolean_t has_props,
+                   svn_boolean_t props_mod,
+                   apr_pool_t *scratch_pool)
+{
+  svn_subst_eol_style_t eol_style;
+  const char *eol_str;
+  apr_hash_t *keywords;
+  svn_boolean_t special = FALSE;
+  svn_boolean_t need_translation;
+  svn_stream_t *v_stream; /* versioned_file */
+  svn_checksum_t *v_checksum;
+  svn_error_t *err;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath));
+
+  if (props_mod)
+    has_props = TRUE; /* Maybe it didn't have properties; but it has now */
+
+  if (has_props)
+    {
+      SVN_ERR(svn_wc__get_translate_info(&eol_style, &eol_str,
+                                         &keywords,
+                                         &special,
+                                         db, versioned_file_abspath, NULL,
+                                         TRUE, scratch_pool, scratch_pool));
+
+      if (eol_style == svn_subst_eol_style_unknown)
+        return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
+
+      need_translation = svn_subst_translation_required(eol_style, eol_str,
+                                                        keywords, special,
+                                                        TRUE);
+    }
+  else
+    need_translation = FALSE;
+
+  if (! need_translation)
+    {
+      svn_filesize_t pristine_size;
+
+      SVN_ERR(svn_wc__db_pristine_read(NULL, &pristine_size, db,
+                                       versioned_file_abspath, pristine_checksum,
+                                       scratch_pool, scratch_pool));
+
+      if (versioned_file_size != pristine_size)
+        {
+          *modified_p = TRUE;
+
+          return SVN_NO_ERROR;
+        }
+    }
+
+  /* ### Other checks possible? */
+
+  /* Reading files is necessary. */
+  if (special && need_translation)
+    {
+      SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath,
+                                          scratch_pool, scratch_pool));
+    }
+  else
+    {
+      /* We don't use APR-level buffering because the comparison function
+       * will do its own buffering. */
+      apr_file_t *file;
+      err = svn_io_file_open(&file, versioned_file_abspath, APR_READ,
+                             APR_OS_DEFAULT, scratch_pool);
+      /* Convert EACCESS on working copy path to WC specific error code. */
+      if (err && APR_STATUS_IS_EACCES(err->apr_err))
+        return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
+      else
+        SVN_ERR(err);
+      v_stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
+
+      if (need_translation)
+        {
+          const char *pristine_eol_str;
+
+          if (eol_style == svn_subst_eol_style_native)
+            pristine_eol_str = SVN_SUBST_NATIVE_EOL_STR;
+          else
+            pristine_eol_str = eol_str;
+
+            /* Wrap file stream to detranslate into normal form,
+             * "repairing" the EOL style if it is inconsistent. */
+            v_stream = svn_subst_stream_translated(v_stream,
+                                                   pristine_eol_str,
+                                                   TRUE /* repair */,
+                                                   keywords,
+                                                   FALSE /* expand */,
+                                                   scratch_pool);
+        }
+    }
+
+  /* Get checksum of detranslated (normalized) content. */
+  err = svn_stream_contents_checksum(&v_checksum, v_stream,
+                                     pristine_checksum->kind,
+                                     scratch_pool, scratch_pool);
+  /* Convert EACCESS on working copy path to WC specific error code. */
+  if (err && APR_STATUS_IS_EACCES(err->apr_err))
+    return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
+  else
+    SVN_ERR(err);
+
+  *modified_p = (! svn_checksum_match(v_checksum, pristine_checksum));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+check_file_modified(svn_boolean_t *modified_p,
+                    svn_wc__db_t *db,
+                    const char *local_abspath,
+                    svn_filesize_t recorded_size,
+                    apr_time_t recorded_time,
+                    const svn_checksum_t *pristine_checksum,
+                    svn_boolean_t has_props,
+                    svn_boolean_t props_mod,
+                    apr_pool_t *scratch_pool)
+{
+  const svn_io_dirent2_t *dirent;
+  svn_boolean_t modified;
+
+  SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
+                              scratch_pool, scratch_pool));
+
+  if (dirent->kind == svn_node_file &&
+      dirent->filesize == recorded_size &&
+      dirent->mtime == recorded_time)
+    {
+      modified = FALSE;
+    }
+  else if (dirent->kind == svn_node_file)
+    {
+      SVN_ERR(compare_and_verify(&modified, db, local_abspath,
+                                 dirent->filesize, pristine_checksum,
+                                 has_props, props_mod, scratch_pool));
+      if (!modified)
+        {
+          svn_boolean_t own_lock;
+
+          /* The timestamp is missing or "broken" so "repair" it if we can. */
+          SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath,
+                                              FALSE, scratch_pool));
+          if (own_lock)
+            SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
+                                                      dirent->filesize,
+                                                      dirent->mtime,
+                                                      scratch_pool));
+        }
+    }
+  else
+    {
+      modified = TRUE;
+    }
+
+  *modified_p = modified;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 open_textbase(svn_stream_t **contents_p,
               svn_wc__db_t *db,
               const char *local_abspath,
@@ -38,12 +205,19 @@ open_textbase(svn_stream_t **contents_p,
   svn_wc__db_status_t status;
   svn_node_kind_t kind;
   const svn_checksum_t *checksum;
+  svn_filesize_t recorded_size;
+  apr_time_t recorded_time;
+  svn_boolean_t have_props;
+  svn_boolean_t props_mod;
   const svn_checksum_t *target_checksum;
 
-  SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
-                                        NULL, &checksum, NULL, NULL,
-                                        NULL, db, local_abspath,
-                                        scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, &checksum, NULL, NULL,
+                               NULL, NULL, NULL, NULL, &recorded_size,
+                               &recorded_time, NULL, NULL, NULL,
+                               &have_props, &props_mod, NULL, NULL,
+                               NULL, db, local_abspath,
+                               scratch_pool, scratch_pool));
 
   /* Sanity */
   if (kind != svn_node_file)
@@ -73,6 +247,16 @@ open_textbase(svn_stream_t **contents_p,
                                svn_dirent_local_style(local_abspath,
                                                       scratch_pool));
     }
+  else if (status == svn_wc__db_status_deleted)
+    {
+      SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
+                                            NULL, &checksum, NULL, &have_props,
+                                            NULL, db, local_abspath,
+                                            scratch_pool, scratch_pool));
+      recorded_size = SVN_INVALID_FILESIZE;
+      recorded_time = -1;
+      props_mod = TRUE;
+    }
 
   if (textbase_checksum)
     target_checksum = textbase_checksum;
@@ -85,6 +269,26 @@ open_textbase(svn_stream_t **contents_p,
       return SVN_NO_ERROR;
     }
 
+  if (checksum && svn_checksum_match(checksum, target_checksum))
+    {
+      svn_boolean_t modified;
+
+      SVN_ERR(check_file_modified(&modified, db, local_abspath, recorded_size,
+                                  recorded_time, target_checksum, have_props,
+                                  props_mod, scratch_pool));
+      if (!modified)
+        {
+          SVN_ERR(svn_wc__internal_translated_stream(contents_p, db,
+                                                     local_abspath,
+                                                     local_abspath,
+                                                     SVN_WC_TRANSLATE_TO_NF,
+                                                     result_pool,
+                                                     scratch_pool));
+
+          return SVN_NO_ERROR;
+        }
+    }
+
   SVN_ERR(svn_wc__db_pristine_read(contents_p, NULL, db, local_abspath,
                                    target_checksum, result_pool, scratch_pool));
 

Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/externals_tests.py?rev=1892649&r1=1892648&r2=1892649&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/externals_tests.py Fri Aug 27 13:53:57 2021
@@ -1625,6 +1625,8 @@ def merge_target_with_externals(sbox):
      "    /A-branch:8\n"],
     [], 'pg', svntest.main.SVN_PROP_MERGEINFO, '-vR', wc_dir)
 
+# Existing issue: `src_stream` not closed in externals.c:apply_textdelta()
+@XFail(svntest.main.is_os_windows)
 def update_modify_file_external(sbox):
   "update that modifies a file external"
 
@@ -2790,6 +2792,8 @@ def shadowing(sbox):
 # Test for issue #4093 'remapping a file external can segfault due to
 # "deleted" props'.
 @Issue(4093)
+# Existing issue: `src_stream` not closed in externals.c:apply_textdelta()
+@XFail(svntest.main.is_os_windows)
 def remap_file_external_with_prop_del(sbox):
   "file external remap segfaults due to deleted props"
 
@@ -4167,6 +4171,8 @@ def file_external_to_normal_file(sbox):
                                         expected_status)
 
 @Issue(4580)
+# Existing issue: `src_stream` not closed in externals.c:apply_textdelta()
+@XFail(svntest.main.is_os_windows)
 def file_external_recorded_info(sbox):
   "check file external recorded info"