From 99547756fef18a796e0d1f8dd99b4b13a166caef Mon Sep 17 00:00:00 2001 From: Olaf Barthel Date: Fri, 18 Feb 2005 18:53:17 +0000 Subject: [PATCH] - Major, major changes! Moved most of the monolithic code out of the file descriptor hook and into the respective functions, such as dup2(), fchmod(), fchown(), fcntl(), fdatasync(), fstatfs(), fsync(), ftruncate() and lseek(). Code which is not strictly required will no longer find its way into your programs if you link with the updated library. NOTE: these changes require that the entire library is rebuilt! git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14833 87f5fb63-7c3d-0410-a384-fd976d0f7a62 --- library/GNUmakefile.68k | 7 +- library/GNUmakefile.os4 | 7 +- library/changes | 9 + library/fcntl_fcntl.c | 49 +- library/fcntl_lseek.c | 120 +- library/mount_fstatfs.c | 49 +- library/smakefile | 7 +- library/socket_accept.c | 4 +- library/socket_bind.c | 4 +- library/socket_connect.c | 4 +- library/socket_get_descriptor.c | 4 +- library/socket_getpeername.c | 4 +- library/socket_getsockname.c | 4 +- library/socket_getsockopt.c | 4 +- library/socket_headers.h | 4 +- library/socket_hook_entry.c | 31 +- library/socket_ioctl.c | 4 +- library/socket_listen.c | 4 +- library/socket_recv.c | 4 +- library/socket_recvfrom.c | 4 +- library/socket_recvmsg.c | 4 +- library/socket_send.c | 4 +- library/socket_sendmsg.c | 4 +- library/socket_sendto.c | 4 +- library/socket_setsockopt.c | 4 +- library/socket_shutdown.c | 4 +- library/stat_fchmod.c | 64 +- library/stdio_examine_fh.c | 70 ++ library/stdio_fdhookentry.c | 1869 +------------------------------ library/stdio_grow_file.c | 161 +++ library/stdio_headers.h | 39 +- library/stdio_parent_of_fh.c | 68 ++ library/stdio_protos.h | 27 +- library/stdio_record_locking.c | 1202 ++++++++++++++++++++ library/unistd_dup2.c | 19 +- library/unistd_fchown.c | 111 +- library/unistd_fdatasync.c | 23 +- library/unistd_fsync.c | 23 +- library/unistd_ftruncate.c | 62 +- library/unistd_sync_fd.c | 70 ++ 40 files changed, 2058 insertions(+), 2101 deletions(-) create mode 100644 library/stdio_examine_fh.c create mode 100644 library/stdio_grow_file.c create mode 100644 library/stdio_parent_of_fh.c create mode 100644 library/stdio_record_locking.c create mode 100644 library/unistd_sync_fd.c diff --git a/library/GNUmakefile.68k b/library/GNUmakefile.68k index 49b9361..474d331 100644 --- a/library/GNUmakefile.68k +++ b/library/GNUmakefile.68k @@ -1,5 +1,5 @@ # -# $Id: GNUmakefile.68k,v 1.26 2005-02-07 10:52:16 obarthel Exp $ +# $Id: GNUmakefile.68k,v 1.27 2005-02-18 18:53:16 obarthel Exp $ # # :ts=8 # @@ -181,6 +181,7 @@ C_LIB = \ stdio_data.o \ stdio_dropiobreadbuffer.o \ stdio_duplicate_fd.o \ + stdio_examine_fh.o \ stdio_fclose.o \ stdio_fdhookentry.o \ stdio_feof.o \ @@ -211,6 +212,7 @@ C_LIB = \ stdio_gets.o \ stdio_growfdtable.o \ stdio_growiobtable.o \ + stdio_grow_file.o \ stdio_initializefd.o \ stdio_initializeiob.o \ stdio_init_exit.o \ @@ -218,6 +220,7 @@ C_LIB = \ stdio_locksemaphorename.o \ stdio_nostdio.o \ stdio_openiob.o \ + stdio_parent_of_fh.o \ stdio_perror.o \ stdio_popen.o \ stdio_printf.o \ @@ -405,6 +408,7 @@ C_LIB = \ unistd_realpath.o \ unistd_sleep.o \ unistd_symlink.o \ + unistd_sync_fd.o \ unistd_timer.o \ unistd_time_delay.o \ unistd_truncate.o \ @@ -438,6 +442,7 @@ UNIX_LIB = \ stdio_locksemaphorename.o \ stdio_openiob.o \ stdio_popen.o \ + stdio_record_locking.o \ stdio_remove.o \ stdio_rename.o \ stdlib_mkdtemp.o \ diff --git a/library/GNUmakefile.os4 b/library/GNUmakefile.os4 index f1ac37d..341b72c 100644 --- a/library/GNUmakefile.os4 +++ b/library/GNUmakefile.os4 @@ -1,5 +1,5 @@ # -# $Id: GNUmakefile.os4,v 1.26 2005-02-07 10:52:16 obarthel Exp $ +# $Id: GNUmakefile.os4,v 1.27 2005-02-18 18:53:16 obarthel Exp $ # # :ts=8 # @@ -185,6 +185,7 @@ C_LIB = \ stdio_data.o \ stdio_dropiobreadbuffer.o \ stdio_duplicate_fd.o \ + stdio_examine_fh.o \ stdio_fclose.o \ stdio_fdhookentry.o \ stdio_feof.o \ @@ -215,6 +216,7 @@ C_LIB = \ stdio_gets.o \ stdio_growfdtable.o \ stdio_growiobtable.o \ + stdio_grow_file.o \ stdio_initializefd.o \ stdio_initializeiob.o \ stdio_init_exit.o \ @@ -222,6 +224,7 @@ C_LIB = \ stdio_locksemaphorename.o \ stdio_nostdio.o \ stdio_openiob.o \ + stdio_parent_of_fh.o \ stdio_perror.o \ stdio_popen.o \ stdio_printf.o \ @@ -409,6 +412,7 @@ C_LIB = \ unistd_realpath.o \ unistd_sleep.o \ unistd_symlink.o \ + unistd_sync_fd.o \ unistd_timer.o \ unistd_time_delay.o \ unistd_truncate.o \ @@ -443,6 +447,7 @@ UNIX_LIB = \ stdio_locksemaphorename.o \ stdio_openiob.o \ stdio_popen.o \ + stdio_record_locking.o \ stdio_remove.o \ stdio_rename.o \ stdlib_mkdtemp.o \ diff --git a/library/changes b/library/changes index 992c872..e4b9f4c 100644 --- a/library/changes +++ b/library/changes @@ -2,6 +2,15 @@ translate patterns such as "foo/bar/../../baz" properly, and to use strlen() less. +- Major, major changes! Moved most of the monolithic code out of + the file descriptor hook and into the respective functions, + such as dup2(), fchmod(), fchown(), fcntl(), fdatasync(), fstatfs(), + fsync(), ftruncate() and lseek(). Code which is not strictly + required will no longer find its way into your programs if you + link with the updated library. + + NOTE: these changes require that the entire library is rebuilt! + c.lib 1.188 (7.2.2005) diff --git a/library/fcntl_fcntl.c b/library/fcntl_fcntl.c index 1b65261..ceae569 100644 --- a/library/fcntl_fcntl.c +++ b/library/fcntl_fcntl.c @@ -1,5 +1,5 @@ /* - * $Id: fcntl_fcntl.c,v 1.6 2005-02-03 16:56:15 obarthel Exp $ + * $Id: fcntl_fcntl.c,v 1.7 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -50,6 +50,7 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) int result = -1; struct fd * fd; va_list arg; + int error; int flags; int fdbase; int i; @@ -83,6 +84,12 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) SHOWMSG("cmd=F_GETLK/F_SETLK/F_SETLKW"); + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } + va_start(arg,cmd); l = va_arg(arg,struct flock *); @@ -107,16 +114,9 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) break; } - message.action = file_hook_action_lock_record; - message.lock = l; - message.command = cmd; - - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - result = message.result; - __set_errno(message.error); + result = __handle_record_locking(cmd,l,fd,&error); + if(result < 0) + __set_errno(error); va_end(arg); @@ -126,6 +126,12 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) SHOWMSG("cmd=F_GETFL"); + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } + result = 0; if(FLAG_IS_SET(fd->fd_Flags,FDF_NON_BLOCKING)) @@ -140,6 +146,12 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) SHOWMSG("cmd=F_SETFL"); + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } + result = 0; va_start(arg,cmd); @@ -236,19 +248,10 @@ fcntl(int file_descriptor, int cmd, ... /* int arg */ ) if(FLAG_IS_CLEAR(__fd[i]->fd_Flags,FDF_IN_USE)) { /* Got a file descriptor, duplicate it */ - message.action = file_hook_action_duplicate_fd; - message.duplicate_fd = __fd[i]; + __duplicate_fd(__fd[i],fd); - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - /* If it worked, leave */ - if (message.result == 0) - { - result = i; - goto out; - } + result = i; + goto out; } } diff --git a/library/fcntl_lseek.c b/library/fcntl_lseek.c index 6093548..1f7c04b 100644 --- a/library/fcntl_lseek.c +++ b/library/fcntl_lseek.c @@ -1,5 +1,5 @@ /* - * $Id: fcntl_lseek.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: fcntl_lseek.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -44,10 +44,13 @@ off_t __lseek(int file_descriptor, off_t offset, int mode, int * error_ptr) { - DECLARE_UTILITYBASE(); - struct file_hook_message message; + D_S(struct FileInfoBlock,fib); off_t result = -1; struct fd * fd; + BOOL fib_is_valid = FALSE; + LONG current_position; + LONG new_position; + LONG new_mode; ENTER(); @@ -55,8 +58,6 @@ __lseek(int file_descriptor, off_t offset, int mode, int * error_ptr) SHOWVALUE(offset); SHOWVALUE(mode); - assert( UtilityBase != NULL ); - assert( error_ptr != NULL ); assert( file_descriptor >= 0 && file_descriptor < __num_fd ); assert( __fd[file_descriptor] != NULL ); @@ -72,6 +73,14 @@ __lseek(int file_descriptor, off_t offset, int mode, int * error_ptr) goto out; } + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + SHOWMSG("can't seek on a socket"); + + (*error_ptr) = ESPIPE; + goto out; + } + if(mode < SEEK_SET || mode > SEEK_END) { SHOWMSG("seek mode is invalid"); @@ -80,28 +89,105 @@ __lseek(int file_descriptor, off_t offset, int mode, int * error_ptr) goto out; } - SHOWMSG("calling the hook"); + if(mode == SEEK_CUR) + new_mode = OFFSET_CURRENT; + else if (mode == SEEK_SET) + new_mode = OFFSET_BEGINNING; + else + new_mode = OFFSET_END; - #if defined(UNIX_PATH_SEMANTICS) + D(("seek&extended to offset %ld, mode %ld; current position = %ld",offset,new_mode,Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT))); + + if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) { - message.action = file_hook_action_seek_and_extend; + current_position = fd->fd_Position; } - #else + else { - message.action = file_hook_action_seek; + PROFILE_OFF(); + current_position = Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT); + PROFILE_ON(); + + if(current_position < 0) + { + (*error_ptr) = EBADF; + goto out; + } } - #endif /* UNIX_PATH_SEMANTICS */ - message.position = offset; - message.mode = mode; + new_position = current_position; - assert( fd->fd_Hook != NULL ); + switch(new_mode) + { + case OFFSET_CURRENT: - CallHookPkt(fd->fd_Hook,fd,&message); + new_position += offset; + break; - (*error_ptr) = message.error; + case OFFSET_BEGINNING: - result = message.result; + new_position = offset; + break; + + case OFFSET_END: + + if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) + { + new_position = fib->fib_Size + offset; + + fib_is_valid = TRUE; + } + + break; + } + + if(new_position != current_position) + { + LONG position; + + PROFILE_OFF(); + position = Seek(fd->fd_DefaultFile,offset,new_mode); + PROFILE_ON(); + + if(position < 0) + { + D(("seek failed, mode=%ld (%ld), offset=%ld, ioerr=%ld",new_mode,message->mode,offset,IoErr())); + + (*error_ptr) = __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_ptr) = __translate_io_error_to_errno(IoErr()); + goto out; + } + + if(new_position <= fib->fib_Size) + { + (*error_ptr) = __translate_io_error_to_errno(IoErr()); + goto out; + } + + if(__grow_file_size(fd,new_position - fib->fib_Size) != OK) + { + (*error_ptr) = __translate_io_error_to_errno(IoErr()); + goto out; + } + } + #else + { + (*error_ptr) = __translate_io_error_to_errno(IoErr()); + goto out; + } + #endif /* UNIX_PATH_SEMANTICS */ + } + } + + if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) + fd->fd_Position = new_position; + + result = new_position; out: diff --git a/library/mount_fstatfs.c b/library/mount_fstatfs.c index 12cf9f1..3b89237 100644 --- a/library/mount_fstatfs.c +++ b/library/mount_fstatfs.c @@ -1,5 +1,5 @@ /* - * $Id: mount_fstatfs.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: mount_fstatfs.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -50,11 +50,11 @@ int fstatfs(int file_descriptor, struct statfs *buf) { - DECLARE_UTILITYBASE(); - struct file_hook_message message; D_S(struct InfoData,id); + BPTR parent_dir = ZERO; int result = -1; struct fd * fd; + LONG success; ENTER(); @@ -62,7 +62,6 @@ fstatfs(int file_descriptor, struct statfs *buf) SHOWPOINTER(buf); assert( buf != NULL ); - assert( UtilityBase != NULL ); #if defined(CHECK_FOR_NULL_POINTERS) { @@ -90,26 +89,44 @@ fstatfs(int file_descriptor, struct statfs *buf) goto out; } - SHOWMSG("calling the hook"); - - message.action = file_hook_action_info; - message.info_data = id; - - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - result = message.result; - if(result != 0) + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) { - __set_errno(message.error); + __set_errno(EINVAL); + goto out; + } + + PROFILE_OFF(); + parent_dir = __safe_parent_of_file_handle(fd->fd_DefaultFile); + PROFILE_ON(); + + if(parent_dir == ZERO) + { + SHOWMSG("couldn't find parent directory"); + + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + PROFILE_OFF(); + success = Info(parent_dir,id); + PROFILE_ON(); + + if(NO success) + { + SHOWMSG("couldn't get info on drive"); + + __set_errno(__translate_io_error_to_errno(IoErr())); goto out; } __convert_info_to_statfs(id,buf); + result = OK; + out: + UnLock(parent_dir); + RETURN(result); return(result); } diff --git a/library/smakefile b/library/smakefile index 399bd1e..5565124 100644 --- a/library/smakefile +++ b/library/smakefile @@ -1,5 +1,5 @@ _# -# $Id: smakefile,v 1.20 2005-02-04 15:03:10 obarthel Exp $ +# $Id: smakefile,v 1.21 2005-02-18 18:53:16 obarthel Exp $ # # :ts=8 # @@ -288,8 +288,10 @@ STDIO_OBJ = \ stdio_data.o \ stdio_dropiobreadbuffer.o \ stdio_duplicate_fd.o \ + stdio_examine_fh.o \ stdio_fclose.o \ stdio_fdhookentry.o \ + stdio_record_locking.o \ stdio_feof.o \ stdio_ferror.o \ stdio_fflush.o \ @@ -318,6 +320,7 @@ STDIO_OBJ = \ stdio_gets.o \ stdio_growfdtable.o \ stdio_growiobtable.o \ + stdio_grow_file.o \ stdio_initializefd.o \ stdio_initializeiob.o \ stdio_init_exit.o \ @@ -325,6 +328,7 @@ STDIO_OBJ = \ stdio_locksemaphorename.o \ stdio_nostdio.o \ stdio_openiob.o \ + stdio_parent_of_fh.o \ stdio_perror.o \ stdio_popen.o \ stdio_printf.o \ @@ -520,6 +524,7 @@ UNISTD_OBJ = \ unistd_sleep.o \ unistd_strip_double_slash.o \ unistd_symlink.o \ + unistd_sync_fd.o \ unistd_time_delay.o \ unistd_timer.o \ unistd_translatea2u.o \ diff --git a/library/socket_accept.c b/library/socket_accept.c index f4ad1b7..760e4a2 100644 --- a/library/socket_accept.c +++ b/library/socket_accept.c @@ -1,5 +1,5 @@ /* - * $Id: socket_accept.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_accept.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -82,7 +82,7 @@ accept(int sockfd,struct sockaddr *cliaddr,int *addrlen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_bind.c b/library/socket_bind.c index e6e1225..67e6a9e 100644 --- a/library/socket_bind.c +++ b/library/socket_bind.c @@ -1,5 +1,5 @@ /* - * $Id: socket_bind.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_bind.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ bind(int sockfd,struct sockaddr *name,int namelen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_connect.c b/library/socket_connect.c index a98e267..712555d 100644 --- a/library/socket_connect.c +++ b/library/socket_connect.c @@ -1,5 +1,5 @@ /* - * $Id: socket_connect.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_connect.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ connect(int sockfd,struct sockaddr *name,int namelen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_get_descriptor.c b/library/socket_get_descriptor.c index 54a7913..c290e96 100644 --- a/library/socket_get_descriptor.c +++ b/library/socket_get_descriptor.c @@ -1,5 +1,5 @@ /* - * $Id: socket_get_descriptor.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_get_descriptor.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -42,7 +42,7 @@ /****************************************************************************/ struct fd * -__get_socket_descriptor(int socket_descriptor) +__get_file_descriptor_socket(int socket_descriptor) { struct fd * result = NULL; struct fd * fd; diff --git a/library/socket_getpeername.c b/library/socket_getpeername.c index 6249471..0e4764c 100644 --- a/library/socket_getpeername.c +++ b/library/socket_getpeername.c @@ -1,5 +1,5 @@ /* - * $Id: socket_getpeername.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_getpeername.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ getpeername(int sockfd,struct sockaddr *name,int *namelen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_getsockname.c b/library/socket_getsockname.c index 659235d..52c3ff5 100644 --- a/library/socket_getsockname.c +++ b/library/socket_getsockname.c @@ -1,5 +1,5 @@ /* - * $Id: socket_getsockname.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_getsockname.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ getsockname(int sockfd,struct sockaddr *name,int *namelen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_getsockopt.c b/library/socket_getsockopt.c index 27ac3a5..66f9271 100644 --- a/library/socket_getsockopt.c +++ b/library/socket_getsockopt.c @@ -1,5 +1,5 @@ /* - * $Id: socket_getsockopt.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_getsockopt.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -81,7 +81,7 @@ getsockopt(int sockfd,int level,int optname,void *optval,int *optlen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_headers.h b/library/socket_headers.h index 0132aec..328b62d 100644 --- a/library/socket_headers.h +++ b/library/socket_headers.h @@ -1,5 +1,5 @@ /* - * $Id: socket_headers.h,v 1.5 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_headers.h,v 1.6 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -70,7 +70,7 @@ extern int NOCOMMON h_errno; /****************************************************************************/ -extern struct fd * __get_socket_descriptor(int socket_descriptor); +extern struct fd * __get_file_descriptor_socket(int socket_descriptor); extern void __socket_hook_entry(struct Hook * hook,struct fd * fd,struct file_hook_message * message); /****************************************************************************/ diff --git a/library/socket_hook_entry.c b/library/socket_hook_entry.c index 611d78d..7bf8bf3 100644 --- a/library/socket_hook_entry.c +++ b/library/socket_hook_entry.c @@ -1,5 +1,5 @@ /* - * $Id: socket_hook_entry.c,v 1.6 2005-02-04 15:03:10 obarthel Exp $ + * $Id: socket_hook_entry.c,v 1.7 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -114,26 +114,6 @@ __socket_hook_entry( break; - case file_hook_action_duplicate_fd: - - SHOWMSG("file_hook_action_duplicate_fd"); - - __duplicate_fd(message->duplicate_fd,fd); - - result = 0; - - break; - - case file_hook_action_seek: - case file_hook_action_seek_and_extend: - - SHOWMSG("file_hook_action_seek"); - - result = -1; - error = ESPIPE; - - break; - case file_hook_action_set_blocking: SHOWMSG("file_hook_action_set_blocking"); @@ -174,15 +154,6 @@ __socket_hook_entry( break; - case file_hook_action_flush: - - SHOWMSG("file_hook_action_flush attempted on socket"); - - result = -1; - error = EINVAL; - - break; - default: SHOWVALUE(message->action); diff --git a/library/socket_ioctl.c b/library/socket_ioctl.c index 0611268..f3af8a1 100644 --- a/library/socket_ioctl.c +++ b/library/socket_ioctl.c @@ -1,5 +1,5 @@ /* - * $Id: socket_ioctl.c,v 1.4 2005-01-02 09:07:08 obarthel Exp $ + * $Id: socket_ioctl.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -71,7 +71,7 @@ ioctl(int sockfd,unsigned long request, ... /* char *arg */) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_listen.c b/library/socket_listen.c index 1f1023c..8ce5108 100644 --- a/library/socket_listen.c +++ b/library/socket_listen.c @@ -1,5 +1,5 @@ /* - * $Id: socket_listen.c,v 1.2 2005-01-02 09:07:08 obarthel Exp $ + * $Id: socket_listen.c,v 1.3 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -59,7 +59,7 @@ listen(int sockfd,int backlog) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_recv.c b/library/socket_recv.c index e4a483e..9c6ef7d 100644 --- a/library/socket_recv.c +++ b/library/socket_recv.c @@ -1,5 +1,5 @@ /* - * $Id: socket_recv.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_recv.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -81,7 +81,7 @@ recv(int sockfd,void *buff,size_t nbytes,int flags) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_recvfrom.c b/library/socket_recvfrom.c index 3950ea7..5fd94e9 100644 --- a/library/socket_recvfrom.c +++ b/library/socket_recvfrom.c @@ -1,5 +1,5 @@ /* - * $Id: socket_recvfrom.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_recvfrom.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -82,7 +82,7 @@ recvfrom(int sockfd,void *buff,int len,int flags,struct sockaddr *from,int *from assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_recvmsg.c b/library/socket_recvmsg.c index 3c7c6c1..a557824 100644 --- a/library/socket_recvmsg.c +++ b/library/socket_recvmsg.c @@ -1,5 +1,5 @@ /* - * $Id: socket_recvmsg.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_recvmsg.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ recvmsg(int sockfd,struct msghdr *msg,int flags) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_send.c b/library/socket_send.c index 8dcb0dc..a069b5d 100644 --- a/library/socket_send.c +++ b/library/socket_send.c @@ -1,5 +1,5 @@ /* - * $Id: socket_send.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_send.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -81,7 +81,7 @@ send(int sockfd,const void *buff,size_t nbytes,int flags) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_sendmsg.c b/library/socket_sendmsg.c index 1f378cc..5138cc8 100644 --- a/library/socket_sendmsg.c +++ b/library/socket_sendmsg.c @@ -1,5 +1,5 @@ /* - * $Id: socket_sendmsg.c,v 1.3 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_sendmsg.c,v 1.4 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -79,7 +79,7 @@ sendmsg(int sockfd,struct msghdr *msg,int flags) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_sendto.c b/library/socket_sendto.c index 98b983e..bdad14c 100644 --- a/library/socket_sendto.c +++ b/library/socket_sendto.c @@ -1,5 +1,5 @@ /* - * $Id: socket_sendto.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_sendto.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -82,7 +82,7 @@ sendto(int sockfd,const void *buff,int len,int flags,struct sockaddr *to,int tol assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_setsockopt.c b/library/socket_setsockopt.c index 66a5afe..d5a43cd 100644 --- a/library/socket_setsockopt.c +++ b/library/socket_setsockopt.c @@ -1,5 +1,5 @@ /* - * $Id: socket_setsockopt.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: socket_setsockopt.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -81,7 +81,7 @@ setsockopt(int sockfd,int level,int optname,const void *optval,int optlen) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/socket_shutdown.c b/library/socket_shutdown.c index 08a5e56..57ccdad 100644 --- a/library/socket_shutdown.c +++ b/library/socket_shutdown.c @@ -1,5 +1,5 @@ /* - * $Id: socket_shutdown.c,v 1.2 2005-01-02 09:07:08 obarthel Exp $ + * $Id: socket_shutdown.c,v 1.3 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -59,7 +59,7 @@ shutdown(int sockfd, int how) assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IN_USE) ); assert( FLAG_IS_SET(__fd[sockfd]->fd_Flags,FDF_IS_SOCKET) ); - fd = __get_socket_descriptor(sockfd); + fd = __get_file_descriptor_socket(sockfd); if(fd == NULL) goto out; diff --git a/library/stat_fchmod.c b/library/stat_fchmod.c index ff86e6a..26711f2 100644 --- a/library/stat_fchmod.c +++ b/library/stat_fchmod.c @@ -1,5 +1,5 @@ /* - * $Id: stat_fchmod.c,v 1.4 2005-02-03 16:56:15 obarthel Exp $ + * $Id: stat_fchmod.c,v 1.5 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -44,19 +44,20 @@ int fchmod(int file_descriptor, mode_t mode) { - DECLARE_UTILITYBASE(); - struct file_hook_message message; + D_S(struct FileInfoBlock,fib); ULONG protection; + BPTR parent_dir = ZERO; + BPTR old_current_dir = ZERO; + BOOL current_dir_changed = FALSE; int result = -1; struct fd * fd; + LONG success; ENTER(); SHOWVALUE(file_descriptor); SHOWVALUE(mode); - assert( UtilityBase != NULL ); - assert( file_descriptor >= 0 && file_descriptor < __num_fd ); assert( __fd[file_descriptor] != NULL ); assert( FLAG_IS_SET(__fd[file_descriptor]->fd_Flags,FDF_IN_USE) ); @@ -71,6 +72,12 @@ fchmod(int file_descriptor, mode_t mode) goto out; } + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } + protection = 0; if(FLAG_IS_SET(mode,S_IRUSR)) @@ -109,21 +116,54 @@ fchmod(int file_descriptor, mode_t mode) if(FLAG_IS_SET(mode,S_IXOTH)) SET_FLAG(protection,FIBF_OTR_EXECUTE); - SHOWMSG("calling the hook"); + PROFILE_OFF(); + parent_dir = __safe_parent_of_file_handle(fd->fd_DefaultFile); + PROFILE_ON(); - message.action = file_hook_action_change_attributes; - message.attributes = protection ^ (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE); + if(parent_dir == ZERO) + { + SHOWMSG("couldn't find parent directory"); - assert( fd->fd_Hook != NULL ); + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } - CallHookPkt(fd->fd_Hook,fd,&message); + PROFILE_OFF(); + success = __safe_examine_file_handle(fd->fd_DefaultFile,fib); + PROFILE_ON(); - result = message.result; + if(NO success) + { + SHOWMSG("could not obtain file name"); - __set_errno(message.error); + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + old_current_dir = CurrentDir(parent_dir); + current_dir_changed = TRUE; + + PROFILE_OFF(); + success = SetProtection(fib->fib_FileName,protection ^ (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE)); + PROFILE_ON(); + + if(NO success) + { + SHOWMSG("could not change protection bits"); + + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + result = OK; out: + if(current_dir_changed) + CurrentDir(old_current_dir); + + UnLock(parent_dir); + RETURN(result); return(result); } diff --git a/library/stdio_examine_fh.c b/library/stdio_examine_fh.c new file mode 100644 index 0000000..bea3505 --- /dev/null +++ b/library/stdio_examine_fh.c @@ -0,0 +1,70 @@ +/* + * $Id: stdio_examine_fh.c,v 1.1 2005-02-18 18:53:16 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2005 by Olaf Barthel + * 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 */ + +/****************************************************************************/ + +/* This is used in place of ExamineFH() in order to work around a bug in + * dos.library V40 and below: a "NIL:" file handle will crash the + * ExamineFH() function. + */ +LONG +__safe_examine_file_handle(BPTR file_handle,struct FileInfoBlock *fib) +{ + LONG result = DOSFALSE; + + assert( fib != NULL ); + + #ifndef __amigaos4__ + { + struct FileHandle * fh = (struct FileHandle *)BADDR(file_handle); + + if(fh == NULL || fh->fh_Type == NULL) + { + SetIoErr(ERROR_OBJECT_WRONG_TYPE); + goto out; + } + } + #endif /* __amigaos4__ */ + + PROFILE_OFF(); + result = ExamineFH(file_handle,fib); + PROFILE_ON(); + + out: + + return(result); +} diff --git a/library/stdio_fdhookentry.c b/library/stdio_fdhookentry.c index bc784d0..80c0ad2 100644 --- a/library/stdio_fdhookentry.c +++ b/library/stdio_fdhookentry.c @@ -1,5 +1,5 @@ /* - * $Id: stdio_fdhookentry.c,v 1.10 2005-02-07 11:19:31 obarthel Exp $ + * $Id: stdio_fdhookentry.c,v 1.11 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -52,1379 +52,6 @@ /****************************************************************************/ -#if defined(__amigaos4__) && !defined(Flush) -#define Flush(fh) FFlush(fh) -#endif /* __amigaos4__ && !Flush */ - -/****************************************************************************/ - -/* This is used in place of ExamineFH() in order to work around a bug in - * dos.library V40 and below: a "NIL:" file handle will crash the - * ExamineFH() function. - */ -static LONG -safe_examine_file_handle(BPTR file_handle,struct FileInfoBlock *fib) -{ - LONG result = DOSFALSE; - - assert( fib != NULL ); - - #ifndef __amigaos4__ - { - struct FileHandle * fh = (struct FileHandle *)BADDR(file_handle); - - if(fh == NULL || fh->fh_Type == NULL) - { - SetIoErr(ERROR_OBJECT_WRONG_TYPE); - goto out; - } - } - #endif /* __amigaos4__ */ - - PROFILE_OFF(); - result = ExamineFH(file_handle,fib); - PROFILE_ON(); - - out: - - return(result); -} - -/****************************************************************************/ - -/* Same as above, only for ParentOfFH(). */ -static BPTR -safe_parent_of_file_handle(BPTR file_handle) -{ - BPTR result = ZERO; - - #ifndef __amigaos4__ - { - struct FileHandle * fh = (struct FileHandle *)BADDR(file_handle); - - if(fh == NULL || fh->fh_Type == NULL) - { - SetIoErr(ERROR_OBJECT_WRONG_TYPE); - goto out; - } - } - #endif /* __amigaos4__ */ - - PROFILE_OFF(); - result = ParentOfFH(file_handle); - PROFILE_ON(); - - out: - - return(result); -} - -/****************************************************************************/ - -#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 -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') - { - 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 make our own"); - - FileLockSemaphore = AllocMem(sizeof(*FileLockSemaphore) + strlen(__file_lock_semaphore_name)+1,MEMF_ANY|MEMF_PUBLIC); - if(FileLockSemaphore != NULL) - { - SHOWMSG("adding our own semaphore"); - - memset(FileLockSemaphore,0,sizeof(*FileLockSemaphore)); - - InitSemaphore(&FileLockSemaphore->fls_Semaphore); - - FileLockSemaphore->fls_Semaphore.ss_Link.ln_Name = (char *)(FileLockSemaphore + 1); - strcpy(FileLockSemaphore->fls_Semaphore.ss_Link.ln_Name,__file_lock_semaphore_name); - - FileLockSemaphore->fls_Semaphore.ss_Link.ln_Pri = 1; - - FileLockSemaphore->fls_Size = sizeof(*FileLockSemaphore); - NewList((struct List *)&FileLockSemaphore->fls_LockList); - - AddSemaphore(&FileLockSemaphore->fls_Semaphore); - } - else - { - SHOWMSG("not enough memory"); - } - } - else if (FileLockSemaphore->fls_Size < sizeof(*FileLockSemaphore)) - { - SHOWMSG("semaphore found, but it's too short"); - - SHOWVALUE(FileLockSemaphore->fls_Size); - SHOWVALUE(sizeof(*FileLockSemaphore)); - - FileLockSemaphore = NULL; - } - - Permit(); - } - - 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(); -} - -/****************************************************************************/ - -static 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 ); - - 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; - - __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 = OK; - - 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 != OK && error != OK) - { - SetIoErr(error); - - (*error_ptr) = __translate_io_error_to_errno(error); - } - - RETURN(result); - return(result); -} - -/****************************************************************************/ - -#endif /* UNIX_PATH_SEMANTICS */ - -/****************************************************************************/ - -/* Seek to the end of a file, then add a certain number of 0 bytes. */ -static int -grow_file_size(struct fd * fd,int num_bytes,int * error_ptr) -{ - unsigned char * aligned_buffer; - unsigned char * buffer; - struct FileHandle * fh; - D_S(struct InfoData,id); - LONG block_size; - int bytes_written; - int buffer_size; - int size; - int result = ERROR; - int position; - int current_position; - int alignment_skip; - - assert( fd != NULL ); - assert( error_ptr != NULL ); - - D(("we have to grow the file by %ld bytes",num_bytes)); - - block_size = 0; - - PROFILE_OFF(); - - fh = BADDR(fd->fd_DefaultFile); - if(fh != NULL && fh->fh_Type != NULL && DoPkt(fh->fh_Type,ACTION_DISK_INFO,MKBADDR(id),0,0,0,0)) - block_size = id->id_BytesPerBlock; - - PROFILE_ON(); - - if(block_size < 512) - block_size = 512; - - /* We have to fill up the file with zero bytes. - * That data comes from a local buffer. How - * large can we make it? - */ - buffer_size = 8 * block_size; - if(buffer_size > num_bytes) - buffer_size = num_bytes; - - /* Allocate a little more memory than required to allow for - * the buffer to be aligned to a cache line boundary. - */ - buffer = malloc((size_t)buffer_size + (CACHE_LINE_SIZE-1)); - if(buffer == NULL) - { - SHOWMSG("not enough memory for write buffer"); - - (*error_ptr) = ENOMEM; - goto out; - } - - /* Align the buffer to a cache line boundary. */ - aligned_buffer = (unsigned char *)(((ULONG)(buffer + (CACHE_LINE_SIZE-1))) & ~(CACHE_LINE_SIZE-1)); - - memset(aligned_buffer,0,(size_t)buffer_size); - - PROFILE_OFF(); - - position = Seek(fd->fd_DefaultFile,0,OFFSET_END); - - PROFILE_ON(); - - if(position == -1) - { - SHOWMSG("could not move to the end of the file"); - - (*error_ptr) = __translate_io_error_to_errno(IoErr()); - goto out; - } - - PROFILE_OFF(); - - current_position = Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT); - - PROFILE_ON(); - - /* Try to make the first write access align the file position - * to a block offset. Subsequent writes will then access the - * file at positions that are multiples of the block size. - */ - if(num_bytes > block_size && (current_position % block_size) != 0) - alignment_skip = block_size - (current_position % block_size); - else - alignment_skip = 0; - - while(num_bytes > 0) - { - if(__check_abort_enabled) - __check_abort(); - - size = buffer_size; - if(size > num_bytes) - size = num_bytes; - - /* If possible, even out the block offset. */ - if(alignment_skip > 0 && size > alignment_skip) - size = alignment_skip; - - alignment_skip = 0; - - PROFILE_OFF(); - - bytes_written = Write(fd->fd_DefaultFile,aligned_buffer,size); - - PROFILE_ON(); - - if(bytes_written != size) - { - (*error_ptr) = __translate_io_error_to_errno(IoErr()); - goto out; - } - - num_bytes -= size; - } - - SHOWMSG("all done."); - - result = OK; - - out: - - if(buffer != NULL) - free(buffer); - - return(result); -} - -/****************************************************************************/ - -static void -sync_fd(struct fd * fd,int mode) -{ - assert( fd != NULL ); - - if(fd->fd_DefaultFile != ZERO) - { - /* The mode tells us what to flush. 0 means "flush just the data", and - everything else means "flush everything. */ - Flush(fd->fd_DefaultFile); - - if(mode != 0) - { - struct FileHandle * fh = BADDR(fd->fd_DefaultFile); - - /* Verify that this file is not bound to "NIL:". */ - if(fh->fh_Type != NULL) - DoPkt(fh->fh_Type,ACTION_FLUSH, 0,0,0,0,0); - } - } -} - -/****************************************************************************/ - void __fd_hook_entry( struct Hook * UNUSED unused_hook, @@ -1434,7 +61,6 @@ __fd_hook_entry( char * buffer = NULL; int result = -1; int error = OK; - LONG position; ENTER(); @@ -1530,144 +156,6 @@ __fd_hook_entry( break; - /* This one implements Unix-like lseek() functionality. We report the - * new file position upon return and will try to extend the size of a - * file if the new seek position lies beyond the end of the file. - */ - case file_hook_action_seek_and_extend: - case file_hook_action_seek: - - SHOWMSG("file_hook_action_seek(_and_extend)"); - - if(fd->fd_DefaultFile != ZERO) - { - D_S(struct FileInfoBlock,fib); - BOOL fib_is_valid = FALSE; - LONG current_position; - LONG new_position; - LONG mode; - - assert( message->mode == SEEK_CUR || - message->mode == SEEK_SET || - message->mode == SEEK_END ); - - if(message->mode == SEEK_CUR) - mode = OFFSET_CURRENT; - else if (message->mode == SEEK_SET) - mode = OFFSET_BEGINNING; - else - mode = OFFSET_END; - - D(("seek&extended to offset %ld, mode %ld; current position = %ld",message->position,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(mode) - { - case OFFSET_CURRENT: - - new_position += message->position; - break; - - case OFFSET_BEGINNING: - - new_position = message->position; - break; - - case OFFSET_END: - - if(safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - new_position = fib->fib_Size + message->position; - - fib_is_valid = TRUE; - } - - break; - } - - if(new_position == current_position) - { - result = new_position; - - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - fd->fd_Position = new_position; - } - else - { - PROFILE_OFF(); - - position = Seek(fd->fd_DefaultFile,message->position,mode); - - PROFILE_ON(); - - if(position >= 0) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - fd->fd_Position = new_position; - - result = new_position; - } - else - { - D(("seek failed, mode=%ld (%ld), offset=%ld, ioerr=%ld",mode,message->mode,message->position,IoErr())); - - error = __translate_io_error_to_errno(IoErr()); - - #if defined(UNIX_PATH_SEMANTICS) - { - /* Check if the file would have become larger as a - * result of this action. - */ - if(message->action != file_hook_action_seek_and_extend) - break; - - if(NOT fib_is_valid && CANNOT safe_examine_file_handle(fd->fd_DefaultFile,fib)) - break; - - if(new_position <= fib->fib_Size) - break; - - if(grow_file_size(fd,new_position - fib->fib_Size,&error) != OK) - break; - - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - fd->fd_Position = new_position; - - result = new_position; - } - #endif /* UNIX_PATH_SEMANTICS */ - } - } - } - else - { - SHOWMSG("file is closed"); - - error = EBADF; - } - - break; - case file_hook_action_close: SHOWMSG("file_hook_action_close"); @@ -1680,18 +168,18 @@ __fd_hook_entry( memset(fib,0,sizeof(*fib)); - #if defined(UNIX_PATH_SEMANTICS) - { - cleanup_locked_records(fd); - } - #endif /* UNIX_PATH_SEMANTICS */ + /* 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); + parent_dir = __safe_parent_of_file_handle(fd->fd_DefaultFile); if(parent_dir != ZERO) { - if(safe_examine_file_handle(fd->fd_DefaultFile,fib)) + if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) name_and_path_valid = TRUE; } @@ -1838,34 +326,6 @@ __fd_hook_entry( break; - case file_hook_action_lock_record: - - SHOWMSG("file_hook_action_lock_record"); - - #if defined(UNIX_PATH_SEMANTICS) - { - SHOWMSG("performing record locking"); - - if(fd->fd_DefaultFile == ZERO) - { - SHOWMSG("file is closed"); - - error = EBADF; - break; - } - - result = handle_record_locking(message->command,message->lock,fd,&error); - } - #else - { - SHOWMSG("no record locking support in this library"); - - error = EBADF; - } - #endif /* UNIX_PATH_SEMANTICS */ - - break; - case file_hook_action_set_blocking: SHOWMSG("file_hook_action_set_blocking"); @@ -1902,194 +362,6 @@ __fd_hook_entry( break; - case file_hook_action_change_owner: - - SHOWMSG("file_hook_action_change_owner"); - - if(message->owner > 65535 || message->group > 65535) - { - SHOWMSG("owner or group not OK"); - - SHOWVALUE(message->owner); - SHOWVALUE(message->group); - - error = EINVAL; - break; - } - - if(fd->fd_DefaultFile != ZERO) - { - D_S(struct FileInfoBlock,fib); - - PROFILE_OFF(); - - if(safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - BPTR parent_dir; - - parent_dir = safe_parent_of_file_handle(fd->fd_DefaultFile); - if(parent_dir != ZERO) - { - BPTR old_current_dir; - - old_current_dir = CurrentDir(parent_dir); - - #if defined(__amigaos4__) - { - if(SetOwner(fib->fib_FileName,(LONG)((((ULONG)message->owner) << 16) | message->group))) - { - result = 0; - } - else - { - SHOWMSG("that didn't work"); - - error = __translate_io_error_to_errno(IoErr()); - } - } - #else - { - if(((struct Library *)DOSBase)->lib_Version >= 39) - { - SHOWMSG("changing owner"); - - if(SetOwner(fib->fib_FileName,(LONG)((((ULONG)message->owner) << 16) | message->group))) - { - result = 0; - } - else - { - SHOWMSG("that didn't work"); - - error = __translate_io_error_to_errno(IoErr()); - } - } - else - { - D_S(struct bcpl_name,new_name); - struct DevProc * dvp; - unsigned int len; - - SHOWMSG("have to do this manually..."); - - len = strlen(fib->fib_FileName); - - assert( len < sizeof(new_name->name) ); - - dvp = GetDeviceProc(fib->fib_FileName,NULL); - if(dvp != NULL) - { - new_name->name[0] = len; - memmove(&new_name->name[1],fib->fib_FileName,len); - - if(DoPkt(dvp->dvp_Port,ACTION_SET_OWNER,dvp->dvp_Lock,MKBADDR(new_name),(LONG)((((ULONG)message->owner) << 16) | message->group),0,0)) - result = 0; - else - error = __translate_io_error_to_errno(IoErr()); - - FreeDeviceProc(dvp); - } - else - { - error = __translate_io_error_to_errno(IoErr()); - } - } - } - #endif /* __amigaos4__ */ - - CurrentDir(old_current_dir); - - UnLock(parent_dir); - } - else - { - SHOWMSG("couldn't find parent directory"); - - error = __translate_io_error_to_errno(IoErr()); - } - } - else - { - SHOWMSG("couldn't examine file handle"); - - error = __translate_io_error_to_errno(IoErr()); - } - - PROFILE_ON(); - } - else - { - SHOWMSG("file is closed"); - - error = EBADF; - } - - break; - - case file_hook_action_truncate: - - SHOWMSG("file_hook_action_truncate"); - - if(fd->fd_DefaultFile != ZERO) - { - D_S(struct FileInfoBlock,fib); - - SHOWMSG("trying to change file size"); - - if(CANNOT safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - error = __translate_io_error_to_errno(IoErr()); - break; - } - - PROFILE_OFF(); - - if(message->size < fib->fib_Size) - { - /* Careful: seek to a position where the file can be safely truncated. */ - if(Seek(fd->fd_DefaultFile,message->size,OFFSET_BEGINNING) != -1 && SetFileSize(fd->fd_DefaultFile,message->size,OFFSET_BEGINNING) != -1) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - fd->fd_Position = message->size; - - result = 0; - } - else - { - error = __translate_io_error_to_errno(IoErr()); - } - } - else if (message->size > fib->fib_Size) - { - if(Seek(fd->fd_DefaultFile,fib->fib_Size,OFFSET_BEGINNING) != -1 && grow_file_size(fd,message->size - fib->fib_Size,&error) == OK) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - fd->fd_Position = message->size; - - result = 0; - } - else - { - error = __translate_io_error_to_errno(IoErr()); - } - } - else - { - /* No action */ - result = 0; - } - - PROFILE_ON(); - } - else - { - SHOWMSG("file is already closed"); - - error = EBADF; - } - - break; - case file_hook_action_examine: SHOWMSG("file_hook_action_examine"); @@ -2098,7 +370,7 @@ __fd_hook_entry( { struct FileHandle * fh; - if(CANNOT safe_examine_file_handle(fd->fd_DefaultFile,message->file_info)) + if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,message->file_info)) { SHOWMSG("couldn't examine the file"); @@ -2121,129 +393,6 @@ __fd_hook_entry( break; - case file_hook_action_info: - - SHOWMSG("file_hook_action_info"); - - if(fd->fd_DefaultFile != ZERO) - { - BPTR parent_dir; - - PROFILE_OFF(); - - parent_dir = safe_parent_of_file_handle(fd->fd_DefaultFile); - if(parent_dir != ZERO) - { - if(Info(parent_dir,message->info_data)) - { - result = 0; - } - else - { - SHOWMSG("couldn't get info on drive"); - - error = __translate_io_error_to_errno(IoErr()); - } - - UnLock(parent_dir); - } - else - { - SHOWMSG("couldn't find parent directory"); - - error = __translate_io_error_to_errno(IoErr()); - } - - PROFILE_ON(); - } - else - { - SHOWMSG("file is already closed"); - - error = EBADF; - } - - break; - - case file_hook_action_change_attributes: - - SHOWMSG("file_hook_action_change_attributes"); - - if(fd->fd_DefaultFile != ZERO) - { - BPTR parent_dir; - - PROFILE_OFF(); - - parent_dir = safe_parent_of_file_handle(fd->fd_DefaultFile); - if(parent_dir != ZERO) - { - D_S(struct FileInfoBlock,fib); - - if(safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - BPTR old_current_dir; - - old_current_dir = CurrentDir(parent_dir); - - if(SetProtection(fib->fib_FileName,(LONG)message->attributes)) - { - result = 0; - } - else - { - SHOWMSG("couldn't change protection bits; oh well, it was worth the effort..."); - - error = __translate_io_error_to_errno(IoErr()); - } - - CurrentDir(old_current_dir); - } - else - { - SHOWMSG("couldn't examine file"); - - error = __translate_io_error_to_errno(IoErr()); - } - - UnLock(parent_dir); - } - else - { - SHOWMSG("couldn't get a lock on the parent directory"); - - error = __translate_io_error_to_errno(IoErr()); - } - - PROFILE_ON(); - } - else - { - SHOWMSG("file is already closed"); - - error = EBADF; - } - - break; - - case file_hook_action_duplicate_fd: - - SHOWMSG("file_hook_action_duplicate_fd"); - - __duplicate_fd(message->duplicate_fd,fd); - - result = 0; - break; - - case file_hook_action_flush: - - SHOWMSG("file_hook_action_flush"); - - sync_fd(fd,message->arg); - - result = 0; - break; - default: SHOWVALUE(message->action); diff --git a/library/stdio_grow_file.c b/library/stdio_grow_file.c new file mode 100644 index 0000000..bf221cc --- /dev/null +++ b/library/stdio_grow_file.c @@ -0,0 +1,161 @@ +/* + * $Id: stdio_grow_file.c,v 1.1 2005-02-18 18:53:16 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2005 by Olaf Barthel + * 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 */ + +/****************************************************************************/ + +/* The following is not part of the ISO 'C' (1994) standard. */ + +/****************************************************************************/ + +/* Seek to the end of a file, then add a certain number of 0 bytes. */ +int +__grow_file_size(struct fd * fd,int num_bytes) +{ + unsigned char * aligned_buffer; + unsigned char * buffer; + struct FileHandle * fh; + D_S(struct InfoData,id); + LONG block_size; + int bytes_written; + int buffer_size; + int size; + int position; + int current_position; + int alignment_skip; + int result = -1; + + assert( fd != NULL ); + + D(("we have to grow the file by %ld bytes",num_bytes)); + + block_size = 0; + + PROFILE_OFF(); + + fh = BADDR(fd->fd_DefaultFile); + if(fh != NULL && fh->fh_Type != NULL && DoPkt(fh->fh_Type,ACTION_DISK_INFO,MKBADDR(id),0,0,0,0)) + block_size = id->id_BytesPerBlock; + + PROFILE_ON(); + + if(block_size < 512) + block_size = 512; + + /* We have to fill up the file with zero bytes. + * That data comes from a local buffer. How + * large can we make it? + */ + buffer_size = 8 * block_size; + if(buffer_size > num_bytes) + buffer_size = num_bytes; + + /* Allocate a little more memory than required to allow for + * the buffer to be aligned to a cache line boundary. + */ + buffer = malloc((size_t)buffer_size + (CACHE_LINE_SIZE-1)); + if(buffer == NULL) + { + SHOWMSG("not enough memory for write buffer"); + + SetIoErr(ERROR_NO_FREE_STORE); + goto out; + } + + /* Align the buffer to a cache line boundary. */ + aligned_buffer = (unsigned char *)(((ULONG)(buffer + (CACHE_LINE_SIZE-1))) & ~(CACHE_LINE_SIZE-1)); + + memset(aligned_buffer,0,(size_t)buffer_size); + + PROFILE_OFF(); + position = Seek(fd->fd_DefaultFile,0,OFFSET_END); + PROFILE_ON(); + + if(position == -1) + { + SHOWMSG("could not move to the end of the file"); + goto out; + } + + PROFILE_OFF(); + current_position = Seek(fd->fd_DefaultFile,0,OFFSET_CURRENT); + PROFILE_ON(); + + /* Try to make the first write access align the file position + * to a block offset. Subsequent writes will then access the + * file at positions that are multiples of the block size. + */ + if(num_bytes > block_size && (current_position % block_size) != 0) + alignment_skip = block_size - (current_position % block_size); + else + alignment_skip = 0; + + while(num_bytes > 0) + { + if(__check_abort_enabled) + __check_abort(); + + size = buffer_size; + if(size > num_bytes) + size = num_bytes; + + /* If possible, even out the block offset. */ + if(alignment_skip > 0 && size > alignment_skip) + size = alignment_skip; + + alignment_skip = 0; + + PROFILE_OFF(); + bytes_written = Write(fd->fd_DefaultFile,aligned_buffer,size); + PROFILE_ON(); + + if(bytes_written != size) + goto out; + + num_bytes -= size; + } + + SHOWMSG("all done."); + + result = 0; + + out: + + if(buffer != NULL) + free(buffer); + + return(result); +} diff --git a/library/stdio_headers.h b/library/stdio_headers.h index 05e8ece..f4c021e 100644 --- a/library/stdio_headers.h +++ b/library/stdio_headers.h @@ -1,5 +1,5 @@ /* - * $Id: stdio_headers.h,v 1.12 2005-02-04 15:03:11 obarthel Exp $ + * $Id: stdio_headers.h,v 1.13 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -271,6 +271,15 @@ struct iob /****************************************************************************/ +/* Forward declaration... */ +struct fd; + +/****************************************************************************/ + +typedef void (*fd_cleanup_t)(struct fd * fd); + +/****************************************************************************/ + struct fd { struct Hook * fd_Hook; /* Hook to invoke to perform actions */ @@ -283,6 +292,8 @@ struct fd struct Hook fd_DefaultHook; /* Static hook */ BPTR fd_DefaultFile; /* A dos.library file handle */ LONG fd_Position; /* Cached file position (seek offset). */ + + fd_cleanup_t fd_Cleanup; /* Cleanup function, if any. */ }; /****************************************************************************/ @@ -313,18 +324,9 @@ enum file_hook_action_t file_hook_action_write, file_hook_action_seek, file_hook_action_close, - file_hook_action_lock_record, file_hook_action_set_blocking, - file_hook_action_change_owner, - file_hook_action_truncate, - file_hook_action_examine, - file_hook_action_change_attributes, - file_hook_action_info, - file_hook_action_duplicate_fd, - file_hook_action_seek_and_extend, - file_hook_action_is_interactive, file_hook_action_set_async, - file_hook_action_flush + file_hook_action_examine }; /****************************************************************************/ @@ -339,24 +341,13 @@ struct file_hook_message long position; /* The seek position */ long mode; /* The seek mode */ - struct flock * lock; /* Record locking request */ - int command; /* What kind of locking command was sent */ - int arg; /* Whether or not this file should - be set non-blocking */ - - uid_t owner; - gid_t group; + be set non-blocking or use + asynchronous I/O */ struct FileInfoBlock * file_info; struct MsgPort * file_system; - struct InfoData * info_data; - - struct fd * duplicate_fd; - - ULONG attributes; - int error; /* Error code, if any... */ long result; /* Whatever this produced */ diff --git a/library/stdio_parent_of_fh.c b/library/stdio_parent_of_fh.c new file mode 100644 index 0000000..07e2800 --- /dev/null +++ b/library/stdio_parent_of_fh.c @@ -0,0 +1,68 @@ +/* + * $Id: stdio_parent_of_fh.c,v 1.1 2005-02-18 18:53:16 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2005 by Olaf Barthel + * 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 */ + +/****************************************************************************/ + +/* This is used in place of ParentOfFH() in order to work around a bug in + * dos.library V40 and below: a "NIL:" file handle will crash the + * ParentOfFH() function. + */ +BPTR +__safe_parent_of_file_handle(BPTR file_handle) +{ + BPTR result = ZERO; + + #ifndef __amigaos4__ + { + struct FileHandle * fh = (struct FileHandle *)BADDR(file_handle); + + if(fh == NULL || fh->fh_Type == NULL) + { + SetIoErr(ERROR_OBJECT_WRONG_TYPE); + goto out; + } + } + #endif /* __amigaos4__ */ + + PROFILE_OFF(); + result = ParentOfFH(file_handle); + PROFILE_ON(); + + out: + + return(result); +} diff --git a/library/stdio_protos.h b/library/stdio_protos.h index 77d54cd..ac016af 100644 --- a/library/stdio_protos.h +++ b/library/stdio_protos.h @@ -1,5 +1,5 @@ /* - * $Id: stdio_protos.h,v 1.5 2005-02-03 16:56:16 obarthel Exp $ + * $Id: stdio_protos.h,v 1.6 2005-02-18 18:53:16 obarthel Exp $ * * :ts=4 * @@ -205,4 +205,29 @@ extern void __duplicate_fd(struct fd * duplicate_fd,struct fd * original_fd); /****************************************************************************/ +/* stdio_examine_fh.c */ +extern LONG __safe_examine_file_handle(BPTR file_handle,struct FileInfoBlock *fib); + +/****************************************************************************/ + +/* stdio_parent_of_fh.c */ +extern BPTR __safe_parent_of_file_handle(BPTR file_handle); + +/****************************************************************************/ + +/* stdio_grow_file.c */ +extern int __grow_file_size(struct fd * fd,int num_bytes); + +/****************************************************************************/ + +/* unistd_sync_fd.c */ +extern void __sync_fd(struct fd * fd,int mode); + +/****************************************************************************/ + +/* stdio_record_locking.c */ +extern int __handle_record_locking(int cmd,struct flock * l,struct fd * fd,int * error_ptr); + +/****************************************************************************/ + #endif /* _STDIO_PROTOS_H */ diff --git a/library/stdio_record_locking.c b/library/stdio_record_locking.c new file mode 100644 index 0000000..2dc2d0f --- /dev/null +++ b/library/stdio_record_locking.c @@ -0,0 +1,1202 @@ +/* + * $Id: stdio_record_locking.c,v 1.1 2005-02-18 18:53:16 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2005 by Olaf Barthel + * 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 +#include + +/****************************************************************************/ + +#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') + { + 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 make our own"); + + FileLockSemaphore = AllocMem(sizeof(*FileLockSemaphore) + strlen(__file_lock_semaphore_name)+1,MEMF_ANY|MEMF_PUBLIC); + if(FileLockSemaphore != NULL) + { + SHOWMSG("adding our own semaphore"); + + memset(FileLockSemaphore,0,sizeof(*FileLockSemaphore)); + + InitSemaphore(&FileLockSemaphore->fls_Semaphore); + + FileLockSemaphore->fls_Semaphore.ss_Link.ln_Name = (char *)(FileLockSemaphore + 1); + strcpy(FileLockSemaphore->fls_Semaphore.ss_Link.ln_Name,__file_lock_semaphore_name); + + FileLockSemaphore->fls_Semaphore.ss_Link.ln_Pri = 1; + + FileLockSemaphore->fls_Size = sizeof(*FileLockSemaphore); + NewList((struct List *)&FileLockSemaphore->fls_LockList); + + AddSemaphore(&FileLockSemaphore->fls_Semaphore); + } + else + { + SHOWMSG("not enough memory"); + } + } + else if (FileLockSemaphore->fls_Size < sizeof(*FileLockSemaphore)) + { + SHOWMSG("semaphore found, but it's too short"); + + SHOWVALUE(FileLockSemaphore->fls_Size); + SHOWVALUE(sizeof(*FileLockSemaphore)); + + FileLockSemaphore = NULL; + } + + Permit(); + } + + 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 ); + + /* 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; + + __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 = OK; + + 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 != OK && error != OK) + { + SetIoErr(error); + + (*error_ptr) = __translate_io_error_to_errno(error); + } + + RETURN(result); + return(result); +} + +/****************************************************************************/ + +#endif /* UNIX_PATH_SEMANTICS */ diff --git a/library/unistd_dup2.c b/library/unistd_dup2.c index 41663c1..a605e30 100644 --- a/library/unistd_dup2.c +++ b/library/unistd_dup2.c @@ -1,5 +1,5 @@ /* - * $Id: unistd_dup2.c,v 1.4 2005-02-03 16:56:17 obarthel Exp $ + * $Id: unistd_dup2.c,v 1.5 2005-02-18 18:53:17 obarthel Exp $ * * :ts=4 * @@ -104,7 +104,6 @@ dup2(int file_descriptor1, int file_descriptor2) if(file_descriptor1 != file_descriptor2) { - struct file_hook_message message; struct fd * fd2; /* Have a look at the requested file descriptor. */ @@ -123,21 +122,7 @@ dup2(int file_descriptor1, int file_descriptor2) goto out; } - SHOWMSG("calling the hook"); - - message.action = file_hook_action_duplicate_fd; - message.duplicate_fd = fd2; - - assert( fd1->fd_Hook != NULL ); - - CallHookPkt(fd1->fd_Hook,fd1,&message); - - result = message.result; - if(result != 0) - { - __set_errno(message.error); - goto out; - } + __duplicate_fd(fd2,fd1); } result = file_descriptor2; diff --git a/library/unistd_fchown.c b/library/unistd_fchown.c index a057569..e561d1d 100644 --- a/library/unistd_fchown.c +++ b/library/unistd_fchown.c @@ -1,5 +1,5 @@ /* - * $Id: unistd_fchown.c,v 1.4 2005-02-03 16:56:17 obarthel Exp $ + * $Id: unistd_fchown.c,v 1.5 2005-02-18 18:53:17 obarthel Exp $ * * :ts=4 * @@ -44,10 +44,13 @@ int fchown(int file_descriptor, uid_t owner, gid_t group) { - DECLARE_UTILITYBASE(); - struct file_hook_message message; + D_S(struct FileInfoBlock,fib); + BPTR parent_dir = ZERO; + BPTR old_current_dir = ZERO; + BOOL current_dir_changed = FALSE; int result = -1; struct fd * fd; + LONG success; ENTER(); @@ -55,8 +58,6 @@ fchown(int file_descriptor, uid_t owner, gid_t group) SHOWVALUE(owner); SHOWVALUE(group); - assert( UtilityBase != NULL ); - assert( file_descriptor >= 0 && file_descriptor < __num_fd ); assert( __fd[file_descriptor] != NULL ); assert( FLAG_IS_SET(__fd[file_descriptor]->fd_Flags,FDF_IN_USE) ); @@ -71,22 +72,106 @@ fchown(int file_descriptor, uid_t owner, gid_t group) goto out; } - SHOWMSG("calling the hook"); + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } - message.action = file_hook_action_change_owner; - message.owner = owner; - message.group = group; + if(owner > 65535 || group > 65535) + { + SHOWMSG("owner or group not OK"); - assert( fd->fd_Hook != NULL ); + SHOWVALUE(owner); + SHOWVALUE(group); - CallHookPkt(fd->fd_Hook,fd,&message); + __set_errno(EINVAL); + goto out; + } - result = message.result; + PROFILE_OFF(); + success = (__safe_examine_file_handle(fd->fd_DefaultFile,fib) && (parent_dir = __safe_parent_of_file_handle(fd->fd_DefaultFile)) != ZERO); + PROFILE_ON(); - __set_errno(message.error); + if(NO success) + { + SHOWMSG("couldn't find parent directory"); + + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + old_current_dir = CurrentDir(parent_dir); + current_dir_changed = TRUE; + + PROFILE_OFF(); + + #if defined(__amigaos4__) + { + success = SetOwner(fib->fib_FileName,(LONG)((((ULONG)owner) << 16) | group)); + } + #else + { + if(((struct Library *)DOSBase)->lib_Version >= 39) + { + success = SetOwner(fib->fib_FileName,(LONG)((((ULONG)owner) << 16) | group)); + } + else + { + D_S(struct bcpl_name,new_name); + struct DevProc * dvp; + unsigned int len; + + SHOWMSG("have to do this manually..."); + + success = DOSFALSE; + + len = strlen(fib->fib_FileName); + + assert( len < sizeof(new_name->name) ); + + dvp = GetDeviceProc(fib->fib_FileName,NULL); + if(dvp != NULL) + { + LONG error; + + new_name->name[0] = len; + memmove(&new_name->name[1],fib->fib_FileName,len); + + success = DoPkt(dvp->dvp_Port,ACTION_SET_OWNER,dvp->dvp_Lock,MKBADDR(new_name),(LONG)((((ULONG)owner) << 16) | group),0,0); + error = IoErr(); + + FreeDeviceProc(dvp); + + SetIoErr(error); + } + } + } + #endif /* __amigaos4__ */ + + PROFILE_ON(); + + if(NO success) + { + SHOWMSG("couldn't change owner/group"); + + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + result = OK; out: + PROFILE_OFF(); + + UnLock(parent_dir); + + if(current_dir_changed) + CurrentDir(old_current_dir); + + PROFILE_ON(); + RETURN(result); return(result); } diff --git a/library/unistd_fdatasync.c b/library/unistd_fdatasync.c index d770e93..89f87d5 100644 --- a/library/unistd_fdatasync.c +++ b/library/unistd_fdatasync.c @@ -1,5 +1,5 @@ /* - * $Id: unistd_fdatasync.c,v 1.1 2005-02-04 15:03:11 obarthel Exp $ + * $Id: unistd_fdatasync.c,v 1.2 2005-02-18 18:53:17 obarthel Exp $ * * :ts=4 * @@ -50,9 +50,6 @@ int fdatasync(int file_descriptor) { - DECLARE_UTILITYBASE(); - - struct file_hook_message message; struct fd * fd; int result = -1; @@ -60,8 +57,6 @@ fdatasync(int file_descriptor) SHOWVALUE(file_descriptor); - assert( UtilityBase != NULL ); - if(__check_abort_enabled) __check_abort(); @@ -76,20 +71,16 @@ fdatasync(int file_descriptor) goto out; } - message.action = file_hook_action_flush; - message.arg = 0; /* flush just the data */ - - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - result = message.result; - if(result != 0) + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) { - __set_errno(message.error); + __set_errno(EINVAL); goto out; } + __sync_fd(fd,0); /* flush just the data */ + + result = OK; + out: RETURN(result); diff --git a/library/unistd_fsync.c b/library/unistd_fsync.c index 939e786..182b6ef 100644 --- a/library/unistd_fsync.c +++ b/library/unistd_fsync.c @@ -1,5 +1,5 @@ /* - * $Id: unistd_fsync.c,v 1.1 2005-02-04 15:03:11 obarthel Exp $ + * $Id: unistd_fsync.c,v 1.2 2005-02-18 18:53:17 obarthel Exp $ * * :ts=4 * @@ -49,9 +49,6 @@ int fsync(int file_descriptor) { - DECLARE_UTILITYBASE(); - - struct file_hook_message message; struct fd * fd; int result = -1; @@ -59,8 +56,6 @@ fsync(int file_descriptor) SHOWVALUE(file_descriptor); - assert( UtilityBase != NULL ); - if(__check_abort_enabled) __check_abort(); @@ -75,20 +70,16 @@ fsync(int file_descriptor) goto out; } - message.action = file_hook_action_flush; - message.arg = 1; /* flush everything */ - - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - result = message.result; - if(result != 0) + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) { - __set_errno(message.error); + __set_errno(EINVAL); goto out; } + __sync_fd(fd,1); /* flush everything */ + + result = OK; + out: RETURN(result); diff --git a/library/unistd_ftruncate.c b/library/unistd_ftruncate.c index 54d4be7..c38aba9 100644 --- a/library/unistd_ftruncate.c +++ b/library/unistd_ftruncate.c @@ -1,5 +1,5 @@ /* - * $Id: unistd_ftruncate.c,v 1.4 2005-02-03 16:56:17 obarthel Exp $ + * $Id: unistd_ftruncate.c,v 1.5 2005-02-18 18:53:17 obarthel Exp $ * * :ts=4 * @@ -44,19 +44,17 @@ int ftruncate(int file_descriptor, off_t length) { - DECLARE_UTILITYBASE(); - struct file_hook_message message; + D_S(struct FileInfoBlock,fib); int result = -1; struct fd * fd; long int position; + BOOL success; ENTER(); SHOWVALUE(file_descriptor); SHOWVALUE(length); - assert( UtilityBase != NULL ); - assert( file_descriptor >= 0 && file_descriptor < __num_fd ); assert( __fd[file_descriptor] != NULL ); assert( FLAG_IS_SET(__fd[file_descriptor]->fd_Flags,FDF_IN_USE) ); @@ -71,6 +69,12 @@ ftruncate(int file_descriptor, off_t length) goto out; } + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + __set_errno(EINVAL); + goto out; + } + if(length < 0) { SHOWMSG("invalid length"); @@ -85,7 +89,7 @@ ftruncate(int file_descriptor, off_t length) { SHOWMSG("file descriptor is not write-enabled"); - __set_errno(EBADF); + __set_errno(EINVAL); goto out; } @@ -94,18 +98,42 @@ ftruncate(int file_descriptor, off_t length) if(position < 0) goto out; - SHOWMSG("calling the hook"); - - message.action = file_hook_action_truncate; - message.size = length; - - assert( fd->fd_Hook != NULL ); - - CallHookPkt(fd->fd_Hook,fd,&message); - - if(message.result < 0) + if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fib)) { - __set_errno(message.error); + SHOWMSG("couldn't examine file"); + + __set_errno(__translate_io_error_to_errno(IoErr())); + goto out; + } + + PROFILE_OFF(); + + if(length < fib->fib_Size) + { + /* Careful: seek to a position where the file can be safely truncated. */ + success = (Seek(fd->fd_DefaultFile,length,OFFSET_BEGINNING) != -1 && SetFileSize(fd->fd_DefaultFile,length,OFFSET_BEGINNING) != -1); + } + else if (length > fib->fib_Size) + { + success = (Seek(fd->fd_DefaultFile,fib->fib_Size,OFFSET_BEGINNING) != -1 && __grow_file_size(fd,length - fib->fib_Size) == OK); + } + else + { + success = TRUE; + } + + PROFILE_ON(); + + if(NO success) + { + int error; + + error = __translate_io_error_to_errno(IoErr()); + + /* Return to the original file position. */ + lseek(file_descriptor,position,SEEK_SET); + + __set_errno(error); goto out; } diff --git a/library/unistd_sync_fd.c b/library/unistd_sync_fd.c new file mode 100644 index 0000000..153da78 --- /dev/null +++ b/library/unistd_sync_fd.c @@ -0,0 +1,70 @@ +/* + * $Id: unistd_sync_fd.c,v 1.1 2005-02-18 18:53:17 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2005 by Olaf Barthel + * 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 _UNISTD_HEADERS_H +#include "unistd_headers.h" +#endif /* _UNISTD_HEADERS_H */ + +/****************************************************************************/ + +#if defined(__amigaos4__) && !defined(Flush) +#define Flush(fh) FFlush(fh) +#endif /* __amigaos4__ && !Flush */ + +/****************************************************************************/ + +/* The following is not part of the ISO 'C' (1994) standard. */ + +/****************************************************************************/ + +void +__sync_fd(struct fd * fd,int mode) +{ + assert( fd != NULL ); + + if(fd->fd_DefaultFile != ZERO) + { + /* The mode tells us what to flush. 0 means "flush just the data", and + everything else means "flush everything. */ + Flush(fd->fd_DefaultFile); + + if(mode != 0) + { + struct FileHandle * fh = BADDR(fd->fd_DefaultFile); + + /* Verify that this file is not bound to "NIL:". */ + if(fh->fh_Type != NULL) + DoPkt(fh->fh_Type,ACTION_FLUSH, 0,0,0,0,0); + } + } +}