1/* Copyright (C) 1992-2024 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. */
32int
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}
284libc_hidden_def (__libc_fcntl)
285
286#ifndef NOCANCEL
287weak_alias (__libc_fcntl, __fcntl)
288libc_hidden_weak (__fcntl)
289weak_alias (__libc_fcntl, fcntl)
290
291strong_alias (__libc_fcntl, __libc_fcntl64)
292libc_hidden_def (__libc_fcntl64)
293weak_alias (__libc_fcntl64, __fcntl64)
294libc_hidden_weak (__fcntl64)
295weak_alias (__fcntl64, fcntl64)
296#endif
297

source code of glibc/sysdeps/mach/hurd/fcntl.c