ホール

ファイルの末尾を越えて lseek() して書き込むと、ファイルの末尾だった地点から書き込んだ地点までの間はゼロで埋められるが、このときファイルシステムによっては物理的にゼロを書き込むことなく sparse なファイルを作るらしい(その結果として生じる、物理ディスク上に存在しない部分はホールと呼ばれる)。たとえば、ls-s オプションの説明には次のように書いてある。

それぞれのファイルのディスク割り当て量をファイル名の左に表示する。これはファイルによって使用されるディスクの総量である。この大きさは普通はファイルのサイズよりいくらか大きいが、ファイルがホール (穴) を持っている場合は小さくなることもある。

HFS+ (with Mac OS) はホールをサポートしていないらしく、実験してみたら次のようになった。

$ cat test.c
#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("zero", O_CREAT | O_RDWR, 0644);
    lseek(fd, 1024 * 1024 * 1024 - 1, SEEK_SET);
    write(fd, "\0", 1);
}
$ gcc test.c && ./a.out
$ stat -f "%N: %z bytes, %b blocks" zero
zero: 1073741824 bytes, 2097152 blocks

ext3 (with Linux) ではホールが出来た。

$ gcc test.c && ./a.out
$ stat -c "%n: %s bytes, %b blocks" zero
zero: 1073741824 bytes, 24 blocks

ちなみに、GNUcp にはホールを保つ機能があって(連続するゼロをコピーせずに lseek() でスキップする)、--sparse オプションで設定できるようだ。以下 ext3 にて。

$ time dd bs=4k count=256k if=/dev/zero of=zero-a 2> /dev/null

real    0m47.057s
user    0m0.015s
sys     0m2.100s
$ time dd bs=4k count=256k if=/dev/zero 2> /dev/null | cp --sparse=always /dev/stdin zero-b

real    0m1.072s
user    0m0.003s
sys     0m1.040s
$ stat -c "%n: %s bytes, %b blocks" zero-a zero-b
zero-a: 1073741824 bytes, 2099208 blocks
zero-b: 1073741824 bytes, 0 blocks

上は 1 GB のゼロを書き込むのに対し、下は単にスキップするだけなので速い。