Reputation: 588
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
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 SeBackupPrivilege
working 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