Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-69001

QDirIterator, QDir::entry[Info]List() do not skip Windows symlinks; fileInfo.isSymLink() == false



    • Bug
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • 5.7.1, 5.10.1
    • Core: I/O
    • None
    • Win7SP1, tested with MSVC2015 and MinGW 5.3.0 (the version installed by Qt).
    • Windows


      When using QDir::NoSymLinks and/or excluding QDirIterator::FollowSymLinks, links on Windows (made with mklink) are not skipped.  Also QDirIterator::fileInfo.isSymLink() returns false on these entries.

      This is rather awkward, because one might expect the relevant QDir and QDirIterator flags to work on them, but they don't.

      As a result, one can't use things like QDir::entryList() or QDirIterator directly if one wishes to exclude Windows symlinks. The links have to be excluded "manually" using a custom WinAPI-based function. Trying to filter out anything behind a linked folder with QDirIterator is especially awkward since it will still iterate into linked folders (can't see how to prevent that), and the actual files inside those folders are not links.... so one has to somehow ignore all those files (keep a list of ignored folders, or whatever).

      They're true links after all, with a target, just like on *nix, so why are they treated differently? I understand it is an "advanced" feature to create such links in Windows, but this is not uncommon at all (there are even GUI apps to do this).

      The following abbreviated example returns true for file links (mklink Link Target) and directory links/junctions (mklink (/D|/J) Link Target). Only file "hard links" are not detected (mklink /H). (I'm not sure the latter can be detected since they don't even show as links in native DIR listing (the other types all do). Note that one cannot create a "hard link" to a folder (mklink /D /H).)

      #include <qplatformdefs.h>
      #include <qt_windows.h>
      bool SyncProcess::isWindowsSymlink(const QString &filename) {
         FindFirstFileExW((const wchar_t *)filename.utf16(), FindExInfoBasic, &findData, FindExSearchNameMatch, NULL, 0);
         return (
      findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
      (findData.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT  // Junction
            || findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK);        // file/folder symlink

      Test script:

      QDirIterator it("c:/test", QDir::Dirs | QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories);
      (it.hasNext()) {
      QFileInfo fi = it.fileInfo();
        if (fi.isSymLink()) {   // always false on Windows, except for .lnk (shortcut) files.
          qDebug() << "Is symlink" << fi.absoluteFilePath() << fi.canonicalFilePath() << fi.symLinkTarget(); }
       else if
      (isWindowsSymlink(fi.absoluteFilePath())) {  // true for symlinks and junctions
         qDebug() << "Windows symlink" << fi.absoluteFilePath() << fi.canonicalFilePath() << fi.symLinkTarget(); }

      QFileInfo::canonicalPath() returns the correct target location for the 3 types of links mentioned (file and folder symlinks, and Junctions).  QFileInfo::symLinkTarget() is blank though.

      I do see in qfilesystemmetadata_p.h that it already checks for IO_REPARSE_TAG_SYMLINK, but I have no idea why even that part wouldn't be working (or if this code is even relevant to the subject).

      For reference:

      MKLINK [[/D] | [/H] | [/J]] Link Target
      /D Creates a directory symbolic link. Default is a file symbolic link.
      /H Creates a hard link instead of a symbolic link.
      /J Creates a Directory Junction.
      Link specifies the new symbolic link name.
      Target specifies the path (relative or absolute) that the new link refers to.

      Possibly related: QTBUG-45344


        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.



              thiago Thiago Macieira
              mpaperno Maxim Paperno
              0 Vote for this issue
              2 Start watching this issue



                Gerrit Reviews

                  There are no open Gerrit changes