2

The code below is Windows native and meant to protect against multiple processes attempt to rename the same file simultaneously - what may cause data loss. In simple, what the code does, is exclusively open the file for current process/thread, then check if renaming is needed, and finally write to the file.

consider a case where 2 processes attempt to run the code below simultaneously. Without exclusive access, the renaming operation may occurs twice, So that on the second renaming, the original file will be overrun by an empty file and all the data will be lost.

I'd like to port this logic into macOS, so I'm looking for an equivalent locking mechanism on the file before deciding to rename it (programmatic locking). both objectiveC or C/C++/POSIX are good options.

Specifically, I'm looking for a macOS equivalent native Windows function CreateFile as called in the following implementation :

while (!handle) {
    handle = CreateFile(currFile, 
                        GENERIC_WRITE | DELETE, //dwDesiredAccess 
                        FILE_SHARE_READ,        //dwShareMode
                        NULL,                   //lpSecurityAttributes
                        OPEN_ALWAYS,            //dwCreationDisposition
                        FILE_ATTRIBUTE_NORMAL,  // dwFlagsAndAttributes
                        NULL);

    if(!handle)
       sleep(...) // retry after sleep
}

// NOTE : This stage is reached exclusively for all processes/threads 


// create c style file pointer for current file.
int chandle = _open_osfhandle((intptr_t)handle, _O_APPEND);
FILE* f = _wfdopen(chandle, L"a+");

if (std::filesystem::file_size(currFile) > MAX_SIZE)
{
    std::wstring prevName = currFile + L".prev";
    DWORD buffSz = (DWORD)
            (sizeof(FILE_RENAME_INFO) + 
            prevName.length() * sizeof(WCHAR) 
            + 16);

    FILE_RENAME_INFO *renameInfo = (FILE_RENAME_INFO *)malloc(buffSz);
    renameInfo->ReplaceIfExists = TRUE;
    renameInfo->RootDirectory = NULL;       
    renameInfo->FileNameLength = (DWORD)prevName.length()*2;
    wcscpy_s(renameInfo->FileName, prevNAme.length() + 2, prevName.c_str());

    //notice that the 'f' still point on the original file name
    BOOL res = SetFileInformationByHandle(handle, FileRenameInfo, renameInfo, buffSz);
}

...

// now print message to the original file name using 'f'
fprintf(f, "blablabla");

Irad K
  • 867
  • 6
  • 20
  • It’s not clear what the requirements are. What do you want to avoid when multiple processes attempt to rename the file? It can’t end up in multiple places no matter what you do, right? – Davis Herring Dec 25 '19 at 14:19
  • consider a case wheere 2 processes attempt to run the code above. without exclusive access the renaming operation may occure twice, thus the original file being overrun by an empty file and all the data will be lost. – Irad K Dec 25 '19 at 17:40
  • so i basically look for inter-process locking for this code section – Irad K Dec 25 '19 at 17:42
  • So the issue is that you check whether to perform the rename twice and then rename the recreated file over the (renamed) original? (If so, edit the question to say so.) – Davis Herring Dec 25 '19 at 17:46
  • I hope my latest update made the question clearer. Thanks you for your comments. – Irad K Dec 25 '19 at 21:09
  • It seems that this is a general locking question. As such, does this answer your question? [How to create a single instance application in C or C++](https://stackoverflow.com/questions/5339200/how-to-create-a-single-instance-application-in-c-or-c) – Davis Herring Dec 25 '19 at 22:47
  • `f` does not point to the "original file *name*". It refers to the kernel file object that's referenced by `handle`. There is only one kernel file object here. Renaming it with a ".prev" extension doesn't change that. However, another thread subsequently will succeed in creating a new file (due to the `OPEN_ALWAYS` disposition) with the initial `currFile` name. – Eryk Sun Dec 27 '19 at 01:58
  • @DavisHerring, Sorry for the late response, but I think my question is not general locking question since it relate to the fact that renaming file isn't blocked by `flock` since it doesn't change the file descriptor. I'll re-write my question to emphasis this. – Irad K Jan 01 '20 at 08:05
  • @IradK: You can use whatever kind of locking to prevent two processes from doing *this* rename at the same time. The lock doesn’t have to have anything to do with the file in question since you’re not trying to interfere with another *uncooperative* process trying to stat/rename the file. – Davis Herring Jan 01 '20 at 08:15

0 Answers0