windows-nt/Source/XPSP1/NT/base/crts/crtw32/lowio/open.c
2020-09-26 16:20:57 +08:00

536 lines
18 KiB
C

/***
*open.c - file open
*
* Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines _open() and _sopen() - open or create a file
*
*Revision History:
* 06-13-89 PHG Module created, based on asm version
* 11-11-89 JCR Replaced DOS32QUERYFILEMODE with DOS32QUERYPATHINFO
* 03-13-90 GJF Made calling type _CALLTYPE2 (for now), added #include
* <cruntime.h>, fixed some compiler warnings and fixed
* copyright. Also, cleaned up the formatting a bit.
* 07-24-90 SBM Removed '32' from API names
* 08-14-90 SBM Compiles cleanly with -W3
* 09-07-90 SBM Added stdarg code (inside #if 0..#endif) to make
* open and sopen match prototypes. Test and use this
* someday.
* 10-01-90 GJF New-style function declarators.
* 11-16-90 GJF Wrote version for Win32 API and appended it via an
* #ifdef. The Win32 version is similar to the old DOS
* version (though in C) and far different from either
* the Cruiser or OS/2 versions.
* 12-03-90 GJF Fixed a dozen or so minor errors in the Win32 version.
* 12-06-90 SRW Changed to use _osfile and _osfhnd instead of _osfinfo
* 12-28-90 SRW Added cast of void * to char * for Mips C Compiler
* 12-31-90 SRW Fixed spen to call CreateFile instead of OpenFile
* 01-16-91 GJF ANSI naming.
* 02-07-91 SRW Changed to call _get_osfhandle [_WIN32_]
* 02-19-91 SRW Adapt to OpenFile/CreateFile changes [_WIN32_]
* 02-25-91 SRW Renamed _get_free_osfhnd to be _alloc_osfhnd [_WIN32_]
* 04-09-91 PNT Added _MAC_ conditional
* 07-10-91 GJF Store fileflags into _osfile array before call to
* _lseek_lk (bug found by LarryO) [_WIN32_].
* 01-02-92 GJF Fixed Win32 version (not Cruiser!) so that pmode is not
* referenced unless _O_CREAT has been specified.
* 02-04-92 GJF Make better use of CreateFile options.
* 04-06-92 SRW Pay attention to _O_NOINHERIT flag in oflag parameter
* 05-02-92 SRW Add support for _O_TEMPORARY flag in oflag parameter.
* Causes FILE_ATTRIBUTE_TEMPORARY flag to be set in call
* to the Win32 CreateFile API.
* 07-01-92 GJF Close handle in case of error. Also, don't try to set
* FRDONLY bit anymore - no longer needed/used. [_WIN32_].
* 01-03-93 SRW Fix va_arg/va_end usage
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
* 05-24-93 PML Add support for _O_SEQUENTIAL, _O_RANDOM,
* and _O_SHORT_LIVED
* 07-12-93 GJF Fixed bug in reading last char in text file. Also,use
* proper SEEK constants in _lseek_lk calls.
* 11-01-93 CFW Enable Unicode variant, rip out CRUISER.
* 02-15-95 GJF Appended Mac version of source file (somewhat cleaned
* up), with appropriate #ifdef-s.
* 06-06-95 CFW Enable shared writing.
* 06-12-95 GJF Replaced _osfile[] with _osfile() (macro referencing
* field in ioinfo struct).
* 05-16-96 GJF Propagate _O_NOINHERIT to FNOINHERIT (new _osfile
* bit). Also detab-ed.
* 05-31-96 SKS Fix expression error in GJF's most recent check-in
* 07-08-96 GJF Replaced defined(_WIN32) with !defined(_MAC), and
* defined(_M_M68K) || defined(_M_MPPC) with
* defined(_MAC). Also, cleaned up the format a bit.
* 08-01-96 RDK For PMac, change data type for _endlowio terminator
* pointer, use safer PBHOpenDFSync call, and process
* _O_TEMPORARY open flag to implement delete after close.
* 12-29-97 GJF Exception-safe locking.
* 02-07-98 GJF Changes for Win64: arg type for _set_osfhnd is now
* intptr_t.
* 04-28-99 PML Wrap __declspec(allocate()) in _CRTALLOC macro.
* 05-17-99 PML Remove all Macintosh support.
* 10-27-99 GB VS7#14742 made a fix for opening the file in
* O_TEMPORARY mode so as to enable sucessive open on the
* same file if share permissions are there.
*
*******************************************************************************/
#include <sect_attribs.h>
#include <cruntime.h>
#include <oscalls.h>
#include <msdos.h>
#include <errno.h>
#include <fcntl.h>
#include <internal.h>
#include <io.h>
#include <share.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <mtdll.h>
#include <stdarg.h>
#include <tchar.h>
#ifdef _MT
static int __cdecl _tsopen_lk ( int *,
int *,
const _TSCHAR *,
int,
int,
int );
#endif /* _MT */
/***
*int _open(path, flag, pmode) - open or create a file
*
*Purpose:
* Opens the file and prepares for subsequent reading or writing.
* the flag argument specifies how to open the file:
* _O_APPEND - reposition file ptr to end before every write
* _O_BINARY - open in binary mode
* _O_CREAT - create a new file* no effect if file already exists
* _O_EXCL - return error if file exists, only use with O_CREAT
* _O_RDONLY - open for reading only
* _O_RDWR - open for reading and writing
* _O_TEXT - open in text mode
* _O_TRUNC - open and truncate to 0 length (must have write permission)
* _O_WRONLY - open for writing only
* _O_NOINHERIT -handle will not be inherited by child processes.
* exactly one of _O_RDONLY, _O_WRONLY, _O_RDWR must be given
*
* The pmode argument is only required when _O_CREAT is specified. Its
* flag settings:
* _S_IWRITE - writing permitted
* _S_IREAD - reading permitted
* _S_IREAD | _S_IWRITE - both reading and writing permitted
* The current file-permission maks is applied to pmode before
* setting the permission (see umask).
*
* The oflag and mode parameter have different meanings under DOS. See
* the A_xxx attributes in msdos.inc
*
* Note, the _creat() function also uses this function but setting up the
* correct arguments and calling _open(). _creat() sets the __creat_flag
* to 1 prior to calling _open() so _open() can return correctly. _open()
* returns the file handle in eax in this case.
*
*Entry:
* _TSCHAR *path - file name
* int flag - flags for _open()
* int pmode - permission mode for new files
*
*Exit:
* returns file handle of open file if successful
* returns -1 (and sets errno) if fails
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _topen (
const _TSCHAR *path,
int oflag,
...
)
{
va_list ap;
int pmode;
#ifdef _MT
int fh;
int retval;
int unlock_flag = 0;
#endif /* MT */
va_start(ap, oflag);
pmode = va_arg(ap, int);
va_end(ap);
#ifdef _MT
__try {
retval = _tsopen_lk( &unlock_flag,
&fh,
path,
oflag,
_SH_DENYNO,
pmode );
}
__finally {
if ( unlock_flag )
_unlock_fh(fh);
}
return retval;
#else /* ndef _MT */
/* default sharing mode is DENY NONE */
return _tsopen(path, oflag, _SH_DENYNO, pmode);
#endif /* _MT */
}
/***
*int _sopen(path, oflag, shflag, pmode) - opne a file with sharing
*
*Purpose:
* Opens the file with possible file sharing.
* shflag defines the sharing flags:
* _SH_COMPAT - set compatability mode
* _SH_DENYRW - deny read and write access to the file
* _SH_DENYWR - deny write access to the file
* _SH_DENYRD - deny read access to the file
* _SH_DENYNO - permit read and write access
*
* Other flags are the same as _open().
*
* SOPEN is the routine used when file sharing is desired.
*
*Entry:
* _TSCHAR *path - file to open
* int oflag - open flag
* int shflag - sharing flag
* int pmode - permission mode (needed only when creating file)
*
*Exit:
* returns file handle for the opened file
* returns -1 and sets errno if fails.
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _tsopen (
const _TSCHAR *path,
int oflag,
int shflag,
...
)
{
#ifdef _MT
va_list ap;
int pmode;
int fh;
int retval;
int unlock_flag = 0;
va_start(ap, shflag);
pmode = va_arg(ap, int);
va_end(ap);
__try {
retval = _tsopen_lk( &unlock_flag,
&fh,
path,
oflag,
shflag,
pmode );
}
__finally {
if ( unlock_flag )
_unlock_fh(fh);
}
return retval;
}
static int __cdecl _tsopen_lk (
int *punlock_flag,
int *pfh,
const _TSCHAR *path,
int oflag,
int shflag,
int pmode
)
{
#endif /* _MT */
int fh; /* handle of opened file */
int filepos; /* length of file - 1 */
_TSCHAR ch; /* character at end of file */
char fileflags; /* _osfile flags */
#ifndef _MT
va_list ap;
int pmode;
#endif /* _MT */
HANDLE osfh; /* OS handle of opened file */
DWORD fileaccess; /* OS file access (requested) */
DWORD fileshare; /* OS file sharing mode */
DWORD filecreate; /* OS method of opening/creating */
DWORD fileattrib; /* OS file attribute flags */
DWORD isdev; /* device indicator in low byte */
SECURITY_ATTRIBUTES SecurityAttributes;
SecurityAttributes.nLength = sizeof( SecurityAttributes );
SecurityAttributes.lpSecurityDescriptor = NULL;
if (oflag & _O_NOINHERIT) {
SecurityAttributes.bInheritHandle = FALSE;
fileflags = FNOINHERIT;
}
else {
SecurityAttributes.bInheritHandle = TRUE;
fileflags = 0;
}
/* figure out binary/text mode */
if ((oflag & _O_BINARY) == 0)
if (oflag & _O_TEXT)
fileflags |= FTEXT;
else if (_fmode != _O_BINARY) /* check default mode */
fileflags |= FTEXT;
/*
* decode the access flags
*/
switch( oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) {
case _O_RDONLY: /* read access */
fileaccess = GENERIC_READ;
break;
case _O_WRONLY: /* write access */
fileaccess = GENERIC_WRITE;
break;
case _O_RDWR: /* read and write access */
fileaccess = GENERIC_READ | GENERIC_WRITE;
break;
default: /* error, bad oflag */
errno = EINVAL;
_doserrno = 0L; /* not an OS error */
return -1;
}
/*
* decode sharing flags
*/
switch ( shflag ) {
case _SH_DENYRW: /* exclusive access */
fileshare = 0L;
break;
case _SH_DENYWR: /* share read access */
fileshare = FILE_SHARE_READ;
break;
case _SH_DENYRD: /* share write access */
fileshare = FILE_SHARE_WRITE;
break;
case _SH_DENYNO: /* share read and write access */
fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE;
break;
default: /* error, bad shflag */
errno = EINVAL;
_doserrno = 0L; /* not an OS error */
return -1;
}
/*
* decode open/create method flags
*/
switch ( oflag & (_O_CREAT | _O_EXCL | _O_TRUNC) ) {
case 0:
case _O_EXCL: // ignore EXCL w/o CREAT
filecreate = OPEN_EXISTING;
break;
case _O_CREAT:
filecreate = OPEN_ALWAYS;
break;
case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
filecreate = CREATE_NEW;
break;
case _O_TRUNC:
case _O_TRUNC | _O_EXCL: // ignore EXCL w/o CREAT
filecreate = TRUNCATE_EXISTING;
break;
case _O_CREAT | _O_TRUNC:
filecreate = CREATE_ALWAYS;
break;
default:
// this can't happen ... all cases are covered
errno = EINVAL;
_doserrno = 0L;
return -1;
}
/*
* decode file attribute flags if _O_CREAT was specified
*/
fileattrib = FILE_ATTRIBUTE_NORMAL; /* default */
if ( oflag & _O_CREAT ) {
#ifndef _MT
/*
* set up variable argument list stuff
*/
va_start(ap, shflag);
pmode = va_arg(ap, int);
va_end(ap);
#endif /* _MT */
if ( !((pmode & ~_umaskval) & _S_IWRITE) )
fileattrib = FILE_ATTRIBUTE_READONLY;
}
/*
* Set temporary file (delete-on-close) attribute if requested.
*/
if ( oflag & _O_TEMPORARY ) {
fileattrib |= FILE_FLAG_DELETE_ON_CLOSE;
fileaccess |= DELETE;
if (_osplatform == VER_PLATFORM_WIN32_NT )
fileshare |= FILE_SHARE_DELETE;
}
/*
* Set temporary file (delay-flush-to-disk) attribute if requested.
*/
if ( oflag & _O_SHORT_LIVED )
fileattrib |= FILE_ATTRIBUTE_TEMPORARY;
/*
* Set sequential or random access attribute if requested.
*/
if ( oflag & _O_SEQUENTIAL )
fileattrib |= FILE_FLAG_SEQUENTIAL_SCAN;
else if ( oflag & _O_RANDOM )
fileattrib |= FILE_FLAG_RANDOM_ACCESS;
/*
* get an available handle.
*
* multi-thread note: the returned handle is locked!
*/
if ( (fh = _alloc_osfhnd()) == -1 ) {
errno = EMFILE; /* too many open files */
_doserrno = 0L; /* not an OS error */
return -1; /* return error to caller */
}
#ifdef _MT
*punlock_flag = 1;
*pfh = fh;
#endif
/*
* try to open/create the file
*/
if ( (osfh = CreateFile( (LPTSTR)path,
fileaccess,
fileshare,
&SecurityAttributes,
filecreate,
fileattrib,
NULL ))
== (HANDLE)(-1) )
{
/*
* OS call to open/create file failed! map the error, release
* the lock, and return -1. note that it's not necessary to
* call _free_osfhnd (it hasn't been used yet).
*/
_dosmaperr(GetLastError()); /* map error */
return -1; /* return error to caller */
}
/* find out what type of file (file/device/pipe) */
if ( (isdev = GetFileType(osfh)) == FILE_TYPE_UNKNOWN ) {
CloseHandle(osfh);
_dosmaperr(GetLastError()); /* map error */
return -1;
}
/* is isdev value to set flags */
if (isdev == FILE_TYPE_CHAR)
fileflags |= FDEV;
else if (isdev == FILE_TYPE_PIPE)
fileflags |= FPIPE;
/*
* the file is open. now, set the info in _osfhnd array
*/
_set_osfhnd(fh, (intptr_t)osfh);
/*
* mark the handle as open. store flags gathered so far in _osfile
* array.
*/
fileflags |= FOPEN;
_osfile(fh) = fileflags;
if ( !(fileflags & (FDEV|FPIPE)) && (fileflags & FTEXT) &&
(oflag & _O_RDWR) )
{
/* We have a text mode file. If it ends in CTRL-Z, we wish to
remove the CTRL-Z character, so that appending will work.
We do this by seeking to the end of file, reading the last
byte, and shortening the file if it is a CTRL-Z. */
if ((filepos = _lseek_lk(fh, -1, SEEK_END)) == -1) {
/* OS error -- should ignore negative seek error,
since that means we had a zero-length file. */
if (_doserrno != ERROR_NEGATIVE_SEEK) {
_close_lk(fh);
return -1;
}
}
else {
/* Seek was OK, read the last char in file. The last
char is a CTRL-Z if and only if _read returns 0
and ch ends up with a CTRL-Z. */
ch = 0;
if (_read_lk(fh, &ch, 1) == 0 && ch == 26) {
/* read was OK and we got CTRL-Z! Wipe it
out! */
if (_chsize_lk(fh,filepos) == -1)
{
_close_lk(fh);
return -1;
}
}
/* now rewind the file to the beginning */
if ((filepos = _lseek_lk(fh, 0, SEEK_SET)) == -1) {
_close_lk(fh);
return -1;
}
}
}
/*
* Set FAPPEND flag if appropriate. Don't do this for devices or pipes.
*/
if ( !(fileflags & (FDEV|FPIPE)) && (oflag & _O_APPEND) )
_osfile(fh) |= FAPPEND;
return fh; /* return handle */
}