Why can I not create a temporary file using a dynamically allocated string?

614 Views Asked by At

I am trying to create a directory with log entries with mkstemp. But, to my understanding I mustn't pass a string constant to mkstemp. I allocate memory for the string and use snprintf to format the output which I thought was going to work but mkstemp returns a negative value setting errno to EINVAL.

However, in the linux manual for mkstemp it clearly says:

EINVAL For mkstemp() and mkostemp(): The last six characters of template were not XXXXXX; now template is unchanged.

Furhtermore mkstemp never modifies my dynamic string.

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

#define LOG_DIR "/tmp"

int main(int argc, char **argv) {
  char *fname;
  FILE *fp;

  if(argc != 3) {
    fprintf(stderr, "Usage: %s <msg> <severity>\n", argv[0]);
    return 0;
  }

  int length = snprintf(NULL, 0, "%s/log_entry.XXXXXX", LOG_DIR); // snprintf returns the required length for my string
  if(length < 0) {
    perror("snprintf failed");
    return 1;
  }
  fname = malloc(sizeof(char) * length); // allocate memory for fname based on the return value of snprintf
  snprintf(fname, length, "%s/log_entry.XXXXXX", LOG_DIR); // send formatted output into fname

  int fd = mkstemp(fname); // this returns -1 and errno is set to 22
  if(fd < 0) {
    perror("failed to create entry file");
    return 1;
  }
  fp = fdopen(fd, "w");
  if(fp == NULL) {
    perror("failed to open entry file");
    return 1;
  }
  fprintf(fp, "\"%s\" %d ",argv[1], atoi(argv[2]));
  fflush(fp);
  fclose(fp);
  free(fname);
  return 0;
}

This snippet spits out an error on both of my Linux machines however if I remove the dynamic allocated string and explicitly set fname it works

char fname[] = "/tmp/log_entry.XXXXXX";
2

There are 2 best solutions below

8
On BEST ANSWER
fname = malloc(sizeof(char) * length);

should be:

fname = malloc(sizeof(char) * (length + 1)); 

Now

snprintf(fname, length+1, "%s/log_entry.XXXXXX", LOG_DIR); 

will create the filename. In your version, the filename did not end with 6 'X's which made mkstemp fail.

1
On
 fname = malloc(sizeof(char) * length); 

You fill it completely , leave no space for '\0' . :eave a space for null terminator -

 fname = malloc(sizeof(char) *(length+1)); 

Then increase length in snprintf also to length+1 .