用于执行Carbon FSExchangeObjectsCompat调用的Cocoa方法是什么?
问题描述:
旧的MoreFilesX,FSExchangeObjectsCompat中有这样一个功能,即“在两个文件之间交换数据”。它通常用作安全保存方法的一部分,在该方法中写入临时文件,然后调用FSExchangeObjectsCompat将新保存的临时文件与旧的“原始”文件进行交换。它保留了所有的元数据,特权等用于执行Carbon FSExchangeObjectsCompat调用的Cocoa方法是什么?
我看到高Sierra上的这个函数在APFS卷上出现故障,这在HFS +卷上从未失败。没有什么大惊喜 - 其中很多电话都被弃用了。
但是做同样的事情的Cocoa NSFileManager方法是什么?
答
您可以使用较低级别的功能做类似的事情。这是我写的与10.12之前的SDK一起使用的代码。如果您针对10.12 SDK或更高版本进行编译,则可以使其更简单,如果您的部署目标为10.12或更高版本,则更简单。
#ifndef RENAME_SWAP
#define RENAME_SWAP 0x00000002
#endif
/*!
@function ExchangeFiles
@abstract Given full paths to two files on the same volume,
swap their contents.
@discussion This is often part of a safe-save strategy.
@param inOldFile Full path to a file.
@param inNewFile Full path to a file.
@result 0 if all went well, -1 otherwise.
*/
int ExchangeFiles(const char* inOldFile, const char* inNewFile)
{
int result = -1;
static dispatch_once_t sOnce = 0;
static renameFuncType sRenameFunc = NULL;
// Try to get a function pointer to renamex_np, which is available in OS 10.12 and later.
dispatch_once(&sOnce,
^{
sRenameFunc = (renameFuncType) dlsym(RTLD_DEFAULT, "renamex_np");
});
// renamex_np is only available on OS 10.12 and later, and does not work on HFS+ volumes
// but does work on APFS volumes. Being the latest and greatest, we try it first.
if (sRenameFunc != NULL)
{
result = (*sRenameFunc)(inOldFile, inNewFile, RENAME_SWAP);
}
if (result != 0)
{
// exchangedata is an older function that works on HFS+ but not APFS.
result = exchangedata(inOldFile, inNewFile, 0);
}
if (result != 0)
{
// Neither function worked, we must go old school.
std::string nameTemplate(inOldFile);
nameTemplate += "-swapXXXX";
// Make a mutable copy of the template
std::vector<char> workPath(nameTemplate.size() + 1);
memcpy(&workPath[0], nameTemplate.c_str(), nameTemplate.size() + 1);
mktemp(&workPath[0]);
std::string tempPath(&workPath[0]);
// Make the old file have a temporary name
result = rename(inOldFile, tempPath.c_str());
// Put the new file data under the old name.
if (result == 0)
{
result = rename(inNewFile, inOldFile);
}
// Put the old data under the new name.
if (result == 0)
{
result = rename(tempPath.c_str(), inNewFile);
}
}
return result;
}
只是要清楚 - 虽然这是首选的方法,但它实际上并没有将replaceItemAtURL与withItemAtURL“交换”。完成此操作后,原始文件将位于同一目录下的backupItemName中,然后需要与withItemAtURL交换以实现与FSExchangeObjectsCompat()相同的功能,对吧? – SMGreenfield
提供'backupItemName'是可选的。如果您没有通过该选项或选项,那么实施可能(并可能)执行您正在寻找的交易类型。 –
为了将来的参考 - withItemAtURL上的“new”文件在替换replaceItemAtURL处的文件后始终为DELETED。 replaceItemAtURL中的“原始”文件将被复制到backupItemName(如果提供),并且也将被删除,除非指定了NSFileManagerItemReplacementWithoutDeletingBackupItem选项标志。所以这与交易所不完全相同,但似乎足够接近。不确定这是优于还是偏好在10.12或更高版本上使用renamex_np或APFS存在时。 – SMGreenfield