⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 file.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:

#elif defined( __BEOS__ ) || defined( __ECOS__ ) || defined( __RTEMS__ ) || \
	  defined( __SYMBIAN32__ ) || defined( __TANDEM_NSK__ ) || \
	  defined( __TANDEM_OSS__ ) || defined( __UNIX__ )

/* Tandem doesn't have ftruncate() even though there's a manpage for it
   (which claims it's prototyped in sys/types.h (!!)).  unistd.h has it
   protected by ( _XOPEN_SOURCE_EXTENDED == 1 && _TNS_R_TARGET ), which
   implies that we'd better emulate it if we want to make use of it.  For
   now we do nothing, this is just a placeholder if the Guardian native
   file layer isn't available */

#if defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ )

int ftruncate( int fd, off_t length )
	{
	return( 0 );
	}
#endif /* Tandem */

/* Open/close a file stream */

#ifdef DDNAME_IO

/* DDNAME I/O can be used under MVS.  Low-level POSIX I/O APIs can't be
   used at this level, only stream I/O functions can be used.  For
   sFileOpen:

	- File permissions are controlled by RACF (or SAF compatable product)
	  and should not be set by the program.

	- No locking mechanism is implemented */

int sFileOpen( STREAM *stream, const char *fileName, const int mode )
	{
#pragma convlit( suspend )
	static const char *modes[] = { MODE_READ, MODE_READ,
								   MODE_WRITE, MODE_READWRITE };
#pragma convlit( resume )
	const char *openMode;
	char fileNameBuffer[ MAX_PATH_LENGTH ];

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( fileName != NULL );
	assert( mode != 0 );

	/* Initialise the stream structure */
	memset( stream, 0, sizeof( STREAM ) );
	stream->type = STREAM_TYPE_FILE;
	if( ( mode & FILE_RW_MASK ) == FILE_READ )
		stream->flags = STREAM_FLAG_READONLY;
	openMode = modes[ mode & FILE_RW_MASK ];

	/* Try and open the file */
	fileName = bufferToEbcdic( fileNameBuffer, fileName );
	stream->filePtr = fopen( fileName, openMode );
	if( stream->filePtr == NULL )
		/* The open failed, determine whether it was because the file doesn't
		   exist or because we can't use that access mode.  An errno value
		   of ENOENT results from a ddname not found, and 67 (no mnemonic
		   name defined by IBM for DYNALLOC return codes) is member not
		   found, and 49 is data set not found */
		return( ( errno == ENOENT || errno == 67 || errno == 49 ) ? \
				CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );

    return( CRYPT_OK );
	}
#else

#ifndef STDIN_FILENO		/* Usually defined in unistd.h */
  #define STDIN_FILENO		0
  #define STDOUT_FILENO		1
  #define STDERR_FILENO		2
#endif /* STDIN_FILENO */

static int openFile( STREAM *stream, const char *fileName,
					 const int flags, const int mode )
	{
	int fd, count = 0;

	/* A malicious user could have exec()'d us after closing standard I/O
	   handles (which we inherit across the exec()), which means that any
	   new files that we open will be allocated the same handles as the
	   former standard I/O ones.  This could cause private data to be
	   written to stdout or error messages emitted by the calling app to go
	   into the opened file.  To avoid this, we retry the open if we get the
	   same handle as a standard I/O one */
	do
		{
		fd = open( fileName, flags, mode );
		if( fd == -1 )
			{
			/* If we're creating the file, the only error condition is a
			   straight open error */
			if( flags & O_CREAT )
				return( CRYPT_ERROR_OPEN );

			/* Determine whether the open failed because the file doesn't
			   exist or because we can't use that access mode */
			return( ( access( fileName, 0 ) == -1 ) ? \
					CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
			}
		}
	while( count++ < 3 && \
		   ( fd == STDIN_FILENO || fd == STDOUT_FILENO || \
		     fd == STDERR_FILENO ) );

	stream->fd = fd;
	return( CRYPT_OK );
	}

int sFileOpen( STREAM *stream, const char *fileName, const int mode )
	{
#if defined( EBCDIC_CHARS )
  #pragma convlit( suspend )
#endif /* EBCDIC_CHARS */
	static const int modes[] = { O_RDONLY, O_RDONLY, O_WRONLY, O_RDWR };
#if defined( EBCDIC_CHARS )
  #pragma convlit( resume )
#endif /* EBCDIC_CHARS */
	int openMode = modes[ mode & FILE_RW_MASK ];
#ifdef EBCDIC_CHARS
	char fileNameBuffer[ MAX_PATH_LENGTH ];
#endif /* EBCDIC_CHARS */
#ifdef USE_FCNTL_LOCKING
	struct flock flockInfo;
#endif /* USE_FCNTL_LOCKING */

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( fileName != NULL );
	assert( mode != 0 );

	/* Initialise the stream structure */
	memset( stream, 0, sizeof( STREAM ) );
	stream->type = STREAM_TYPE_FILE;
	if( ( mode & FILE_RW_MASK ) == FILE_READ )
		stream->flags = STREAM_FLAG_READONLY;

	/* If we're trying to write to the file, check whether we've got
	   permission to do so */
	if( ( mode & FILE_WRITE ) && fileReadonly( fileName ) )
		return( CRYPT_ERROR_PERMISSION );

#ifdef EBCDIC_CHARS
	fileName = bufferToEbcdic( fileNameBuffer, fileName );
#endif /* EBCDIC_CHARS */

	/* Defending against writing through links is somewhat difficult since
	   there's no atomic way to do this.  What we do is lstat() the file,
	   open it as appropriate, and if it's an existing file ftstat() it and
	   compare various important fields to make sure that the file wasn't
	   changed between the lstat() and the open().  If everything is OK, we
	   then use the lstat() information to make sure that it isn't a symlink
	   (or at least that it's a normal file) and that the link count is 1.
	   These checks also catch other weird things like STREAMS stuff
	   fattach()'d over files.  If these checks pass and the file already
	   exists we truncate it to mimic the effect of an open with create */
	if( ( mode & FILE_RW_MASK ) == FILE_WRITE )
		{
		struct stat lstatInfo;
		int status;

		/* lstat() the file.  If it doesn't exist, create it with O_EXCL.  If
		   it does exist, open it for read/write and perform the fstat()
		   check */
		if( lstat( fileName, &lstatInfo ) == -1 )
			{
			/* If the lstat() failed for reasons other than the file not
			   existing, return a file open error */
			if( errno != ENOENT )
				return( CRYPT_ERROR_OPEN );

			/* The file doesn't exist, create it with O_EXCL to make sure
			   that an attacker can't slip in a file between the lstat() and
			   open() */
			status = openFile( stream, fileName, O_CREAT | O_EXCL | O_RDWR,
							   0600 );
			if( cryptStatusError( status ) )
				return( status );
			}
		else
			{
			struct stat fstatInfo;

			/* Open an existing file */
			status = openFile( stream, fileName, O_RDWR, 0 );
			if( cryptStatusError( status ) )
				return( status );

			/* fstat() the opened file and check that the file mode bits and
			   inode and device match */
			if( fstat( stream->fd, &fstatInfo ) == -1 || \
				lstatInfo.st_mode != fstatInfo.st_mode || \
				lstatInfo.st_ino != fstatInfo.st_ino || \
				lstatInfo.st_dev != fstatInfo.st_dev )
				{
				close( stream->fd );
				return( CRYPT_ERROR_OPEN );
				}

			/* If the above check was passed, we know that the lstat() and
			   fstat() were done to the same file.  Now check that there's
			   only one link, and that it's a normal file (this isn't
			   strictly necessary because the fstat() vs. lstat() st_mode
			   check would also find this).  This also catches tricks like
			   an attacker closing stdin/stdout so that a newly-opened file
			   ends up with those file handles, with the result that the app
			   using cryptlib ends up corrupting cryptlib's files when it
			   sends data to stdout.  In order to counter this we could
			   simply repeatedly open /dev/null until we get a handle > 2,
			   but the fstat() check will catch this in a manner that's also
			   safe with systems that don't have a stdout (so the handle > 2
			   check won't make much sense) */
			if( fstatInfo.st_nlink > 1 || !S_ISREG( lstatInfo.st_mode ) )
				{
				close( stream->fd );
				return( CRYPT_ERROR_OPEN );
				}

			/* Turn the file into an empty file */
			ftruncate( stream->fd, 0 );
			}
		}
	else
		{
		int status;

		/* Open an existing file for read access */
		status = openFile( stream, fileName, openMode, 0 );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Set the file access permissions so that only the owner can access it */
	if( mode & FILE_PRIVATE )
		chmod( fileName, 0600 );

	/* Lock the file if possible to make sure that no-one else tries to do
	   things to it.  If available we used the (BSD-style) flock(), if not we
	   fall back to Posix fcntl() locking (both mechanisms are broken, but
	   flock() is less broken).  fcntl() locking has two disadvantages over
	   flock():

	   1. Locking is per-process rather than per-thread (specifically it's
		  based on processes and inodes rather than flock()'s file table
		  entries, for which any new handles created via dup()/fork()/open()
		  all refer to the same file table entry so there's a single location
		  at which to handle locking), so another thread in the same process
		  could still access the file.  Whether this is a good thing or not
		  is context-dependant: We want multiple threads to be able to read
		  from the file (if one keyset handle is shared among threads), but
		  not necessarily for multiple threads to be able to write.  We could
		  if necessary use mutexes for per-thread lock synchronisation, but
		  this gets incredibly ugly since we then have to duplicate parts of
		  the the system file table with per-thread mutexes, mess around with
		  an fstat() on each file access to determine if we're accessing an
		  already-open file, wrap all that up in more mutexes, etc etc, as
		  well as being something that's symtomatic of a user application bug
		  rather than normal behaviour that we can defend against.

	   2. Closing *any* descriptor for an fcntl()-locked file releases *all*
		  locks on the file (!!) (one manpage appropriately describes this
		  behaviour as "the completely stupid semantics of System V and IEEE
		  Std 1003.1-1988 (= POSIX.1)").  In other words if two threads or
		  processes open an fcntl()-locked file for shared read access then
		  the first close of the file releases all locks on it.  Since
		  fcntl() requires a file handle to work, the only way to determine
		  whether a file is locked requires opening it, but as soon as we
		  close it again (for example to abort the access if there's a lock
		  on it) all locks are released.

	   flock() sticks with the much more sensible 4.2BSD-based last-close
	   semantics, however it doesn't usually work with NFS unless special
	   hacks have been applied.  fcntl() passes lock requests to rpc.lockd
	   to handle, but this is its own type of mess since it's often
	   unreliable, so it's really not much worse than flock().  In addition
	   locking support under filesystems like AFS is often nonexistant, with
	   the lock apparently succeeding but no lock actually being applied.
	   Finally, locking is almost always advisory only, but even mandatory
	   locking can be bypassed by tricks such as copying the original,
	   unlinking it, and renaming the copy back to the original (the
	   unlinked - and still locked - original goes away once the handle is
	   closed) - this mechanism is standard practice for many Unix utilities
	   like text editors.  In addition mandatory locking is wierd in that an
	   open for write (or read, on a write-locked file) will succeed, it's
	   only a later attempt to read/write that will fail.

	   This mess is why dotfile-locking is still so popular, but that's
	   probably going a bit far for simple keyset accesses */
#ifndef USE_FCNTL_LOCKING
	if( flock( stream->fd, ( mode & FILE_EXCLUSIVE_ACCESS ) ? \
						   LOCK_EX | LOCK_NB : LOCK_SH | LOCK_NB ) == -1 && \
		errno == EWOULDBLOCK )
		{
		close( stream->fd );
		return( CRYPT_ERROR_PERMISSION );
		}
#else
	memset( &flockInfo, 0, sizeof( struct flock ) );
	flockInfo.l_type = ( mode & FILE_EXCLUSIVE_ACCESS ) ? \
					   F_WRLCK : F_RDLCK;
	flockInfo.l_whence = SEEK_SET;
	flockInfo.l_start = flockInfo.l_len = 0;
	if( fcntl( stream->fd, F_SETLK, flockInfo ) == -1 && \
		( errno == EACCES || errno == EDEADLK ) )
		{
		/* Now we're in a bind.  If we close the file and exit, the lock
		   we've just detected on the file is released (see the comment on
		   this utter braindamage above).  OTOH if we don't close the file
		   we'll leak the file handle, which is bad for long-running
		   processes.  Feedback from users indicates that leaking file
		   handles is less desirable than the possiblity of having the file
		   unlocked during an update (the former is a situation that occurs
		   far more frequently than the latter), so we close the handle and
		   hope that the update by the other process completes quickly */
		close( stream->fd );
		return( CRYPT_ERROR_PERMISSION );
		}
#endif /* flock() vs. fcntl() locking */

	return( CRYPT_OK );
	}
#endif /* MVS USS special-case handling */

int sFileClose( STREAM *stream )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_FILE );

	/* Unlock the file if necessary.  If we're using fcntl() locking there's
	   no need to unlock the file since all locks are automatically released
	   as soon as any handle to it is closed (see the long comment above for
	   more on this complete braindamage) */
#ifndef USE_FCNTL_LOCKING
	flock( stream->fd, LOCK_UN );
#endif /* !USE_FCNTL_LOCKING */
	close( stream->fd );
	zeroise( stream, sizeof( STREAM ) );

	return( CRYPT_OK );
	}

/* Read/write a block of data from/to a file stream */

int fileRead( STREAM *stream, void *buffer, const int length )
	{
	int bytesRead;

	if( ( bytesRead = read( stream->fd, buffer, length ) ) == -1 )
		return( CRYPT_ERROR_READ );
	return( bytesRead );
	}

int fileWrite( STREAM *stream, const void *buffer, const int length )
	{
	if( write( stream->fd, buffer, length ) != length )
		return( CRYPT_ERROR_WRITE );
	return( CRYPT_OK );
	}

/* Commit data in a file stream to backing storage */

int fileFlush( STREAM *stream )
	{
	return( fsync( stream->fd ) == 0 ? CRYPT_OK : CRYPT_ERROR_WRITE );
	}

/* Change the read/write position in a file */

int fileSeek( STREAM *stream, const long position )
	{
#if defined( DDNAME_IO )
	/* If we're using ddnames, we only seek if we're not already at the
	   start of the file to prevent postioning to 0 in a new empty PDS
	   member, which fails */
	if( ( stream->bufCount > 0 || stream->bufPos > 0 || position > 0 ) )
		/* Drop through */
#endif /* MVS USS special-case */
	if( lseek( stream->fd, position, SEEK_SET ) == ( off_t ) -1 )
		return( CRYPT_ERROR_WRITE );
	return( CRYPT_OK );
	}

/* Check whether a file is writeable */

BOOLEAN fileReadonly( const char *fileName )
	{
#ifdef EBCDIC_CHARS
	char fileNameBuffer[ MAX_PATH_LENGTH ];

	fileName = bufferToEbcdic( fileNameBuffer, fileName );
#endif /* EBCDIC_CHARS */
#if defined( DDNAME_IO )
	/* Requires a RACF check to determine this */
	return( FALSE );
#else
	if( access( fileName, W_OK ) == -1 && errno != ENOENT )
		return( TRUE );
#endif /* OS-specific file accessibility check */

	return( FALSE );
	}

/* File deletion functions: Wipe a file from the current position to EOF,
   and wipe and delete a file (although it's not terribly rigorous

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -