Linux 檔案的hole

大綱

檔案的hole檔案中連續的null 字元。

當lseek指定的offset (假設叫o_lseek)超過目前檔案最後的offset(o_end)時,多出來的delta系統並不會寫入到實體空件,而是有人要讀取這段空間時會讀到連續的null字元。也就是說會有檔案大小遠大於實際佔用的儲存空間的特性。

一般來說,hole會應在sparse的檔案,最典型的的例子就是Virtual machine的image檔案。

範例

測試程式行為描述

  1. 讀入任意字串
  2. 將字串寫入檔案,每個字元間隔1G的空間

執行結果

請注意檔案大小和目錄大小的差異

$ ./create_hole_file
Enter any text: test111
Get test111
, used to create test hole file

$ ls -lh file_in_the_hole
-rw------- 1 nobody nobody 7.1G Dec 31 14:24 file_in_the_hole

$ du -sh .
128K	.

Makefile

CFLAGS=-g -Wall -pedantic
LDFLAGS=

TARGET=create_hole_file
HOLE_FILE_NAME=file_in_the_hole

all: $(TARGET)
create_hole_file: create_hole_file.c
	gcc -o $@ $(CFLAGS) $^ -DHOLE_FILE_NAME="\"$(HOLE_FILE_NAME)\""

clean:
	rm -rf *.o *~ $(TARGET) $(HOLE_FILE_NAME)

程式

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#ifndef HOLE_FILE_NAME
#error You need to define HOLE_FILE_NAME
#endif

#define MAX_CHAR_LINE  (256)
#define HOLE_GAP       (1024*1024*1024) /* 1G */
int main(void)
{
    char line[MAX_CHAR_LINE] = {0};
    int rval = 0;
    int fd   = 0;
    int i    = 0;

    off_t   lseek_offset = 0;
    ssize_t write_size   = 0;

    /* Open file */
    fd = open(HOLE_FILE_NAME, O_CREAT | O_WRONLY, S_IRUSR  | S_IWUSR);
    if (fd == -1) {
        fprintf(stderr, "%d: %s\n", __LINE__, strerror(errno));
        return -1;
    }

    /* Get string from stdin */
    fprintf(stderr, "Enter any text: ");
    fgets(line, MAX_CHAR_LINE, stdin);

    fprintf(stderr, "Get %s, used to create test hole file\n", line);

    /* Implent hole */
    for (i = 0; i < strlen(line); i++) {
        write_size = write(fd, &line[i], sizeof(char));
        if (write_size == -1) {
            fprintf(stderr, "%d: %s\n", __LINE__, strerror(errno));
            return -1;
        }

        /* file in the hole! */
        lseek_offset = lseek(fd, HOLE_GAP, SEEK_CUR);
        if (lseek_offset == -1) {
            fprintf(stderr, "%d: %s\n", __LINE__, strerror(errno));
            return -1;
        }

    }

    /* Close */
    rval = close(fd);
    if (rval == -1) {
        fprintf(stderr, "%d: %s\n", __LINE__, strerror(errno));
        return -1;
    }
    return 0;
}

參考資料

  • man lseek
  • The Linux Programming Interface: Chapter 4

书籍推荐