Branch: Tag:

2017-09-01

2017-09-01 12:10:56 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Stdio.File(): close() fixes.

Unify low level calls close_fd() and close_fd_quietly() to reduce
code duplication.

Fix error handling in close_fd() and do_close_fd(). POSIX says that
fds are closed on all errors except EINTR, where it is unspecified.
Don't complain about close(2) failing with EBADF if it previously
failed with EINTR.

Ignore close(2) failing with ECONNRESET. This happens on eg FreeBSD
when there is still data pending to be sent and the peer has already
closed the connection.

Fix documentation for the return value of close().

Potential fix for testsuite failures on FreeBSD.

447:      /* Use ptrdiff_t for the fd since we're passed a void * and should    * read it as an integer of the same size. */ - static void do_close_fd(ptrdiff_t fd) + static int do_close_fd(ptrdiff_t fd)   {    int ret; -  if (fd < 0) return; +  int preve; +  if (fd < 0) return 0; +  errno = 0;    do { -  +  preve = errno;    ret = fd_close(fd);    } while ((ret == -1) && (errno == EINTR)); -  +  if ((ret == -1) && preve) return 0; +  return ret;   }      #ifdef HAVE_PIKE_SEND_FD
555:    }   }    - static void close_fd_quietly(void) + static void close_fd(int quiet)   {    int fd=FD; -  +  int olde = 0;    if(fd<0) return;       free_fd_stuff();    SUB_FD_EVENTS (THIS, ~0); -  +  /* NB: The fd will always be closed on return from fd_close() +  * (except for the EINTR case on eg HPUX). +  */    change_fd_for_box (&THIS->box, -1);    -  +  if ( (THIS->flags & FILE_NOT_OPENED) ) +  return; +     while(1)    {    int i, e;
572:    e=errno;    THREADS_DISALLOW_UID();    +  /* fprintf(stderr, "fd_close(%d): ret: %d, errno: %d\n", fd, i, e); */ +     check_threads_etc();       if(i < 0)    { -  +  ERRNO = errno = e;    switch(e)    { -  default: { +  default: +  push_int(e); +  f_strerror(1); +  +  if (quiet) { +  /* NB: FIXME: This has quite a bit of overhead... */    JMP_BUF jmp;    if (SETJMP (jmp))    call_handle_error();    else { -  ERRNO=errno=e; -  change_fd_for_box (&THIS->box, fd); -  push_int(e); -  f_strerror(1); +     Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string);    }    UNSETJMP (jmp); -  break; +  } else { +  Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string);    } -  -  case EBADF: +     break;    - #ifdef SOLARIS -  /* It's actually OK. This is a bug in Solaris 8. */ -  case EAGAIN: + #if 0 + #ifdef ENOSPC +  case ENOSPC: +  /* FreeBSD: The underlying object did not fit, cached data was lost. */    break;   #endif -  -  case EINTR: -  continue; -  } -  } + #endif + #ifdef ECONNRESET +  case ECONNRESET: +  /* FreeBSD: The peer shut down the connection before all pending data +  * was delivered. +  */    break; -  } - } + #endif    - static void close_fd(void) - { -  int fd=FD; -  if(fd<0) return; -  -  free_fd_stuff(); -  SUB_FD_EVENTS (THIS, ~0); -  change_fd_for_box (&THIS->box, -1); -  -  if ( (THIS->flags & FILE_NOT_OPENED) ) -  return; -  -  while(1) -  { -  int i, e; -  THREADS_ALLOW_UID(); -  i=fd_close(fd); -  e=errno; -  THREADS_DISALLOW_UID(); -  -  check_threads_etc(); -  -  if(i < 0) -  { -  switch(e) -  { -  default: -  ERRNO=errno=e; -  change_fd_for_box (&THIS->box, fd); -  push_int(e); -  f_strerror(1); -  Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string); -  break; -  +     case EBADF: -  +  if (olde) { +  /* Probably an OS where fds are closed on EINTR (ie most). */ +  ERRNO = errno = 0; +  break; +  }    Pike_error("Internal error: Closing a non-active file descriptor %d.\n",fd);    break;   
653: Inside #if defined(SOLARIS)
   case EAGAIN:    break;   #endif -  +     case EINTR: -  +  olde = e;    continue;    }    }
2360:   }   #endif    + #ifndef SHUT_RD + #define SHUT_RD 0 + #endif + #ifndef SHUT_WR + #define SHUT_WR 1 + #endif +    static int do_close(int flags)   {    struct my_file *f = THIS;
2377:    if(f->open_mode & FILE_WRITE)    {    SUB_FD_EVENTS (f, PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB|PIKE_BIT_FD_FS_EVENT); -  fd_shutdown(FD, 0); +  fd_shutdown(FD, SHUT_RD);    f->open_mode &=~ FILE_READ;    return 0;    }else{    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;    }   
2390:    if(f->open_mode & FILE_READ)    {    SUB_FD_EVENTS (f, PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB|PIKE_BIT_FD_FS_EVENT); -  fd_shutdown(FD, 1); +  fd_shutdown(FD, SHUT_WR);    f->open_mode &=~ FILE_WRITE;   #ifdef HAVE_PIKE_SEND_FD    if (f->fd_info) do_close_fd_info(f->fd_info);
2398:    return 0;    }else{    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;    }       case FILE_READ | FILE_WRITE:    f->flags&=~FILE_NOT_OPENED; -  close_fd(); +  close_fd(0);    return 1;       default:
2491:    *! Close a file or stream.    *!    *! If direction is not specified, both the read and the write -  *! direction is closed. Otherwise only the directions specified is +  *! direction are closed. Otherwise only the directions specified is    *! closed.    *!    *! @returns -  *! Nonzero is returned if the file or stream wasn't open in the -  *! specified direction, zero otherwise. +  *! Returns @expr{1@} if the file or stream now is closed in +  *! all directions, and @expr{0@} otherwise.    *!    *! @throws    *! An exception is thrown if an I/O error occurs.
2614:    int access;    int err;    struct pike_string *str, *flag_str; -  close_fd(); +  close_fd(0);       if(args < 2)    SIMPLE_WRONG_NUM_ARGS_ERROR("open", 2);
2774: Inside #if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME)) and #if defined(HAVE_POSIX_OPENPT)
  #ifdef HAVE_POSIX_OPENPT    struct pike_string *flag_str;   #endif -  close_fd(); +  close_fd(0);       if(args < 1)    SIMPLE_WRONG_NUM_ARGS_ERROR("openpt", 1);
4278:    reverse = type & fd_REVERSE;    type &= ~fd_REVERSE;    -  close_fd(); +  close_fd(0);    pop_n_elems(args);    ERRNO=0;   
4391:    if(!(f->flags & (FILE_NO_CLOSE_ON_DESTRUCT |    FILE_LOCK_FD |    FILE_NOT_OPENED))) -  close_fd_quietly(); +  close_fd(1);    else    free_fd_stuff();   #ifdef HAVE_PIKE_SEND_FD
4574:    int fd;    int family=-1;    -  close_fd(); +  close_fd(0);       if (args > 2 && TYPEOF(Pike_sp[2-args]) == PIKE_T_INT &&    Pike_sp[2-args].u.integer != 0)
4890:   #endif    pop_n_elems(args);    -  close_fd(); +  close_fd(0);    change_fd_for_box (&THIS->box, socket(AF_UNIX,SOCK_STREAM,0));       if( FD < 0 )
5207:    TYPEOF(Pike_sp[-args]) != PIKE_T_INT)    SIMPLE_ARG_TYPE_ERROR("create", 1, "int|string");    -  close_fd(); +  close_fd(0);    file_open(args);   }