A customer wanted to accept a directory entered by the user and verify that the user has permission to create files in that folder. The directory itself might not even be on a local hard drive; it could be a DVD or a remote network volume. They tried calling GetÂFileÂAttributes, but all they were told was that it was a directory.¹ How can they find out whether the user can create files in it?
The file attributes are largely legacy flags carried over from MS-DOS. The actual control over what operations are permitted comes not from the file attributes but from the security attributes.
Fortunately, you don’t have to learn how to parse security attributes. You can just specify the desired attributes when you open the file or directory. In other words, to find out if you can do the thing, ask for permission to do the thing.
The security attribute that controls whether users can create new files in a directory is FILE_. You can find a complete list in the documentation under File Access Rights Constants.
Directories are a little tricky because you have to open them with backup semantics.
bool HasAccessToDirectory(PCWSTR directoryPath, DWORD access)
{
HANDLE h = CreateFileW(directoryPath, access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (h == INVALID_FILE_HANDLE) {
return false;
} else {
CloseHandle(h);
return true;
}
}
bool CanCreateFilesInDirectory(PCWSTR directoryPath)
{
return HasAccessToDirectory(directoryPath, FILE_ADD_FILE);
}
You can choose other access flags to detect other things. For example, checking for FILE_ checks whether the user can create subdirectories, and checking for FILE_ checks whether the user can delete files and remove subdirectories from that directory. If you want to check multiple things, you can OR them together, because security checks require that you be able to do all of the things you requested before it will let you in.
bool CanCreateFilesAndSubdirectoriesInDirectory(PCWSTR directoryPath)
{
return HasAccessToDirectory(directoryPath,
FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY);
}
Note that these are moment-in-time checks. You will have to be prepared for the possibility that the user has lost access by the time you actually try to perform the operation. But this will at least give you an opportunity to tell the user up front, “You don’t have permission to create files in this folder. Pick another one.”²
As I noted, this technique applies to files as well. If you want to know if the user can write to a file, open it for writing and see if it succeeds!
¹ And we learned some time ago that the read-only attribute on directories doesn’t actually make the directory read-only.
² This could be handy if the act of creating the files happens much later in the workflow. For example, maybe you’re asking the user where to save the query results. The query itself might take a long time, so you don’t want to let the user pick a directory, and then 30 minutes later, put up a dialog box saying “Oops, I couldn’t save the files in that directory. Maybe you should have picked a better one 30 minutes ago.”
What about auditing and alarms?
In real life an access denied sign warns me I cannot go into that room, and that I will be in trouble if I am found in that room.
A piece of software validating write permission by writing a file feels akin to me going into the room and checking if I get kicked out.
If I was able to check beforehand, after which permissions change, and subsequently my access is denied, I get to claim that I actually had permissions beforehand.
I’d say it’s not “going into the room and checking if you get kicked out”.
It’s more “putting your badge on the door’s access card reader and seeing if it lets you in”.
If you are allowed in the room, it will let you in. If you aren’t allowed in the room, it won’t.
You don’t get in trouble with the bouncer just asking to go in.
We don't have the context of the request here but I'm assuming there was a reason why the customer didn't want to just go ahead and create the file. Otherwise all this seems like overkill vs just trying to create the file. I see several problems with going this route vs just creating the file already.
- You still have to handle failures when creating the file irrelevant of what you do here so why bother doing 2 different checks.
- The point in time problem has already been mentioned but there is another scenario where the file you may want...
I don’t know if it’s still the case, but creating a zero-byte placeholder file to fill in later used to cause some problems with antiviruses. For example, some AVs would lock the file for scanning, and when your process tries to fill the file it gets a sharing violation.
@Raymond Chen:
Do the thing. That’s basically what I said in this Stack Overflow answer, but I got downvoted by people using similar reasoning as your customer asking this question.
Good info. I wish more apps did these sort of preemptive checks, but then also allowed for graceful recovery.
I love a good hunt and trip down memory lane, so I clicked the link to the 2003 (!!) article. Sadly the KB article links in that are long dead, but I've found similar info that was ported over to the MicrosoftDocs github org's win32 repo at desktop-src/shell/how-to-customize-folders-with-desktop-ini.md
It's nice to keep info like this in the back pocket. Knowing how REG_EXPAND_SZ works helped me sort out some 32-bit app hosting on a cloud provider's remote-desktop-like platform where folder redirections would work,...