1  /* Copyright (C) 1992-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <fcntl.h>
      20  #include <hurd.h>
      21  #include <hurd/fd.h>
      22  #include <stdarg.h>
      23  #include <sys/file.h>		/* XXX for LOCK_* */
      24  #ifdef NOCANCEL
      25  #include <not-cancel.h>
      26  #else
      27  #include <sysdep-cancel.h>
      28  #endif
      29  #include "f_setlk.h"
      30  
      31  /* Perform file control operations on FD.  */
      32  int
      33  __libc_fcntl (int fd, int cmd, ...)
      34  {
      35    va_list ap;
      36    struct hurd_fd *d;
      37    int result;
      38  
      39    d = _hurd_fd_get (fd);
      40  
      41    if (d == NULL)
      42      return __hurd_fail (EBADF);
      43  
      44    va_start (ap, cmd);
      45  
      46    switch (cmd)
      47      {
      48        error_t err;
      49  
      50      default:			/* Bad command.  */
      51        result = __hurd_fail (EINVAL);
      52        break;
      53  
      54        /* First the descriptor-based commands, which do no RPCs.  */
      55  
      56      case F_DUPFD:		/* Duplicate the file descriptor.  */
      57      case F_DUPFD_CLOEXEC:
      58        {
      59  	struct hurd_fd *new;
      60  	io_t port, ctty;
      61  	struct hurd_userlink ulink, ctty_ulink;
      62  	int flags;
      63  
      64  	HURD_CRITICAL_BEGIN;
      65  
      66  	/* Extract the ports and flags from the file descriptor.  */
      67  	__spin_lock (&d->port.lock);
      68  	flags = d->flags;
      69  	ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
      70  	port = _hurd_port_locked_get (&d->port, &ulink); /* Unlocks D.  */
      71  
      72  	if (cmd == F_DUPFD_CLOEXEC)
      73  	  flags |= FD_CLOEXEC;
      74  	else
      75  	  /* Duplication clears the FD_CLOEXEC flag.  */
      76  	  flags &= ~FD_CLOEXEC;
      77  
      78  	/* Get a new file descriptor.  The third argument to __fcntl is the
      79  	   minimum file descriptor number for it.  */
      80  	new = _hurd_alloc_fd (&result, va_arg (ap, int));
      81  	if (new == NULL)
      82  	  /* _hurd_alloc_fd has set errno.  */
      83  	  result = -1;
      84  	else
      85  	  {
      86  	    /* Give the ports each a user ref for the new descriptor.  */
      87  	    __mach_port_mod_refs (__mach_task_self (), port,
      88  				  MACH_PORT_RIGHT_SEND, 1);
      89  	    if (ctty != MACH_PORT_NULL)
      90  	      __mach_port_mod_refs (__mach_task_self (), ctty,
      91  				    MACH_PORT_RIGHT_SEND, 1);
      92  
      93  	    /* Install the ports and flags in the new descriptor.  */
      94  	    if (ctty != MACH_PORT_NULL)
      95  	      _hurd_port_set (&new->ctty, ctty);
      96  	    new->flags = flags;
      97  	    _hurd_port_locked_set (&new->port, port); /* Unlocks NEW.  */
      98  	  }
      99  
     100  	HURD_CRITICAL_END;
     101  
     102  	_hurd_port_free (&d->port, &ulink, port);
     103  	if (ctty != MACH_PORT_NULL)
     104  	  _hurd_port_free (&d->ctty, &ctty_ulink, port);
     105  
     106  	break;
     107        }
     108  
     109        /* Set RESULT by evaluating EXPR with the descriptor locked.
     110  	 Check for an empty descriptor and return EBADF.  */
     111  #define LOCKED(expr) do {						      \
     112        HURD_CRITICAL_BEGIN;						      \
     113        __spin_lock (&d->port.lock);					      \
     114        if (d->port.port == MACH_PORT_NULL)				      \
     115  	result = __hurd_fail (EBADF);					      \
     116        else								      \
     117  	result = (expr);						      \
     118        __spin_unlock (&d->port.lock);					      \
     119        HURD_CRITICAL_END;						      \
     120  } while(0)
     121  
     122      case F_GETFD:		/* Get descriptor flags.  */
     123        LOCKED (d->flags);
     124        break;
     125  
     126      case F_SETFD:		/* Set descriptor flags.  */
     127        LOCKED ((d->flags = va_arg (ap, int), 0));
     128        break;
     129  
     130  
     131        /* Now the real io operations, done by RPCs to io servers.  */
     132  
     133      case F_GETLK:
     134      case F_SETLK:
     135      case F_SETLKW:
     136        {
     137  	struct flock *fl = va_arg (ap, struct flock *);
     138  
     139  	switch (cmd)
     140  	  {
     141  	  case F_GETLK:
     142  	    cmd = F_GETLK64;
     143  	    break;
     144  	  case F_SETLK:
     145  	    cmd = F_SETLK64;
     146  	    break;
     147  	  case F_SETLKW:
     148  	    cmd = F_SETLKW64;
     149  	    break;
     150  	  default:
     151  	    return __hurd_fail (EINVAL);
     152  	  }
     153  
     154  	struct flock64 fl64 = {
     155  	  .l_type = fl->l_type,
     156  	  .l_whence = fl->l_whence,
     157  	  .l_start = fl->l_start,
     158  	  .l_len = fl->l_len,
     159  	  .l_pid = fl->l_pid
     160  	};
     161  
     162  #ifndef NOCANCEL
     163  	if (cmd == F_SETLKW64)
     164  	  {
     165  	    int cancel_oldtype = LIBC_CANCEL_ASYNC();
     166  	    err = HURD_FD_PORT_USE_CANCEL (d, __file_record_lock (port, cmd,
     167  					   &fl64, MACH_PORT_NULL,
     168  					   MACH_MSG_TYPE_MAKE_SEND));
     169  	    LIBC_CANCEL_RESET (cancel_oldtype);
     170  	  }
     171  	else
     172  #endif
     173  	  err = HURD_FD_PORT_USE (d, __file_record_lock (port, cmd, &fl64,
     174  				  MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND));
     175  
     176  	/* XXX: To remove once file_record_lock RPC is settled.  */
     177  	if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
     178  	  {
     179  	    int wait = 0;
     180  	    va_end (ap);
     181  	    switch (cmd)
     182  	      {
     183  	      case F_GETLK64:
     184  		return __hurd_fail (ENOSYS);
     185  	      case F_SETLKW64:
     186  		wait = 1;
     187  		/* FALLTHROUGH */
     188  	      case F_SETLK64:
     189  		return __f_setlk (fd, fl->l_type, fl->l_whence,
     190  				  fl->l_start, fl->l_len, wait);
     191  	      default:
     192  		return __hurd_fail (EINVAL);
     193  	      }
     194  	  }
     195  	else if (cmd == F_GETLK64)
     196  	  {
     197  	    fl->l_type = fl64.l_type;
     198  	    fl->l_whence = fl64.l_whence;
     199  	    fl->l_start = fl64.l_start;
     200  	    fl->l_len = fl64.l_len;
     201  	    fl->l_pid = fl64.l_pid;
     202  
     203  	    if ((sizeof fl->l_start != sizeof fl64.l_start
     204  		 && fl->l_start != fl64.l_start)
     205  	     || (sizeof fl->l_len != sizeof fl64.l_len
     206  		 && fl->l_len != fl64.l_len))
     207  	      return __hurd_fail (EOVERFLOW);
     208  	  }
     209  
     210  	result = err ? __hurd_dfail (fd, err) : 0;
     211  	break;
     212        }
     213  
     214      case F_GETLK64:
     215      case F_SETLK64:
     216      case F_SETLKW64:
     217        {
     218  	struct flock64 *fl = va_arg (ap, struct flock64 *);
     219  
     220  #ifndef NOCANCEL
     221  	if (cmd == F_SETLKW64)
     222  	  {
     223  	    int cancel_oldtype = LIBC_CANCEL_ASYNC();
     224  	    err = HURD_FD_PORT_USE_CANCEL (d, __file_record_lock (port, cmd,
     225  					   fl, MACH_PORT_NULL,
     226  					   MACH_MSG_TYPE_MAKE_SEND));
     227  	    LIBC_CANCEL_RESET (cancel_oldtype);
     228  	  }
     229  	else
     230  #endif
     231  	  err = HURD_FD_PORT_USE (d, __file_record_lock (port, cmd, fl,
     232  				  MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND));
     233  
     234  	/* XXX: To remove once file_record_lock RPC is settled.  */
     235  	if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
     236  	  {
     237  	    int wait = 0;
     238  	    va_end (ap);
     239  	    switch (cmd)
     240  	      {
     241  	      case F_GETLK64:
     242  		return __hurd_fail (ENOSYS);
     243  	      case F_SETLKW64:
     244  		wait = 1;
     245  		/* FALLTHROUGH */
     246  	      case F_SETLK64:
     247  		return __f_setlk (fd, fl->l_type, fl->l_whence,
     248  				  fl->l_start, fl->l_len, wait);
     249  	      default:
     250  		return __hurd_fail (EINVAL);
     251  	      }
     252  	  }
     253  
     254  	result = err ? __hurd_dfail (fd, err) : 0;
     255  	break;
     256        }
     257  
     258      case F_GETFL:		/* Get per-open flags.  */
     259        if (err = HURD_FD_PORT_USE (d, __io_get_openmodes (port, &result)))
     260  	result = __hurd_dfail (fd, err);
     261        break;
     262  
     263      case F_SETFL:		/* Set per-open flags.  */
     264        err = HURD_FD_PORT_USE (d, __io_set_all_openmodes (port,
     265  							 va_arg (ap, int)));
     266        result = err ? __hurd_dfail (fd, err) : 0;
     267        break;
     268  
     269      case F_GETOWN:		/* Get owner.  */
     270        if (err = HURD_FD_PORT_USE (d, __io_get_owner (port, &result)))
     271  	result = __hurd_dfail (fd, err);
     272        break;
     273  
     274      case F_SETOWN:		/* Set owner.  */
     275        err = HURD_FD_PORT_USE (d, __io_mod_owner (port, va_arg (ap, pid_t)));
     276        result = err ? __hurd_dfail (fd, err) : 0;
     277        break;
     278      }
     279  
     280    va_end (ap);
     281  
     282    return result;
     283  }
     284  libc_hidden_def (__libc_fcntl)
     285  
     286  #ifndef NOCANCEL
     287  weak_alias (__libc_fcntl, __fcntl)
     288  libc_hidden_weak (__fcntl)
     289  weak_alias (__libc_fcntl, fcntl)
     290  
     291  strong_alias (__libc_fcntl, __libc_fcntl64)
     292  libc_hidden_def (__libc_fcntl64)
     293  weak_alias (__libc_fcntl64, __fcntl64)
     294  libc_hidden_weak (__fcntl64)
     295  weak_alias (__fcntl64, fcntl64)
     296  #endif