172 lines
6.3 KiB
C
172 lines
6.3 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* Open Watcom Project
|
||
|
*
|
||
|
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original
|
||
|
* Code as defined in and that are subject to the Sybase Open Watcom
|
||
|
* Public License version 1.0 (the 'License'). You may not use this file
|
||
|
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
||
|
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
||
|
* provided with the Original Code and Modifications, and is also
|
||
|
* available at www.sybase.com/developer/opensource.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
||
|
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||
|
* NON-INFRINGEMENT. Please see the License for the specific language
|
||
|
* governing rights and limitations under the License.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* Description: Platform independent tmpfile() implementation.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
|
||
|
#include "variety.h"
|
||
|
#include "rtinit.h"
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stddef.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <direct.h>
|
||
|
#include <string.h>
|
||
|
#include <process.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include "rtdata.h"
|
||
|
#include "tmpfname.h"
|
||
|
#include "seterrno.h"
|
||
|
#include "openmode.h"
|
||
|
|
||
|
#define OPEN_MODE (O_RDWR | O_CREAT | O_BINARY)
|
||
|
#define PMODE (S_IREAD | S_IWRITE)
|
||
|
|
||
|
/* Netware doesn't define these */
|
||
|
/* Symbolic constants for the access() function */
|
||
|
|
||
|
#if !defined( F_OK )
|
||
|
#define R_OK 4 /* Test for read permission */
|
||
|
#define W_OK 2 /* Test for write permission */
|
||
|
#define X_OK 1 /* Test for execute permission */
|
||
|
#define F_OK 0 /* Test for existence of file */
|
||
|
#endif
|
||
|
|
||
|
extern void __MkTmpFile( char *buf, int num );
|
||
|
extern void __RmTmpFile( FILE *fp );
|
||
|
extern void (*__RmTmpFileFn)( FILE *fp );
|
||
|
|
||
|
char __tmpfnext = _TMP_INIT_CHAR;
|
||
|
|
||
|
_WCRTLINK FILE *tmpfile( void ) /* create a temporary file */
|
||
|
{
|
||
|
int hdl;
|
||
|
int old_errno;
|
||
|
int our_errno;
|
||
|
char suffix1;
|
||
|
char suffix2;
|
||
|
FILE *fp;
|
||
|
char name1[PATH_MAX + _TMPFNAME_LENGTH + 1];
|
||
|
char name2[PATH_MAX + _TMPFNAME_LENGTH + 1];
|
||
|
|
||
|
old_errno = _RWD_errno;
|
||
|
suffix1 = 0;
|
||
|
for( ;; ) {
|
||
|
// Part I
|
||
|
for( ;; ) {
|
||
|
__MkTmpFile( name1, suffix1 );
|
||
|
// if a file by this name does not exist
|
||
|
if( access( name1, F_OK ) != 0 ) {
|
||
|
|
||
|
// then let's try to create it
|
||
|
hdl = sopen( name1, OPEN_MODE, OPENMODE_DENY_COMPAT, PMODE );
|
||
|
|
||
|
// if we created it then continue with part II
|
||
|
if( hdl != -1 ) break;
|
||
|
__set_errno( EAGAIN );
|
||
|
}
|
||
|
suffix1++;
|
||
|
// give up after _TMP_INIT_CHAR tries JBS 99/10/26
|
||
|
if( suffix1 >= _TMP_INIT_CHAR ) return NULL;
|
||
|
}
|
||
|
close( hdl );
|
||
|
|
||
|
// Part II
|
||
|
/* we now have a empty file. Let's try to rename it
|
||
|
rename should be an atomic operation in the operating system
|
||
|
so if it succeeds we can be sure no one else has this file.
|
||
|
Consider the following sequence:
|
||
|
|
||
|
task1: access x.y => file does not exist
|
||
|
task2: access x.y => file does not exist
|
||
|
task1: fopen x.y => succeeds
|
||
|
task2: fopen x.y => succeeds (now have both tasks with x.y open)
|
||
|
task1: rename x.y to y.y => succeeds, can use this file
|
||
|
task2: rename x.y to y.y => fails (because x.y no longer exists)
|
||
|
task2: start over again to get a new file name
|
||
|
task2: succeeds second time around since no more race condition
|
||
|
with task1.
|
||
|
*/
|
||
|
suffix2 = _RWD_tmpfnext; // only one of these per process
|
||
|
for( ;; ) {
|
||
|
if( suffix2 == suffix1 ) {
|
||
|
suffix2++;
|
||
|
}
|
||
|
__MkTmpFile( name2, suffix2 );
|
||
|
|
||
|
if( rename( name1, name2 ) == 0 ) { // if rename worked
|
||
|
|
||
|
// The file is now ours. Let's try to open it.
|
||
|
fp = fopen( name2, "wb+" );
|
||
|
if( fp != NULL ) {
|
||
|
fp->_flag |= _TMPFIL;
|
||
|
_FP_TMPFCHAR(fp) = suffix2;
|
||
|
__set_errno( old_errno );
|
||
|
return( fp );
|
||
|
}
|
||
|
// We couldn't open it, probably because we have run out of handles.
|
||
|
// Remove the renamed file.
|
||
|
our_errno = errno;
|
||
|
remove( name2 );
|
||
|
__set_errno( our_errno );
|
||
|
return( NULL );
|
||
|
}
|
||
|
// The rename didn't work or we couldn't open the renamed file.
|
||
|
// One of two possibilities:
|
||
|
// (1) The "to" name already exists.
|
||
|
// (2) Another process renamed it away from us.
|
||
|
|
||
|
// Check for case (2).
|
||
|
// Quit if "from" file is gone and start over.
|
||
|
if( access( name1, F_OK ) != 0 ) break;
|
||
|
|
||
|
// Must be case (1). Try another "to" name.
|
||
|
++suffix2;
|
||
|
if( suffix2 == 0 ) {
|
||
|
suffix2 = _TMP_INIT_CHAR;
|
||
|
}
|
||
|
_RWD_tmpfnext = suffix2; // update for all processes
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* tmpfil() pulls in a lot of overhead that many programs do not need. But */
|
||
|
/* since temp files are removed on program shutdown, the code to remove */
|
||
|
/* them would always get linked in even if the program never heard of temp */
|
||
|
/* files. Since we know that temporary files can _only_ be created through */
|
||
|
/* tmpfile(), we can have a dummy __RmTmpFile() by default and use the */
|
||
|
/* real thing only if tmpfil() was called. */
|
||
|
void __Init_Tmpfl( void )
|
||
|
{
|
||
|
// Just assign the function address
|
||
|
__RmTmpFileFn = __RmTmpFile;
|
||
|
}
|
||
|
|
||
|
AXI( __Init_Tmpfl, INIT_PRIORITY_RUNTIME )
|