Zsar
Zsar

Reputation: 425

What is the most concise way to ascertain whether the program itself has a certain access right to a file?

Question:

Note that for the purpose of this question I will assume that a directory is a special kind of file.

Furthermore, I will assume (and please do correct me if I am wrong), that writing rights to a directory encompass the right to create new files within said directory.

std::filesystem::perms lists the following kinds of permission, each for the three operation types read, write and execute:

Now, as can easily be seen, if one wishes to know whether the running program has a given right, this information is non-trivial to derive from these:

Interestingly, there does not seem to exist any function in the library, which would correspond to the following function declaration:

enum FileAction { READ = 1, WRITE = 2, RW = READ|WRITE };

bool canDo(const std::filesystem::path & path, FileAction action);

How could such a function be implemented then?

Of course, the good old pre-C++17 way would be to just try and start reading/writing and catch the exception if it fails. I would like to think that the Filesystem library has to offer a more elegant solution.

I am interested in answers applicable to Windows (XP+) and Linux. Please remark, if your answer only applies to one (even if one should hope that a universal answer does exist and can be found).


Use case: (Belatedly added to ward against "you should not ask this" answers.)

Ascertain at start of a game whether its own directory is in a valid state: No need to run, after all, if it would fail anyway.

We can assume that no other program will mess with the installation directory of an application. If we cannot, then the user and their system have bigger problems than a malfunctioning game and we do not care.

This is typically a local directory on a physical disk in a personal computer, which will not fail during operation (again, if it does we stop caring). Neither the user nor any other program will try to move files out of our installation directory or delete them or steal our permissions because that is stupid (or deliberate) and so, should that happen, we stop caring.

As long as we still care, we wish to provide a best effort aka greedy algorithm aka educated guess as to whether we can expect to run successfully.

The user might be a non-professional in dealing with their operating system and require hints to provide a working environment. We wish to provide all the hints we can and only then stop caring, because we are an application, not an OS and certainly not the system administrator.

We want to help the user if helping is possible with little effort, because we are nice. We are no samaritan and no superhero, just nice. We also have zero interest to try and intercept shenanigans the user might deliberately be up to, because then any resulting troubles are their own fault and therefore we stop caring.

It is well established practice for games to heuristically check their installation directory for integrity. Steam offers this as a service for all games installed through it. Star Trek Online, League of Legends, Warframe have launchers that do the same.

These games typically check file integrity by calculating (via reading) hashes of their files and comparing them to known valid hashes.

A game wherein almost all files are intended for modification by the user, such as Dwarf Fortress cannot do that. It could, however check for reading rights to files, which must be read, and for writing rights to files, which must be written. Again: there is no purpose in manipulating such rights while the game is running and therefore it should not be assumed to occur.

Upvotes: 1

Views: 77

Answers (2)

Ben Voigt
Ben Voigt

Reputation: 283624

"Just try the I/O and handle failures" is still the best approach, for multiple reasons (in roughly descending order of the rate you'll run into them in the real world):

  1. Predicting failure requires reproducing the OS's entire access control logic, which may very well be version-dependent. Don't reinvent the wheel.

  2. Predicting failure even if done perfectly only tells you about now, not what will happen when the I/O operation actually is attempted. The filesystem is a shared resource and permissions can change. This race condition is so common it has its own name: TOCTOU (Time of Check - Time of Use)

  3. The I/O can still fail for a multitude of other reasons not related to permission (drive was disconnected by user, network went down, another process used up all the free space).

Ultimately you can't avoid the failure handling, and doing both permission evaluation and failure handling is adding complexity without any benefit.

I don't know why you brought exceptions into the mix -- simply open the file in the usual way (either fstream or fopen) and check the result. fstream would give an object with failbit set, fopen would give NULL, and in both cases errno may help diagnose the cause.

Upvotes: 3

Andrew Henle
Andrew Henle

Reputation: 1

Of course, the good old pre-C++17 way would be to just try and start reading/writing and catch the exception if it fails. I would like to think that the Filesystem library has to offer a more elegant solution.

There is no "more elegant solution".

Any such purported "solution" would be susceptible to a TOCTOU race condition.

You can't reliably check for permission to do X and then try to do X in a separate operation.

Upvotes: 0

Related Questions