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); + } + } +}