Rework the timestamp handling in the working file writer utility layer.

This change aims at solving two problems where using that writer could
have been causing differences between the timestamp values on the working
files and in the wc.db.

The first problem is that on Windows (NTFS on some older versions and other
file systems such as ReFS, FAT32, ...), the file systems may defer processing
of timestamps until the file handle is closed [1].  So when we peek and return
the current timestamp, we must also ensure that the timestamp does not change
after the handle to the working file is closed.

We haven't been taking that into the account before, and that is what has
been causing some of the timestamp discrepancies.

Luckily, there are two options that guarantee that the file will keep its
current timestamp after close.  We can either explicitly set a new timestamp,
or use a special option that instructs the file system to suspend updates for
timestamp values for all subsequent I/O operations. Both of these options
guarantee [2, 3] that no other operation will change the final timestamp.
And we use both of them, depending on whether we have to set a specific
timestamp, or not.

The second problem is that sometimes, e.g. when using the `use-commit-times`
option, the install stream would return a timestamp without re-fetching it
from the OS.  That would cause a timestamp discrepancy if the timestamp
precision of the filesystem is different from the microsecond precision
of the apr_time_t.

Technically, we solve both problems by introducing a private API that allows
"finalizing" an install stream, during which we set the final attributes and
the timestamps of the file, and optionally return them to the caller.
The timestamp values are always re-fetched from the OS.

This is a follow-up to r1886490.

[1: MS-FSA, 2.1.4.17, Note <42>]
File systems may choose to defer processing for a file that has been
modified to a later time, favoring performance over accuracy. The NTFS
file system on versions prior to Windows 10 v1809 operating system,
Windows Server v1809 operating system, and Windows Server 2019, and
non-NTFS file systems on all versions of Windows, defer this processing
until the Open gets closed.
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/4e3695bd-7574-4f24-a223-b4679c065b63#Appendix_A_42

[2: MS-FSA 2.1.5.14.2]
If InputBuffer.LastWriteTime != 0:
  If InputBuffer.LastWriteTime != -2:
    The object store MUST set Open.UserSetModificationTime to TRUE.
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/a36513b4-73c8-4888-ad29-8f3a196567e8

[3: MS-FSA 2.1.4.17]
If Open.UserSetModificationTime is FALSE, set Open.File.LastModificationTime
to the current system time.
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/75cdaba1-4401-4c53-b09c-69ba6cd50ce6


* subversion/include/private/svn_io_private.h
  (svn_stream__install_get_info): Remove.
  (svn_stream__install_finalize): New.
  (svn_stream__install_stream): Tweak docstring.
  (SVN_IO__WIN_TIME_UNCHANGED,
   SVN_IO__WIN_TIME_SUSPEND_UPDATE): New constants.
  (svn_io__win_set_file_basic_info): Tweak docstring.

* subversion/libsvn_subr/io.c
  (svn_io__win_set_file_basic_info): Handle two new special values,
   SVN_IO__WIN_TIME_UNCHANGED and SVN_IO__WIN_TIME_SUSPEND_UPDATE,
   for the SET_MTIME argument.

* subversion/libsvn_subr/stream.c
  (svn_stream__install_finalize): Implement this new function.
   On Windows, either explicitly set the new timestamp, or suspend
   updates for timestamp values for all subsequent I/O operations.
   Always ask for the new timestamp value from the filesystem to
   properly handle a case where the filesystem has lower timestamp
   granularity than apr_time_t.
  (svn_stream__install_stream,
   svn_stream__install_delete): Handle a case where the file handle has
   been already closed.  That can now happen, for example, if the call to
   finalize() resulted in the fallback due to being unable to set file
   information by handle.

* subversion/libsvn_wc/working_file_writer.h
  (svn_wc__working_file_writer_get_info): Remove.
  (svn_wc__working_file_writer_finalize): New.
  (svn_wc__working_file_writer_stream): Tweak docstring.

* subversion/libsvn_wc/working_file_writer.c
  (svn_wc__working_file_writer_get_info): Remove.
  (svn_wc__working_file_writer_finalize): Implement this new function.
   Forward to the finalize() function for the install stream.

* subversion/libsvn_wc/wc_db.c
  (install_working_file): Finalize the working file and get its
   timestamp and size before installing it.

* subversion/libsvn_wc/wc_db_pristine.c
  (pristine_install_txn): Finalize the working file and get its size
   before installing it.

* subversion/libsvn_wc/workqueue.c
  (run_file_install): Finalize the working file before installing it.

* subversion/tests/libsvn_subr/io-test.c
  (test_install_stream_to_longpath,
   test_install_stream_over_readonly_file,
   test_install_stream_set_read_only,
   test_install_stream_set_affected_time,
   test_install_stream): Finalize the working file before installing it.
  (test_install_stream_delete,
   test_install_stream_delete_after_finalize): New tests.
  (test_funcs): Run the new tests.


git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1886774 13f79535-47bb-0310-9956-ffa450edef68
9 files changed