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

svn commit: r944540 - /subversion/trunk/subversion/libsvn_subr/io.c

Author: rhuijben
Date: Sat May 15 01:21:21 2010
New Revision: 944540

URL: http://svn.apache.org/viewvc?rev=944540&view=rev
Log:
Create a Windows specific implementation of file_mktemp() to work around
access denied errors returned from apr's internal gettemp implementation. This
moves the windows code back to an improved version of our 1.6 tempfile
creation code.

The collision avoidance code I added in file_mktemp() is not really high tech,
but it doesn't cause a single collision when running the ra_local tests.
(Not sure if that is a good or a bad thing ;-)

The windows standard tempfile creation function doesn't support long path
names, so it can't be used in places where our temp functions are used.

* subversion/libsvn_subr/io.c
  (tempname_counter): New variable.
  (file_mktemp): Update argument to receive a directory path instead of a
    template and create the template for apr. Add a Windows specific
    implementation using some code borrowed from svn_io_open_uniquely_named().

Modified:
    subversion/trunk/subversion/libsvn_subr/io.c

Modified: subversion/trunk/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/io.c?rev=944540&r1=944539&r2=944540&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/io.c (original)
+++ subversion/trunk/subversion/libsvn_subr/io.c Sat May 15 01:21:21 2010
@@ -3525,15 +3525,27 @@ svn_io_files_contents_same_p(svn_boolean
   return SVN_NO_ERROR;
 }
 
-/* Wrapper for apr_file_mktemp(). */
+#ifdef WIN32
+/* Counter value of file_mktemp request (used in a threadsafe way), to make
+   sure that a single process normally never generates the same tempname
+   twice */
+static volatile apr_uint32_t tempname_counter = 0;
+#endif
+
+/* Creates a new temporary file in DIRECTORY with apr flags FLAGS.
+   Set *NEW_FILE to the file handle */
 static svn_error_t *
-file_mktemp(apr_file_t **new_file, const char *templ,
+file_mktemp(apr_file_t **new_file, const char *directory,
             apr_int32_t flags, apr_pool_t *pool)
 {
+#ifndef WIN32
   const char *templ_apr;
   apr_status_t status;
 
-  SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, pool));
+  SVN_ERR(svn_path_cstring_from_utf8(&templ_apr,
+                                     svn_dirent_join(dirpath, "svn-XXXXXX",
+                                                     scratch_pool),
+                                     pool));
 
   /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the
          data available in POOL and we need a non-const pointer here,
@@ -3548,6 +3560,93 @@ file_mktemp(apr_file_t **new_file, const
                               "template '%s'"), templ);
   else
     return SVN_NO_ERROR;
+#else
+  /* The Windows implementation of apr_file_mktemp doesn't handle access
+     denied errors correctly. Therefore we implement our own temp file
+     creation function here */
+
+  /* ### Most of this is borrowed from the svn_io_open_uniquely_named(),
+     ### the function we used before. But we try to guess a more unique
+     ### name before trying if it exists. */
+
+  /* Offset by some time value and a unique request nr to make the number
+     +- unique for both this process and on the computer */
+  int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter) 
+               + GetCurrentProcessId();
+  int i;
+
+  for (i = 0; i <= 99999; i++)
+    {
+      apr_uint32_t unique_nr;
+      const char *unique_name;
+      const char *unique_name_apr;
+      apr_file_t *try_file;
+      apr_status_t apr_err;
+
+      /* Generate a number that should be unique for this application and
+         usually for the entire computer to reduce the number of cycles
+         through this loop. (A bit of calculation is much cheaper then
+         disk io) */
+      unique_nr = baseNr + 3 * i;
+
+      unique_name = apr_psprintf(pool, "%s/svn-%X", directory,
+                                 unique_nr);
+
+      SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool));
+
+      apr_err = file_open(&try_file, unique_name_apr, flags,
+                          APR_OS_DEFAULT, FALSE, pool);
+
+      if (APR_STATUS_IS_EEXIST(apr_err))
+          continue;
+      else if (apr_err)
+        {
+          /* On Win32, CreateFile fails with an "Access Denied" error
+             code, rather than "File Already Exists", if the colliding
+             name belongs to a directory. */
+
+          if (APR_STATUS_IS_EACCES(apr_err))
+            {
+              apr_finfo_t finfo;
+              apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr,
+                                                APR_FINFO_TYPE, pool);
+
+              if (!apr_err_2 && finfo.filetype == APR_DIR)
+                continue;
+
+#ifdef WIN32
+              apr_err_2 = APR_TO_OS_ERROR(apr_err);
+
+              if (apr_err_2 == ERROR_ACCESS_DENIED ||
+                  apr_err_2 == ERROR_SHARING_VIOLATION)
+                {
+                  /* The file is in use by another process or is hidden;
+                     create a new name, but don't do this 99999 times in
+                     case the folder is not writable */
+                  i += 797;
+                  continue;
+                }
+#endif
+
+              /* Else fall through and return the original error. */
+            }
+
+          return svn_error_wrap_apr(apr_err, _("Can't open '%s'"),
+                                    svn_dirent_local_style(unique_name, pool));
+        }
+      else
+        {
+          *new_file = try_file;
+
+          return SVN_NO_ERROR;
+        }
+    }
+
+  return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
+                           NULL,
+                           _("Unable to make name in '%s'"),
+                           svn_dirent_local_style(directory, pool));
+#endif
 }
 
 /* Wrapper for apr_file_name_get(). */
@@ -3582,7 +3681,6 @@ svn_io_open_unique_file3(apr_file_t **fi
 {
   apr_file_t *tempfile;
   const char *tempname;
-  char *path;
   struct temp_file_cleanup_s *baton = NULL;
   apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL |
                        APR_BUFFERED | APR_BINARY);
@@ -3599,8 +3697,6 @@ svn_io_open_unique_file3(apr_file_t **fi
   if (dirpath == NULL)
     SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool));
 
-  path = svn_dirent_join(dirpath, "svn-XXXXXX", scratch_pool);
-
   switch (delete_when)
     {
       case svn_io_file_del_on_pool_cleanup:
@@ -3625,7 +3721,7 @@ svn_io_open_unique_file3(apr_file_t **fi
         break;
     }
 
-  SVN_ERR(file_mktemp(&tempfile, path, flags, result_pool));
+  SVN_ERR(file_mktemp(&tempfile, dirpath, flags, result_pool));
   SVN_ERR(svn_io_file_name_get(&tempname, tempfile, scratch_pool));
 
 #ifndef WIN32