Salamandar
Salamandar

Reputation: 588

C++ filesystem iterator Invalid Argument

I'm using the fs::recursive_directory_iterator to list all the files from a drive.

I'm also passing fs::directory_options::skip_permission_denied to prevent the iterator to throw when trying to go inside non-allowed directories. So there shouldn't be any problem…

But when trying to iterate inside special directories (Volume Information, Recycle.Bin,…):

filesystem error: cannot increment recursive directory iterator: Invalid argument

I have to try…catch the iteration as a workaround, but why do I have this issue ? How should I fix this ?

I can reproduce it with the minimal example from cppreference.com:

#include <fstream>
#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;

int main()
{
    for(auto& p: fs::recursive_directory_iterator("M:", fs::directory_options::skip_permission_denied))
        std::cout << p << '\n';
}

Result:

… (Listing real files)

"M:\\System Volume Information"
terminate called after throwing an instance of 'std::experimental::filesystem::v1::__cxx11::filesystem_error'
  what():  filesystem error: cannot increment recursive directory iterator: Invalid argument

EDIT: Also, the filesystem_error methods path1(), path2() return empty paths. Is it a gcc bug ?

Upvotes: 3

Views: 5027

Answers (1)

RbMm
RbMm

Reputation: 33716

if simply search skip_permission_denied symbol in header files easy can view that this symbol exist only in <filesystem> but not in <experimental/filesystem>. you include wrong header file.

and we need use namespace fs = std::filesystem; - without experimental term.

about file permissions - if caller have SE_BACKUP_PRIVILEGE :

This privilege allows the user to circumvent file and directory permissions to back up the system. This privilege causes the system to grant all read access control to any file, regardless of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. The following access rights are granted if this privilege is held:

  • READ_CONTROL
  • ACCESS_SYSTEM_SECURITY
  • FILE_GENERIC_READ
  • FILE_TRAVERSE

User-mode applications represent this privilege as the following user-right string: "Back up files and directories".

also we need use FILE_OPEN_FOR_BACKUP_INTENT in NtCreateFile or NtOpenFile call. or FILE_FLAG_BACKUP_SEMANTICS in CreateFile.

when we use recursive_directory_iterator we not control this point (not direct open folder yourself). however in current implementation this class call FindFirstFileExW function for iterate. this api internal call NtOpenFile always with FILE_OPEN_FOR_BACKUP_INTENT option. as result SeBackupPrivilegeworking here. we need enable SE_BACKUP_PRIVILEGE in thread or process token, of course if we have this privilege in token:

#define LAA(se) {{se},SE_PRIVILEGE_ENABLED|SE_PRIVILEGE_ENABLED_BY_DEFAULT}

#define BEGIN_PRIVILEGES(tp, n) static const struct {ULONG PrivilegeCount;LUID_AND_ATTRIBUTES Privileges[n];} tp = {n,{
#define END_PRIVILEGES }};

// in case you not include wdm.h, where this defined
#define SE_BACKUP_PRIVILEGE (17L)

ULONG AdjustPrivileges()
{
    if (ImpersonateSelf(SecurityImpersonation))
    {
        HANDLE hToken;
        if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &hToken))
        {
            BEGIN_PRIVILEGES(tp, 1)
                LAA(SE_BACKUP_PRIVILEGE),
            END_PRIVILEGES
            AdjustTokenPrivileges(hToken, FALSE, (PTOKEN_PRIVILEGES)&tp, 0, 0, 0);
            CloseHandle(hToken);
        }
    }

    return GetLastError();
}

final code can look like:

#include <filesystem>

void demo()
{
    AdjustPrivileges();

    fs::recursive_directory_iterator item(L"c:\\System Volume Information", 
        fs::directory_options::skip_permission_denied), end;

    while (item != end)
    {
        DbgPrint("%S\n", item->path().c_str());
        item++;
    }
}

Upvotes: 4

Related Questions