Philip Martin <philip.mar...@wandisco.com> writes: > Evgeny Kotkov <evgeny.kot...@visualsvn.com> writes: > >> The reason why this error is propagated up the stack is that we only examine >> the 'ignore_enoent' argument after the first apr_file_remove() call. This is >> racy — if we get a EACCES during the first attempt to remove a file, and the >> file is simultaneously removed from the disk, the next attempt to remove it >> would fail with a ENOENT, even with 'ignore_enoent'. I think we should >> fix this by suppressing ENOENTs from every apr_file_remove() call, not >> just the first one. > > Sounds plausible.
Here is the patch for this part of the issue. Log message: [[[ Prevent svn_io_remove_file2() from misbehaving in certain racy situations with ignore_enoent = TRUE. On Windows, this function might call apr_file_remove() more than once due to a special handling of access denied errors. However, we only handle ENOENTs during the first call. If we happen to receive a EACCES during the first attempt to remove a file, and the file is gone at the moment we try again, a caller will receive an ENOENT; this will happen regardless of the ignore_enoent argument value. Given that the caller explicitly told us that he/she is okay with the files not being present, returning an error is just wrong. * subversion/libsvn_subr/io.c (svn_io_remove_file2): Respect ignore_enoent argument after every possible attempt to remove a file, including retries under WIN32. While here, make the conditional statements a bit more readable by splitting them. Found by: Cory Riddell <cory{_AT_}codeware.com> (Message-ID: <54c02169.8090...@codeware.com> in the users mailing list, http://svn.haxx.se/users/archive-2015-01/0082.shtml) ]]] Any objections? Regards, Evgeny Kotkov
Index: subversion/libsvn_subr/io.c =================================================================== --- subversion/libsvn_subr/io.c (revision 1653810) +++ subversion/libsvn_subr/io.c (working copy) @@ -2456,11 +2456,6 @@ svn_io_remove_file2(const char *path, SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool)); apr_err = apr_file_remove(path_apr, scratch_pool); - if (!apr_err - || (ignore_enoent - && (APR_STATUS_IS_ENOENT(apr_err) - || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))) - return SVN_NO_ERROR; #ifdef WIN32 /* If the target is read only NTFS reports EACCESS and FAT/FAT32 @@ -2495,11 +2490,20 @@ svn_io_remove_file2(const char *path, } #endif - if (apr_err) - return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"), - svn_dirent_local_style(path, scratch_pool)); - - return SVN_NO_ERROR; + if (!apr_err) + { + return SVN_NO_ERROR; + } + else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(apr_err))) + { + return SVN_NO_ERROR; + } + else + { + return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"), + svn_dirent_local_style(path, scratch_pool)); + } }