我正在尝试创建一个具有唯一名称的文件并在后台向其写入数据。
mktemp
说 只要有可能,就应该使用 mkstemp(),因为它没有竞争条件。
使用 mkstemp
会产生一个打开的文件描述符,因此 dispatch_write
似乎很明显。
现在 NSData
必须使用 dispatch_data_create
包装在 dispatch_data_t
中。必须注意释放需要释放的内存,保留必须保留的内存。在 ARC 下,这不太明显。
+ (void) createUnique:(NSData*)content name:(NSString*)name
extension:(NSString*)extension
completion:(void (^)(NSURL* url, NSError* error))completion {
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_data_t data = dispatch_data_create(content.bytes, content.length,
queue, ^{});
// dispatch_data_create() copies the buffer if DISPATCH_DATA_DESTRUCTOR_DEFAULT
// (= NULL) is specified, and attempts to free the buffer if
// DISPATCH_DATA_DESTRUCTOR_FREE is specified, so an empty destructor is
// specified.
dispatch_fd_t descriptor;
// Ignore details of creating the template C string
strcpy(nameCString, templateCString);
descriptor = mkstemps(nameCString, extensionLength);
free(nameCString);
if (descriptor != -1) {
dispatch_write(descriptor, data, queue,
^(dispatch_data_t data, int error) {
NSData* strongContent = content;
// Will this keep the NSData reference until the
// write is finished?
if (error) {
completion(nil, [NSError
errorWithDomain:NSURLErrorDomain
code:error userInfo:nil]);
} else {
// Ignore details of getting path from nameCString.
completion([NSURL URLWithString:path], nil);
}
// How does the file get closed?
});
} else {
completion(nil, [NSError errorWithDomain:NSURLErrorDomain code:errno
userInfo:nil]);
}
}
所以问题是:
- 这有必要吗?
mktemp
是否应该与NSData
的writeToFile:options:error:
一起使用而不用担心安全/竞争条件? - 使用空析构函数调用
dispatch_data_create
是否可以避免不必要的复制(保留指向NSData
缓冲区的指针)? mkstemps
打开的描述符可以和dispatch_write
一起使用吗?- 保留对
NSData
的引用是否会使dispatch_data_t
有效?这是必要的吗? ARC 在这里做什么? - 文件是如何关闭的?
dispatch_io_close
?
最佳答案
这并不是 dispatch_write
(和一般的 dispatch_data
)的真正用途。正如您所发现的,dispatch_data
关注的是功能和性能,而不是易用性。而你有一个如此简单的问题。
另请注意,您正在讨论的竞争条件与正在您的临时目录中快速创建文件的活跃攻击者有关。攻击是这样的:
- 您正在以某个特权用户的身份运行。 Eve(攻击者)正在运行一个非特权用户。
- 你想在
/tmp
中创建一个你和 Eve 都可以读写的临时文件。 - 您查看
/tmp
并发现某些文件名不存在 - 在您创建文件之前,Eve 使用您刚刚检查过的名称创建了文件。她使文件世界可写(但它归 Eve 所有)。
- 您现在打开文件并开始写入,但它仍归 Eve 所有。
- 现在 Eve 可以读取和修改您的数据。这可能会转化为特权升级。
这是对 Unix 系统的真正攻击。很明显,这不是对 iOS 系统的真正攻击。这并不意味着您不应该使用 mkstemp
。你应该。但重要的是要了解您要防范的是什么。这不是“哎呀;我撞到自己了”竞争条件,除非您每秒制作数百个文件(不要那样做)。
好的,那你是怎么做到的呢? Matt Gallagher 在 Cocoa with Love: Temporary files and folders in Cocoa 中有一个很好的例子.复制到这里以供将来的搜索者使用,但我强烈推荐这篇文章:
NSString *tempFileTemplate =
[NSTemporaryDirectory() stringByAppendingPathComponent:@"myapptempfile.XXXXXX"];
const char *tempFileTemplateCString =
[tempFileTemplate fileSystemRepresentation];
char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
strcpy(tempFileNameCString, tempFileTemplateCString);
int fileDescriptor = mkstemp(tempFileNameCString);
if (fileDescriptor == -1)
{
// handle file creation failure
}
// This is the file name if you need to access the file by name, otherwise you can remove
// this line.
tempFileName =
[[NSFileManager defaultManager]
stringWithFileSystemRepresentation:tempFileNameCString
length:strlen(tempFileNameCString)];
free(tempFileNameCString);
tempFileHandle =
[[NSFileHandle alloc]
initWithFileDescriptor:fileDescriptor
closeOnDealloc:NO];
现在,在最后,您会看到 Matt 创建了一个文件名和一个 NSFileHandle
。两者都可以使用。您可以使用 NSData
方法写入文件名,也可以使用 NSFileHandle
写入方法。此时不存在使用文件名的竞争条件,因为该文件已经存在并且归您所有。
要在后台编写它,只需将它放在 dispatch_async
block 中即可。
关于ios - 在 ARC 下使用 dispatch_write 将 NSData 写入后台唯一文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30447381/