From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from rock.gnat.com (rock.gnat.com [IPv6:2620:20:4000:0:a9e:1ff:fe9b:1d1]) by sourceware.org (Postfix) with ESMTPS id 19A133835769; Wed, 22 Jun 2022 06:20:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 19A133835769 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id DC94D1160FC; Wed, 22 Jun 2022 02:19:59 -0400 (EDT) X-Virus-Scanned: Debian amavisd-new at gnat.com Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id U2zgfWAirUWp; Wed, 22 Jun 2022 02:19:59 -0400 (EDT) Received: from free.home (tron.gnat.com [IPv6:2620:20:4000:0:46a8:42ff:fe0e:e294]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by rock.gnat.com (Postfix) with ESMTPS id A5E761160F1; Wed, 22 Jun 2022 02:19:59 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 25M6JplH723792 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 22 Jun 2022 03:19:51 -0300 From: Alexandre Oliva To: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: [PATCH] libstdc++: retry removal of dir entries if dir removal fails Organization: Free thinker, does not speak for AdaCore Errors-To: aoliva@lxoliva.fsfla.org Date: Wed, 22 Jun 2022 03:19:51 -0300 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Scanned-By: MIMEDefang 2.84 X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Jun 2022 06:20:01 -0000 On some target systems (e.g. rtems6.0), removing directory components while iterating over directory entries may cause some of the directory entries to be skipped, which prevents the removal of the parent directory from succeeding. Advancing the iterator before removing a member proved not to be enough, so I've instead arranged for remove_all to retry the removal of components if the removal of the parent dir fails after removing at least one entry. The fail will be permanent only if no components got removed in the current try. Regstrapped on x86_64-linux-gnu, also tested with a cross to aarch64-rtems6. Ok to install? PS: The implementation of remove_all has changed completely, compared with the gcc-11 environment in which the need for this patch came up. I have reimplemented it for mainline, and I have attempted to test it in this environment, but new filesystem tests and subtests that fail on rtems6.0 have impaired testing and prevented the full pass rate I got for them with a similar patchset on gcc-11. for libstdc++-v3/ChangeLog * src/c++17/fs_ops.cc (remove_all): Retry removal of directory entries. --- libstdc++-v3/src/c++17/fs_ops.cc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc index 435368fa5c5ff..b3390310132b4 100644 --- a/libstdc++-v3/src/c++17/fs_ops.cc +++ b/libstdc++-v3/src/c++17/fs_ops.cc @@ -1286,6 +1286,8 @@ fs::remove_all(const path& p) { error_code ec; uintmax_t count = 0; + retry: + uintmax_t init_count = count; recursive_directory_iterator dir(p, directory_options{64|128}, ec); switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() { @@ -1303,7 +1305,7 @@ fs::remove_all(const path& p) break; case ENOENT: // Our work here is done. - return 0; + return count; case ENOTDIR: case ELOOP: // Not a directory, will remove below. @@ -1313,6 +1315,18 @@ fs::remove_all(const path& p) _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec)); } + if (count > init_count) + { + if (int last = fs::remove(p, ec); !ec) + return count + last; + else + // Some systems seem to skip entries in the dir iteration if + // you remove dir entries while iterating, so if we removed + // anything in the dir in this round, and failed to remove + // the dir (presumably because it wasn't empty), retry. + goto retry; + } + // Remove p itself, which is either a non-directory or is now empty. return count + fs::remove(p); } @@ -1321,6 +1335,8 @@ std::uintmax_t fs::remove_all(const path& p, error_code& ec) { uintmax_t count = 0; + retry: + uintmax_t init_count = count; recursive_directory_iterator dir(p, directory_options{64|128}, ec); switch (ec.value()) // N.B. assumes ec.category() == std::generic_category() { @@ -1341,7 +1357,7 @@ fs::remove_all(const path& p, error_code& ec) case ENOENT: // Our work here is done. ec.clear(); - return 0; + return count; case ENOTDIR: case ELOOP: // Not a directory, will remove below. @@ -1354,6 +1370,8 @@ fs::remove_all(const path& p, error_code& ec) // Remove p itself, which is either a non-directory or is now empty. if (int last = fs::remove(p, ec); !ec) return count + last; + if (count > init_count) + goto retry; return -1; } -- Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ Free Software Activist GNU Toolchain Engineer Disinformation flourishes because many people care deeply about injustice but very few check the facts. Ask me about