ARC下使用dispatch_write将NSData写入后台唯一文件

Mat*_*att 4 nsdata grand-central-dispatch ios automatic-ref-counting

我正在尝试创建一个具有唯一名称的文件并在后台向其中写入数据。

mktempWhenever it is possible, mkstemp() should be used instead, since it does not have the race condition.

mkstemp在打开的文件描述符中使用结果,因此dispatch_write似乎很明显。

现在NSData必须包裹在dispatch_data_tusing中dispatch_data_create。必须注意释放需要释放的内存,保留必须保留的内存。在 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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以问题是:

  1. 这有必要吗?应该与' 一起mktemp使用而不担心安全/竞争条件吗?NSDatawriteToFile:options:error:
  2. 是否使用空析构函数调用dispatch_data_createOK 以避免不必要的复制(保留指向缓冲区的指针NSData)?
  3. 由 打开的描述符可以与mkstemps一起使用吗dispatch_write
  4. 保留对 的引用会NSData保持dispatch_data_t有效吗?有这个必要吗?ARC在这里做什么?
  5. 文件是如何关闭的?dispatch_io_close

Rob*_*ier 5

这并不是真正的目的dispatch_writedispatch_data一般而言)。正如您所发现的,dispatch_data重点是功能和性能,而不是易用性。你有一个如此简单的问题。

另请注意,您正在讨论的竞争条件与正在您的临时目录中快速创建文件的主动攻击者有关。攻击过程是这样的:

  • 您正在以某些特权用户的身份运行。Eve(攻击者)正在以非特权用户身份运行。
  • 您想在 中创建一个/tmp您和 Eve 都可以读写的临时文件。
  • 您查看/tmp并发现某些文件名不存在
  • 在创建文件之前,Eve 会使用您刚刚检查的名称创建文件。她使文件世界可写(但它归 Eve 所有)。
  • 现在,您打开该文件并开始写入,但它仍然归 Eve 所有。
  • 现在 Eve 可以读取和修改您的数据。这可能会转化为特权升级。

这是对 Unix 系统的真正攻击。很明显,这并不是针对 iOS 系统的真正攻击。这并不意味着您不应该使用mkstemp. 你应该。但重要的是要了解您要防范的是什么。这不是“哎呀;我与自己相撞”的竞争条件,除非您每秒生成数百个文件(不要这样做)。

好的,那你要怎么做呢?Matt Gallagher 在 Cocoa with Love 中有一个很好的例子: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];
Run Code Online (Sandbox Code Playgroud)

现在,最后,您会看到 Matt 创建了一个文件名和一个NSFileHandle. 两者都可以很好地使用。您可以使用NSData方法写入文件名,也可以使用NSFileHandlewrite 方法。此时使用文件名不存在竞争条件,因为该文件已经存在并且归您所有。

要在后台编写此内容,只需将其粘贴到一个dispatch_async块中即可。