Checksum VFS Shim
Checksum VFS Shim (체크섬 VFS 심)
checksum VFS 확장은 SQLite 데이터베이스의 모든 페이지 끝에 8바이트 체크섬을 추가하는 VFS shim이에요. 페이지가 기록될 때 체크섬이 추가되고, 각 페이지가 읽힐 때 검증돼요. 체크섬은 대량 저장 장치에서 발생하는 무작위 비트 플립으로 인한 데이터베이스 손상을 탐지하는 데 도움을 주기 위한 것이에요.
출처: 문서
본문
checksum VFS 확장은 SQLite 데이터베이스의 모든 페이지 끝에 8바이트 체크섬을 추가하는 VFS shim이에요. 체크섬은 각 페이지가 기록될 때 추가되고 각 페이지가 읽힐 때 검증돼요. 이 체크섬은 대량 저장 장치에서 무작위 비트 플립으로 인한 데이터베이스 손상을 탐지하는 데 도움을 주기 위한 것이에요.
checksum VFS 확장은 SQLite 3.32.0 (2020-05-22) 이상 버전이 필요해요. 이전 버전의 SQLite에서는 동작하지 않아요.
2. 컴파일
checksum VFS 모듈은 로더블 확장이에요. amalgamation에는 포함되어 있지 않고, 컴파일 시점이나 런타임에 SQLite에 추가해야 해요. checksum VFS 모듈의 소스 코드는 SQLite 소스 트리의 ext/misc/cksumvfs.c 소스 파일에 있어요.
checksum VFS 모듈을 런타임 로더블 확장으로 빌드하려면 다음과 유사한 명령을 사용해요:
- (linux) →
gcc -fPIC -shared cksumvfs.c -o cksumvfs.so - (mac) →
clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib - (windows) →
cl cksumvfs.c -link -dll -out:cksumvfs.dll
물론 프로젝트의 필요에 따라 추가 컴파일러 옵션을 붙이고 싶을 수도 있어요.
이 확장을 제품에 정적으로 링크하려면 다른 C언어 모듈처럼 컴파일하되 -DSQLITE_CKSUMVFS_STATIC 옵션을 추가해서 이 모듈이 동적 링크가 아니라 정적 링크되고 있음을 알게 해야 해요.
3. 로딩
이 확장을 공유 라이브러리로 로드하려면 먼저 sqlite3_load_extension() API 호출의 인자로 쓸 더미 SQLite 데이터베이스 연결을 만들어야 해요. 그런 다음 sqlite3_load_extension() API를 호출하고 더미 데이터베이스 연결을 종료해요. 이후 열리는 모든 데이터베이스 연결에는 이 확장이 포함될 거예요. 예를 들어:
sqlite3 *db;
sqlite3_open(":memory:", &db);
sqlite3_load_extension(db, "./cksumvfs");
sqlite3_close(db);
이 확장이 -DSQLITE_CKSUMVFS_STATIC으로 컴파일되어 애플리케이션에 정적으로 링크되었다면, 아래처럼 단일 API 호출로 초기화하면 돼요:
sqlite3_cksumvfs_init();
Cksumvfs는 VFS shim이에요. 로드되면 "cksmvfs"가 새 기본 VFS가 되고, 스택의 다음 VFS로 이전 기본 VFS를 사용해요. 이것이 보통 원하는 동작이에요. 하지만 여러 VFS shim이 로드되는 복잡한 상황에서는, cksumvfs가 기본 VFS Shim 스택에 올바른 순서로 배치되도록 올바른 순서로 로드되는 것이 중요할 수 있어요.
4. 사용법
평소처럼 sqlite3_open() 또는 sqlite3_open_v2() 인터페이스로 데이터베이스 연결을 열어요. (체크섬이 없는) 일반 데이터베이스 파일은 정상적으로 동작해요. 체크섬이 있는 데이터베이스에서 잘못된 체크섬을 포함한 페이지를 만나면 SQLITE_IOERR_DATA 오류가 반환돼요.
체크섬은 reserve bytes 값이 정확히 8인 데이터베이스에서만 동작해요. reserve-bytes의 기본값은 0이에요. 따라서 새로 생성된 데이터베이스 파일은 기본적으로 체크섬을 생략해요. 체크섬을 포함한 데이터베이스를 만들려면 다음과 유사한 코드로 reserve-bytes 값을 8로 바꿔요:
int n = 8;
sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
새 데이터베이스 파일을 만든 직후, 파일에 다른 것이 기록되기 전에 이 작업을 하면 그것만으로 충분할 수 있어요. 그렇지 않으면 위 API 호출 뒤에 아래를 이어서 실행해야 해요:
sqlite3_exec(db, "VACUUM", 0, 0, 0);
필요하지 않아도 VACUUM을 실행하는 건 해가 되지 않아요. 데이터베이스가 WAL 모드라면 계속하기 전에 모든 데이터베이스 연결을 종료하고 다시 열어야 해요.
CLI에서는 ".filectrl reserve_bytes 8" 명령 다음에 "VACUUM;"을 실행해요.
참고로 SQLite는 reserve-bytes 수를 증가시킬 수는 있지만 감소시킬 수는 없어요. 따라서 데이터베이스 파일의 reserve-bytes 값이 이미 8보다 크면, 데이터베이스 파일을 덤프하고 복원하는 것 외에는 그 데이터베이스에서 체크섬을 활성화할 방법이 없어요. 또한 다른 확장도 reserve-bytes를 사용할 수 있다는 점을 알아두세요. 체크섬은 그런 다른 확장들과 호환되지 않을 거예요.
5. 체크섬 검증
어떤 체크섬이라도 잘못되면 "PRAGMA quick_check" 명령이 찾아낼 거예요. 체크섬이 실제로 활성화되어 실행 중인지 검증하려면 다음과 같은 SQL을 사용해요:
SELECT count(*), verify_checksum(data)
FROM sqlite_dbpage
GROUP BY 2;
verify_checksum() 함수는 1, 0, NULL 세 가지 출력을 내놓아요. 체크섬이 정확하면 1, 잘못되면 0, 페이지를 읽을 수 없으면 NULL을 반환해요. 체크섬이 활성화되어 있으면 체크섬이 틀릴 때 읽기가 실패하므로, 잘못된 체크섬에서 verify_checksum()의 일반적인 결과는 NULL이에요.
모든 것이 정상이면 위 쿼리는 두 번째 컬럼이 1인 단일 행을 반환해야 해요. 다른 결과는 체크섬 오류가 있거나 체크섬 검증이 비활성화되었음을 나타내요.
6. 체크섬 검증 제어
cksumvfs 확장은 체크섬 검증을 비활성화하거나 재활성화하거나 상태를 조회하는 데 쓸 수 있는 새 PRAGMA 문을 구현해요:
PRAGMA checksum_verification; -- query status
PRAGMA checksum_verification=OFF; -- disable verification
PRAGMA checksum_verification=ON; -- re-enable verification
"checksum_verification" pragma는 체크섬 검증이 활성화되어 있으면 "1"(true), 비활성화되어 있으면 "0"(false)을 반환해요. 여기서 "검증"이라는 컨텍스트는 읽는 동안 체크섬 불일치가 감지되면 SQLITE_IOERR_DATA 오류를 일으키는 기능을 뜻해요. 데이터베이스의 reserve bytes 값이 8인 한, 이 pragma 설정과 무관하게 체크섬은 항상 최신으로 유지돼요. 체크섬 검증은 (예를 들어) 이전에 체크섬 오류를 보고한 데이터베이스의 포렌식 분석을 위해 비활성화할 수 있어요.
데이터베이스 파일에 reserve bytes 값이 8이 없으면 "checksum_verification" pragma는 항상 "0"으로 응답해요. cksumvfs 확장이 로드되지 않았으면 pragma는 아무 행도 반환하지 않아요.