Monday, September 1, 2014

Unix Prog: Temporary Files

1. tmpnam, tmpfile

tmpnam is used to return a unique temporary file string, which is normally used to create temporary file. tmpfile system call is used to create one temporary file, which will be removed when the process is terminated or the file stream is closed.

Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdio.h  
 ......  
 /* Generate a temporary filename. */  
 extern char *tmpnam (char *__s) __THROW __wur;  
 ......  
 /* Create a temporary file and open it read/write.  
   
   This function is a possible cancellation point and therefore not  
   marked with __THROW. */  
 #ifndef __USE_FILE_OFFSET64  
 extern FILE *tmpfile (void) __wur;  
 ......  

fileio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  char *pn = tmpnam(NULL);  
  printf("pn pointer: %p file name: %s\n", pn, pn);  
   
  // Create another temporary name while giving the NULL as parameter  
  // It will create another temporary name at the same address(static address)  
  // which means it overwrites the previous name  
  printf("create another temporary name:\n");  
  pn = tmpnam(NULL);  
  printf("pn pointer: %p file name: %s\n", pn, pn);  
   
  // Provide one buffer address to tmpnam, and tmpnam will populate the name  
  // to this buffer place.  
  // The buffer length is at least "L_tmpnam", which is defined at stdio.h  
  // tmpnam function is called at most TMP_MAX times, which is defined at stdio.h  
  char tn[L_tmpnam];  
  tmpnam(tn);  
  printf("tn pointer: %p file name: %s\n", tn, tn);  
   
  // Create one temporary file, write "Hello world!" into it  
  // Rewind to the beginning of the file, read the line out and output to  
  // standard output.  
  FILE *fp;  
  if((fp = tmpfile()) == NULL) {  
   printf("tmpfile error!\n");  
   exit(1);  
  }  
   
  fputs("Hello world!", fp);  
  char line[13];  
  rewind(fp);  
  if(fgets(line, sizeof(line), fp) == NULL) {  
   printf("fgets error!\n");  
   exit(2);  
  }  
  printf("%s\n", line);  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./io.out  
 pn pointer: 0x7f70bf6ee930 file name: /tmp/filecqrV8F  
 create another temporary name:  
 pn pointer: 0x7f70bf6ee930 file name: /tmp/fileCMeZ3T  
 tn pointer: 0x7fff91cc5e60 file name: /tmp/fileDxO3Y7  
 Hello world!  

2. tempnam
tempnam is a variation of tmpnam system call. It provides another way to generate one unique temporary name.

Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdio.h  
 ......  
 /* Default path prefix for `tempnam' and `tmpnam'. */  
 # define P_tmpdir    "/tmp"  
 ......  
 /* Generate a unique temporary filename using up to five characters of PFX  
   if it is not NULL. The directory to put this file in is searched for  
   as follows: First the environment variable "TMPDIR" is checked.  
   If it contains the name of a writable directory, that directory is used.  
   If not and if DIR is not NULL, that value is checked. If that fails,  
   P_tmpdir is tried and finally "/tmp". The storage for the filename  
   is allocated by `malloc'. */  
 extern char *tempnam (const char *__dir, const char *__pfx)  
    __THROW __attribute_malloc__ __wur;  
 .....  

fileio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  char *pn;  
  // tempnam is using malloc to return the unique temporary name string, so we have to  
  // free that part of memory to avoid memory leak  
   
  // The order of file path determination:  
  // 1. If TMPDIR envrionment variable is set, then use that value as directory name  
  // 2. if first argument is not NULL, use that value as directory name  
  // 3. If P_tmpdir is set, then use that value as directory name  
  // 4. Lastly if all above conditions are not met, use /tmp  
   
  // If second argument is not NULL, provide at most 5 bytes as the prefix of name  
  pn = tempnam(argv[1][0] != ' ' ? argv[1]:NULL, argv[2][0] != ' '?argv[2]: NULL);  
  printf("%s\n", pn);  
  free(pn);  
  exit(0);  
 }  


shell:
1) Although we provide 8 'X' as prefix, but, only first 5 bytes are used
2) We provide one non-exist place for path name, it will detect that this place doesn't exist, then fall to step 3, use P_tmpdir as path name, which is "/tmp" in current system.
3) We provide NULL to the path name argument, and then system will fall to step 3 to use P_tmpdir as pathname
4) Setup TMPDIR, it will then use TMPDIR value "/dev" as the pathname.
5) Setup TMPDIR to a non-exist place, and also self-provided argument is NULL, so it will fall to step 3 to use P_tmpdir as the path name.
 ubuntu@ip-172-31-23-227:~$ ./io.out /bin XXXXXXXX  
 /bin/XXXXX2QQ1qE  
 ubuntu@ip-172-31-23-227:~$ ./io.out /bin/noexist XXXXX  
 /tmp/XXXXXeRnepO  
 ubuntu@ip-172-31-23-227:~$ ./io.out " " XXXXX  
 /tmp/XXXXXMfsRxs  
 ubuntu@ip-172-31-23-227:~$ TMPDIR=/dev ./io.out /bin XXXXX  
 /dev/XXXXXWO5xwQ  
 ubuntu@ip-172-31-23-227:~$ TMPDIR=/noexist ./io.out " " XXXXX  
 /tmp/XXXXXogD6BK  

3. mkstemp

mkstemp is another way to create temporary file with given template path name string.

definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/stdlib.h  
 ......  
 /* Generate a unique temporary file name from TEMPLATE.  
   The last six characters of TEMPLATE must be "XXXXXX";  
   they are replaced with a string that makes the filename unique.  
   Returns a file descriptor open on the file for reading and writing,  
   or -1 if it cannot create a uniquely-named file.  
   
   This function is a possible cancellation point and therefore not  
   marked with __THROW. */  
 # ifndef __USE_FILE_OFFSET64  
 extern int mkstemp (char *__template) __nonnull ((1)) __wur;  
 ......  

fileio.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<string.h>  
   
 int main(int argc, char* argv[])  
 {  
  char pn[10];  
  int fd;  
   
  // provided template string's last 6 characters have to be "XXXXXX"  
  // mkstemp will change last 6 characters to make the string unique  
  strcpy(pn, "TMPXXXXXX");  
  if((fd = mkstemp(pn)) == -1) {  
   printf("mkstemp error!\n");  
   exit(1);  
  }  
   
  printf("pn will be changed to: %s\n", pn);  
  close(fd);  
   
  // The temporary file created by mkstemp will not be removed  
  // We have to unlink it by ourselves  
  unlink(pn);  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./io.out  
 pn will be changed to: TMPZnhreZ  

Note:
If we use tmpnam or tempnam to create temporary file string, and then use that string to create temporary file manually, there is a small time window between, during which other process may create the temp file with same name. In practice, we normally use tmpfile and mkstemp to create temp file directly.

No comments:

Post a Comment