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. */ |
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 |
297 | |