覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

truncate(ftruncate) はサイズを切り詰める(減らす)だけじゃないよ

Linuxのシステムコールの truncate, ftruncate は、対象のファイルのサイズを切り詰めるものです。

書式は以下の通りです。

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
  • 引数
    • pathname / fd・・・サイズを切り詰めるファイルのパス / サイズを切り詰めるファイルのファイルディスクリプター
    • length ・・・切り詰めるサイズ(byte)
  • 戻り値
    • 0 ・・・成功
    • -1 ・・・エラー発生 (この場合 errno が適切にセットされる)

各関数で、ファイルのパスを指定するのか、ファイルディスクリプター(ファイル記述子)を指定するのかの違いはありますが、
基本的にファイサイズを切り詰めるという動作は同じです。

で、私がいつの間にか勘違いしてただけなのですが、
これらシステムコールは、ファイルのサイズを減らす動きをさせることができると同時に、元のファイルのサイズより大きな lengthを指定してファイルを伸張することも可能です。
伸張された場合、サイズが増えた部分を読み出すとヌルバイト (0x00) のデータが返されます。
(サイズが増えた部分には、すぐには物理ディスクは割り当てられず、対象ファイルはholeを含んだファイル(Sparse File)になるのが普通です)。

伸長される(ファイルサイズが増える)というパターンが、私の頭からは欠落していました。


ファイルサイズが増えるパターンも含めて念のためにtruncate, ftruncate の動作確認をしてみます

truncate の動作確認

sample01.txt というファイルのサイズを 10 byteにします。(システムコール呼び出しとは別で対象のファイルは先に用意しておきます)

■サンプルプログラム

truncate_sample.c

#include <unistd.h>
#include <sys/types.h>

#include <stdio.h>

int main() {
    if (truncate("sample01.txt", 10) < 0) {
        perror("truncate error!");
        return 1;
    }

    return 0;
}

以下でコンパイルします

gcc truncate_sample.c -o truncate_sample


そして以下で動作確認してみます

■ファイルサイズが減るパターン

$ echo 12345678901234 > sample01.txt
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  15 10 20 14:50 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 0a   
000000f
$
$ ./truncate_sample 
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  10 10 20 14:51 sample01.txt
$ 
$ hexdump sample01.txt 
0000000 31 32 33 34 35 36 37 38 39 30                  
000000a

サンプルプログラム実行後にサイズが10バイトに減っていることがわかります


■ファイルサイズが増えるパターン

$ echo 1234 > sample01.txt
$
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  5 10 20 14:57 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 0a                                 
0000005
$ ./truncate_sample 
$ ls -l sample01.txt 
-rw-r--r--  1 user  user  10 10 21 14:57 sample01.txt
$
$ hexdump sample01.txt 
0000000 31 32 33 34 0a 00 00 00 00 00                  
000000a

サンプルプログラム実行後にサイズが10バイトに増えて、増えた部分がヌルバイト(0x00)のデータになっていることがわかります。


ftruncate の動作確認

sample02.txt というファイルのサイズを 20byte にします。(システムコール呼び出しとは別で対象のファイルは先に用意しておきます)

サンプルプログラム

#include <unistd.h>
#include <sys/types.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main() {

    int fd = open("sample02.txt", O_WRONLY);

    if (fd < 0) {
        perror("open error!");
        return 1;
    }

    if (ftruncate(fd, 20) < 0) {
        perror("ftruncate error!");
        close(fd);
        return 1;
    }

    close(fd);

    return 0;
}

以下でコンパイルします

 gcc ftruncate_sample.c -o ftruncate_sample


そして以下で動作確認してみます

■ファイルサイズが減るパターン

$ echo 1234567890123456789012345 > sample02.txt
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  26 10 20 15:06 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
0000010 37 38 39 30 31 32 33 34 35 0a                  
000001a
$
$ ./ftruncate_sample 
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  20 10 20 15:06 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
0000010 37 38 39 30                                    
0000014

サンプルプログラム実行後にサイズが20バイトに減っていることがわかります


■ファイルサイズが増えるパターン

$ echo 123456789 > sample02.txt
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  10 10 20 15:08 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 0a                  
000000a
$
$ ./ftruncate_sample 
$
$ ls -l sample02.txt 
-rw-r--r--  1 user  user  20 10 20 15:08 sample02.txt
$
$ hexdump sample02.txt 
0000000 31 32 33 34 35 36 37 38 39 0a 00 00 00 00 00 00
0000010 00 00 00 00                                    
0000014

サンプルプログラム実行後にサイズが20バイトに増えて、増えた部分がヌルバイト(0x00)のデータになっていることがわかります


まとめ

truncate, ftruncate はファイルサイズを減らすだけではなく、増やすこともできることがわかりました。
そして増えた場合は、ヌルバイトのデータが読み出されることがわかりました。

そうそう無いと思いますが、仮想ファイルシステムなんかを作る場合にもこの仕様は明確に把握しておく必要があります。



関連エントリ