delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1998/03/08/07:46:06

Date: Sun, 8 Mar 1998 14:42:43 +0200 (IST)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
To: DJ Delorie <dj AT delorie DOT com>
cc: djgpp-workers AT delorie DOT com
Subject: Temporary files considered unsafe
Message-ID: <Pine.SUN.3.91.980308143758.22686h-100000@is>
MIME-Version: 1.0

A discussion on gnu.utils.bug caused me to look at how safe are our
temporary files in a multi-processing environment (e.g., Windows or when
the temporray directory is shared over a network).  It turns out they are
unsafe.  (Maybe that's the reason we cannot compile in two different DOS
boxes at the same time?)

Here's a new function `_creatnew', and based on it, a change in `tmpfile'
to make it safe.  (Nate, please note that Borland has `creatnew' with 
similar but not identical interface.)

*** /dev/null	Fri Mar  6 19:23:35 1998
--- src/libc/dos/io/_creat_n.c	Fri Mar  6 19:22:54 1998
***************
*** 0 ****
--- 1,59 ----
+ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
+ #include <libc/stubs.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <go32.h>
+ #include <dpmi.h>
+ #include <io.h>
+ #include <dos.h>
+ #include <libc/dosio.h>
+ #include <sys/fsext.h>
+ 
+ int
+ _creatnew(const char* filename, int attrib, int flags)
+ {
+   __dpmi_regs r;
+   int rv;
+   unsigned use_lfn = _USE_LFN;
+ 
+   if (filename == 0)
+   {
+     errno = EINVAL;
+     return -1;
+   }
+ 
+   if (__FSEXT_call_open_handlers(__FSEXT_creat, &rv, &filename))
+     return rv;
+ 
+   _put_path(filename);
+   r.x.bx =
+     0x2002 | (flags & 0xfff0);	/* r/w, no Int 24h, use caller-defined flags */
+   r.x.dx = 0x0010;		/* Create, fail if exists */
+   r.x.si = __tb_offset;
+   if(use_lfn)
+     r.x.ax = 0x716c;
+   else
+   {
+     if (_osmajor == 0)
+       _get_dos_version (0);
+     if (_osmajor > 3)
+       r.x.ax = 0x6c00;
+     else
+     {
+       r.h.ah = 0x5b;
+       r.x.bx = 0;		/* lose support for fancy flags in DOS 3.x */
+       r.x.dx = __tb_offset;
+       r.x.si = 0;
+     }
+   }
+   r.x.cx = attrib & 0xffff;
+   r.x.ds = __tb_segment;
+   __dpmi_int(0x21, &r);
+   if(r.x.flags & 1)
+   {
+     errno = __doserr_to_errno(r.x.ax);
+     return -1;
+   }
+   __file_handle_set(r.x.ax, O_BINARY);
+   return r.x.ax;
+ }
*** /dev/null	Fri Mar  6 19:26:15 1998
--- src/libc/dos/io/_creat_n.txh	Fri Mar  6 19:25:58 1998
***************
*** 0 ****
--- 1,83 ----
+ @node _creatnew, file system
+ @subheading Syntax
+ 
+ @example
+ #include <fcntl.h>
+ #include <dir.h>
+ #include <io.h>
+ 
+ int _creatnew(const char *path, int attrib, int flags);
+ @end example
+ 
+ @subheading Description
+ 
+ This function creates a file given by @var{path} and opens it, like
+ @code{_creat} does, but only if it didn't already exist.  If the named
+ file exists, @code{_creatnew} fails.  (In contrast, @code{_creat} opens
+ existing files and overwrites their contents, see @ref{_creat}.)
+ 
+ The attributes of the created file are determined by @var{attrib}.  The
+ file is usually given the normal attribute (00H).  If @var{attrib} is
+ non-zero, additional attributes will be set.  The following macros,
+ defined on @code{<dir.h>}, can be used to control the attributes of the
+ created file (the associated numeric values appear in parentheses):
+ 
+ @table @code
+ @item FA_RDONLY (1)
+ The file is created with the read-only bit set.
+ 
+ @item FA_HIDDEN (2)
+ The file is created with the hidden bit set.  Such files will not appear
+ in directory listings unless you use special options to the commands
+ which list files.
+ 
+ @item FA_SYSTEM (4)
+ The file is created with the system bit set.  Such files will not appear
+ in directory listings unless you use special options to the commands
+ which list files.
+ @end table
+ 
+ Other bits (@code{FA_LABEL} and @code{FA_DIREC}) are ignored by DOS.
+ 
+ The argument @var{flags} controls the sharing mode and the fine details
+ of how the file is handled by the operating system.  The following
+ macros, defined on @code{<fcntl.h>}, can be used for this (associated
+ numeric values are given in parentheses):
+ 
+ @table @code
+ @item SH_COMPAT (00h)
+ Opens the file in compatibility mode, which allows any other process to
+ open the file and read from the file any number of times.
+ 
+ @item SH_DENYRW (10h)
+ Denies both read and write access by other processes.
+ 
+ @item SH_DENYWR (20h)
+ Denies write access by other processes.
+ 
+ @item SH_DENYRD (30h)
+ Denies read access by other processes.
+ 
+ @item SH_DENYNO (40h)
+ Allows read and write access by other processes, but prevents other
+ processes from opening the file in compatibility mode.
+ @end table
+ 
+ Note that the file is always open for both reading and writing;
+ @code{_creatnew} ignores any bits in the lower nibble of @var{flags}
+ (@code{O_RDONLY}, @code{O_WRONLY}, etc.).
+ 
+ @code{_creatnew} calls DOS function 716Ch when long file names are
+ supported, 6C00h otherwise.  (On DOS version 3.x, function 5B00h is
+ called which ignores the value of @var{flags}, since function 6C00h is
+ only supported by DOS 4.0 and later.)
+ 
+ The file handle returned by @code{_creatnew} is set to binary mode. 
+ 
+ This function can be hooked by the Filesystem Extensions handlers, as
+ described in @ref{File System Extensions}.  If you don't want this, you
+ should use @code{_dos_creatnew} (@pxref{_dos_creatnew}) instead.
+ 
+ @subheading Return Value
+ 
+ The new file descriptor, else -1 on error.
*** src/libc/dos/io/makefile.~0	Fri Sep 20 02:38:56 1996
--- src/libc/dos/io/makefile	Fri Mar  6 14:31:12 1998
*************** TOP=../..
*** 4,9 ****
--- 4,10 ----
  SRC += _chmod.c
  SRC += _close.c
  SRC += _creat.c
+ SRC += _creat_n.c
  SRC += _open.c
  SRC += _read.c
  SRC += _write.c
*** include/io.h~0	Fri Sep 20 02:38:56 1996
--- include/io.h	Fri Mar  6 15:46:40 1998
***************
*** 17,22 ****
--- 17,23 ----
  int		chsize(int handle, long size);
  int		_close(int _fd);
  int		_creat(const char *_path, int _attrib);
+ int		_creatnew(const char *_path, int _attrib, int _mode);
  ssize_t		crlf2nl(char *_buffer, ssize_t _length);
  int		_dos_lock(int _fd, long _offset, long _length);
  long		filelength(int _handle);
*** src/libc/ansi/stdio/tmpfile.c~0	Sun Aug 31 18:36:10 1997
--- src/libc/ansi/stdio/tmpfile.c	Fri Mar  6 15:38:10 1998
***************
*** 1,32 ****
  /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  #include <libc/stubs.h>
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <libc/file.h>
  
  FILE *
  tmpfile(void)
  {
    FILE *f;
!   char *temp_name;
!   char *n_t_r;
  
!   temp_name = tmpnam(0);
!   if (temp_name == 0)
      return 0;
  
!   n_t_r = (char *)malloc(strlen(temp_name)+1);
!   if (!n_t_r)
      return 0;
  
!   f = fopen(temp_name, "wb+");
    if (f)
    {
!     f->_flag |= _IORMONCL;
      f->_name_to_remove = n_t_r;
      strcpy(f->_name_to_remove, temp_name);
    }
    return f;
  }
--- 1,58 ----
+ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  #include <libc/stubs.h>
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include <fcntl.h>
+ #include <unistd.h>
+ #include <io.h>
  #include <libc/file.h>
+ #include <libc/local.h>
  
  FILE *
  tmpfile(void)
  {
+   int  temp_fd;
    FILE *f;
!   char *temp_name = tmpnam(0);
!   char *n_t_r = (char *)malloc(L_tmpnam);
  
!   if (!n_t_r)
      return 0;
  
!   /* We could have a race condition, whereby another program
!      (in another virtual machine, or if the temporary file is
!      in a directory which is shared via a network) opens the
!      file returned by `tmpnam' between the call above and the
!      moment when we actually open the file below.  This loop
!      retries the call to `tmpnam' until we actually succeed
!      to create the file which didn't exist before.  */
!   do
!     temp_fd = _creatnew(temp_name, 0, SH_DENYRW);
!   while (temp_fd == -1 && (temp_name = tmpnam(0)) != 0);
! 
!   if (temp_name == 0)
      return 0;
  
!   /* This should have been fdopen(temp_fd, "wb+"), but `fdopen'
!      is non-ANSI.  So we need to dump some of its guts here.  Sigh...  */
!   f = __alloc_file();
    if (f)
    {
!     f->_file   = temp_fd;
!     f->_cnt    = 0;
!     f->_bufsiz = 0;
!     f->_flag   = _IORMONCL | _IORW;
      f->_name_to_remove = n_t_r;
      strcpy(f->_name_to_remove, temp_name);
+     f->_base = f->_ptr = NULL;
+   }
+   else
+   {
+     close(temp_fd);
+     remove(temp_name);
+     free(n_t_r);
    }
    return f;
  }
*** src/docs/kb/wc202.t~3	Sun Feb  1 00:24:04 1998
--- src/docs/kb/wc202.txi	Fri Mar  6 19:31:56 1998
***************
*** 326,328 ****
--- 326,342 ----
  
  The new functions @code{basename} and @code{dirname} can be used to
  extract directory and basename parts from file names.
+ @findex basename
+ @findex dirname
+ 
+ The new function @code{_creatnew} creates a file only if it didn't
+ already exist.
+ @findex _creatnew
+ 
+ @code{tmpfile} makes sure it opens a file which is not and will not be
+ used by any other program.  It does so by repeatedly calling
+ @code{tmpnam} until @code{_creatnew} succeeds to create a file which
+ didn't exist before.  The temporary file is opened in DENY_ALL mode, so
+ that no other process can neither read from nor write to it.  In case of
+ a failure, @code{tmpfile} does not leak memory anymore.
+ @findex tmpfile


- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019