<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - std::experimental::filesystem::file_time_type::min() exceeds values supported by the underlying file system"
   href="https://bugs.llvm.org/show_bug.cgi?id=35990">35990</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>std::experimental::filesystem::file_time_type::min() exceeds values supported by the underlying file system
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libc++
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>PC
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>All Bugs
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedclangbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>vsapsai@apple.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>eric@efcs.ca, llvm-bugs@lists.llvm.org, mclow.lists@gmail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>Created <span class=""><a href="attachment.cgi?id=19698" name="attach_19698" title="Code to test with low-level API.">attachment 19698</a> <a href="attachment.cgi?id=19698&action=edit" title="Code to test with low-level API.">[details]</a></span>
Code to test with low-level API.

std::experimental::filesystem::file_time_type::min() exceeds values supported
by the underlying file system. When you set file modification time with
filesystem::last_write_time(path, time) with min value and read it back with
filesystem::last_write_time(path), received value can be significantly
different from the supplied one.

Steps to reproduce:
1. Call
    auto min_time = std::experimental::filesystem::file_time_type::min();
    std::experimental::filesystem::last_write_time(path, min_time);
2. Call
    auto current_time = std::experimental::filesystem::last_write_time(path);
3. Reboot machine you are running the test on.
4. Call
    auto current_time2 = std::experimental::filesystem::last_write_time(path);

Expected result:
min_time, current_time, current_time2 should be equal, at least to the second.

Actual result:
Results depend on the underlying file system.

On macOS with APFS min_time != current_time, current_time == current_time2.
Inequality is detected by the test
std/experimental/filesystem/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp

On Ubuntu 16.04.3 with ext3 min_time == current_time, current_time !=
current_time2.

Details:
For writing and reading file modification timestamp we are using utimensat and
stat, respectively. In my testing filesystem::file_time_type::min() was -2^63
microseconds which can be expressed with `struct timespec` which has 64-bit
tv_sec and 64-bit tv_nsec. But file systems I tested don't use 128 bits to
store timestamps on the disk. One common approach seems to be using 128-bit
value while inode metadata is still in memory. That allows the test
last_write_time.pass.cpp to pass on ext3 and probably ext4. But according to
POSIX.1-2008 <a href="http://pubs.opengroup.org/onlinepubs/9699919799/">http://pubs.opengroup.org/onlinepubs/9699919799/</a> that is not
conformant

<span class="quote">> Upon assignment, file timestamps are immediately converted to the resolution
> of the file system by truncation (i.e., the recorded time can be older than
> the actual time). For example, if the file system resolution is 1
> microsecond, then a conforming stat() must always return an st_mtim.tv_nsec
> that is a multiple of 1000. Some older implementations returned
> higher-resolution timestamps while the inode information was cached, and then
> spontaneously truncated the tv_nsec fields when they were stored to and
> retrieved from disk, but this behavior does not conform.
> - Section 11. Headers, <sys/stat.h></span >

Regardless of the POSIX standard, I think the developers expect that values in
file_time_type::min()/max() range are safe to use and won't change on their
own.

I have attached `testing.c` to test utimensat and stat directly and the results
are
On APFS:
Will update file mtime to
 tv_sec  = -9223372036854,
 tv_nsec = 0
File mtime now is
 tv_sec  = -9223372036,
 tv_nsec = -854775808

On ext3:
Will update file mtime to
 tv_sec  = -9223372036854,
 tv_nsec = 0
File mtime now is
 tv_sec  = -9223372036854,
 tv_nsec = 0

after rebooting
File mtime now is
 tv_sec  = 2217714954,
 tv_nsec = 0</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>