https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127555
Bug ID: 127555
Summary: filesystem::remove_all fails on targets with neither
unlinkat nor dirent::d_type
Product: gcc
Version: 16.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: gokbeykeskin at gmail dot com
Target Milestone: ---
Created attachment 65660
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65660&action=edit
Suggested patch
When libstdc++ is configured with both _GLIBCXX_HAVE_UNLINKAT and
_GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE undefined, remove_all() fails on the
first non-empty sub-directory, returning (uintmax_t)-1.
Reproduced on x86_64-pc-linux-gnu (trunk, 7ca5288cc68) by forcing the two
configure cache variables off:
../gcc/configure --disable-bootstrap --enable-languages=c,c++ \
glibcxx_cv_unlinkat=no glibcxx_cv_dirent_d_type=no
make && make check-target-libstdc++-v3 \
RUNTESTFLAGS="conformance.exp=27_io/filesystem/operations/create_directories.cc
\
conformance.exp=27_io/filesystem/operations/remove_all.cc"
Both FAIL; create_directories.cc:82 "VERIFY( count == 6 )" gets count ==
(uintmax_t)-1 and ec == ENOTEMPTY. I originally hit this on a target whose
rmdir() reports EEXIST rather than ENOTEMPTY for a non-empty directory
(POSIX permits either), where ec == EEXIST instead.
Analysis:
Without d_type every entry has file_type::none (fs_dir.cc:71-78), so
__erase() skips the directory branch and calls _Dir::unlink() at
fs_dir.cc:510. Without unlinkat, _Dir::do_unlink() ignores its
is_directory argument and always calls fs::remove() (fs_dir.cc:146-166),
i.e. ::remove(), which internally retries as rmdir() for a directory. So
unlinking a non-empty directory reports the rmdir errno for "not empty"
(ENOTEMPTY or EEXIST), not the unlink-on-a-directory errno. The retry
logic at fs_dir.cc:512-531 only accepts EPERM and EISDIR, so the loop
exits with ec set and remove_all() returns -1 (fs_ops.cc:1565-1570).
An empty directory of unknown type is still removed correctly (::remove
falls back to rmdir and succeeds), so the failure needs a tree at least
two levels deep.
I attached the suggested patch.