Skip to content

Race condition in std::fs::create_dir_all. #33707

Closed
@dpc

Description

@dpc

I just got bitten by this, took me a long time to find it and I see the issue was raised before in #30152

It's very non-obvious that std::fs::create_dir_all - which whole purpose of is to make sure the dir exists, can fail due to the fact that the dir was created concurrently.

The PR to fix it #30152 was rejected due to "Concurrent operations can't always be detected and when they do happen on the filesystem it often indicates that something else is going awry and needs to be kicked up further."

But please note that std::fs::create_dir_all is a convenience function, and not one directly mapping to a posix fs operation. If stdlib provides a convenience function, I think it should provide the sanest, and most robust one - not one with a big gotcha inside. The fact that there's a race condition between multiple calls to std::fs::create_dir_all is entirely a implementation choice and just a wrong behavior.

IMO any sane implementation should consider not takign care of this race condition a bug:

I've checked some other standard libraries and all are doing the right thing:

    BOOST_FS_FUNC(bool) create_directories(const Path& ph)
    {
         if (ph.empty() || exists(ph))
         {
           if ( !ph.empty() && !is_directory(ph) )
               boost::throw_exception( basic_filesystem_error<Path>(
                 "boost::filesystem::create_directories", ph,
                 make_error_code( boost::system::posix::file_exists ) ) );
           return false;
         }

         // First create branch, by calling ourself recursively
         create_directories(ph.parent_path());
         // Now that parent's path exists, create the directory
         create_directory(ph);
         return true;
     }

Note that return value of create_directories(ph.parent_path()); is ignored, not returned upwards, like in Rust implementation. And exception is thrown only if ph exists but is not a directory.

Think how many other Rust projects might have undetected race condition as they assumed the create_dir_all will do the right thing. Short googling already pointed other project that got bitten: https://github.com/droundy/bigbro/blob/464bfae81de4ad695a158315e3c75d6d21e382a2/src/lib.rs#L43

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions