Banyak algoritma butuh scratch space sementara untuk perhitungan, sorting file besar, atau dekompresi multi-tahap. Kebutuhan ini muncul di mana-mana: compiler menyimpan intermediate code di file temp, browser menyimpan download parsial sebelum dipindah ke nama final, database menyimpan hasil join sementara. Pertanyaan kuncinya: bagaimana membuat file sementara dengan nama unik yang tidak bentrok dengan file lain, dan yang dijamin bersih saat program keluar? C standar dan POSIX menyediakan dua fungsi untuk ini: tmpfile() (otomatis terhapus) dan mkstemp() (nama bisa dikendalikan). Pada chapter ini kita akan memakai keduanya dan memahami trade-off masing-masing.

Mengapa Tidak Boleh Pakai Nama Hardcoded #

Cara naif membuat file sementara adalah menamainya "temp.txt" atau "/tmp/myprog.tmp". Ini berbahaya karena:

  1. Bentrok dengan file pengguna — bila pengguna sudah punya temp.txt di direktori yang sama, program kita akan menimpanya.
  2. Race condition — penyerang bisa membuat symlink /tmp/myprog.tmp yang menunjuk ke file sensitif (misalnya ~/.bashrc). Saat program kita menulis ke temp.txt, ia sebenarnya menulis ke ~/.bashrc penyerang (serangan symlink attack).
  3. Tidak bersih saat crash — bila program keluar abnormal, file sementara tidak terhapus dan menumpuk di disk.

Karena itu, selalu gunakan tmpfile atau mkstemp untuk membuat file sementara. Kedua fungsi ini memilih nama acak yang tidak bisa ditebak penyerang dan menjamin file dibuka secara atomik.

tmpfile: File Sementara yang Auto-Delete #

tmpfile() adalah fungsi C standar (ISO/IEC 9899 §7.21.4.3) yang membuat file sementara dengan mode "wb+" (baca+tulis, biner). File akan otomatis dihapus saat program keluar atau saat fclose dipanggil—kita tidak perlu memanggil remove() manual.

c
#include <stdio.h>
#include <string.h>

int main(void) {
    printf("=== tmpfile: file sementara yang auto-delete ===\n");
    /* tmpfile membuka file sementara dengan mode "wb+" */
    /* File akan dihapus otomatis saat program exit atau fclose */
    FILE *f = tmpfile();
    if (f == NULL) {
        perror("tmpfile");
        return 1;
    }
    printf("  tmpfile() berhasil dibuka\n");

    /* Tulis data */
    fputs("data sementara di tmpfile\n", f);
    fprintf(f, "angka penting: %d\n", 42);

    /* Baca kembali */
    rewind(f);
    char line[128];
    while (fgets(line, sizeof(line), f) != NULL) {
        printf("  Baca: %s", line);
    }

    /* Tidak perlu remove() - file akan auto-delete pada fclose */
    fclose(f);
    printf("  File sementara otomatis dihapus setelah fclose\n");

    return 0;
}

Output yang Dihasilkan:

text
=== tmpfile: file sementara yang auto-delete ===
  tmpfile() berhasil dibuka
  Baca: data sementara di tmpfile
  Baca: angka penting: 42
  File sementara otomatis dihapus setelah fclose

Pada output di atas, kita bisa menulis dan membaca file sementara seperti file biasa. Setelah fclose, file hilang dari sistem—tidak ada jejak di direktori /tmp. Inilah keunggulan utama tmpfile: cleanup otomatis, tidak peduli apakah program keluar normal atau crash (selama sistem operasi tidak mati tiba-tiba).

Mari kita telaah baris-baris kode di atas:

  1. Baris 8 (FILE *f = tmpfile();): tmpfile membuat file sementara dengan mode "wb+". Nama file tidak diungkapkan ke kita—ini sengaja, agar kita tidak bisa lupa menghapusnya. Implementasi glibc Linux biasanya memakai open(O_CREAT|O_EXCL|O_RDWR) dengan nama acak, lalu langsung unlink(2)—sehingga file ada di disk tetapi tidak punya directory entry (akan benar-benar hilang saat fclose).
  2. Baris 14 (fputs(...)): menulis ke file sementara seperti file biasa. Karena mode "wb+", kita bisa baca dan tulis dengan fseek/rewind di antaranya.
  3. Baris 18 (rewind(f);): mengembalikan file position indicator ke awal untuk membaca ulang data yang baru ditulis.
  4. Baris 22 (fclose(f);): menutup file. Karena file sudah di-unlink sejak awal, ketika fclose menutup file descriptor terakhir, kernel menghapus data dari disk.

mkstemp: File Sementara dengan Nama #

mkstemp (POSIX.1-2001) membuat file sementara dengan template nama yang kita berikan. Template harus berakhir dengan tepat enam karakter XXXXXX—yang akan diganti dengan karakter acak oleh mkstemp.

c
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(void) {
    printf("=== mkstemp: file sementara dengan nama ===\n");
    /* Template harus berakhir dengan XXXXXX (6 X) */
    char tmpl[] = "/tmp/test_XXXXXX";
    int fd = mkstemp(tmpl);
    if (fd < 0) {
        perror("mkstemp");
        return 1;
    }
    printf("  File dibuat: %s (fd=%d)\n", tmpl, fd);

    /* Tulis dengan write(2) langsung */
    const char *msg = "data dari mkstemp\n";
    write(fd, msg, strlen(msg));

    /* Buka sebagai FILE * untuk I/O standar */
    FILE *f = fdopen(fd, "r+");
    if (f == NULL) {
        perror("fdopen");
        close(fd);
        return 1;
    }
    rewind(f);
    char line[128];
    if (fgets(line, sizeof(line), f) != NULL) {
        printf("  Baca: %s", line);
    }
    fclose(f);   /* fclose juga menutup fd */

    /* File belum terhapus - kita harus remove() manual */
    printf("  File masih ada di disk: %s\n", tmpl);
    if (access(tmpl, F_OK) == 0) {
        printf("  -> access() konfirmasi ada\n");
    }

    /* Hapus manual */
    if (remove(tmpl) == 0) {
        printf("  File dihapus dengan remove()\n");
    }
    if (access(tmpl, F_OK) != 0) {
        printf("  -> access() konfirmasi hilang\n");
    }

    return 0;
}

Output yang Dihasilkan:

text
=== mkstemp: file sementara dengan nama ===
  File dibuat: /tmp/test_UdURpg (fd=3)
  Baca: data dari mkstemp
  File masih ada di disk: /tmp/test_UdURpg
  -> access() konfirmasi ada
  File dihapus dengan remove()
  -> access() konfirmasi hilang

Perhatikan bahwa mkstemp mengembalikan file descriptor integer (bukan FILE *)—ini adalah antarmuka level sistem operasi. Untuk memakai fungsi C standard library (fread, fprintf, dll.), kita perlu membungkusnya dengan fdopen. Setelah fclose, file masih ada di disk—kita harus memanggil remove() secara eksplisit untuk menghapusnya. Inilah trade-off: mkstemp memberi kita kendali penuh (nama bisa dipakai untuk komunikasi antar proses), tetapi kita yang bertanggung jawab atas cleanup.

Mari kita telaah baris-baris kode di atas:

  1. Baris 11 (char tmpl[] = "/tmp/test_XXXXXX";): template harus berakhir dengan tepat 6 karakter X. Kita pakai array mutable (bukan pointer ke string literal) karena mkstemp akan memodifikasi template dengan mengganti XXXXXX menjadi karakter acak.
  2. Baris 12 (int fd = mkstemp(tmpl);): mkstemp membuat file baru dengan nama yang sudah dimodifikasi (misalnya /tmp/test_UdURpg), mengembalikan file descriptor integer. File dibuka dengan O_RDWR | O_CREAT | O_EXCL dengan mode 0600 (hanya pemilik bisa baca/tulis).
  3. Baris 19 (write(fd, msg, strlen(msg));): menulis langsung dengan system call POSIX write(2). Catatan: write bisa menulis kurang dari yang diminta—kode produksi harus loop sampai semua byte ditulis.
  4. Baris 22 (FILE *f = fdopen(fd, "r+");): membungkus file descriptor menjadi FILE * untuk memakai fungsi I/O standar. fdopen tidak menutup fd—fclose(f) di baris 30 akan menutupnya.
  5. Baris 37 (access(tmpl, F_OK);): access(2) memeriksa keberadaan file. F_OK berarti “cek eksistensi saja”. Mengembalikan 0 bila ada, -1 bila tidak.
  6. Baris 42 (remove(tmpl);): menghapus file dengan unlink(2) internal. Setelah ini, access mengembalikan -1 dengan errno=ENOENT.

Perbandingan tmpfile vs mkstemp #

Aspektmpfilemkstemp
StandarISO/IEC 9899 (C)POSIX.1
Tipe kembalianFILE *int (fd)
Nama fileTidak diungkapDiberikan via template
Auto-deleteYa, saat fcloseTidak, perlu remove()
Mode"wb+"`O_RDWR
Use caseScratch space internalKomunikasi antar proses
KeamananAman (unlink saat dibuka)Aman (O_EXCL mencegah race)

Pola Aman dengan mkstemp #

Karena mkstemp tidak menghapus file otomatis, kita harus mengelola cleanup sendiri. Pola yang aman adalah:

  1. Panggil mkstemp untuk dapatkan fd dan nama.
  2. Segera unlink(tmpl) bila tidak perlu nama untuk komunikasi antar proses.
  3. Gunakan fd dengan write/read atau bungkus dengan fdopen untuk FILE *.
  4. Tutup fd atau fclose di akhir.
c
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(void) {
    char tmpl[] = "/tmp/test_safe_XXXXXX";
    int fd = mkstemp(tmpl);
    if (fd < 0) {
        perror("mkstemp");
        return 1;
    }

    /* Segera unlink - file akan hilang saat fd ditutup */
    if (unlink(tmpl) != 0) {
        perror("unlink");
        close(fd);
        return 1;
    }
    printf("File dibuat dan langsung di-unlink: %s\n", tmpl);
    printf("File tetap bisa diakses via fd=%d sampai ditutup\n", fd);

    /* Tulis dan baca seperti biasa */
    const char *data = "data rahasia sementara\n";
    write(fd, data, strlen(data));

    lseek(fd, 0, SEEK_SET);   /* rewind untuk fd */
    char buf[64] = {0};
    ssize_t n = read(fd, buf, sizeof(buf)-1);
    printf("Baca %zd byte: %s", n, buf);

    close(fd);
    printf("Setelah close(fd), file benar-benar hilang dari disk\n");
    return 0;
}

Output yang Dihasilkan:

text
File dibuat dan langsung di-unlink: /tmp/test_safe_h5WQyO
File tetap bisa diakses via fd=3 sampai ditutup
Baca 23 byte: data rahasia sementara
Setelah close(fd), file benar-benar hilang dari disk

Pola di atas memberi kita keunggulan tmpfile (auto-delete saat close) sekaligus fleksibilitas mkstemp (kemampuan menggunakan file descriptor langsung). Setelah unlink, directory entry dihapus tetapi inode tetap hidup selama ada file descriptor yang terbuka—ini adalah fitur Unix yang memungkinkan anonymous file.