PR libstdc++/86910 fix filesystem::create_directories
Implement the proposed semantics from P1164R0, which reverts the changes of LWG 2935. This means that failure to create a directory because a non-directory already exists with that name will be reported as an error. While rewriting the function, also fix PR 87846, which is a result of the C++17 changes to how a trailing slash on a path affects the last component of a path. PR libstdc++/86910 PR libstdc++/87846 * src/filesystem/ops.cc (experimental::create_directories): Report an error when the path resolves to an existing non-directory (P1164). * src/filesystem/std-ops.cc (create_directories): Likewise. Handle empty filenames due to trailing slashes. * testsuite/27_io/filesystem/operations/create_directories.cc: Test when some component of the path exists and is not a directory. Test trailing slashes. * testsuite/experimental/filesystem/operations/create_directories.cc: Likewise. From-SVN: r266598
This commit is contained in:
parent
2182a27d85
commit
ffe2c05539
|
@ -1,3 +1,17 @@
|
||||||
|
2018-11-29 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
|
PR libstdc++/86910
|
||||||
|
PR libstdc++/87846
|
||||||
|
* src/filesystem/ops.cc (experimental::create_directories): Report
|
||||||
|
an error when the path resolves to an existing non-directory (P1164).
|
||||||
|
* src/filesystem/std-ops.cc (create_directories): Likewise. Handle
|
||||||
|
empty filenames due to trailing slashes.
|
||||||
|
* testsuite/27_io/filesystem/operations/create_directories.cc: Test
|
||||||
|
when some component of the path exists and is not a directory. Test
|
||||||
|
trailing slashes.
|
||||||
|
* testsuite/experimental/filesystem/operations/create_directories.cc:
|
||||||
|
Likewise.
|
||||||
|
|
||||||
2018-11-28 Jonathan Wakely <jwakely@redhat.com>
|
2018-11-28 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
PR libstdc++/83306
|
PR libstdc++/83306
|
||||||
|
|
|
@ -423,6 +423,19 @@ fs::create_directories(const path& p, error_code& ec) noexcept
|
||||||
ec = std::make_error_code(errc::invalid_argument);
|
ec = std::make_error_code(errc::invalid_argument);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_status st = symlink_status(p, ec);
|
||||||
|
if (is_directory(st))
|
||||||
|
return false;
|
||||||
|
else if (ec && !status_known(st))
|
||||||
|
return false;
|
||||||
|
else if (exists(st))
|
||||||
|
{
|
||||||
|
if (!ec)
|
||||||
|
ec = std::make_error_code(std::errc::not_a_directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::stack<path> missing;
|
std::stack<path> missing;
|
||||||
path pp = p;
|
path pp = p;
|
||||||
|
|
||||||
|
@ -431,24 +444,29 @@ fs::create_directories(const path& p, error_code& ec) noexcept
|
||||||
ec.clear();
|
ec.clear();
|
||||||
const auto& filename = pp.filename();
|
const auto& filename = pp.filename();
|
||||||
if (!is_dot(filename) && !is_dotdot(filename))
|
if (!is_dot(filename) && !is_dotdot(filename))
|
||||||
missing.push(pp);
|
{
|
||||||
pp.remove_filename();
|
missing.push(std::move(pp));
|
||||||
|
pp = missing.top().parent_path();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pp = pp.parent_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ec || missing.empty())
|
if (ec || missing.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
bool created;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const path& top = missing.top();
|
const path& top = missing.top();
|
||||||
create_directory(top, ec);
|
created = create_directory(top, ec);
|
||||||
if (ec && is_directory(top))
|
if (ec)
|
||||||
ec.clear();
|
return false;
|
||||||
missing.pop();
|
missing.pop();
|
||||||
}
|
}
|
||||||
while (!missing.empty() && !ec);
|
while (!missing.empty());
|
||||||
|
|
||||||
return missing.empty();
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
@ -646,38 +646,74 @@ fs::create_directories(const path& p, error_code& ec)
|
||||||
ec = std::make_error_code(errc::invalid_argument);
|
ec = std::make_error_code(errc::invalid_argument);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_status st = symlink_status(p, ec);
|
||||||
|
if (is_directory(st))
|
||||||
|
return false;
|
||||||
|
else if (ec && !status_known(st))
|
||||||
|
return false;
|
||||||
|
else if (exists(st))
|
||||||
|
{
|
||||||
|
if (!ec)
|
||||||
|
ec = std::make_error_code(std::errc::not_a_directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::stack<path> missing;
|
std::stack<path> missing;
|
||||||
path pp = p;
|
path pp = p;
|
||||||
|
|
||||||
while (pp.has_filename() && status(pp, ec).type() == file_type::not_found)
|
// Strip any trailing slash
|
||||||
{
|
if (pp.has_relative_path() && !pp.has_filename())
|
||||||
ec.clear();
|
pp = pp.parent_path();
|
||||||
const auto& filename = pp.filename();
|
|
||||||
if (!is_dot(filename) && !is_dotdot(filename))
|
|
||||||
missing.push(pp);
|
|
||||||
pp = pp.parent_path();
|
|
||||||
|
|
||||||
if (missing.size() > 1000) // sanity check
|
|
||||||
{
|
|
||||||
ec = std::make_error_code(std::errc::filename_too_long);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ec || missing.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto& filename = pp.filename();
|
||||||
|
if (is_dot(filename) || is_dotdot(filename))
|
||||||
|
pp = pp.parent_path();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
missing.push(std::move(pp));
|
||||||
|
if (missing.size() > 1000) // sanity check
|
||||||
|
{
|
||||||
|
ec = std::make_error_code(std::errc::filename_too_long);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pp = missing.top().parent_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pp.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
st = status(pp, ec);
|
||||||
|
if (exists(st))
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return false;
|
||||||
|
if (!is_directory(st))
|
||||||
|
{
|
||||||
|
ec = std::make_error_code(std::errc::not_a_directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec && exists(st))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (st.type() == file_type::not_found);
|
||||||
|
|
||||||
|
bool created;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
const path& top = missing.top();
|
const path& top = missing.top();
|
||||||
create_directory(top, ec);
|
created = create_directory(top, ec);
|
||||||
if (ec && is_directory(top))
|
if (ec)
|
||||||
ec.clear();
|
return false;
|
||||||
missing.pop();
|
missing.pop();
|
||||||
}
|
}
|
||||||
while (!missing.empty() && !ec);
|
while (!missing.empty());
|
||||||
|
|
||||||
return missing.empty();
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
@ -76,8 +76,59 @@ test01()
|
||||||
VERIFY( count == 6 );
|
VERIFY( count == 6 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test02()
|
||||||
|
{
|
||||||
|
// PR libstdc++/86910
|
||||||
|
const auto p = __gnu_test::nonexistent_path();
|
||||||
|
std::error_code ec;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
{
|
||||||
|
__gnu_test::scoped_file file;
|
||||||
|
|
||||||
|
result = create_directories(file.path, ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
result = create_directories(file.path / "foo", ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
}
|
||||||
|
|
||||||
|
create_directories(p);
|
||||||
|
{
|
||||||
|
__gnu_test::scoped_file dir(p, __gnu_test::scoped_file::adopt_file);
|
||||||
|
__gnu_test::scoped_file file(dir.path/"file");
|
||||||
|
|
||||||
|
result = create_directories(file.path, ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
result = create_directories(file.path/"../bar", ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test03()
|
||||||
|
{
|
||||||
|
// PR libstdc++/87846
|
||||||
|
const auto p = __gnu_test::nonexistent_path() / "";
|
||||||
|
bool result = create_directories(p);
|
||||||
|
VERIFY( result );
|
||||||
|
VERIFY( exists(p) );
|
||||||
|
remove(p);
|
||||||
|
result = create_directories(p/"foo/");
|
||||||
|
VERIFY( result );
|
||||||
|
VERIFY( exists(p) );
|
||||||
|
VERIFY( exists(p/"foo") );
|
||||||
|
remove_all(p);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
test01();
|
test01();
|
||||||
|
test02();
|
||||||
|
test03();
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,60 @@ test01()
|
||||||
VERIFY( count == 6 );
|
VERIFY( count == 6 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test02()
|
||||||
|
{
|
||||||
|
// PR libstdc++/86910
|
||||||
|
const auto p = __gnu_test::nonexistent_path();
|
||||||
|
std::error_code ec;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
{
|
||||||
|
__gnu_test::scoped_file file;
|
||||||
|
|
||||||
|
result = create_directories(file.path, ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
result = create_directories(file.path / "foo", ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
__builtin_printf("%d\n", ec.value());
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
}
|
||||||
|
|
||||||
|
create_directories(p);
|
||||||
|
{
|
||||||
|
__gnu_test::scoped_file dir(p, __gnu_test::scoped_file::adopt_file);
|
||||||
|
__gnu_test::scoped_file file(dir.path/"file");
|
||||||
|
|
||||||
|
result = create_directories(file.path, ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
result = create_directories(file.path/"../bar", ec);
|
||||||
|
VERIFY( !result );
|
||||||
|
VERIFY( ec == std::errc::not_a_directory );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test03()
|
||||||
|
{
|
||||||
|
// PR libstdc++/87846
|
||||||
|
const auto p = __gnu_test::nonexistent_path() / "/";
|
||||||
|
bool result = create_directories(p);
|
||||||
|
VERIFY( result );
|
||||||
|
VERIFY( exists(p) );
|
||||||
|
remove(p);
|
||||||
|
result = create_directories(p/"foo/");
|
||||||
|
VERIFY( result );
|
||||||
|
VERIFY( exists(p) );
|
||||||
|
VERIFY( exists(p/"foo") );
|
||||||
|
remove_all(p);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
test01();
|
test01();
|
||||||
|
test02();
|
||||||
|
test03();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue