0,0 → 1,171 |
/**************************************************************************** |
* |
* 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 ) |