microsoft / microsoft/STL

<filesystem>: weakly_canonical("nonexistent.txt") and weakly_canonical("./nonexistent.txt") behavior

Open
#294 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug filesystem
Dominant language
C++
Stars
11.1k
Forks
1.7k
Avg merge
4d 15h
Merged PRs (30d)
22

Description

C:\Temp>type repro.cpp
#include <assert.h>
#include <filesystem>
#include <fstream>
#include <iostream>
using std::cout, std::endl, std::ofstream;
using std::filesystem::absolute, std::filesystem::remove, std::filesystem::weakly_canonical;

int main() {
    {
        ofstream f("temporary.txt");
        f << "meow" << endl;
    }

    cout << R"(        absolute("temporary.txt"  ): )" <<         absolute("temporary.txt"  ) << endl;
    cout << R"(weakly_canonical("temporary.txt"  ): )" << weakly_canonical("temporary.txt"  ) << endl;
    cout << R"(weakly_canonical("./temporary.txt"): )" << weakly_canonical("./temporary.txt") << endl;

    assert(weakly_canonical("temporary.txt"  ) == absolute("temporary.txt"));
    assert(weakly_canonical("./temporary.txt") == absolute("temporary.txt"));

    remove("temporary.txt");
    cout << endl;

    cout << R"(        absolute("nonexistent.txt"  ): )" <<         absolute("nonexistent.txt"  ) << endl;
    cout << R"(weakly_canonical("nonexistent.txt"  ): )" << weakly_canonical("nonexistent.txt"  ) << endl;
    cout << R"(weakly_canonical("./nonexistent.txt"): )" << weakly_canonical("./nonexistent.txt") << endl;

    assert(weakly_canonical("nonexistent.txt"  ) == "nonexistent.txt");
    assert(weakly_canonical("./nonexistent.txt") == absolute("nonexistent.txt"));
}

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 repro.cpp
repro.cpp

C:\Temp>repro
        absolute("temporary.txt"  ): "C:\\Temp\\temporary.txt"
weakly_canonical("temporary.txt"  ): "C:\\Temp\\temporary.txt"
weakly_canonical("./temporary.txt"): "C:\\Temp\\temporary.txt"

        absolute("nonexistent.txt"  ): "C:\\Temp\\nonexistent.txt"
weakly_canonical("nonexistent.txt"  ): "nonexistent.txt"
weakly_canonical("./nonexistent.txt"): "nonexistent.txt"
Assertion failed: weakly_canonical("./nonexistent.txt") == absolute("nonexistent.txt"), file repro.cpp, line 29

The Standardese is WG21-N4835 [fs.op.weakly.canonical] https://eel.is/c++draft/fs.op.weakly.canonical :

path weakly_canonical(const path& p);
path weakly_canonical(const path& p, error_code& ec);
1 Returns: p with symlinks resolved and the result normalized (29.11.7.1).
2 Effects: Using status(p) or status(p, ec), respectively, to determine existence, return a path composed by operator/= from the result of calling canonical() with a path argument composed of the leading elements of p that exist, if any, followed by the elements of p that do not exist, if any. For the first form, canonical() is called without an error_code argument. For the second form, canonical() is called with ec as an error_code argument, and path() is returned at the first error occurrence, if any.
3 Ensures: The returned path is in normal form (29.11.7.1).
4 Remarks: Implementations should avoid unnecessary normalization such as when canonical has already been called on the entirety of p.
5 Throws: As specified in 29.11.6.

There's implementation divergence here; see https://godbolt.org/z/BRkB4V . The behavior that I observe is:

[MSVC]
weakly_canonical("nonexistent.txt"  ): "nonexistent.txt"
weakly_canonical("./nonexistent.txt"): "nonexistent.txt"
Assertion failed: weakly_canonical("./nonexistent.txt") == absolute("nonexistent.txt"), file repro.cpp, line 29

[GCC 9.2, libstdc++]
weakly_canonical("nonexistent.txt"  ): "nonexistent.txt"
weakly_canonical("./nonexistent.txt"): "/home/ubuntu/nonexistent.txt"
[assertions pass]

[Clang 9.0.0, libc++]
weakly_canonical("nonexistent.txt"  ): "/home/ubuntu/nonexistent.txt"
weakly_canonical("./nonexistent.txt"): "/home/ubuntu/nonexistent.txt"
output.s: ./example.cpp:28: int main(): Assertion `weakly_canonical("nonexistent.txt" ) == "nonexistent.txt"' failed.

Also tracked by DevCom-818027 and Microsoft-internal VSO-1021558 / AB#1021558.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling and running the supplied C++17 repro.cpp with MSVC, then compare the weakly_canonical results against [fs.op.weakly.canonical] in WG21-N4835 and the GCC/libstdc++ behavior described. Trace the filesystem implementation entry point used by weakly_canonical; done means the nonexistent-path cases follow the specified normalization behavior and the repro assertions pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.