如何创建一个对并发执行安全的临时文件名?

时间:2017-04-04 07:30:06

标签: c linux temporary-files

在下面的代码中,我需要一个唯一的文件名,用它来做一些事情,并让它成为。它是关于将.class文件转换为二进制文件,让我们将其称为编译。

在隔离运行或一次完成3次时效果很好;但是,当我启动许多多个进程(例如7)时,我遇到了一些问题,其中我的一个或多个编译失败了。

这是代码:

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

static unsigned int numFiles = 0;
static unsigned long numBytes = 0;

FILE* rawf;
char* raw_file_name_end = ".raw_ujc";
char * rawfilename;

static void byte(unsigned char v){
    if(numBytes) printf(", ");

    printf((numBytes & 0x0F) ? "0x%02X" : "\n\t0x%02X", v);

    fwrite(&v,sizeof(v),1,rawf);

    numBytes++;
}

int main(int argc, char** argv){

    const char* self = argv[0];
    int c;
    const char* classCvt = 0;
    long len;

    if(argc == 1){

        fprintf(stderr, "USAGE: %s [-c <path_to_classCvt>] <file 1> [<file 2> [ <file 3> [...]]] > result.c\n", self);
        return -1;
    }

    argv++;
    argc--;

    if(argv[0][0] == '-' && argv[0][1] == 'c' && !argv[0][2]){

        classCvt = argv[1];
        argv += 2;
        argc -= 2;
    }

    printf("\nService optimized bytecode = {\n\t");

    while(argc--){
        char* filename = *argv;

        rawfilename = malloc(sizeof(char) * (strlen(filename)-strlen(".class")) + sizeof(char) * strlen(raw_file_name_end)+1);

        strncpy(rawfilename,filename,(strlen(filename)-strlen(".class")));
        strcat(rawfilename,raw_file_name_end);
        fprintf(stderr, "rawfilename after alloc: %s \n", rawfilename);

        if(classCvt){

            char* t;

            filename = tempnam(NULL, NULL);
            if(!filename){
                fprintf(stderr, "%s: failed to create a tempfile: %d\n", self, errno);
                return -10;
            }

            t = malloc(strlen(filename) + strlen(classCvt) + strlen(*argv) + 32);
            if(!t){
                fprintf(stderr, "%s: failed to alloc a small string. This is unlikely\n", self);
                free(t);
                return -11;
            }
            sprintf(t, "%s < %s > %s", classCvt, *argv, filename);

            if(system(t)){

                fprintf(stderr, "%s: system() fail: %d\n", self, errno);
                free(t);
                return -12;
            }
            free(t);
        }
        printf("filename is %s\n",filename);
        FILE* f = fopen(filename, "r");
        rawf = fopen(rawfilename, "wb");


        if(filename != *argv){
            unlink(filename);
            free(filename);
        }

        if(!f){
            fprintf(stderr, "%s: failed to open '%s': %d\n", self, *argv, errno);
            fclose(f);
            return -2;
        }
        if(!f){
            fprintf(stderr, "%s: failed to open '%s': %d\n", self, *argv, errno);
            fclose(f);
            return -2;
        }
        if(fseek(f, 0, SEEK_END)){
            fprintf(stderr, "%s: failed to seek(1) in '%s': %d\n", self, *argv, errno);
            fclose(f);
            return -3;
        }
        len = ftell(f);
        if(len < 0){
            fprintf(stderr, "%s: failed to tell in '%s': %d\n", self, *argv, errno);
            fclose(f);
            return -4;
        }
        if(fseek(f, 0, SEEK_SET)){
            fprintf(stderr, "%s: failed to seek(2) in '%s': %d\n", self, *argv, errno);
            fclose(f);
            return -5;
        }
        if(len > 0x00FFFFFFUL){
            fprintf(stderr, "%s:  file '%s' is %lu bytes, while maximum allowable size is %lu.\n", self, *argv, len, 0x00FFFFFFUL);
            fclose(f);
            return -6;
        }

        byte(len >> 16);
        byte(len >> 8);
        byte(len);

        while((c = fgetc(f)) != EOF){
            byte(c);
        }

        numFiles++;
        fclose(f);
        fclose(rawf);

        argv++;
    }

    byte(0);
    byte(0);
    byte(0);

    printf("\n};\n");

    fprintf(stderr, "%s: processed %u files, producing %lu (0x%lX) bytes of output\n", self, numFiles, numBytes, numBytes);
    fprintf(stderr, "rawfilename at end: %s \n", rawfilename);
    free(rawfilename);

    return 0;
}

环顾四周后,人们建议使用mkstemp();但是,正如您所看到的,我确实需要在几个地方使用文件名。

我试过调整这个,但一直遇到错误。我该如何安全地调整这种工作方法?

1 个答案:

答案 0 :(得分:4)

来自mkstemp

的联机帮助页
int mkstemp(char *template);
  

mkstemp()函数从模板生成唯一的临时文件名,创建并打开文件,并返回文件的打开文件描述符。          模板的最后六个字符必须是&#34; XXXXXX&#34;这些都是          替换为使文件名唯一的字符串。 既然会          被修改,模板不能是字符串常量,但应该是          声明为字符数组。          使用权限0600创建文件,即读取和写入          仅限所有者。返回的文件描述符同时提供read和          写访问该文件。使用open(2)O_EXCL打开该文件          flag,保证调用者是创建调用者的进程          文件。

因此,如果您需要文件名,可以在传递给mkstemp的template参数中找到它。