3

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.

Nathan Friedly
  • 7,837
  • 3
  • 42
  • 59
  • I think this might be what I need: https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileattributesa - I'll post an answer if I can get it working (from https://stackoverflow.com/a/1343643/933879) – Nathan Friedly Feb 08 '19 at 15:03
  • 1
    Why do you not use the standard filesystem lib of C++17? There you might want to try this function: https://en.cppreference.com/w/cpp/filesystem/is_symlink. – taminob Feb 08 '19 at 20:51
  • Because symlinks are not quite the same thing as junctions - I'm admittedly a c++ noob, but I couldn't find anything in the std lib about junctions. – Nathan Friedly Feb 08 '19 at 23:56
  • https://blogs.msdn.microsoft.com/aaron_margosis/2012/12/09/using-ntfs-junctions-to-fix-application-compatibility-issues-on-64-bit-editions-of-windows/ has a bit more info about the difference between symlinks and junctions on Windows. – Nathan Friedly Feb 08 '19 at 23:58
  • 1
    I think there is no way to do this without scanning and analyzing the entire drive. But you might want to take a look at https://en.cppreference.com/w/cpp/filesystem/hard_link_count. But even with this function you can't differ between the original and the hardlink, you can only check if there is anywhere a hardlink to your path (at least in Linux hard_link_count is 2+the number of subdirectories+the number of hardlinks). I don't know if this might help you. – taminob Feb 09 '19 at 10:30

1 Answers1

0

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
}
Nathan Friedly
  • 7,837
  • 3
  • 42
  • 59