Search code examples
c++boostntfspoco-librariesjunction

How can I iterate over a directory and identify or omit NTFS junctions (symlink-ish)


I have some code to list the files in a directory. For Windows Systems, I'm hoping to end up with a list of files and folders that matches what you'd see in Windows Explorer. For example, when I list C:\ on Server 2016, I want to have the Users folder but not the Documents and Settings junction. Currently I'm getting both, with no apparent way of differentiating between them.

My current code looks like this:

boost::filesystem::directory_iterator itr(dir);
boost::filesystem::directory_iterator end;
Poco::SharedPtr<Poco::JSON::Array> fileList(new Poco::JSON::Array);
for (; itr != end; ++itr) {
    boost::filesystem::path entryPath = itr->path();
    Poco::File file(entryPath.string());
    // ...

I tried the Poco isLink() method, but it returns false for junctions.

I also tried Poco::DirectoryIterator, which gives the same behavior as Boost, and Poco::SortedDirectoryIterator, which always throws File access error: sharing violation: \pagefile.sys when reading C:\.

Ideally, this code should include symlinks on Linux and MacOS systems, while ignoring junctions on Windows.


Solution

  • Here's what I eventually came up with. It isn't a perfect solution - it's more of a heuristic than a proper identifier - but it seems to work well enough for my use case:

    #ifdef _WIN32
        #include <windows.h>
    #endif
    
    bool FileController::isNtfsJunction(const std::string& dirPath) const {
        #ifdef _WIN32
            DWORD attrs = GetFileAttributesA(dirPath.c_str());
            if (INVALID_FILE_ATTRIBUTES == attrs) {
                DWORD err = GetLastError();
                logger.error("Could not determine if path is NTFS Junction: %s. Error: %s", dirPath, err);
                return false;
            }
            return attrs & FILE_ATTRIBUTE_DIRECTORY &&
                attrs & FILE_ATTRIBUTE_REPARSE_POINT &&
                attrs & FILE_ATTRIBUTE_HIDDEN &&
                attrs & FILE_ATTRIBUTE_SYSTEM;
        #else
            return false;
        #endif
    }