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

How to I make a temporary filename that's safe for concurrent execution?

在下面的代码中,我需要一个唯一的文件名,用它做一些事情,让它成为现实。它是关于将 .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();但是,如您所见,我实际上确实在几个地方需要文件名。

我尝试对此进行调整,但一直 运行 出错。我怎样才能安全地调整这种工作方式?

来自 mkstemp

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

The mkstemp() function generates a unique temporary filename from template, creates and opens the file, and returns an open file descriptor for the file. The last six characters of template must be "XXXXXX" and these are replaced with a string that makes the filename unique. Since it will be modified, template must not be a string constant, but should be declared as a character array. The file is created with permissions 0600, that is, read plus write for owner only. The returned file descriptor provides both read and write access to the file. The file is opened with the open(2) O_EXCL flag, guaranteeing that the caller is the process that creates the file.

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