1 /* Copyright (C) 1992-2022 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
__libc_fcntl(int fd,int cmd,...)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       errno = EINVAL;
52       result = -1;
53       break;
54 
55       /* First the descriptor-based commands, which do no RPCs.  */
56 
57     case F_DUPFD:		/* Duplicate the file descriptor.  */
58     case F_DUPFD_CLOEXEC:
59       {
60 	struct hurd_fd *new;
61 	io_t port, ctty;
62 	struct hurd_userlink ulink, ctty_ulink;
63 	int flags;
64 
65 	HURD_CRITICAL_BEGIN;
66 
67 	/* Extract the ports and flags from the file descriptor.  */
68 	__spin_lock (&d->port.lock);
69 	flags = d->flags;
70 	ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
71 	port = _hurd_port_locked_get (&d->port, &ulink); /* Unlocks D.  */
72 
73 	if (cmd == F_DUPFD_CLOEXEC)
74 	  flags |= FD_CLOEXEC;
75 	else
76 	  /* Duplication clears the FD_CLOEXEC flag.  */
77 	  flags &= ~FD_CLOEXEC;
78 
79 	/* Get a new file descriptor.  The third argument to __fcntl is the
80 	   minimum file descriptor number for it.  */
81 	new = _hurd_alloc_fd (&result, va_arg (ap, int));
82 	if (new == NULL)
83 	  /* _hurd_alloc_fd has set errno.  */
84 	  result = -1;
85 	else
86 	  {
87 	    /* Give the ports each a user ref for the new descriptor.  */
88 	    __mach_port_mod_refs (__mach_task_self (), port,
89 				  MACH_PORT_RIGHT_SEND, 1);
90 	    if (ctty != MACH_PORT_NULL)
91 	      __mach_port_mod_refs (__mach_task_self (), ctty,
92 				    MACH_PORT_RIGHT_SEND, 1);
93 
94 	    /* Install the ports and flags in the new descriptor.  */
95 	    if (ctty != MACH_PORT_NULL)
96 	      _hurd_port_set (&new->ctty, ctty);
97 	    new->flags = flags;
98 	    _hurd_port_locked_set (&new->port, port); /* Unlocks NEW.  */
99 	  }
100 
101 	HURD_CRITICAL_END;
102 
103 	_hurd_port_free (&d->port, &ulink, port);
104 	if (ctty != MACH_PORT_NULL)
105 	  _hurd_port_free (&d->ctty, &ctty_ulink, port);
106 
107 	break;
108       }
109 
110       /* Set RESULT by evaluating EXPR with the descriptor locked.
111 	 Check for an empty descriptor and return EBADF.  */
112 #define LOCKED(expr)							      \
113       HURD_CRITICAL_BEGIN;						      \
114       __spin_lock (&d->port.lock);					      \
115       if (d->port.port == MACH_PORT_NULL)				      \
116 	result = __hurd_fail (EBADF);					      \
117       else								      \
118 	result = (expr);						      \
119       __spin_unlock (&d->port.lock);					      \
120       HURD_CRITICAL_END;
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 	    errno = EINVAL;
152 	    return -1;
153 	  }
154 
155 	struct flock64 fl64 = {
156 	  .l_type = fl->l_type,
157 	  .l_whence = fl->l_whence,
158 	  .l_start = fl->l_start,
159 	  .l_len = fl->l_len,
160 	  .l_pid = fl->l_pid
161 	};
162 
163 #ifndef NOCANCEL
164 	if (cmd == F_SETLKW64)
165 	  {
166 	    int cancel_oldtype = LIBC_CANCEL_ASYNC();
167 	    err = HURD_FD_PORT_USE_CANCEL (d, __file_record_lock (port, cmd,
168 					   &fl64, MACH_PORT_NULL,
169 					   MACH_MSG_TYPE_MAKE_SEND));
170 	    LIBC_CANCEL_RESET (cancel_oldtype);
171 	  }
172 	else
173 #endif
174 	  err = HURD_FD_PORT_USE (d, __file_record_lock (port, cmd, &fl64,
175 				  MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND));
176 
177 	/* XXX: To remove once file_record_lock RPC is settled.  */
178 	if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
179 	  {
180 	    int wait = 0;
181 	    va_end (ap);
182 	    switch (cmd)
183 	      {
184 	      case F_GETLK64:
185 		errno = ENOSYS;
186 		return -1;
187 	      case F_SETLKW64:
188 		wait = 1;
189 		/* FALLTHROUGH */
190 	      case F_SETLK64:
191 		return __f_setlk (fd, fl->l_type, fl->l_whence,
192 				  fl->l_start, fl->l_len, wait);
193 	      default:
194 		errno = EINVAL;
195 		return -1;
196 	      }
197 	  }
198 	else if (cmd == F_GETLK64)
199 	  {
200 	    fl->l_type = fl64.l_type;
201 	    fl->l_whence = fl64.l_whence;
202 	    fl->l_start = fl64.l_start;
203 	    fl->l_len = fl64.l_len;
204 	    fl->l_pid = fl64.l_pid;
205 
206 	    if ((sizeof fl->l_start != sizeof fl64.l_start
207 		 && fl->l_start != fl64.l_start)
208 	     || (sizeof fl->l_len != sizeof fl64.l_len
209 		 && fl->l_len != fl64.l_len))
210 	      {
211 		errno = EOVERFLOW;
212 		return -1;
213 	      }
214 	  }
215 
216 	result = err ? __hurd_dfail (fd, err) : 0;
217 	break;
218       }
219 
220     case F_GETLK64:
221     case F_SETLK64:
222     case F_SETLKW64:
223       {
224 	struct flock64 *fl = va_arg (ap, struct flock64 *);
225 
226 #ifndef NOCANCEL
227 	if (cmd == F_SETLKW64)
228 	  {
229 	    int cancel_oldtype = LIBC_CANCEL_ASYNC();
230 	    err = HURD_FD_PORT_USE_CANCEL (d, __file_record_lock (port, cmd,
231 					   fl, MACH_PORT_NULL,
232 					   MACH_MSG_TYPE_MAKE_SEND));
233 	    LIBC_CANCEL_RESET (cancel_oldtype);
234 	  }
235 	else
236 #endif
237 	  err = HURD_FD_PORT_USE (d, __file_record_lock (port, cmd, fl,
238 				  MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND));
239 
240 	/* XXX: To remove once file_record_lock RPC is settled.  */
241 	if (err == EMIG_BAD_ID || err == EOPNOTSUPP)
242 	  {
243 	    int wait = 0;
244 	    va_end (ap);
245 	    switch (cmd)
246 	      {
247 	      case F_GETLK64:
248 		errno = ENOSYS;
249 		return -1;
250 	      case F_SETLKW64:
251 		wait = 1;
252 		/* FALLTHROUGH */
253 	      case F_SETLK64:
254 		return __f_setlk (fd, fl->l_type, fl->l_whence,
255 				  fl->l_start, fl->l_len, wait);
256 	      default:
257 		errno = EINVAL;
258 		return -1;
259 	      }
260 	  }
261 
262 	result = err ? __hurd_dfail (fd, err) : 0;
263 	break;
264       }
265 
266     case F_GETFL:		/* Get per-open flags.  */
267       if (err = HURD_FD_PORT_USE (d, __io_get_openmodes (port, &result)))
268 	result = __hurd_dfail (fd, err);
269       break;
270 
271     case F_SETFL:		/* Set per-open flags.  */
272       err = HURD_FD_PORT_USE (d, __io_set_all_openmodes (port,
273 							 va_arg (ap, int)));
274       result = err ? __hurd_dfail (fd, err) : 0;
275       break;
276 
277     case F_GETOWN:		/* Get owner.  */
278       if (err = HURD_FD_PORT_USE (d, __io_get_owner (port, &result)))
279 	result = __hurd_dfail (fd, err);
280       break;
281 
282     case F_SETOWN:		/* Set owner.  */
283       err = HURD_FD_PORT_USE (d, __io_mod_owner (port, va_arg (ap, pid_t)));
284       result = err ? __hurd_dfail (fd, err) : 0;
285       break;
286     }
287 
288   va_end (ap);
289 
290   return result;
291 }
292 libc_hidden_def (__libc_fcntl)
293 
294 #ifndef NOCANCEL
295 weak_alias (__libc_fcntl, __fcntl)
296 libc_hidden_weak (__fcntl)
297 weak_alias (__libc_fcntl, fcntl)
298 
299 strong_alias (__libc_fcntl, __libc_fcntl64)
300 libc_hidden_def (__libc_fcntl64)
301 weak_alias (__libc_fcntl64, __fcntl64)
302 libc_hidden_weak (__fcntl64)
303 weak_alias (__fcntl64, fcntl64)
304 #endif
305