Check for overflow in filesystem::last_write_time

* include/experimental/bits/fs_fwd.h (file_time_type): Simplify
	definition.
	* src/filesystem/ops.cc (file_time): Take error_code parameter and
	check for overflow.
	(do_copy_file, last_write_time): Pass error_code in file_time calls.
	* testsuite/experimental/filesystem/operations/last_write_time.cc:
	New.
	* testsuite/util/testsuite_fs.h (scoped_file): Define RAII helper.

From-SVN: r240587
This commit is contained in:
Jonathan Wakely 2016-09-28 19:02:25 +01:00 committed by Jonathan Wakely
parent 93c9b105be
commit fd5effb17e
5 changed files with 159 additions and 14 deletions

View File

@ -1,5 +1,14 @@
2016-09-28 Jonathan Wakely <jwakely@redhat.com>
* include/experimental/bits/fs_fwd.h (file_time_type): Simplify
definition.
* src/filesystem/ops.cc (file_time): Take error_code parameter and
check for overflow.
(do_copy_file, last_write_time): Pass error_code in file_time calls.
* testsuite/experimental/filesystem/operations/last_write_time.cc:
New.
* testsuite/util/testsuite_fs.h (scoped_file): Define RAII helper.
PR libstdc++/77686
* include/std/functional (_Any_data): Add may_alias attribute.

View File

@ -253,7 +253,7 @@ _GLIBCXX_END_NAMESPACE_CXX11
operator^=(directory_options& __x, directory_options __y) noexcept
{ return __x = __x ^ __y; }
typedef chrono::time_point<chrono::system_clock> file_time_type;
using file_time_type = std::chrono::system_clock::time_point;
// operational functions

View File

@ -288,16 +288,24 @@ namespace
}
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,
@ -373,11 +381,11 @@ namespace
}
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))
{
@ -1036,7 +1044,7 @@ fs::last_write_time(const path& p)
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());
}

View File

@ -0,0 +1,111 @@
// 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(), &times) );
#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();
}

View File

@ -23,7 +23,7 @@
#define _TESTSUITE_FS_H 1
#include <experimental/filesystem>
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include <stdlib.h>
@ -40,7 +40,6 @@ namespace __gnu_test
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 );
@ -95,5 +94,23 @@ namespace __gnu_test
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