Как эффективно усечь голову файла?
Всем хорошо изветсна функция truncate(file,size)
, изменяющая размер файла до заданного размера, путём усечения хвоста файла. Но как сделать то-же, только с усечением не хвоста файла а его головы?



Ответы - Как эффективно усечь голову файла? / Как эффективно усечь голову файла?

11.02.2019 05:16:07
В Linux есть системный вызов fallocate()
, который помимо своего основного назначения — резервирования пространства под файл может творить всякую магию. В частности начиная с 3.15 у него появился флаг FALLOC_FL_COLLAPSE_RANGE
, который и позволяет эффективно убирать часть данных из середины/начала файла без необходимости двигать их на диске. Например, удаление первого блока будет выглядеть так:
fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, 0, 4096);
Поддерживаемые ФС: ext4 и XFS. У ФС также есть свои ограничения на значение начала смещения, обычно длина/смещение должны быть кратны началу блока ФС и для ext4 файл должен быть аллоцирован на основе экстентов (почти все файлы в современной ext4).
Для доступа из консоли к возможностям этого системного вызова есть одноимённая утилитка из linux-utils
— fallocate
; FALLOC_FL_COLLAPSE_RANGE
соответствует ключ -с
.
Демонстрация действия.
Проверка, что ФС подходящая, определение размера блока:
$ mount | grep "$(df --output=source .)"
/dev/mapper/vg_main-gentoo_home on /home type ext4 (rw,relatime,data=ordered)
$ sudo tune2fs -l /dev/mapper/vg_main-gentoo_home | grep -i 'block size'
Block size: 4096
Создание, тестового файла:
$ echo "file_start" | cat - /dev/zero | dd bs=4k count=1 >>testfile 2>/dev/null
$ echo "file_middle" | cat - /dev/zero | dd bs=4k count=1 >>testfile 2>/dev/null
$ echo "file_end" | cat - /dev/zero | dd bs=4k count=1 >>testfile 2>/dev/null
Проверка, что файл аллоцирован с помощью экстентов:
$ lsattr testfile
--------------e--- testfile
Состояние файла до операции:
$ sudo debugfs -R "stat alexander/tmp/testfile" /dev/mapper/vg_main-gentoo_home |grep -A3 EXTENTS
EXTENTS:
(0-1):26281280-26281281, (2):26281396
alexander@goblin ~/tmp $ hexdump -C testfile
00000000 66 69 6c 65 5f 73 74 61 72 74 0a 00 00 00 00 00 |file_start......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 66 69 6c 65 5f 6d 69 64 64 6c 65 0a 00 00 00 00 |file_middle.....|
00001010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 66 69 6c 65 5f 65 6e 64 0a 00 00 00 00 00 00 00 |file_end........|
00002010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00003000
Удалим первый блок из файла:
$ fallocate -c -o 0 -l 4096 testfile
Состояние файла после операции:
$ sudo debugfs -R "stat alexander/tmp/testfile" /dev/mapper/vg_main-gentoo_home |grep -A3 EXTENTS
EXTENTS:
(0):26281281, (1):26281396
$ hexdump -C testfile
00000000 66 69 6c 65 5f 6d 69 64 64 6c 65 0a 00 00 00 00 |file_middle.....|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 66 69 6c 65 5f 65 6e 64 0a 00 00 00 00 00 00 00 |file_end........|
00001010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000
Как видно, файл был обрезан с начала при этом все блоки остались на месте, данные на диске не двигались.
При всех плюсах FALLOC_FL_COLLAPSE_RANGE
— это очень специфическая операция со множеством ограничений и на сегодняшний день AFAIK не имеет аналогов в других ОС. Так что к ней всегда следует предоставлять запасную реализацию с классическим алгоритмом: «переписать все данные в начало и отсечь хвост».





