Save blank file in Objective-C

I am working on a code that, among other things, must save a zero-filled file. It can expect to create a file from 10MB to even 1GB blank.

It must be similar to this Unix command:

  • Compiling external C++ library for use with iOS project
  • “isEqualToString” Cocoa error
  • Calling NSLog from C++: “Format string is not a string literal (potentially insecure)”
  • Objective-C: Assertion vs. Exception vs. Error
  • How can I create a window with transparent background with swift on osx?
  • Subclassing and overriding UITextField in Monotouch
  • dd if=/dev/zero of=my_image.dsk count=20480
    

    I could do this one work with small sizes:

    int totalSize = 1024*1024;
    char buf[totalSize];
    strcpy(buf,"");
    
    NSData *theData = [NSData dataWithBytes:buf length:sizeof(buf)];
    
    //NSLog(@"%@", theData);
    NSLog(@"%lu", sizeof(buf));
    
    [theData writeToFile:@"/Users/foo/Desktop/my_image.dsk" atomically:YES];
    

    But if I try a bigger value (1024*1024*10, for instance), it crashes.
    So I tried:

    NSFileManager *ddd = [[NSFileManager alloc ] init];
    [ddd createFileAtPath:@"/Users/foo/Desktop/my_image.dsk" contents:[NSData data] attributes:nil];
    

    It creates an empty file, but is not good because the file size is zero. It should not be zero.

    I spent hours trying to find an answer without success. I want to do this in Obj-C, but C is also an option before I go nuts.

    Please, someone give-me some light!

    Thanks in advance!

    — Edit —

    Thanks everyone, but one more thing: is it possible to write without allocating everything on memory?

    4 Solutions Collect From Internet About “Save blank file in Objective-C”

    You can use dd. This will let you write, e.g., 10 GB without worrying about memory or address space.

    NSTask *task = [NSTask new];
    long size = ...; // Note! dd multiplies this by 512 by default
    NSString *path = ...;
    [task setLaunchPath:@"/bin/dd"];
    [task setArguments:[NSArray arrayWithObjects:@"dd", @"if=/dev/zero",
        [NSString stringWithFormat:@"of=%s", [path fileSystemRepresentation]],
        [NSString stringWithFormat:@"count=%ld", size],
        nil]];
    [task launch];
    [task waitUntilExit];
    if ([task terminationStatus] != 0) {
        // an error occurred...
    }
    [task release];
    

    An advantage is that sophisticated users can kill dd if something goes wrong.

    About sandboxing: My suspicion is that this will work fine even in a sandbox, but I’d be curious to know for sure. According to documentation (link), a subprocess “simply inherits the sandbox of the process that created it.” This is exactly how you’d expect it to work on Unix.

    You can be sure that dd will stick around since Apple claims that OS X conforms to SUSv3.

    This is relatively simple in the POSIX API:

    int f = open("filename", O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
    if (f < 0) {
        perror("open filename");
        exit(1);
    }
    
    char empty = 0;
    
    pwrite(f, &empty, sizeof(char), <offset into the file>);
    

    The offset is specified using an integer of type off_t, which might not be large enough for your specific needs. Check your system API documentation to discover the 64-bit file interfaces if it isn’t large enough.

    The best part of this approach is that it only takes a few bytes of program memory — you’re not allocating 10 gigabytes of nothing to write your file.

    Another even simpler approach is to use the truncate(2) system call. Not all platforms support extending the file with truncate(2), but for the ones that do, it’s one call.

    Try this code:

    int totalSize = 1024*1024;
    // don't allocate on the stack
    char *buf = calloc(totalSize, sizeof(char));
    
    NSData *theData = [NSData dataWithBytesNoCopy:buf length:totalSize];
    
    //NSLog(@"%@", theData);
    NSLog(@"%lu", totalSize);
    
    [theData writeToFile:@"/Users/foo/Desktop/my_image.dsk" atomically:YES];
    

    You could also try this, less of a memory hog, and it works with a 1 GB file.

    void writeBlankBytes(FILE *file, size_t numKB)
    {
        static char *oneKBZero = NULL;
    
        if (oneKBZero == NULL)
        {
            oneKBZero = calloc(1024, sizeof(char));
        }
    
        for (int i = 0; i < numKB; i++) {
            fwrite(oneKBZero, 1, 1024, file);
        }
    }
    

    Thanks everyone. I came to a solution. This is basically a sketch of the code that I am about to put in a cocoa app.

    Works fine and I think I only need some precautions with the size variable and it will be enough.

    long size = 2000*500;
    
    NSString *path = [[NSString alloc] initWithFormat: @"/Users/foo/Desktop/testeFile.dsk"];
    
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/dd"];
    [task setArguments:[NSArray arrayWithObjects:
                        @"if=/dev/zero",
                        [NSString stringWithFormat:@"of=%s", [path fileSystemRepresentation]],
                        [NSString stringWithFormat:@"count=%ld", size],
                        nil]];
    NSLog(@"%@",[task arguments]);
    [task launch];
    
    //[task waitUntilExit]; //The app would stuck here.. better use the loop
    
    int cont = 0;
    while ([task isRunning]) {
        cont++;
        if(cont%100==0) NSLog(@". %d", cont);
    }
    
    if ([task terminationStatus] != 0) {
        NSLog(@"an error occurred...");
    }
    [task release];
    [path release];
    
    NSLog(@"Done!");