1
0
mirror of https://github.com/adtools/clib2.git synced 2025-12-08 14:59:05 +00:00
Files
amiga-clib2/library/stdio_fdhookentry.c
Olaf Barthel 410833d2ed - The buffered and unbuffered file hook code is now invoked through
function pointers alone. The utility.library/CallHookPkt mechanism
  is no longer required.

- Moved the entire lseek() code relevant for files into the hook
  function.

- Simplified the close() function which now just calls into the
  hook code to perform whatever is necessary. The hook code is
  responsible for cleaning up after aliases, etc. This change in
  turn made it possible to greatly simplify the hook code for
  buffered files which now bypasses close/read/write/lseek and
  directly invokes the hook code for unbuffered files.


git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14835 87f5fb63-7c3d-0410-a384-fd976d0f7a62
2005-02-20 13:19:40 +00:00

546 lines
12 KiB
C

/*
* $Id: stdio_fdhookentry.c,v 1.12 2005-02-20 13:19:40 obarthel Exp $
*
* :ts=4
*
* Portable ISO 'C' (1994) runtime library for the Amiga computer
* Copyright (c) 2002-2005 by Olaf Barthel <olsen@sourcery.han.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of Olaf Barthel nor the names of contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _STDLIB_MEM_DEBUG_H
#include "stdlib_mem_debug.h"
#endif /* _STDLIB_MEM_DEBUG_H */
/****************************************************************************/
#ifndef _STDIO_HEADERS_H
#include "stdio_headers.h"
#endif /* _STDIO_HEADERS_H */
#ifndef _UNISTD_HEADERS_H
#include "unistd_headers.h"
#endif /* _UNISTD_HEADERS_H */
/****************************************************************************/
#include <strings.h>
#include <limits.h>
/****************************************************************************/
int
__fd_hook_entry(
struct fd * fd,
struct file_action_message * fam)
{
D_S(struct FileInfoBlock,fib);
BOOL fib_is_valid = FALSE;
LONG current_position;
LONG new_position;
LONG new_mode;
char * buffer = NULL;
int result = -1;
int error = OK;
ENTER();
assert( fam != NULL && fd != NULL );
assert( __is_valid_fd(fd) );
switch(fam->fam_Action)
{
case file_action_read:
SHOWMSG("file_action_read");
if(fd->fd_DefaultFile == ZERO)
{
SHOWMSG("file is closed");
error = EBADF;
break;
}
assert( fam->fam_Data != NULL );
assert( fam->fam_Size > 0 );
D(("read %ld bytes from position %ld to 0x%08lx",fam->fam_Size,Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT),fam->fam_Data));
PROFILE_OFF();
result = Read(fd->fd_DefaultFile,fam->fam_Data,fam->fam_Size);
PROFILE_ON();
if(result < 0)
{
D(("read failed ioerr=%ld",IoErr()));
error = __translate_io_error_to_errno(IoErr());
break;
}
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
fd->fd_Position += result;
break;
case file_action_write:
SHOWMSG("file_action_write");
if(fd->fd_DefaultFile == ZERO)
{
SHOWMSG("file is closed");
error = EBADF;
break;
}
assert( fam->fam_Data != NULL );
assert( fam->fam_Size > 0 );
if(FLAG_IS_SET(fd->fd_Flags,FDF_APPEND))
{
SHOWMSG("appending data");
PROFILE_OFF();
if(Seek(fd->fd_DefaultFile,0,OFFSET_END) >= 0)
{
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
fd->fd_Position = Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT);
}
PROFILE_ON();
}
D(("write %ld bytes to position %ld from 0x%08lx",fam->fam_Size,Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT),fam->fam_Data));
PROFILE_OFF();
result = Write(fd->fd_DefaultFile,fam->fam_Data,fam->fam_Size);
PROFILE_ON();
if(result < 0)
{
D(("write failed ioerr=%ld",IoErr()));
error = __translate_io_error_to_errno(IoErr());
break;
}
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
fd->fd_Position += result;
break;
case file_action_close:
SHOWMSG("file_action_close");
/* If this is an alias, just remove it. */
if(__fd_is_aliased(fd))
{
__remove_fd_alias(fd);
}
else
{
/* Is this file open in the first place? */
if(fd->fd_DefaultFile == ZERO)
{
SHOWMSG("file is closed");
error = EBADF;
break;
}
/* Are we disallowed to close this file? */
if(FLAG_IS_SET(fd->fd_Flags,FDF_NO_CLOSE))
{
/* OK, so we cannot close it. But we might be obliged to
reset a console into buffered mode. */
if(FLAG_IS_SET(fd->fd_Flags,FDF_NON_BLOCKING) && FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE))
SetMode(fd->fd_DefaultFile,DOSFALSE);
}
else
{
BOOL name_and_path_valid = FALSE;
D_S(struct FileInfoBlock,fib);
BPTR parent_dir;
memset(fib,0,sizeof(*fib));
/* Call a cleanup function, such as the one which
* releases locked records.
*/
if(fd->fd_Cleanup != NULL)
(*fd->fd_Cleanup)(fd);
PROFILE_OFF();
parent_dir = __safe_parent_of_file_handle(fd->fd_DefaultFile);
if(parent_dir != ZERO)
{
if(__safe_examine_file_handle(fd->fd_DefaultFile,fib))
name_and_path_valid = TRUE;
}
if(CANNOT Close(fd->fd_DefaultFile))
error = __translate_io_error_to_errno(IoErr());
PROFILE_ON();
fd->fd_DefaultFile = ZERO;
#if defined(UNIX_PATH_SEMANTICS)
{
DECLARE_UTILITYBASE();
assert( UtilityBase != NULL );
/* Now that we have closed this file, know where it is and what its
* name would be, check if we tried to unlink it earlier. If we did,
* we'll try to finish the job here and now.
*/
if(name_and_path_valid)
{
struct UnlinkNode * node;
struct UnlinkNode * uln_next;
struct UnlinkNode * uln;
BOOL file_deleted = FALSE;
assert( __unlink_list.mlh_Head != NULL );
/* Check all files to be unlinked when this program exits. */
for(uln = (struct UnlinkNode *)__unlink_list.mlh_Head ;
(uln_next = (struct UnlinkNode *)uln->uln_MinNode.mln_Succ) != NULL ;
uln = uln_next)
{
node = NULL;
/* If the file name matches, check if the path matches, too. */
if(Stricmp(FilePart(uln->uln_Name),fib->fib_FileName) == SAME)
{
BPTR old_dir;
BPTR node_lock;
BPTR path_lock = ZERO;
PROFILE_OFF();
/* Try to get a lock on the file first, then move on to
* the directory it is stored in.
*/
old_dir = CurrentDir(uln->uln_Lock);
node_lock = Lock(uln->uln_Name,SHARED_LOCK);
if(node_lock != ZERO)
{
path_lock = ParentDir(node_lock);
UnLock(node_lock);
}
CurrentDir(old_dir);
/* If we found the file's parent directory, check if it matches
* the parent directory of the file we just closed.
*/
if(path_lock != ZERO)
{
if(SameLock(path_lock,parent_dir) == LOCK_SAME)
node = uln;
UnLock(path_lock);
}
PROFILE_ON();
}
/* If we found that this file was set up for deletion,
* delete it here and now.
*/
if(node != NULL)
{
if(NOT file_deleted)
{
BPTR old_dir;
PROFILE_OFF();
old_dir = CurrentDir(parent_dir);
if(DeleteFile(fib->fib_FileName))
{
file_deleted = TRUE;
name_and_path_valid = FALSE;
}
CurrentDir(old_dir);
PROFILE_ON();
}
if(file_deleted)
{
Remove((struct Node *)node);
free(node);
}
}
}
}
}
#endif /* UNIX_PATH_SEMANTICS */
if(FLAG_IS_SET(fd->fd_Flags,FDF_CREATED) && name_and_path_valid)
{
ULONG flags;
BPTR old_dir;
PROFILE_OFF();
old_dir = CurrentDir(parent_dir);
flags = fib->fib_Protection ^ (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
CLEAR_FLAG(flags,FIBF_EXECUTE);
CLEAR_FLAG(flags,FIBF_OTR_EXECUTE);
CLEAR_FLAG(flags,FIBF_GRP_EXECUTE);
SetProtection(fib->fib_FileName,(LONG)(flags ^ (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE)));
CurrentDir(old_dir);
PROFILE_ON();
}
PROFILE_OFF();
UnLock(parent_dir);
PROFILE_ON();
}
}
/* And that's the last for this file descriptor. */
memset(fd,0,sizeof(*fd));
if(error == OK)
result = 0;
break;
case file_action_seek:
SHOWMSG("file_action_seek");
if(fam->fam_Mode == SEEK_CUR)
new_mode = OFFSET_CURRENT;
else if (fam->fam_Mode == SEEK_SET)
new_mode = OFFSET_BEGINNING;
else
new_mode = OFFSET_END;
D(("seek to offset %ld, new_mode %ld; current position = %ld",fam->fam_Position,new_mode,Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT)));
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
{
current_position = fd->fd_Position;
}
else
{
PROFILE_OFF();
current_position = Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT);
PROFILE_ON();
if(current_position < 0)
{
error = EBADF;
break;
}
}
new_position = current_position;
switch(new_mode)
{
case OFFSET_CURRENT:
new_position += fam->fam_Position;
break;
case OFFSET_BEGINNING:
new_position = fam->fam_Position;
break;
case OFFSET_END:
if(__safe_examine_file_handle(fd->fd_DefaultFile,fib))
{
new_position = fib->fib_Size + fam->fam_Position;
fib_is_valid = TRUE;
}
break;
}
if(new_position != current_position)
{
LONG position;
PROFILE_OFF();
position = Seek(fd->fd_DefaultFile,fam->fam_Position,new_mode);
PROFILE_ON();
if(position < 0)
{
D(("seek failed, fam->fam_Mode=%ld (%ld), offset=%ld, ioerr=%ld",new_mode,fam->fam_Mode,fam->fam_Position,IoErr()));
error = __translate_io_error_to_errno(IoErr());
#if defined(UNIX_PATH_SEMANTICS)
{
if(NOT fib_is_valid && CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fib))
{
error = __translate_io_error_to_errno(IoErr());
break;
}
if(new_position <= fib->fib_Size)
{
error = __translate_io_error_to_errno(IoErr());
break;
}
if(__grow_file_size(fd,new_position - fib->fib_Size) != OK)
{
error = __translate_io_error_to_errno(IoErr());
break;
}
}
#else
{
break;
}
#endif /* UNIX_PATH_SEMANTICS */
}
}
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
fd->fd_Position = new_position;
result = new_position;
break;
case file_action_set_blocking:
SHOWMSG("file_action_set_blocking");
PROFILE_OFF();
if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE))
{
LONG mode;
SHOWMSG("changing the mode");
if(fam->fam_Arg != 0)
mode = DOSFALSE; /* buffered mode */
else
mode = DOSTRUE; /* single character mode */
if(CANNOT SetMode(fd->fd_DefaultFile,mode))
{
error = __translate_io_error_to_errno(IoErr());
break;
}
result = 0;
}
else
{
SHOWMSG("can't do anything here");
error = EBADF;
}
PROFILE_ON();
break;
case file_action_examine:
SHOWMSG("file_action_examine");
if(fd->fd_DefaultFile != ZERO)
{
struct FileHandle * fh;
if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fam->fam_FileInfo))
{
SHOWMSG("couldn't examine the file");
error = __translate_io_error_to_errno(IoErr());
break;
}
fh = BADDR(fd->fd_DefaultFile);
fam->fam_FileSystem = fh->fh_Type;
result = 0;
}
else
{
SHOWMSG("file is already closed");
error = EBADF;
}
break;
default:
SHOWVALUE(fam->fam_Action);
error = EBADF;
break;
}
if(buffer != NULL)
free(buffer);
SHOWVALUE(result);
fam->fam_Error = error;
RETURN(result);
return(result);
}