1
0
mirror of https://github.com/adtools/clib2.git synced 2025-12-08 14:59:05 +00:00
Files
amiga-clib2/library/stdio_record_locking.c
Olaf Barthel 6f010ddf87 - File descriptors produced by dup() or dup2() now work exactly like
the original file descriptors they are duplicates of. I modified the
  function which maps file descriptor numbers to file descriptor
  table entries to return the table entries of the original files.

- In the thread-safe library, duplicated stdin/stdout/stderr
  descriptors now work like the "real" ones. This is true even if
  the "real" ones were closed and only their former aliases remain.


git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14909 87f5fb63-7c3d-0410-a384-fd976d0f7a62
2005-04-01 18:46:37 +00:00

1253 lines
28 KiB
C

/*
* $Id: stdio_record_locking.c,v 1.9 2005-04-01 18:46:37 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 _STDIO_HEADERS_H
#include "stdio_headers.h"
#endif /* _STDIO_HEADERS_H */
#ifndef _UNISTD_HEADERS_H
#include "unistd_headers.h"
#endif /* _UNISTD_HEADERS_H */
/****************************************************************************/
#ifndef _STDLIB_MEMORY_H
#include "stdlib_memory.h"
#endif /* _STDLIB_MEMORY_H */
/****************************************************************************/
#include <strings.h>
#include <limits.h>
/****************************************************************************/
#if defined(UNIX_PATH_SEMANTICS)
/****************************************************************************/
/* System-wide global data structure all programs which use advisory file record
* locking with this runtime library will use.
*/
struct FileLockSemaphore
{
struct SignalSemaphore fls_Semaphore; /* Standard signal semaphore part */
UWORD fls_Size; /* Size of this data structure (may grow) */
struct MinList fls_LockList; /* List of lock nodes */
};
/* Each file which has regions locked is registered through the following
* data structure.
*/
struct FileLockNode
{
struct MinNode fln_MinNode; /* Standard node */
struct MinList fln_LockedRegionList; /* List of locked regions */
BPTR fln_FileParentDir; /* Refers to the file's parent directory */
UBYTE fln_FileName[1]; /* Name of the file; together with the
* parent directory, this should uniquely
* identify the file.
*/
};
/* A single locked region, as associated with a file. */
struct LockedRegionNode
{
struct MinNode lrn_MinNode; /* Standard node */
LONG lrn_Start; /* Where the region begins */
LONG lrn_Stop; /* Where the region ends (inclusive) */
LONG lrn_Length; /* Original length requested */
pid_t lrn_Owner; /* Which process owns the region */
BOOL lrn_Shared; /* Whether or not this region has been locked
* for shared access.
*/
};
/****************************************************************************/
static struct FileLockSemaphore * FileLockSemaphore;
/****************************************************************************/
STATIC VOID release_file_lock_semaphore(struct FileLockSemaphore *fls);
STATIC struct FileLockSemaphore *obtain_file_lock_semaphore(BOOL shared);
STATIC VOID remove_locked_region_node(struct FileLockSemaphore *fls, struct fd *fd, LONG start, LONG stop, LONG original_length);
STATIC VOID delete_locked_region_node(struct LockedRegionNode *lrn);
STATIC LONG create_locked_region_node(struct LockedRegionNode **result_ptr);
STATIC VOID delete_file_lock_node(struct FileLockNode *fln);
STATIC LONG create_file_lock_node(struct fd *fd, struct FileLockNode **result_ptr);
STATIC LONG find_file_lock_node_by_file_handle(struct FileLockSemaphore *fls, BPTR file_handle, struct FileLockNode **result_ptr);
STATIC LONG find_file_lock_node_by_drawer_and_name(struct FileLockSemaphore *fls, BPTR dir_lock, STRPTR file_name, struct FileLockNode **result_ptr);
STATIC struct LockedRegionNode *find_colliding_region(struct FileLockNode *fln, LONG start, LONG stop, BOOL shared);
STATIC VOID cleanup_locked_records(struct fd * fd);
/****************************************************************************/
STATIC VOID
release_file_lock_semaphore(struct FileLockSemaphore * fls)
{
ENTER();
SHOWPOINTER(fls);
if(fls != NULL)
ReleaseSemaphore((struct SignalSemaphore *)fls);
LEAVE();
}
/****************************************************************************/
STATIC struct FileLockSemaphore *
obtain_file_lock_semaphore(BOOL shared)
{
struct FileLockSemaphore * result = NULL;
ENTER();
if(FileLockSemaphore == NULL && __file_lock_semaphore_name != NULL && __file_lock_semaphore_name[0] != '\0')
{
struct FileLockSemaphore * fls = NULL;
char * semaphore_name_copy = NULL;
/* We allocate the new semaphore first, so that we don't spend
any time in Forbid() allocating memory. */
#if defined(__amigaos4__)
{
semaphore_name_copy = AllocVec(strlen(__file_lock_semaphore_name)+1,MEMF_ANY|MEMF_PUBLIC);
if(semaphore_name_copy != NULL)
{
strcpy(semaphore_name_copy,__file_lock_semaphore_name);
fls = AllocSysObjectTags(ASOT_SEMAPHORE,
ASOSEM_Size, sizeof(*fls),
ASOSEM_Name, semaphore_name_copy,
ASOSEM_Pri, 1,
TAG_END);
}
}
#else
{
fls = AllocVec(sizeof(*fls) + strlen(__file_lock_semaphore_name) + 1,MEMF_ANY|MEMF_PUBLIC);
if(fls != NULL)
{
memset(fls,0,sizeof(*fls));
fls->fls_Semaphore.ss_Link.ln_Name = (char *)(fls + 1);
strcpy(fls->fls_Semaphore.ss_Link.ln_Name,__file_lock_semaphore_name);
fls->fls_Semaphore.ss_Link.ln_Pri = 1;
}
}
#endif /* __amigaos4__ */
SHOWMSG("try to find the locking semaphore");
Forbid();
FileLockSemaphore = (struct FileLockSemaphore *)FindSemaphore((STRPTR)__file_lock_semaphore_name);
if(FileLockSemaphore == NULL)
{
SHOWMSG("didn't find it; we're going to add our own");
if(fls != NULL)
{
SHOWMSG("adding our own semaphore");
fls->fls_Size = sizeof(*fls);
NewList((struct List *)&fls->fls_LockList);
AddSemaphore(&fls->fls_Semaphore);
FileLockSemaphore = fls;
fls = NULL;
semaphore_name_copy = NULL;
}
else
{
SHOWMSG("not enough memory");
}
}
Permit();
if(FileLockSemaphore != NULL && FileLockSemaphore->fls_Size < sizeof(*FileLockSemaphore))
{
SHOWMSG("semaphore found, but it's too short");
SHOWVALUE(FileLockSemaphore->fls_Size);
SHOWVALUE(sizeof(*FileLockSemaphore));
FileLockSemaphore = NULL;
}
/* Release the memory allocated for the semaphore, in case
we didn't need it after all. */
if(fls != NULL)
{
#if defined(__amigaos4__)
{
FreeVec(semaphore_name_copy);
FreeSysObject(ASOT_SEMAPHORE,fls);
}
#else
{
FreeVec(fls);
}
#endif /* __amigaos4__ */
}
}
if(FileLockSemaphore != NULL)
{
SHOWMSG("got a semaphore, using it...");
PROFILE_OFF();
if(shared)
{
#if defined(__amigaos4__)
{
ObtainSemaphoreShared((struct SignalSemaphore *)FileLockSemaphore);
}
#else
{
if(((struct Library *)SysBase)->lib_Version >= 39)
{
ObtainSemaphoreShared((struct SignalSemaphore *)FileLockSemaphore);
}
else
{
/* Workaround for shared semaphore nesting problem. */
if(CANNOT AttemptSemaphoreShared((struct SignalSemaphore *)FileLockSemaphore))
{
if(CANNOT AttemptSemaphore((struct SignalSemaphore *)FileLockSemaphore))
ObtainSemaphoreShared((struct SignalSemaphore *)FileLockSemaphore);
}
}
}
#endif /* __amigaos4__ */
}
else
{
ObtainSemaphore((struct SignalSemaphore *)FileLockSemaphore);
}
PROFILE_ON();
result = FileLockSemaphore;
}
else
{
SHOWMSG("didn't get the semaphore");
}
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC VOID
remove_locked_region_node(struct FileLockSemaphore * fls,struct fd * fd,LONG start,LONG UNUSED stop,LONG original_length)
{
ENTER();
assert( fls != NULL && fd != NULL );
if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_LOCKED))
{
struct FileLockNode * which_lock = NULL;
SHOWMSG("found a locked file");
/* Find the locked file this descriptor
* buffer belongs to.
*/
if(find_file_lock_node_by_file_handle(fls,fd->fd_DefaultFile,&which_lock) == OK)
{
struct LockedRegionNode * lrn;
struct LockedRegionNode * lrn_next;
pid_t this_task = getpid();
assert( which_lock != NULL );
D(("trying to unlock the region %ld..%ld",start,stop));
/* Find the region to unlock and remove it. */
for(lrn = (struct LockedRegionNode *)which_lock->fln_LockedRegionList.mlh_Head ;
(lrn_next = (struct LockedRegionNode *)lrn->lrn_MinNode.mln_Succ) != NULL ;
lrn = lrn_next)
{
if(lrn->lrn_Owner == this_task &&
lrn->lrn_Start == start &&
lrn->lrn_Length == original_length)
{
SHOWMSG("unlocking all regions on this file");
Remove((struct Node *)lrn);
delete_locked_region_node(lrn);
}
}
/* Check if there are any locked regions left.
* If not, mark the entire file as unlocked.
*/
if(IsListEmpty((struct List *)&which_lock->fln_LockedRegionList))
{
SHOWMSG("no more regions are locked; removing the file lock node");
Remove((struct Node *)which_lock);
delete_file_lock_node(which_lock);
/* If this is an alias, move up to the real thing. */
if(fd->fd_Original != NULL)
fd = fd->fd_Original;
do
{
CLEAR_FLAG(fd->fd_Flags,FDF_IS_LOCKED);
}
while((fd = fd->fd_NextLink) != NULL);
}
}
}
LEAVE();
}
/****************************************************************************/
STATIC VOID
delete_locked_region_node(struct LockedRegionNode * lrn)
{
ENTER();
SHOWPOINTER(lrn);
FreeVec(lrn);
LEAVE();
}
/****************************************************************************/
STATIC LONG
create_locked_region_node(struct LockedRegionNode ** result_ptr)
{
struct LockedRegionNode * lrn;
LONG error = OK;
ENTER();
assert( result_ptr != NULL );
lrn = AllocVec(sizeof(*lrn),MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR);
if(lrn == NULL)
{
SHOWMSG("not enough memory for locked region node");
error = ERROR_NO_FREE_STORE;
goto out;
}
lrn->lrn_Owner = getpid();
out:
(*result_ptr) = lrn;
RETURN(error);
return(error);
}
/****************************************************************************/
STATIC VOID
delete_file_lock_node(struct FileLockNode * fln)
{
ENTER();
SHOWPOINTER(fln);
if(fln != NULL)
{
PROFILE_OFF();
UnLock(fln->fln_FileParentDir);
PROFILE_ON();
FreeVec(fln);
}
LEAVE();
}
/****************************************************************************/
STATIC LONG
create_file_lock_node(struct fd * fd,struct FileLockNode ** result_ptr)
{
struct FileLockNode * result = NULL;
struct FileLockNode * fln = NULL;
D_S(struct FileInfoBlock,fib);
LONG error = OK;
ENTER();
assert( fd != NULL && result_ptr != NULL );
/* We store a lock on the file's parent directory
* and the name of the file for later use in
* comparisons.
*/
if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fib))
{
SHOWMSG("couldn't examine file handle");
error = IoErr();
goto out;
}
fln = AllocVec(sizeof(*fln) + strlen(fib->fib_FileName),MEMF_ANY|MEMF_PUBLIC);
if(fln == NULL)
{
SHOWMSG("not enough memory for lock node");
error = ERROR_NO_FREE_STORE;
goto out;
}
memset(fln,0,sizeof(*fln));
fln->fln_FileParentDir = __safe_parent_of_file_handle(fd->fd_DefaultFile);
if(fln->fln_FileParentDir == ZERO)
{
SHOWMSG("couldn't get parent directory");
error = IoErr();
goto out;
}
strcpy(fln->fln_FileName,fib->fib_FileName);
NewList((struct List *)&fln->fln_LockedRegionList);
result = fln;
fln = NULL;
out:
delete_file_lock_node(fln);
(*result_ptr) = result;
RETURN(error);
return(error);
}
/****************************************************************************/
STATIC LONG
find_file_lock_node_by_drawer_and_name(
struct FileLockSemaphore * fls,
BPTR dir_lock,
STRPTR file_name,
struct FileLockNode ** result_ptr)
{
DECLARE_UTILITYBASE();
struct FileLockNode * result = NULL;
struct FileLockNode * fln;
LONG status;
LONG error;
ENTER();
assert( fls != NULL && dir_lock != ZERO && file_name != NULL && result_ptr != NULL );
assert( UtilityBase != NULL );
#if DEBUG
{
char name[FILENAME_MAX];
if(NameFromLock(dir_lock,name,sizeof(name)))
{
if(AddPart(name,file_name,sizeof(name)))
D(("Looking for a lock on file |%s|",name));
}
}
#endif /* DEBUG */
error = ERROR_OBJECT_NOT_FOUND;
for(fln = (struct FileLockNode *)fls->fls_LockList.mlh_Head ;
fln->fln_MinNode.mln_Succ != NULL ;
fln = (struct FileLockNode *)fln->fln_MinNode.mln_Succ)
{
PROFILE_OFF();
status = SameLock(fln->fln_FileParentDir,dir_lock);
PROFILE_ON();
if(status == LOCK_SAME)
{
if(Stricmp(fln->fln_FileName,file_name) == SAME)
{
error = OK;
result = fln;
break;
}
}
}
if(result != NULL)
SHOWMSG("found one");
else
SHOWMSG("didn't find one");
(*result_ptr) = result;
RETURN(error);
return(error);
}
/****************************************************************************/
STATIC LONG
find_file_lock_node_by_file_handle(
struct FileLockSemaphore * fls,
BPTR file_handle,
struct FileLockNode ** result_ptr)
{
D_S(struct FileInfoBlock,this_fib);
BPTR parent_dir = ZERO;
LONG error;
ENTER();
assert( fls != NULL && file_handle != ZERO && result_ptr != NULL );
(*result_ptr) = NULL;
if(CANNOT __safe_examine_file_handle(file_handle,this_fib))
{
SHOWMSG("couldn't examine file handle");
error = IoErr();
goto out;
}
/* Determine the file's parent directory and
* name. These will be compared against the
* global file lock data.
*/
parent_dir = __safe_parent_of_file_handle(file_handle);
if(parent_dir == ZERO)
{
SHOWMSG("couldn't get parent directory");
error = IoErr();
goto out;
}
error = find_file_lock_node_by_drawer_and_name(fls,parent_dir,this_fib->fib_FileName,result_ptr);
if(error != OK)
{
SHOWMSG("couldn't find a lock node for the file");
goto out;
}
out:
PROFILE_OFF();
UnLock(parent_dir);
PROFILE_ON();
RETURN(error);
return(error);
}
/****************************************************************************/
STATIC struct LockedRegionNode *
find_colliding_region(struct FileLockNode * fln,LONG start,LONG stop,BOOL shared)
{
struct LockedRegionNode * result = NULL;
struct LockedRegionNode * lrn;
pid_t this_task = getpid();
ENTER();
assert( fln != NULL && start <= stop );
SHOWVALUE(start);
SHOWVALUE(stop);
SHOWVALUE(shared);
/* This routine looks for a locked region that overlaps
* with the specified region. It returns a pointer to the
* region that would collide with the specified region if
* a new lock were to be added.
*/
for(lrn = (struct LockedRegionNode *)fln->fln_LockedRegionList.mlh_Head ;
lrn->lrn_MinNode.mln_Succ != NULL ;
lrn = (struct LockedRegionNode *)lrn->lrn_MinNode.mln_Succ)
{
/* Do the regions overlap? */
if(lrn->lrn_Start <= stop && start <= lrn->lrn_Stop)
{
/* Two shared regions may always overlap.
* How about the rest?
*/
if(NOT shared || NOT lrn->lrn_Shared)
{
/* The lock owner may add as many exclusive
* or shared locks to the same region as
* necessary.
*/
if(lrn->lrn_Owner == this_task)
{
/*kprintf("lock collision [%ld..%ld] : [%ld..%ld]\n",start,stop,lrn->lrn_Start,lrn->lrn_Stop);*/
continue;
}
/* So we found a region that would
* cause a collision.
*/
result = lrn;
break;
}
}
}
RETURN(result);
return(result);
}
/****************************************************************************/
STATIC VOID
cleanup_locked_records(struct fd * fd)
{
ENTER();
assert( fd != NULL );
/* This routine removes all locked regions from a file
* before it is eventually closed.
*/
if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_LOCKED))
{
struct FileLockSemaphore * fls;
fls = obtain_file_lock_semaphore(FALSE);
if(fls != NULL)
{
BPTR file_handle = fd->fd_DefaultFile;
struct FileLockNode * which_lock = NULL;
pid_t this_task = getpid();
LONG error;
error = find_file_lock_node_by_file_handle(fls,file_handle,&which_lock);
if(error == OK)
{
struct LockedRegionNode * lrn_this;
struct LockedRegionNode * lrn_next;
assert( which_lock != NULL );
SHOWMSG("unlocking all regions on this file");
for(lrn_this = (struct LockedRegionNode *)which_lock->fln_LockedRegionList.mlh_Head ;
(lrn_next = (struct LockedRegionNode *)lrn_this->lrn_MinNode.mln_Succ) != NULL ;
lrn_this = lrn_next)
{
if(lrn_this->lrn_Owner == this_task)
{
Remove((struct Node *)lrn_this);
delete_locked_region_node(lrn_this);
}
}
if(IsListEmpty((struct List *)&which_lock->fln_LockedRegionList))
{
SHOWMSG("no more regions are locked; removing the file lock node");
Remove((struct Node *)which_lock);
delete_file_lock_node(which_lock);
}
}
if(error == OK || error == ERROR_OBJECT_NOT_FOUND)
{
/* If this is an alias, move up to the real thing. */
if(fd->fd_Original != NULL)
fd = fd->fd_Original;
do
{
CLEAR_FLAG(fd->fd_Flags,FDF_IS_LOCKED);
}
while((fd = fd->fd_NextLink) != NULL);
}
release_file_lock_semaphore(fls);
}
}
LEAVE();
}
/****************************************************************************/
int
__handle_record_locking(int cmd,struct flock * l,struct fd * fd,int * error_ptr)
{
struct FileLockSemaphore * fls = NULL;
BPTR file_handle = fd->fd_DefaultFile;
struct LockedRegionNode * lrn = NULL;
struct FileLockNode * fln = NULL;
D_S(struct FileInfoBlock,fib);
BOOL fib_is_valid = FALSE;
BPTR parent_dir = ZERO;
LONG current_position;
int result = ERROR;
LONG original_len;
LONG error = OK;
LONG start = 0;
LONG len = 0;
LONG stop;
ENTER();
/* This routine implements advisory file segment locking
* similar to 4.4BSD, but not quite the same. The functionality
* is a subset, somewhat similar to the functionality offered
* by the AmigaDOS LockRecord() and UnlockRecord() functions.
* This means for example that every unlock request must
* match the size and position of the corresponding locking
* request.
*
* This implementation was chosen because not every Amiga
* filing system implements record locking and Samba
* absolutely requires this functionality to work.
*/
assert( l != NULL && fd != NULL);
assert( F_RDLCK <= l->l_type && l->l_type <= F_WRLCK );
assert( SEEK_SET <= l->l_whence && l->l_whence <= SEEK_END );
assert( error_ptr != NULL );
assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO) );
/* Remember to unlock any records before closing the file. */
fd->fd_Cleanup = cleanup_locked_records;
if((cmd == F_SETLK || cmd == F_SETLKW) && (l->l_type != F_UNLCK))
{
SHOWMSG("this is a lock request");
error = create_file_lock_node(fd,&fln);
if(error != OK)
{
SHOWMSG("could not create lock node");
goto out;
}
error = create_locked_region_node(&lrn);
if(error != OK)
{
SHOWMSG("could not create region node");
goto out;
}
}
else
{
SHOWMSG("this is not a lock request");
}
original_len = l->l_len;
/* Now calculate the position of the first byte to lock and the number
* of bytes to lock.
*/
switch(l->l_whence)
{
case SEEK_SET:
SHOWMSG("SEEK_SET");
start = l->l_start;
if(l->l_len == 0)
len = LONG_MAX;
else
len = l->l_len;
SHOWVALUE(start);
SHOWVALUE(len);
break;
case SEEK_CUR:
SHOWMSG("SEEK_CUR");
PROFILE_OFF();
current_position = Seek(file_handle,0,OFFSET_CURRENT);
PROFILE_ON();
if(current_position == SEEK_ERROR)
{
SHOWMSG("could not obtain current seek position");
error = IoErr();
goto out;
}
start = current_position + l->l_start;
if(l->l_len == 0)
len = LONG_MAX;
else
len = l->l_len;
SHOWVALUE(start);
SHOWVALUE(len);
break;
case SEEK_END:
SHOWMSG("SEEK_END");
if(CANNOT __safe_examine_file_handle(file_handle,fib))
{
SHOWMSG("could not examine file");
error = IoErr();
goto out;
}
fib_is_valid = TRUE;
start = fib->fib_Size + l->l_start;
if(l->l_len == 0)
len = LONG_MAX;
else
len = l->l_len;
SHOWVALUE(start);
SHOWVALUE(len);
break;
}
SHOWVALUE(start);
SHOWVALUE(len);
if(start < 0)
{
SHOWMSG("invalid start");
(*error_ptr) = EINVAL;
goto out;
}
if(len < 0)
{
start += len;
if(start < 0)
{
SHOWMSG("invalid start");
(*error_ptr) = EINVAL;
goto out;
}
stop = start - len - 1;
if(stop < start)
stop = LONG_MAX;
}
else
{
stop = start - 1 + len;
if(stop < start - 1) /* Check for overflow */
stop = LONG_MAX;
}
if(l->l_type == F_UNLCK)
{
SHOWMSG("F_UNLCK");
fls = obtain_file_lock_semaphore(FALSE);
if(fls == NULL)
{
SHOWMSG("couldn't obtain file locking semaphore");
(*error_ptr) = EBADF;
goto out;
}
D(("unlocking %ld..%ld",start,stop));
remove_locked_region_node(fls,fd,start,stop,original_len);
}
else if (cmd == F_SETLKW)
{
struct FileLockNode * existing_fln;
BOOL locked;
BOOL shared;
SHOWMSG("F_SETLKW");
D((" locking %ld..%ld",start,stop));
if(NOT fib_is_valid && CANNOT __safe_examine_file_handle(file_handle,fib))
{
SHOWMSG("couldn't read this file's name");
error = IoErr();
goto out;
}
parent_dir = __safe_parent_of_file_handle(file_handle);
if(parent_dir == ZERO)
{
SHOWMSG("couldn't get a lock on the file's parent directory");
error = IoErr();
goto out;
}
shared = (BOOL)(l->l_type == F_RDLCK);
if(shared)
D(("this is a shared lock; waiting for completion"));
else
D(("this is an exclusive lock; waiting for completion"));
/* Shared locks require readable files, exclusive locks require
writable files. */
if((shared && FLAG_IS_CLEAR(fd->fd_Flags,FDF_READ)) ||
(NOT shared && FLAG_IS_CLEAR(fd->fd_Flags,FDF_WRITE)))
{
(*error_ptr) = EBADF;
goto out;
}
lrn->lrn_Start = start;
lrn->lrn_Stop = stop;
lrn->lrn_Length = original_len;
lrn->lrn_Shared = shared;
/* Retry until we manage to lock the record. */
locked = FALSE;
do
{
fls = obtain_file_lock_semaphore(FALSE);
if(fls == NULL)
{
SHOWMSG("couldn't obtain file locking semaphore");
(*error_ptr) = EBADF;
goto out;
}
if(find_file_lock_node_by_drawer_and_name(fls,parent_dir,fib->fib_FileName,&existing_fln) == OK)
{
SHOWMSG("that file is already locked by someone");
if(find_colliding_region(existing_fln,start,stop,shared) == NULL)
{
SHOWMSG("but the locks don't collide");
AddTail((struct List *)&existing_fln->fln_LockedRegionList,(struct Node *)lrn);
lrn = NULL;
locked = TRUE;
}
else
{
SHOWMSG("and the locks collide");
}
}
else
{
SHOWMSG("nobody has any locks on this file");
AddTail((struct List *)&fls->fls_LockList,(struct Node *)fln);
AddTail((struct List *)&fln->fln_LockedRegionList,(struct Node *)lrn);
fln = NULL;
lrn = NULL;
locked = TRUE;
}
release_file_lock_semaphore(fls);
fls = NULL;
if(NOT locked)
{
const int rand_max = RAND_MAX / 65536;
int num_random_ticks;
if(__check_abort_enabled && (SetSignal(0,0) & SIGBREAKF_CTRL_C) != 0)
{
SHOWMSG("lock polling loop stopped");
delete_file_lock_node(fln);
fln = NULL;
delete_locked_region_node(lrn);
lrn = NULL;
PROFILE_OFF();
UnLock(parent_dir);
PROFILE_ON();
parent_dir = ZERO;
(*error_ptr) = EINTR;
if(__check_abort_enabled)
__check_abort();
goto out;
}
/* Wait a little before retrying
* the locking operation. We add
* a little randomness here to
* reduce the likelihood of two
* competing processes trying to
* lock the same file at the
* same time.
*/
num_random_ticks = ((TICKS_PER_SECOND / 2) * (rand() / 65536)) / rand_max;
if(num_random_ticks > 0)
{
PROFILE_OFF();
Delay(num_random_ticks);
PROFILE_ON();
}
}
}
while(NOT locked);
SHOWMSG("the file now has a lock set");
/* If this is an alias, move up to the real thing. */
if(fd->fd_Original != NULL)
fd = fd->fd_Original;
do
{
SET_FLAG(fd->fd_Flags,FDF_IS_LOCKED);
}
while((fd = fd->fd_NextLink) != NULL);
}
else if (cmd == F_SETLK)
{
BOOL shared = (BOOL)(l->l_type == F_RDLCK);
struct FileLockNode * existing_fln = NULL;
SHOWMSG("F_SETLK");
if(shared)
D(("this is a shared lock"));
else
D(("this is an exclusive lock"));
/* Shared locks require readable files, exclusive locks require
writable files. */
if((shared && FLAG_IS_CLEAR(fd->fd_Flags,FDF_READ)) ||
(NOT shared && FLAG_IS_CLEAR(fd->fd_Flags,FDF_WRITE)))
{
(*error_ptr) = EBADF;
goto out;
}
lrn->lrn_Start = start;
lrn->lrn_Stop = stop;
lrn->lrn_Length = original_len;
lrn->lrn_Shared = shared;
fls = obtain_file_lock_semaphore(FALSE);
if(fls == NULL)
{
SHOWMSG("couldn't obtain file locking semaphore");
(*error_ptr) = EBADF;
goto out;
}
error = find_file_lock_node_by_file_handle(fls,file_handle,&existing_fln);
if(error == OK)
{
SHOWMSG("that file is already locked by someone else");
if(find_colliding_region(existing_fln,start,stop,shared) != NULL)
{
SHOWMSG("and the locks collide");
(*error_ptr) = EACCES;
goto out;
}
SHOWMSG("but the locks don't collide");
AddTail((struct List *)&existing_fln->fln_LockedRegionList,(struct Node *)lrn);
lrn = NULL;
}
else
{
if(error != ERROR_OBJECT_NOT_FOUND)
goto out;
SHOWMSG("nobody has any locks on this file");
AddTail((struct List *)&fls->fls_LockList,(struct Node *)fln);
AddTail((struct List *)&fln->fln_LockedRegionList,(struct Node *)lrn);
fln = NULL;
lrn = NULL;
}
SHOWMSG("the file now has a lock set");
/* If this is an alias, move up to the real thing. */
if(fd->fd_Original != NULL)
fd = fd->fd_Original;
do
{
SET_FLAG(fd->fd_Flags,FDF_IS_LOCKED);
}
while((fd = fd->fd_NextLink) != NULL);
}
else if (cmd == F_GETLK)
{
struct FileLockNode * fln_found = NULL;
SHOWMSG("F_GETLK");
fls = obtain_file_lock_semaphore(TRUE);
if(fls == NULL)
{
SHOWMSG("couldn't obtain file locking semaphore");
(*error_ptr) = EBADF;
goto out;
}
SHOWMSG("checking for possible lock collision");
error = find_file_lock_node_by_file_handle(fls,file_handle,&fln_found);
if(error == OK)
{
BOOL shared = (BOOL)(l->l_type == F_RDLCK);
struct LockedRegionNode * lrn_found;
SHOWMSG("somebody has locked this file");
lrn_found = find_colliding_region(fln_found,start,stop,shared);
if(lrn_found != NULL)
{
SHOWMSG("there is a possible lock collision");
l->l_type = (lrn_found->lrn_Shared ? F_RDLCK : F_WRLCK);
l->l_whence = SEEK_SET;
l->l_start = lrn_found->lrn_Start;
l->l_len = lrn_found->lrn_Length;
l->l_pid = lrn_found->lrn_Owner;
SHOWVALUE(l->l_type);
SHOWVALUE(l->l_whence);
SHOWVALUE(l->l_start);
SHOWVALUE(l->l_len);
SHOWVALUE(l->l_pid);
}
else
{
SHOWMSG("there is no lock collision");
l->l_type = F_UNLCK;
}
}
else
{
if(error != ERROR_OBJECT_NOT_FOUND)
goto out;
SHOWMSG("nobody has locked this file");
l->l_type = F_UNLCK;
}
}
result = 0;
out:
delete_file_lock_node(fln);
delete_locked_region_node(lrn);
release_file_lock_semaphore(fls);
PROFILE_OFF();
UnLock(parent_dir);
PROFILE_ON();
if(result != 0 && error != OK)
{
SetIoErr(error);
(*error_ptr) = __translate_io_error_to_errno(error);
}
RETURN(result);
return(result);
}
/****************************************************************************/
#endif /* UNIX_PATH_SEMANTICS */