DI Management Home > C Programming > An all-singing, all-dancing C function to create a temporary file

An all-singing, all-dancing C function to create a temporary file


This page gives some C source code to create a temporary file avoiding the problems with existing standard ANSI and POSIX functions. We explain what we wanted, review the existing standard functions, and give our source code with examples. Please feel free to use it in your own applications. Version 2 is released under an MPL v2.0 license. If you think it is wrong or could be improved, please tell us at the contact link below.

updated1 June 2016: Released version 2.0.0 with an extra function to receive the filename in a fixed-length buffer with no need to free allocated memory afterwards.

What we wanted

We wanted a C function that would create a temporary file with the following properties:

Existing functions

The existing ANSI and POSIX temporary file functions all have a problem.

FunctionDescriptionProblems`tmpfileplus` equivalent
FILE *tmpfile(void) Returns a pointer to the stream of a temporary file with a guaranteed non-conflicting name that will be removed when the program closes it. User has no control over where the file is created and does not know the filename. FILE *fp;
fp=tmpfileplus(NULL,NULL,NULL,0);
char *tmpnam(char *s) Creates a unique filename and returns a pointer to the file, which is created in the P_tmpdir directory, defined in stdio.h. If s is non-NULL, the file name is copied to the buffer it references. Otherwise, the file name is copied to a static buffer. Race condition exists between selecting file name and creating it. The P_tmpdir directory may be insecure (in MSVC it is "\", which is useless!). Potential for overflow error if buffer s is too short. The number of unique filenames is limited to a small range. char *s;
fp=tmpfileplus(NULL,NULL,&s,0);
/*...*/
free(s);
char *tempnam(const char *dir, const char *pfx) Similar to tmpnam(), but the user can specify the directory which will contain the temporary file and the file name prefix. Returns a pointer to a file name in allocated memory that the user should free later. Race condition exists between selecting file name and creating it. char *dir="/mypath/mysub";
char *pfx="myprefix";
char *s;
fp=tmpfileplus(dir,pfx,&s,0);
char *mktemp(char *template) Takes the given file name template and overwrites a portion of it to create a file name. template may contain a directory path and requires a number of `X's at the end, e.g. /tmp/temp.XXXXXX. The `X' will be replaced by some unique letter combination. Returns a pointer to a string in allocated memory. Race condition exists between selecting file name and creating it. Number of `X's in template may be limited to six. fp=tmpfileplus(dir,pfx,&s,0);
int mkstemp(char *template) Similar to mktemp but returns a file descriptor opened for reading and writing. User does not know the filename. Non-ANSI file descriptor. Not available in MSVC. fp=tmpfileplus(dir,pfx,NULL,0);

Our solution

 NAME
        tmpfileplus - create a unique temporary file
 
 SYNOPSIS
        FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep)

 DESCRIPTION
        The tmpfileplus() function opens a unique temporary file in binary
        read/write (w+b) mode. The file is opened with the O_EXCL flag,
        guaranteeing that the caller is the only user. The filename will consist
        of the string given by `prefix` followed by 10 random characters. If
        `prefix` is NULL, then the string "tmp." will be used instead. The file
        will be created in an appropriate directory chosen by the first
        successful attempt in the following sequence:
        
        a) The directory given by the `dir` argument (so the caller can specify
        a secure directory to take precedence).
        
        b) The directory name in the environment variables:
        
          (i)   "TMP" [Windows only]   
          (ii)  "TEMP" [Windows only]   
          (iii) "TMPDIR" [Unix only]
        
        c) `P_tmpdir` as defined in <stdio.h> [Unix only] (in Windows, this is
        usually "\", which is no good).
        
        d) The current working directory.
        
        If a file cannot be created in any of the above directories, then the
        function fails and NULL is returned. 
        
        If the argument `pathname` is not a null pointer, then it will point to
        the full pathname of the file. The pathname is allocated using `malloc`
        and therefore should be freed by `free`.
        
        If `keep` is nonzero and `pathname` is not a null pointer, then the file
        will be kept after it is closed. Otherwise the file will be
        automatically deleted when it is closed or the program terminates. 


 RETURN VALUE
        The tmpfileplus() function returns a pointer to the open file stream, 
        or NULL if a unique file cannot be opened.


 ERRORS
        ENOMEM Not enough memory to allocate filename.

ADDED IN v2.0:

NAME
       tmpfileplus_f - create a unique temporary file with filename stored in a fixed-length buffer

SYNOPSIS
       FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep);

DESCRIPTION
       Same as tmpfileplus() except receives filename in a fixed-length buffer. No allocated memory to free.

ERRORS
       E2BIG Resulting filename is too big for the buffer `pathnamebuf`.

Source code

Zipped source files: tmpfileplus-2.0.0.zip (6.2 kB) [md5=0f8aa59b51ab4d26d4e5c351dfb99d4f]

Examples

  FILE *fp;
  char *pathname;
  int keep;

  /* Test with no pathname, like tmpfile() */
  fp = tmpfileplus(NULL, NULL, NULL, 0);
  /* ... */ 
  fclose(fp);

  /* Test with a pathname */
  fp = tmpfileplus(NULL, NULL, &pathname, 0);
  /* ... */ 
  fclose(fp);
  if (pathname) free(pathname);

  /* Test with a given prefix */
  fp = tmpfileplus(NULL, "THIS-", &pathname, 0);
  /* ... */ 
  fclose(fp);
  if (pathname) free(pathname);

  /* Test with a specified directory 
     -- directory "C:\Test" must exist 
     -- otherwise default is used */
  fp = tmpfileplus("C:\Test", "THAT~", &pathname, 0);
  /* ... */ 
  fclose(fp);
  if (pathname) free(pathname);

  /* Test with a specified directory but keep the file */
  keep = 1;
  fp = tmpfileplus("C:\Test", NULL, &pathname, keep);
  /* ... */ 
  fclose(fp);
  if (pathname) free(pathname);

Sample output of test code: on Windows and on Linux.

Programmer's notes

Random characters and security
We reckon that using the stdlib rand() is sufficient here provided you keep control of the seed. And we reckon that ten random characters is sufficient, and that's more to prevent name collisions. An adversary who wants to access your temp file can simply watch the temp directory for a new file that appears at the same time as you make the call, and can act on that. What's more important is that file is locked for exclusive use and there's no opportunity for a race condition to occur between checking the name is free and creating the file.

Anyway, feel free to change the code for the random characters and use, say, SHA-256 to digest input from your geiger counter and lump of Uranium-235 to get truly random input. You are probably better off making sure the destination directory is secured properly.

Deleting the file after use
On Linux you can simply unlink the file immediately after creating it and it will be deleted once closed. Windows will not let you do that: you cannot unlink an open file. So we use the _O_TEMPORARY flag when opening the file, instead.

Contact us

To comment on this page or to contact us, please send us a message.

This page first published 16 May 2013. Last updated 6 June 2016.