Как программно создать абсолютно пустой разреженный файл в linux?


Если вы запускаете dd с этим:

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

Вы, кажется, получаете полностью нераспределенный разреженный файл (это ext4)

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

Fibmap соглашается:

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

Без необходимости копаться в источнике dd, я пытаюсь выяснить, как это сделать в C.

Я попробовал fseeking и fwriting нулевые байты, но это ничего не дало. Не зная, что еще можно попробовать, я решил, что кто-то может знать, прежде чем я выслежу внутренности ДД.

Правка: включая мой пример...

FILE *f = fopen("/sp/sparse2", "wb");
fseek(f, 1048576, SEEK_CUR);
fwrite("x", 1, 0, f);
fclose(f);
2 6

2 ответа:

При записи в файл с помощью write или различных библиотечных подпрограмм, которые в конечном счете вызывают write, существует указатель смещения файла, связанный с дескриптором файла, который определяет, куда в файле будут идти байты. Обычно он располагается в конце данных, которые были обработаны последним вызовом read или write. Но вы можете использовать lseek для размещения указателя в любом месте файла и даже за пределами текущего конца файла. Когда вы записываете данные в точку за пределами текущего EOF область, которая была пропущена, концептуально заполнена нулями. Многие системы оптимизируют вещи так, что любые целые блоки файловой системы в этой пропущенной области просто не выделяются, производя разреженный файл. Попытки считывания таких блоков увенчаются успехом, возвращая нули.

Запись в файл областей размером с блок, полных нулей, как правило, не приводит к созданию разреженного файла, хотя это возможно для некоторых файловых систем.

Еще один способ получения разреженный файл, используемый GNU dd, должен вызывать ftruncate. В документации говорится следующее:

Функция ftruncate () приводит к тому, что обычный файл, на который ссылается fildes, имеет размер в байтах длины.

Если файл ранее был больше длины, то лишние данные отбрасываются. Если ранее он был короче длины, то не указано, изменяется ли файл или увеличивается его размер. Если файл расширен, расширенная область выглядит так, как если бы она была расширена. заполнены нулями.

Поддержка разреженных файлов зависит от файловой системы, хотя практически все локальные файловые системы, предназначенные для UNIX, поддерживают их.

Это дополнение к ответу @MarkPlotnick, это пример простой реализации функции, которую вы запросили с помощью 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;
}