user3493202
user3493202

Reputation:

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

In the following code, I need a unique filename, do some stuff with it, and let it be. It is about converting a .class file to binary, let us call it compilation.

It works perfectly when run in isolation or done 3 times at a time; however, I run into issues when I start up many multiple processes (e.g., 7) where one or more of my compilations fail.

This is the code:

#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;
}

After looking around, people recommend using mkstemp(); however, as you can see, I actually do need the filename in several places.

I tried adjusting this but keep running into errors. How can I safely adjust this work method?

Upvotes: 1

Views: 209

Answers (1)

Mathieu Border&#233;
Mathieu Border&#233;

Reputation: 4367

From the manpage for 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.

so if you need the filename, you can find it in the template argument passed to mkstemp.

Upvotes: 4

Related Questions