Well, I know Carbon is deprecated but some of it still works and provides functionality not present in Cocoa or Core Foundation.
One of the parts of the Carbon that survived 64bit transition handles file range locking. The functions PBXLockRangeSync() and PBXLockRange() have this remark in documentation:
Lock a range of bytes of the file fork specified by forkRefNum. This is only supported on some volume formats.
I've been testing it and in fact, they don't work on local file systems but do work on shares. So far I have tested this call only on two afp shares and it worked well. Changing the same file from two different computers did not corrupt the file. On local volume functions return an error like not implemented.
I wonder if maybe someone with this 'ancient' knowledge of macOS programming has any idea if I can test a file system weather it supports this feature?
So far I am very confused as I have found Tech Note by Apple and the code to test if a volume supports file locks. Well, it returns exact opposite of where PBXLockRange() does work. Gives me TRUE on my local disk but FALSE for a shared volume where PBXLockRange() does work.
The code for testing if a volume supports file locks, from that link but modified for a function that exists in 64bit Carbon. (I tested the original version on an older Mac OS in 32bit code and they behave the same.)
#ifndef gestaltFSSupportsExclusiveLocks
#define gestaltFSSupportsExclusiveLocks 15
#define bSupportsExclusiveLocks 18
#endif
Boolean VolumeSupportsExclusiveFileAccess (short vRefNum)
{
OSErr err;
SInt32 response;
Boolean exclusiveAccess = FALSE;
err = Gestalt (gestaltFSAttr, &response);
if (!err && (response & (1L << gestaltFSSupportsExclusiveLocks))) {
GetVolParmsInfoBuffer vparams = { 0 };
OSStatus status = FSGetVolumeParms (vRefNum, // use default volume
&vparams, // write
sizeof(GetVolParmsInfoBuffer));
if (!status)
exclusiveAccess = (vparams.vMExtendedAttributes & (1L << bSupportsExclusiveLocks)) != 0;
}
return (exclusiveAccess);
}
Plus, I wanted to check if a volume is a share. No luck with FSGetVolumeParms() as vMAttrib or vMExtendedAttributes of GetVolParmsInfoBuffer don't have anything that looks like a 'shared volume' flag.
edit - I found out about NSURL method -getResourceValue:forKey:error: with NSURLVolumeIsLocalKey so this solves the second question.
edit 2:
Next day I found about NSURLVolumeSupportsAdvisoryFileLockingKey and using that with -getResourceValue:forKey:error: is consistent with all other methods - value is positive when I can't have locks and negative when I can.
In the end, this is how I'll check if a volume is a shared one in both 32bit and 64bit Carbon:
Boolean VolumeIsSharedVolume (short vRefNum, Boolean *supportsFileLocks) // ExclusiveFileAccess
{
OSErr err;
SInt32 response;
Boolean exclusiveLocks = FALSE;
Boolean volumeIsShared = FALSE;
GetVolParmsInfoBuffer volParmsBuffer = { 0 };
err = Gestalt (gestaltSystemVersion, &response);
if ((err == noErr) && (response < 0x01000)) {
err = Gestalt (gestaltMacOSCompatibilityBoxAttr, &response);
if ((err != noErr) || ((response & (1 << gestaltMacOSCompatibilityBoxPresent)) == 0))
return (TRUE); // Running on Mac OS 9, not in Classic
}
err = Gestalt (gestaltFSAttr, &response);
if (!err && (response & (1L << gestaltFSSupportsExclusiveLocks))) {
#if __LP64__
err = FSGetVolumeParms (vRefNum, &volParmsBuffer, sizeof(GetVolParmsInfoBuffer));
#else
HParamBlockRec hPB;
hPB.ioParam.ioVRefNum = vRefNum;
hPB.ioParam.ioNamePtr = NULL;
hPB.ioParam.ioBuffer = (Ptr) &volParmsBuffer;
hPB.ioParam.ioReqCount = sizeof (volParmsBuffer);
err = PBHGetVolParmsSync (&hPB);
#endif // __LP64__
if (!err) {
exclusiveLocks = (volParmsBuffer.vMExtendedAttributes & (1L << bSupportsExclusiveLocks)) != 0;
if (volParmsBuffer.vMServerAdr)
volumeIsShared = TRUE;
}
}
if (supportsFileLocks)
*supportsFileLocks = exclusiveLocks;
return (volumeIsShared);
}
All of this is the same on both AFP and SMB shares. Locking works fine even when I mix them from two different client computers. Tested on one Mac OS server and later on Synology NAS. It would be interesting if I try one Windows client in the mix.