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