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:
- Bentrok dengan file pengguna — bila pengguna sudah punya
temp.txtdi direktori yang sama, program kita akan menimpanya. - Race condition — penyerang bisa membuat symlink
/tmp/myprog.tmpyang menunjuk ke file sensitif (misalnya~/.bashrc). Saat program kita menulis ketemp.txt, ia sebenarnya menulis ke~/.bashrcpenyerang (serangan symlink attack). - 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.
#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:
=== tmpfile: file sementara yang auto-delete ===
tmpfile() berhasil dibuka
Baca: data sementara di tmpfile
Baca: angka penting: 42
File sementara otomatis dihapus setelah fclosePada 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:
- Baris 8 (
FILE *f = tmpfile();):tmpfilemembuat file sementara dengan mode"wb+". Nama file tidak diungkapkan ke kita—ini sengaja, agar kita tidak bisa lupa menghapusnya. Implementasi glibc Linux biasanya memakaiopen(O_CREAT|O_EXCL|O_RDWR)dengan nama acak, lalu langsungunlink(2)—sehingga file ada di disk tetapi tidak punya directory entry (akan benar-benar hilang saatfclose). - Baris 14 (
fputs(...)): menulis ke file sementara seperti file biasa. Karena mode"wb+", kita bisa baca dan tulis denganfseek/rewinddi antaranya. - Baris 18 (
rewind(f);): mengembalikan file position indicator ke awal untuk membaca ulang data yang baru ditulis. - Baris 22 (
fclose(f);): menutup file. Karena file sudah di-unlink sejak awal, ketikafclosemenutup 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.
#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:
=== 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 hilangPerhatikan 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:
- Baris 11 (
char tmpl[] = "/tmp/test_XXXXXX";): template harus berakhir dengan tepat 6 karakterX. Kita pakai array mutable (bukan pointer ke string literal) karenamkstempakan memodifikasi template dengan menggantiXXXXXXmenjadi karakter acak. - Baris 12 (
int fd = mkstemp(tmpl);):mkstempmembuat file baru dengan nama yang sudah dimodifikasi (misalnya/tmp/test_UdURpg), mengembalikan file descriptor integer. File dibuka denganO_RDWR | O_CREAT | O_EXCLdengan mode 0600 (hanya pemilik bisa baca/tulis). - Baris 19 (
write(fd, msg, strlen(msg));): menulis langsung dengan system call POSIXwrite(2). Catatan:writebisa menulis kurang dari yang diminta—kode produksi harus loop sampai semua byte ditulis. - Baris 22 (
FILE *f = fdopen(fd, "r+");): membungkus file descriptor menjadiFILE *untuk memakai fungsi I/O standar.fdopentidak menutupfd—fclose(f)di baris 30 akan menutupnya. - Baris 37 (
access(tmpl, F_OK);):access(2)memeriksa keberadaan file.F_OKberarti “cek eksistensi saja”. Mengembalikan0bila ada,-1bila tidak. - Baris 42 (
remove(tmpl);): menghapus file denganunlink(2)internal. Setelah ini,accessmengembalikan-1denganerrno=ENOENT.
Perbandingan tmpfile vs mkstemp #
| Aspek | tmpfile | mkstemp |
|---|---|---|
| Standar | ISO/IEC 9899 (C) | POSIX.1 |
| Tipe kembalian | FILE * | int (fd) |
| Nama file | Tidak diungkap | Diberikan via template |
| Auto-delete | Ya, saat fclose | Tidak, perlu remove() |
| Mode | "wb+" | `O_RDWR |
| Use case | Scratch space internal | Komunikasi antar proses |
| Keamanan | Aman (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:
- Panggil
mkstempuntuk dapatkanfddan nama. - Segera
unlink(tmpl)bila tidak perlu nama untuk komunikasi antar proses. - Gunakan
fddenganwrite/readatau bungkus denganfdopenuntukFILE *. - Tutup
fdataufclosedi akhir.
#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:
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 diskPola 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.