Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you programmatically create a completely empty sparse file on linux?

If you run dd with this:

dd if=/dev/zero of=sparsefile bs=1 count=0 seek=1048576

You appear to get a completely unallocated sparse file (this is ext4)

smark@we:/sp$ ls -ls sparsefile 
0 -rw-rw-r-- 1 smark smark 1048576 Nov 24 16:19 sparsefile

fibmap agrees:

smark@we:/sp$ sudo hdparm --fibmap sparsefile 
sparsefile:
 filesystem blocksize 4096, begins at LBA 2048; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors

Without having to dig through the source of dd, I'm trying to figure out how to do that in C.

I tried fseeking and fwriting zero bytes, but it did nothing. Not sure what else to try, I figured somebody might know before I hunt down dd's innards.

EDIT: including my example...

FILE *f = fopen("/sp/sparse2", "wb");
fseek(f, 1048576, SEEK_CUR);
fwrite("x", 1, 0, f);
fclose(f);
like image 949
stu Avatar asked Nov 24 '15 21:11

stu


2 Answers

When you write to a file using write or various library routines that ultimately call write, there's a file offset pointer associated with the file descriptor that determines where in the file the bytes will go. It's normally positioned at the end of the data that was processed by the most recent call to read or write. But you can use lseek to position the pointer anywhere within the file, and even beyond the current end of the file. When you write data at a point beyond the current EOF, the area that was skipped is conceptually filled with zeroes. Many systems will optimize things so that any whole filesystem blocks in that skipped area simply aren't allocated, producing a sparse file. Attempts to read such blocks will succeed, returning zeroes.

Writing block-sized areas full of zeroes to a file generally won't produce a sparse file, although it's possible for some filesystems to do this.

Another way to produce a sparse file, used by GNU dd, is to call ftruncate. The documentation says this:

The ftruncate() function causes the regular file referenced by fildes to have a size of length bytes.

If the file previously was larger than length, the extra data is discarded. If it was previously shorter than length, it is unspecified whether the file is changed or its size increased. If the file is extended, the extended area appears as if it were zero-filled.

Support for sparse files is filesystem-specific, although virtually all designed-for-UNIX local filesystems support them.

like image 85
Mark Plotnick Avatar answered Oct 02 '22 13:10

Mark Plotnick


This is complementary to the answer by @MarkPlotnick, it's a sample simple implementation of the feature you requested using ftruncate():

#include <unistd.h>
#include <fcntl.h>

#include <sys/stat.h>

int
main(void)
{
    int file;
    int mode;

    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    file = open("sparsefile", O_WRONLY | O_CREAT, mode);
    if (file == -1)
        return -1;
    ftruncate(file, 0x100000);
    close(file);

    return 0;
}
like image 33
Iharob Al Asimi Avatar answered Oct 02 '22 14:10

Iharob Al Asimi