}
inline fs::file_time_type
- file_time(const stat_type& st) noexcept
+ file_time(const stat_type& st, std::error_code& ec) noexcept
{
using namespace std::chrono;
- return fs::file_time_type{
#ifdef _GLIBCXX_USE_ST_MTIM
- seconds{st.st_mtim.tv_sec} + nanoseconds{st.st_mtim.tv_nsec}
+ time_t s = st.st_mtim.tv_sec;
+ nanoseconds ns{st.st_mtim.tv_nsec};
#else
- seconds{st.st_mtime}
+ time_t s = st.st_mtime;
+ nanoseconds ns{};
#endif
- };
+
+ if (s >= (nanoseconds::max().count() / 1e9))
+ {
+ ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW
+ return fs::file_time_type::min();
+ }
+ ec.clear();
+ return fs::file_time_type{seconds{s} + ns};
}
// Returns true if the file descriptor was successfully closed,
}
else if (is_set(option, opts::update_existing))
{
- if (file_time(*from_st) <= file_time(*to_st))
- {
- ec.clear();
- return false;
- }
+ const auto from_mtime = file_time(*from_st, ec);
+ if (ec)
+ return false;
+ if ((from_mtime <= file_time(*to_st, ec)) || ec)
+ return false;
}
else if (!is_set(option, opts::overwrite_existing))
{
fs::file_time_type
fs::last_write_time(const path& p, error_code& ec) noexcept
{
- return do_stat(p, ec, [](const auto& st) { return file_time(st); },
+ return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
file_time_type::min());
}
--- /dev/null
+// Copyright (C) 2016 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-lstdc++fs" }
+// { dg-do run { target c++11 } }
+// { dg-require-filesystem-ts "" }
+
+// 15.25 Permissions [fs.op.last_write_time]
+
+#include <experimental/filesystem>
+#include <testsuite_fs.h>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#if _GLIBCXX_HAVE_UTIME_H
+# include <utime.h>
+#endif
+
+void
+test01()
+{
+ bool test __attribute__((unused)) = true;
+ using time_type = std::experimental::filesystem::file_time_type;
+
+ auto p = __gnu_test::nonexistent_path();
+ std::error_code ec;
+ time_type mtime = last_write_time(p, ec);
+ VERIFY( ec );
+ VERIFY( ec == std::make_error_code(std::errc::no_such_file_or_directory) );
+#if __cpp_exceptions
+ bool caught = false;
+ try {
+ mtime = last_write_time(p);
+ } catch (std::system_error const& e) {
+ caught = true;
+ ec = e.code();
+ }
+ VERIFY( caught );
+ VERIFY( ec );
+ VERIFY( ec == std::make_error_code(std::errc::no_such_file_or_directory) );
+#endif
+
+ __gnu_test::scoped_file file(p);
+ VERIFY( exists(p) );
+ mtime = last_write_time(p, ec);
+ VERIFY( !ec );
+ VERIFY( mtime <= time_type::clock::now() );
+ VERIFY( mtime == last_write_time(p) );
+
+ auto end_of_time = time_type::duration::max();
+ auto last_second
+ = std::chrono::duration_cast<std::chrono::seconds>(end_of_time).count();
+ if (last_second > std::numeric_limits<std::time_t>::max())
+ return; // can't test overflow
+
+#if _GLIBCXX_USE_UTIMENSAT
+ struct ::timespec ts[2];
+ ts[0].tv_sec = 0;
+ ts[0].tv_nsec = UTIME_NOW;
+ ts[1].tv_sec = std::numeric_limits<std::time_t>::max() - 1;
+ ts[1].tv_nsec = 0;
+ VERIFY( !::utimensat(AT_FDCWD, p.c_str(), ts, 0) );
+#elif _GLIBCXX_HAVE_UTIME_H
+ ::utimbuf times;
+ times.modtime = std::numeric_limits<std::time_t>::max() - 1;
+ times.actime = std::numeric_limits<std::time_t>::max() - 1;
+ VERIFY( !::utime(p.c_str(), ×) );
+#else
+ return;
+#endif
+
+ mtime = last_write_time(p, ec);
+ VERIFY( ec );
+ VERIFY( ec == std::make_error_code(std::errc::value_too_large) );
+ VERIFY( mtime == time_type::min() );
+
+#if __cpp_exceptions
+ caught = false;
+ try {
+ mtime = last_write_time(p);
+ } catch (std::system_error const& e) {
+ caught = true;
+ ec = e.code();
+ }
+ VERIFY( caught );
+ VERIFY( ec );
+ VERIFY( ec == std::make_error_code(std::errc::value_too_large) );
+#endif
+}
+
+int
+main()
+{
+ test01();
+}
#define _TESTSUITE_FS_H 1
#include <experimental/filesystem>
-#include <iostream>
+#include <fstream>
#include <string>
#include <cstdio>
#include <stdlib.h>
compare_paths(const std::experimental::filesystem::path& p1,
const std::experimental::filesystem::path& p2)
{
- // std::cout << "Comparing " << p1 << " and " << p2 << std::endl;
PATH_CHK( p1, p2, string );
PATH_CHK( p1, p2, empty );
PATH_CHK( p1, p2, has_root_path );
return p;
}
+ // RAII helper to remove a file on scope exit.
+ struct scoped_file
+ {
+ using path_type = std::experimental::filesystem::path;
+
+ enum adopt_file_t { adopt_file };
+
+ explicit
+ scoped_file(const path_type& p = nonexistent_path()) : path(p)
+ { std::ofstream{p.native()}; }
+
+ scoped_file(path_type p, adopt_file_t) : path(p) { }
+
+ ~scoped_file() { remove(path); }
+
+ path_type path;
+ };
+
} // namespace __gnu_test
#endif