问题起源:
lighttpd 在接收到上传数据(PUT/POST),会生成大小为1M的很多临时文件。
线上最近有很多上传请求,lighttpd 经常报警,说上传的临时文件丢失,
分析
起初怀疑是被其它程序删除,后来勇哥提到,会不会是我们生成临时文件的方法有问题,生成了重名的临时文件?
查了lighttpd 的实现,使用的是mktemp函数:
http://linux.die.net/man/3/mktemp If the call was successful, the last six bytes of template will have been modified in such a way that the resulting name is unique (does not exist already). If the call was unsuccessful, template is made an empty string. Never use mktemp(). Some implementations follow 4.3BSD and replace XXXXXX by the current process ID and a single letter, so that at most 26 different names can be returned. The tempnam() function is re-entrant while mktemp() isn't 不可重入,可能两个进程产生同一个文件名 |
实验:
a.c |
#include<stdio.h> int main(){ int i = 0; for (i=0; i< 1024*1024*8; i++){ char s[200] = "/tmp/XXXXXX"; mktemp(s); printf("%s\n", s); } }~ |
ning@ning-baidu ~/idning/langtest/c/mktemp$ cc a.c
/tmp/ccXoEO8Z.o: In function `main':
a.c:(.text+0x63): warning: the use of `mktemp' is dangerous, better use `mkstemp' or `mkdtemp'
ning@ning-baidu ~/idning/langtest/c/mktemp$ ./a.out > /tmp/x
^C
ning@ning-baidu ~/idning/langtest/c/mktemp$ cat /tmp/x | sort | uniq -c | sort -nr | head
2 /tmp/ywysSJ
2 /tmp/YbfzNb
2 /tmp/wzJUMa
2 /tmp/WwwcEJ
2 /tmp/wV6oJe
2 /tmp/wjsmMR
2 /tmp/WIefPG
2 /tmp/wbOxzX
2 /tmp/UPUz9A
2 /tmp/uGJcIG
可以看出确实有重名文件生成。
lighttpd源码中使用了:
#define mkstemp(x) open(mktemp(x), O_RDWR)
解决
建议去掉该#define,直接使用可重入的 mkstemp ,它会返回:fd = __open (tmpl,
(flags & ~O_ACCMODE)
| O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
打开的文件
Warning: Between the time the pathname is constructed and the file is created another process might have created a file with the same name using mktemp , leading to a possible security hole. The implementation generates names which can hardly be predicted, but when opening the file you should use the O_EXCL flag. Using mkstemp is a safe way to avoid this problem.