12、临时文件:tmpfile函数、tmpnam函数、mkstemp(Linux扩展)、临时文件的安全问题

临时文件这东西,说白了就是「用完就扔」的文件。我在嵌入式项目里经常用它来存中间数据、日志片段,或者进程间通信的临时缓存。但你别小看它——用不好,轻则数据泄露,重则系统被黑。今天咱们就把临时文件的几种玩法掰开揉碎讲清楚。

12.1 tmpfile:最省心的临时文件

tmpfile() 是我个人最常用的临时文件函数。它帮你自动创建一个临时文件,返回一个 FILE* 指针。文件以 wb+ 模式打开,可读可写。最关键的是——程序结束或你调用 fclose() 时,文件自动删除。

#include <stdio.h>

int main() {
    FILE *fp = tmpfile();
    if (fp == NULL) {
        perror("tmpfile failed");
        return -1;
    }

    // 写入一些数据
    fprintf(fp, "临时数据: %d\n", 42);

    // 重置文件位置指针
    rewind(fp);

    // 读回来
    char buf[64];
    fgets(buf, sizeof(buf), fp);
    printf("读到的内容: %s", buf);

    // fclose 时自动删除
    fclose(fp);
    return 0;
}
我的习惯:在嵌入式 Linux 开发板上跑测试程序时,我经常用 tmpfile 存传感器校准参数。省心,不怕残留文件把 flash 撑爆。

tmpfile 有个局限——它不给你文件名。你没法在文件系统里看到它,也没法让其他进程访问它。如果你需要文件名,就得用下面这些函数。

12.2 tmpnam:生成唯一文件名(但不安全)

tmpnam() 的作用是生成一个不重复的文件名字符串。你可以用它创建临时文件,但要注意——它只生成名字,不创建文件。

#include <stdio.h>

int main() {
    char name[L_tmpnam];
    if (tmpnam(name) != NULL) {
        printf("临时文件名: %s\n", name);
    }
    return 0;
}
警告:tmpnam 存在严重的安全问题。我在一次安全审计中遇到过——攻击者可以预测 tmpnam 生成的文件名,在文件创建前先创建同名文件,诱导程序写入恶意数据。这就是经典的 TOCTOU(Time of Check, Time of Use)漏洞。

说白了,tmpnam 只保证名字唯一,不保证文件安全。你拿到名字后,到真正创建文件之间,存在时间窗口。这个窗口足够黑客做手脚了。

所以,我个人建议:不要在安全敏感场景中使用 tmpnam。C 标准也把它标记为「不推荐使用」。

12.3 mkstemp:Linux 下的安全选择

mkstemp() 是 Linux 扩展函数,它解决了 tmpnam 的安全问题。它直接创建并打开文件,返回文件描述符。没有时间窗口,攻击者没法插队。

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
    char template[] = "/tmp/myapp_XXXXXX";
    int fd = mkstemp(template);
    if (fd == -1) {
        perror("mkstemp failed");
        return -1;
    }

    printf("创建的文件: %s\n", template);

    // 写入数据
    write(fd, "Hello temp\n", 11);

    close(fd);

    // 手动删除
    unlink(template);
    return 0;
}

注意模板字符串里的 XXXXXX——这是必须的占位符,mkstemp 会用随机字符替换它们。我刚开始用的时候忘了写够6个X,结果函数一直返回 -1,排查了半天才发现。

关键区别:
  • tmpnam:只生成名字,不创建文件,不安全
  • tmpfile:创建并打开,自动删除,但无文件名
  • mkstemp:创建并打开,安全,需要手动删除

12.4 临时文件的安全问题

临时文件的安全问题,我把它总结为三类:

风险类型 描述 解决方案
TOCTOU 竞争 检查文件名和创建文件之间存在时间差 使用 mkstemp 等原子操作函数
权限泄露 临时文件权限设置不当,其他进程可读 umask 设置严格权限,或使用 tmpfile
残留文件 程序崩溃后临时文件未被清理 注册 atexit 处理函数,或使用 tmpfile

我曾经在一个嵌入式网关项目里踩过权限泄露的坑。当时用 fopen 创建临时文件,默认权限是 0666,结果另一个服务进程把临时文件里的密钥读走了。从那以后,我但凡创建临时文件,都会显式设置 umask。

#include <sys/stat.h>

// 创建临时文件前设置严格权限
umask(0077);  // 只允许当前用户读写
int fd = mkstemp(template);
避坑指南:我曾经在信号处理函数里创建临时文件,结果文件描述符泄漏了。后来我改用 tmpfile,配合 atexit 注册清理函数,才彻底解决。记住:临时文件的生命周期一定要清晰。

12.5 知识体系图

下面这张图帮你理清临时文件的核心脉络:

临时文件核心知识体系 tmpfile tmpnam mkstemp 自动创建 + 自动删除 无文件名,安全 返回 FILE* 只生成名字 TOCTOU 漏洞 不推荐使用 原子创建 + 打开 需手动删除 返回 fd 安全问题:TOCTOU · 权限泄露 · 残留文件 最佳实践:优先用 tmpfile · 安全场景用 mkstemp · 严格 umask

嗯,临时文件这块内容其实不难,但细节决定成败。我个人建议:能用 tmpfile 就用 tmpfile,省心又安全。如果非要文件名不可,那就上 mkstemp,别碰 tmpnam。至于权限问题,养成设置 umask 的习惯,能省掉很多麻烦。

一句话总结:临时文件的核心就三个字——「用完扔」。但怎么扔得干净、扔得安全,才是真功夫。

公众号:蓝海资料掘金营,微信deep3321