더 나은 블록 수준 중복 제거를 위해 tar(또는 기타) 아카이브를 만들고 원본 파일처럼 블록을 정렬하시겠습니까?

더 나은 블록 수준 중복 제거를 위해 tar(또는 기타) 아카이브를 만들고 원본 파일처럼 블록을 정렬하시겠습니까?

tar 파일의 내용이 원본 파일처럼 블록 정렬되어 블록 수준 중복 제거의 이점을 얻을 수 있도록 tar 파일을 생성하는 방법(https://unix.stackexchange.com/a/208847/9689)?

(우리가 그러한 이점을 얻지 못하게 하는 tar 형식에 본질적인 것이 없다는 것이 맞습니까? 그렇지 않으면 tar가 아니라면 그러한 기능이 내장된 다른 아카이버가 있을 수 있습니까?)

PS 나는 "압축되지 않은 tar"를 의미했습니다. tar + gz 또는 기타가 아닌 압축되지 않은 tar와 질문은 파일 블록 수준 정렬을 허용하는 몇 가지 트릭을 요청했습니다. AAFAIRecall tar는 테이프 드라이브와 함께 사용하도록 설계되었으므로 정렬을 위해 파일 형식에 추가 비트를 추가하는 것이 가능하고 쉬울 수 있습니까? 그것을 달성할 수 있는 도구라도 있었으면 좋겠습니다. ;) 내가 기억하는 한 tar 파일은 연결될 수 있으므로 정렬을 위해 공간을 채우는 트릭이 있을 수 있습니다.

답변1

이론적으로는 가능합니다. 그러나 이는 매우 보기 흉하며 본질적으로 아카이브를 수동으로 구축해야 합니다.

우리가 직면한 과제

tar체재512바이트 블록에서 실행. 크기는 고정되어 있으며 기존 디스크 섹터 크기와 일치하도록 설계되었습니다. 파일이 아카이브에 저장되면 첫 번째 512바이트 블록은 파일의 메타데이터(이름, 크기, 유형 등)가 포함된 헤더이고 후속 블록에는 파일 내용이 포함됩니다. 따라서 아카이브 데이터는 512바이트만큼 오프셋됩니다.

btrfs의 블록 크기("--sectorsize")일반적으로 4096바이트. 이론적으로는 이것을 선택할 수 있지만 실제로는 CPU의 페이지 크기와 일치해야 하는 것 같습니다. 따라서 btrfs 블록을 축소할 수 없습니다.

tar프로그램은 블록 크기의 배수로 정의되는 더 큰 "레코드" 크기 개념을 갖고 있는데 이는 거의 유용해 보입니다. 이는 tar부분적인 테이프 레코드 쓰기를 피할 수 있도록 특정 테이프 드라이브에 대한 섹터 크기를 지정하는 것으로 밝혀졌습니다 . 그러나 데이터는 여전히 512바이트 단위로 구축되고 압축되어 있으므로 tar이를 사용하여 원하는 대로 청크를 늘릴 수는 없습니다.

알아야 할 마지막 데이터 비트 tar는 다음과 같습니다.아카이브 종료 태그이 블록이 파일 데이터 내부에 있지 않는 한 모두 0인 두 개의 연속 블록입니다. 따라서 어떤 종류의 순진한 패딩 블록도 허용되지 않을 수 있습니다.

해커 공격

우리가 할 수 있는 일은 필러 파일을 삽입하는 것입니다. 아카이브 시작 부분에 중복 항목(이라고 함)을 제거하려는 파일을 추가하기 전에 크기가 다음과 같은 dup파일을 추가합니다.pad

pad's header + pad's data + dup's header = 4096 bytes.

이렇게 하면 dup데이터가 블록 경계에서 시작되어 중복 제거될 수 있습니다.

그런 다음 각 후속 파일에 대해 올바른 패딩을 계산하기 위해 이전 파일의 크기도 추적해야 합니다. 또한 어떤 종류의 헤더 확장이 필요한지도 예측해야 합니다.기본 tar 헤더파일 경로 공간은 100바이트에 불과하므로 데이터가 전체 경로인 구조적으로 명명된 파일을 사용하여 더 긴 경로가 인코딩됩니다. 일반적으로 헤더 크기를 예측하는 데에는 잠재적인 복잡성이 많이 있습니다. tar파일 형식에는 여러 역사적 구현에서 많은 결함이 있었습니다.

다행스러운 점은 모든 패딩 파일이 동일한 이름을 공유할 수 있다는 것입니다. 따라서 압축을 풀면 크기가 4096바이트 미만인 추가 파일만 남게 됩니다.

아마도 그러한 아카이브를 안정적으로 생성하는 가장 깔끔한 방법은 GNU tar프로그램을 수정하는 것입니다. 그러나 CPU 및 I/O 시간을 희생하면서 빠르고 지저분한 작업을 원하는 경우 각 파일에 대해 다음과 같이 수행할 수 있습니다.

#!/bin/bash

# Proof of concept and probably buggy.
# If I ever find this script in a production environment,
# I don't know whether I'll laugh or cry.

my_file="$2"
my_archive="$1"

file_size="$(wc -c <"$my_file")"
arch_size="$(tar cb 1 "$my_file" | wc -c)"  # "b 1": Remember that record size I mentioned?  Set it to equal the block size so we can measure usefully.
end_marker_size=1024  # End-of-archive marker: 2 blocks' worth of 0 bytes

hdr_size="$(( (arch_size - file_size - end_marker_size) % 4096 ))"
pad_size="$(( (4096 - 512 - hdr_size) % 4096 ))"
(( pad_size < 512 )) && pad_size="$(( pad_size + 4096 ))"

# Assume the pre-existing archive is already a multiple of 4096 bytes long
# (not including the end-of-archive marker), and add extra padding to the end
# so that it stays that way.
file_blocks_size="$(( ((file_size+511) / 512) * 512 ))"
end_pad_size="$(( 4096 - 512 - (file_blocks_size % 4096) ))"
(( end_pad_size < 512 )) && end_pad_size="$(( end_pad_size + 4096 ))"

head -c $pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_ "$my_file"
head -c $end_pad_size /dev/zero > _PADDING_
tar rf "$my_archive" _PADDING_

관련 정보