5.12临时文件

5.12临时文件

ISO C标准I/O提供了两个函数以帮助创建临时文件。

#include <stdio.h>

char*tmpnam(char*ptr);//返回指向唯一路径名的指针;

FILE*tmpfile(void);//成功则返回文件指针;失败返回NULL;

tmpnam产生一个与现在文件名不同的一个有效路径名字符串。每次调用它时,它都产生一个不同的路径名,最多调用次数是TMP_MAX238328TMP_MAX定义在<stdio.h>中。有时候在程序运行的时候,可能需要创建一个临时文件,保存一些数据,以后再用,在程序退出时希望这些文件能够被自动删除。而创建的时候有希望一次创建成功,不会覆盖可能重名的文件,这时需要使用临时文件。该函数的功能是产生一个唯一的文件名,结果存放在数组sptr中,该函数的主要用途是生成与目录中现有文件名不同的有效路径名字符串,其中sptr中保存了所产生的文件名。

ptrNULL,则所产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。下一次再调用tmpnam时,会重写该静态区。(这意味着,如果我们调用此函数多次,而且想保存路径名,则我们应当保存该路径名的副本,而不是指针的副本。)如若ptrNULL,则认为它指向长度至少是L_tmpnam20个字符的数组。(常数L_tmpnam定义在头文件<stdio.h>中。)所产生的路径名存放在该数组中,ptr也作为函数值返回。

tmpfile创建一个临时二进制文件(类型wb+),在关闭该文件或程序结束时将自动删除这种文件。注意,UNIX对二进制文件不作特殊区分。

下述程序演示了两个函数的应用

#include <stdio.h>

#include <stdlib.h>



#define MAXLINE 1024



int main(void)

{

char name[L_tmpnam],line[MAXLINE];

FILE *fp;



printf("%s\n",tmpnam(NULL)); //first temp name

tmpnam(NULL); //second temp name



printf("%s\n",name);



if((fp = tmpfile()) == NULL) //create temp file

perror("tmpfile");

fputs("one line of output\n",fp); //write to temp file

rewind(fp); //then read it back

if(fgets(line,sizeof(line),fp) == NULL)

perror("fgets");

fputs(line,stdout); //print the line we write

exit(0);

}


该程序在编译的时候会有警告:warning: the use of `tmpnam' is dangerous, better use `mkstemp'

`tmpnam' is dangerous,原因:

l   Race conditions: tmpnam() generates a file name that is not in use at the moment of the call, but there's no guarantee that some other program might not create such a file two nanoseconds later, before you get a chance to use the name tmpnam() built for you.

l   Security holes: It's at least conceivable that the race condition mentioned above could be exploited as part of a penetration of privilege barriers.

l   Disk pollution: When you create a file using the name tmpnam() gave you, you must remember to remove() it when you're through (assuming you want it to be temporary). If your program crashes or is stopped by ^C or some such and you don't remove() the file, it will hang around on the disk and take up space. This could become troublesome, especially if the "temporary" files tend to be large.

l   mktemp() is dangerous, since it allows an attacker to guess names.

一个mkstemp的例子为

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

int main(void)

{

FILE *pfile, *pfile2;

int fileSize, readSize;

char *fileBuff = NULL;

char buff[128] = { 0 };

char test[100] = "abcdefghijklmn";

/****************(1) tmpfile() *****************/

pfile2 = tmpfile();

if (pfile2 == NULL) {

fputs("creat temp file error", stderr);

exit(1);

}

if (EOF == fputs("tmpfile function create me !", pfile2)) {

fputs("write err.\n", stderr);

exit(1);

}

rewind(pfile2); // positions the stream stream at the beginning of the file

fgets(buff, sizeof(buff), pfile2);

puts(buff);





/********************(2) tmpnam() + unlink() *****************/

memset(buff, 0, sizeof(buff));

puts(tmpnam(buff));



/*************read or write file***************/

pfile = fopen(buff, "a+");

unlink(buff);

if (pfile == NULL) {

fputs("open error", stderr);

exit(1);

}

if (sizeof(test) !=

fwrite(test, sizeof(char),

sizeof(test) / sizeof(char), pfile)) {

fputs("write error", stderr);

exit(1);

}

//get the file size

fseek(pfile, 0, SEEK_END);

fileSize = ftell(pfile);

rewind(pfile);



printf("filesize:%d\n", fileSize);

fileBuff = (char *) malloc(sizeof(char) * fileSize);

if (fileSize !=

(readSize = fread(fileBuff, sizeof(char), fileSize, pfile))) {

fputs("Read error", stderr);

exit(3);

} else {

printf("readContent: %s \nreadsize:%d\n", fileBuff,

readSize);

}

fclose(pfile);

free(fileBuff);



/********************(3) fopen() and unlink() **************/

pfile = NULL;

pfile = fopen("/tmp/tempA.txt", "w+");

if (pfile == NULL) {

fputs("create file failed.", stderr);

exit(1);

}

fputs("fopen create me", pfile);

rewind(pfile);

if (fgets(buff, sizeof(buff), pfile) == NULL) {

fputs("read file error.\n", stderr);

exit(1);

}

fputs(buff, stdout);

fclose(pfile);

unlink("/tmp/tempA.txt");

puts("\nDONE.......\n");

return EXIT_SUCCESS;

}


关于mkstemp函数

int mkstemp(char *template);

mkstemp函数在系统中以唯一的文件名创建一个文件并打开,而且只有当前用户才能访问这个临时文件,并进行读、写操作(这个就确保文件比较安全)。

建立唯一临时文件名, template须以数组形式声明而非指针形式.

template格式为: template.XXXXXX. 最后6位必须为XXXXXX, 前缀随意

函数返回一个文件描述符,如果执行失败返回-1
glibc 2.0.6 以及更早的glibc库中这个文件的访问权限是0666glibc 2.0.7以后的库这个文件的访问权限是0600
由于mkstemp函数创建的临时文件不能自动删除,所以
执行完mkstemp函数后要调用unlink函数unlink函数删除文件的目录入口,但临时文件还可以通过文件描述符进行访问,直到最后一个打开的进程关闭文件操作符,或者程序退出后临时文件被自动彻底地删除。
下面是一个使用mkstemp的例子:

#include <stdio.h>

#include <stdlib.h>



int main(void)

{

int fd;

char temp_file[] = "tmp_XXXXXX";

/*Creat a temp file.*/

if ((fd = mkstemp(temp_file)) == -1) {

printf("Creat temp file faile./n");

exit(1);

}

/*Unlink the temp file.*/

unlink(temp_file);

close(fd);

}


tmpfile函数经常使用的标准UNIX技术是先调用tmpnam产生一个唯一的路径名,然后立即unlink它。

Single UNIX Specification为处理临时文件定义了另外两个函数,它们是XSI的扩展部分,其中第一个是tempnam函数:

#include <stdio.h>

char* tempnam(const char*directory, const char*prefix);返回值为指向唯一路径名的指针;

tempnamtmpnam的一个变体,它允许调用者为所产生的路径名指定目录和前缀。对于目录有四种不同的选择,按下列顺序判断条件是否为真,并且使用第一个为真的作为目录:

(1) 如果定义了环境变量TMPDIR,则用其作为目录。

(2) 如果参数directoryNULL,则用其作为目录。

(3) <stdio.h>中的字符串P_tmpdir用作为目录。

(4) 将本地目录,通常是/tmp,用作为目录。

如果prefixNULL,则它应该是最多包含5个字符的字符串,用其作为文件名的头几个字符。该函数调用malloc函数分配动态存储区,用其存放所构造的路径名。当不再使用此路径名时就可释放此存储区。

tempnam函数的应用

#include <stdio.h>

#include <stdlib.h>



int main(int argc, char *argv[])

{

if (argc != 3)

perror("usage : a.out <directory> <prefix>");

printf("%s\n",

tempnam(argv[1][0] != ' ' ? argv[1] : NULL,

argv[2][0] != ' ' ? argv[2] : NULL));

exit(0);

}


XSI定义的第二个函数是mkstemp。它类似于tmpfile,但是该函数返回的不是文件指针,而是临时文件的打开文件描述符。

#include <stdlib.h>

int mkstemp(char* template);

返回值:成功返回文件描述符,失败返回-1

它所返回的文件描述符可用于读、写该文件。临时文件的名字是用template字符串参数选择的。该字符串是一个路径名,其最后6个字符设置为XXXXXX。该函数用不同字符代替XXXXXX,以创建一个唯一路径名。若mkstemp成功返回,它就会修改template字符串以反映临时文件的名字。

tmpfile不同的是,mkstemp创建的临时文件不会自动删除,它需要我们自行unlink

使用tmpfiletempnam的不足之处:在返回唯一路径名和应用程序用该路径名创建文件之间有一个时间窗口。在该时间窗口期间,另一个进程可能创建一个同名文件。tmpfilemkstemp函数则不会产生此种问题。可以使用它们代替tmpnamtempname

posted @ 2011-10-09 10:27  郭—大—侠  阅读(1935)  评论(0编辑  收藏  举报