3

Is there a portable (std::filesystem) method of testing if a file is "locked" or has "read-only" permissions? MacOS Finder, for example, has a "Locked" setting, which is DIFFERENT than the standard POSIX "permissions".

I need to test if a file can be deleted BEFORE I try to do the delete operation. Ideally, I'd like to avoid OPENING the file for R/W as a test.

This is during a SAVE/RENAME process, and (at least in past MacOS filesystems), OS calls to "exchange" two files worked even if the file was "Locked" in the Finder. Because the of the complexity surrounding how files are saved, and how PREVIOUS versions are "preserved", it's just better to know ahead of time so the operation can be avoided.

FURTHER NOTE: Opening a stream as R/W (std::ios::out | std::ios::in) on a LOCKED file with Read & Write permissions will fail with errno = 1 (operation not permitted). If the file is Read only but not locked, it'll fail with errno 13 (permission denied).

A MacOS (Cocoa) specific approach to testing the lock bit is discussed here:

How to check whether a file is locked in Cocoa?

SMGreenfield
  • 1,680
  • 19
  • 35
  • 3
    Out of curiosity, why do you want to avoid that solution? It seems like the canonical thing to do. Obtain a handle, and if it worked, unlink the path, then close the handle. That's how you avoid race conditions. However I don't think writeability == deleteability so that's not foolproof either... – Lightness Races in Orbit May 28 '19 at 17:51
  • I dont know what system youre on but, couldnt the file potentially become "un-deletable" between your "is deletable" check and the deletion operation? Follow up question: why cant you just delete the file and check if it failed or not? – Borgleader May 28 '19 at 17:53
  • 3
    Even if there was such a test, it would merely lead to a race condition as correctly stated by the above comments. Delete the file, and it if fails, log a warning, that's the proper procedure. Delete operation is cheap anyway. – Michael Chourdakis May 28 '19 at 17:54
  • 1
    I think you'll need to explain in your question why just calling [std::remove](https://en.cppreference.com/w/cpp/io/c/remove) and checking for failure is not sufficient, especially given what others have already commented on regarding race conditions of testing to see if a file is locked. – Wyck May 28 '19 at 17:59
  • 2
    How can something be "portable" if it relates to a concept that only exists on certain OSes and isn't part of "standard POSIX" ? What does "portable" even mean here? – Brennan Vincent May 28 '19 at 18:08
  • @Wyck -- fair enough. I've edited the question. True about the race condition, but that's a bad error I need to handle, and different than trying to be strategic. It's all about what can go wrong during a complex save process. – SMGreenfield May 28 '19 at 18:09
  • @BrennanVincent -- From reading the Boost.Filesystem documentation, I understood those implementors elected to go with POSIX-like permissions, because "that's what everyone was familiar with". So my real interest is determining the state of deletability using std::filesystem / Boost.Filesystem -- prior to trying to delete the file. – SMGreenfield May 28 '19 at 18:12
  • 1
    @SMGreenfield I think there simply isn't a way -- `std::filesystem` exposes a "lowest common denominator" interface closely based on what is available in POSIX. Since "deletability" of a file is not a meaningful concept in POSIX, I expect you will have to use some OS-specific (or FS-specific) code. (I have not made this an answer since I am not absolutely confident that there is no way, just pretty sure). In general I think it would be weird for the standard to specify something that only exists in macOS. – Brennan Vincent May 28 '19 at 18:15
  • 1
    I think you get it backwards. Why you do not want to open the file if it is not deletable? On Linux, for example, deletion of a file is controlled by permissions on it's parent directory, not on the file itself. – SergeyA May 28 '19 at 18:19
  • 2
    "How does one test if a file is LOCKED and/or read-only without opening?" - That's completely OS (and file system) specific. As far as I know there is no cross-platform/portable way to do that. You'll just have to write the code to do that check for each platform you support in whatever way that platform requires you to do it. – Jesper Juhl May 28 '19 at 18:25
  • @SergeyA -- It's hard to explain all the complexity surrounding this particular SAVE FILE code, but as it's been rock-solid for a decade I'm nervous about changing its logic. Apparently MacOS will allow me to open a stream to a file with R+W permissions located in a directory with just Read permissions. I guess I could write a platform-specific call to NSFileManager that would reveal the file's lock-bit status. – SMGreenfield May 28 '19 at 18:25

1 Answers1

1

OK -- this may not be the precise answer, but it APPEARS that you can do THIS (as suggested here:

#include <sys/stat.h>

bool IsLocked(std::string& thePath)
{
    bool isLocked = false;

    struct stat buf;
    int retval = stat(thePath.c_str(), &buf);     // NOTE: retval could be an error, like ENOENT (file doesn't exist)
    if (0 != (buf.st_flags & UF_IMMUTABLE)) {
        isLocked = true;
         //is immutable
    }

    return isLocked;
}

In my own code I deal with retval errors in a more robust manner. You should, too!

But NOTE: while this DOES correctly indicate the status of the Finder's Lock bit/flag for the file, it DOES NOT reflect if the file is "read only". The documentation on UF_IMMUTABLE says "file may not be changed" -- which is obviously NOT accurate.

SMGreenfield
  • 1,680
  • 19
  • 35