Mail Archives: djgpp-workers/1998/03/08/07:46:06
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 -