1 /*******************************************************************************
2 Copyright (c) 2011 Dmitry Matveev <me@dmitrymatveev.co.uk>
3 Copyright (c) 2014-2018 Vladimir Kondratyev <vladimir@kondratyev.su>
4 Copyright 2008, 2013, 2014
5 The Board of Trustees of the Leland Stanford Junior University
6 Copyright (c) 2004, 2005, 2006
7 by Internet Systems Consortium, Inc. ("ISC")
8 Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
9 2002, 2003 by The Internet Software Consortium and Rich Salz
10 SPDX-License-Identifier: MIT AND ISC
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in
20 all copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 THE SOFTWARE.
29 *******************************************************************************/
30
31 #include "compat.h"
32
33 #include <unistd.h> /* read, write */
34 #include <errno.h> /* EINTR */
35 #include <stdlib.h> /* malloc */
36 #include <string.h> /* strlen */
37 #include <fcntl.h> /* fcntl */
38 #include <stdio.h>
39 #include <assert.h>
40
41 #include <sys/types.h>
42 #include <sys/socket.h>/* send, sendmsg */
43 #include <sys/stat.h> /* fstat */
44 #include <sys/uio.h> /* writev */
45
46 #include "sys/inotify.h"
47 #include "utils.h"
48
49 /**
50 * Create a new inotify event.
51 *
52 * @param[in] wd An associated watch's id.
53 * @param[in] mask An inotify watch mask.
54 * @param[in] cookie Event cookie.
55 * @param[in] name File name (may be NULL).
56 * @param[out] event_len The length of the created event, in bytes.
57 * @return A pointer to a created event on NULL on a failure.
58 **/
59 struct inotify_event*
create_inotify_event(int wd,uint32_t mask,uint32_t cookie,const char * name,size_t * event_len)60 create_inotify_event (int wd,
61 uint32_t mask,
62 uint32_t cookie,
63 const char *name,
64 size_t *event_len)
65 {
66 struct inotify_event *event = NULL;
67 size_t name_len = name ? strlen (name) + 1 : 0;
68 *event_len = sizeof (struct inotify_event) + name_len;
69 event = calloc (1, *event_len);
70
71 if (event == NULL) {
72 perror_msg ("Failed to allocate a new inotify event [%s, %X]",
73 name,
74 mask);
75 return NULL;
76 }
77
78 event->wd = wd;
79 event->mask = mask;
80 event->cookie = cookie;
81 event->len = name_len;
82
83 if (name) {
84 strlcpy (event->name, name, name_len);
85 }
86
87 return event;
88 }
89
90
91 #define SAFE_GENERIC_OP(fcn, fd, data, size, ...) \
92 size_t total = 0; \
93 if (fd == -1) { \
94 return -1; \
95 } \
96 while (size > 0) { \
97 ssize_t retval = fcn (fd, data, size, ##__VA_ARGS__); \
98 if (retval == -1) { \
99 if (errno == EINTR) { \
100 continue; \
101 } else { \
102 return -1; \
103 } \
104 } \
105 total += retval; \
106 size -= retval; \
107 data = (char *)data + retval; \
108 } \
109 return (ssize_t) total;
110
111 /**
112 * EINTR-ready version of read().
113 *
114 * @param[in] fd A file descriptor to read from.
115 * @param[out] data A receiving buffer.
116 * @param[in] size The number of bytes to read.
117 * @return Number of bytes which were read on success, -1 on failure.
118 **/
119 ssize_t
safe_read(int fd,void * data,size_t size)120 safe_read (int fd, void *data, size_t size)
121 {
122 SAFE_GENERIC_OP (read, fd, data, size);
123 }
124
125 /**
126 * EINTR-ready version of write().
127 *
128 * @param[in] fd A file descriptor to write to.
129 * @param[in] data A buffer to wtite.
130 * @param[in] size The number of bytes to write.
131 * @return Number of bytes which were written on success, -1 on failure.
132 **/
133 ssize_t
safe_write(int fd,const void * data,size_t size)134 safe_write (int fd, const void *data, size_t size)
135 {
136 SAFE_GENERIC_OP (write, fd, data, size);
137 }
138
139 /**
140 * EINTR-ready version of send().
141 *
142 * @param[in] fd A file descriptor to send to.
143 * @param[in] data A buffer to send.
144 * @param[in] size The number of bytes to send.
145 * @param[in] flags A send(3) flags.
146 * @return Number of bytes which were sent on success, -1 on failure.
147 **/
148 ssize_t
safe_send(int fd,const void * data,size_t size,int flags)149 safe_send (int fd, const void *data, size_t size, int flags)
150 {
151 SAFE_GENERIC_OP (send, fd, data, size, flags);
152 }
153
154 /**
155 * The canonical version of this routine is maintained in the rra-c-util,
156 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
157 */
158 #define SAFE_GENERIC_VOP(fcn, fd, iov, iovcnt, ...) \
159 \
160 ssize_t total, status = 0; \
161 size_t left, offset; \
162 int iovleft, i, count; \
163 struct iovec *tmpiov; \
164 \
165 /* \
166 * Bounds-check the iovcnt argument. This is just for our safety. The \
167 * system will probably impose a lower limit on iovcnt, causing the later \
168 * writev to fail with an error we'll return. \
169 */ \
170 if (iovcnt == 0) \
171 return 0; \
172 if (iovcnt < 0 || (size_t) iovcnt > SIZE_MAX / sizeof(struct iovec)) { \
173 errno = EINVAL; \
174 return -1; \
175 } \
176 \
177 /* Get a count of the total number of bytes in the iov array. */ \
178 for (total = 0, i = 0; i < iovcnt; i++) \
179 total += iov[i].iov_len; \
180 if (total == 0) \
181 return 0; \
182 \
183 /* \
184 * First, try just writing it all out. Most of the time this will succeed \
185 * and save us lots of work. Abort the write if we try ten times with no \
186 * forward progress. \
187 */ \
188 count = 0; \
189 do { \
190 if (++count > 10) \
191 break; \
192 status = fcn(fd, iov, iovcnt, ##__VA_ARGS__); \
193 if (status > 0) \
194 count = 0; \
195 } while (status < 0 && errno == EINTR); \
196 if (status < 0) \
197 return -1; \
198 if (status == total) \
199 return total; \
200 \
201 /* \
202 * If we fell through to here, the first write partially succeeded. \
203 * Figure out how far through the iov array we got, and then duplicate the \
204 * rest of it so that we can modify it to reflect how much we manage to \
205 * write on successive tries. \
206 */ \
207 offset = status; \
208 left = total - offset; \
209 for (i = 0; offset >= (size_t) iov[i].iov_len; i++) \
210 offset -= iov[i].iov_len; \
211 iovleft = iovcnt - i; \
212 assert(iovleft > 0); \
213 tmpiov = calloc(iovleft, sizeof(struct iovec)); \
214 if (tmpiov == NULL) \
215 return -1; \
216 memcpy(tmpiov, iov + i, iovleft * sizeof(struct iovec)); \
217 \
218 /* \
219 * status now contains the offset into the first iovec struct in tmpiov. \
220 * Go into the write loop, trying to write out everything remaining at \
221 * each point. At the top of the loop, status will contain a count of \
222 * bytes written out at the beginning of the set of iovec structs. \
223 */ \
224 i = 0; \
225 do { \
226 if (++count > 10) \
227 break; \
228 \
229 /* Skip any leading data that has been written out. */ \
230 for (; offset >= (size_t) tmpiov[i].iov_len && iovleft > 0; i++) { \
231 offset -= tmpiov[i].iov_len; \
232 iovleft--; \
233 } \
234 tmpiov[i].iov_base = (char *) tmpiov[i].iov_base + offset; \
235 tmpiov[i].iov_len -= offset; \
236 \
237 /* Write out what's left and return success if it's all written. */ \
238 status = fcn(fd, tmpiov + i, iovleft, ##__VA_ARGS__); \
239 if (status <= 0) \
240 offset = 0; \
241 else { \
242 offset = status; \
243 left -= offset; \
244 count = 0; \
245 } \
246 } while (left > 0 && (status >= 0 || errno == EINTR)); \
247 \
248 /* We're either done or got an error; if we're done, left is now 0. */ \
249 free(tmpiov); \
250 return (left == 0) ? total : -1;
251
252 /**
253 * scatter-gather version of send with writev()-style parameters.
254 *
255 * @param[in] fd A file descriptor to send to.
256 * @param[in] iov An array of iovec buffers to wtite.
257 * @param[in] iovcnt A number of iovec buffers to write.
258 * @param[in] flags A send(3) flags.
259 * @return Number of bytes which were written on success, -1 on failure.
260 **/
261 ssize_t
sendv(int fd,struct iovec iov[],int iovcnt,int flags)262 sendv (int fd, struct iovec iov[], int iovcnt, int flags)
263 {
264 struct msghdr msg;
265
266 memset (&msg, 0, sizeof (msg));
267 msg.msg_iov = iov;
268 msg.msg_iovlen = iovcnt;
269
270 return (sendmsg (fd, &msg, flags));
271 }
272
273 /**
274 * EINTR-ready version of writev().
275 *
276 * @param[in] fd A file descriptor to write to.
277 * @param[in] iov An array of iovec buffers to wtite.
278 * @param[in] iovcnt A number of iovec buffers to write.
279 * @return Number of bytes which were written on success, -1 on failure.
280 **/
281 ssize_t
safe_writev(int fd,const struct iovec iov[],int iovcnt)282 safe_writev (int fd, const struct iovec iov[], int iovcnt)
283 {
284 SAFE_GENERIC_VOP (writev, fd, iov, iovcnt);
285 }
286
287 /**
288 * EINTR-ready version of sendv().
289 *
290 * @param[in] fd A file descriptor to send to.
291 * @param[in] iov An array of iovec buffers to wtite.
292 * @param[in] iovcnt A number of iovec buffers to write.
293 * @param[in] flags A send(3) flags.
294 * @return Number of bytes which were written on success, -1 on failure.
295 **/
296 ssize_t
safe_sendv(int fd,struct iovec iov[],int iovcnt,int flags)297 safe_sendv (int fd, struct iovec iov[], int iovcnt, int flags)
298 {
299 SAFE_GENERIC_VOP (sendv, fd, iov, iovcnt, flags);
300 }
301
302 /**
303 * Check if the specified file descriptor is still opened.
304 *
305 * @param[in] fd A file descriptor to check.
306 * @return 1 if still opened, 0 if closed or an error has occured.
307 **/
308 int
is_opened(int fd)309 is_opened (int fd)
310 {
311 int ret = (fcntl (fd, F_GETFL) != -1);
312 return ret;
313 }
314
315 /**
316 * Check if the file referenced by specified descriptor is deleted.
317 *
318 * @param[in] fd A file descriptor to check.
319 * @return 1 if deleted or error occured, 0 if hardlinks to file still exist.
320 **/
321 int
is_deleted(int fd)322 is_deleted (int fd)
323 {
324 struct stat st;
325
326 if (fstat (fd, &st) == -1) {
327 if (errno != ENOENT) {
328 perror_msg ("fstat %d failed", fd);
329 }
330 return 1;
331 }
332
333 return (st.st_nlink == 0);
334 }
335
336 /**
337 * Set the FD_CLOEXEC flag of file descriptor fd if value is nonzero
338 * clear the flag if value is 0.
339 *
340 * @param[in] fd A file descriptor to modify.
341 * @param[in] value A cloexec flag value to set.
342 * @return 0 on success, or -1 on error with errno set.
343 **/
344 int
set_cloexec_flag(int fd,int value)345 set_cloexec_flag (int fd, int value)
346 {
347 int flags = fcntl (fd, F_GETFD, 0);
348 if (flags < 0)
349 return flags;
350
351 if (value != 0)
352 flags |= FD_CLOEXEC;
353 else
354 flags &= ~FD_CLOEXEC;
355
356 return fcntl (fd, F_SETFD, flags);
357 }
358
359 /*
360 * Set the O_NONBLOCK flag of file descriptor fd if value is nonzero
361 * clear the flag if value is 0.
362 *
363 * @param[in] fd A file descriptor to modify.
364 * @param[in] value A nonblock flag value to set.
365 * @return 0 on success, or -1 on error with errno set.
366 **/
367 int
set_nonblock_flag(int fd,int value)368 set_nonblock_flag (int fd, int value)
369 {
370 int flags = fcntl (fd, F_GETFL, 0);
371 if (flags < 0)
372 return flags;
373
374 if (value != 0)
375 flags |= O_NONBLOCK;
376 else
377 flags &= ~O_NONBLOCK;
378
379 return fcntl (fd, F_SETFL, flags);
380 }
381
382 /**
383 * Perform dup(2) and set the FD_CLOEXEC flag on the new file descriptor
384 *
385 * @param[in] oldd A file descriptor to duplicate.
386 * @return A new file descriptor on success, or -1 if an error occurs.
387 * The external variable errno indicates the cause of the error.
388 **/
389 int
dup_cloexec(int oldd)390 dup_cloexec (int oldd)
391 {
392 #ifdef F_DUPFD_CLOEXEC
393 int newd = fcntl (oldd, F_DUPFD_CLOEXEC, 0);
394 #else
395 int newd = fcntl (oldd, F_DUPFD, 0);
396
397 if ((newd != -1) && (set_cloexec_flag (newd, 1) == -1)) {
398 close (newd);
399 newd = -1;
400 }
401 #endif
402 return newd;
403 }
404
405 /**
406 * Open directory one more time by realtive path "."
407 *
408 * @param[in] fd A file descriptor to inherit
409 * @return A new file descriptor on success, or -1 if an error occured.
410 **/
411 DIR *
fdreopendir(int oldd)412 fdreopendir (int oldd)
413 {
414 DIR *dir;
415
416 #if (READDIR_DOES_OPENDIR == 2)
417 int openflags = O_RDONLY | O_NONBLOCK;
418 #ifdef O_CLOEXEC
419 openflags |= O_CLOEXEC;
420 #endif
421 #ifdef O_DIRECTORY
422 openflags |= O_DIRECTORY;
423 #endif
424 int fd = openat (oldd, ".", openflags);
425 #else
426 int fd = dup_cloexec (oldd);
427 /*
428 * Rewind directory content as fdopendir() does not do it for us.
429 * Note: rewinddir() right after fdopendir() is not working here
430 * due to rewinddir() bug in some versions of FreeBSD and Darwin libc.
431 */
432 lseek (fd, 0, SEEK_SET);
433 #endif
434 if (fd == -1) {
435 return NULL;
436 }
437
438 #if (READDIR_DOES_OPENDIR == 2) && !defined(O_CLOEXEC)
439 if (set_cloexec_flag (fd, 1) == -1) {
440 close (fd);
441 return NULL;
442 }
443 #endif
444
445 dir = fdopendir (fd);
446 if (dir == NULL) {
447 close (fd);
448 }
449
450 return dir;
451 }
452