create_rsync_argv(): Allocate correctly sized arg array.
[dss.git] / file.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <assert.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <dirent.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12
13 #include "gcc-compat.h"
14 #include "err.h"
15 #include "str.h"
16 #include "file.h"
17
18 /**
19  * Call a function for each subdirectory of the current working directory.
20  *
21  * \param dirname The directory to traverse.
22  * \param func The function to call for each subdirectory.
23  * \param private_data Pointer to an arbitrary data structure.
24  *
25  * For each top-level directory under \a dirname, the supplied function \a func is
26  * called.  The full path of the subdirectory and the \a private_data pointer
27  * are passed to \a func.
28  *
29  * \return This function returns immediately if \a func returned a negative
30  * value. In this case \a func must set error_txt and this negative value is
31  * returned to the caller. Otherwise the function returns when all
32  * subdirectories have been passed to \a func.
33  */
34
35 int for_each_subdir(int (*func)(const char *, void *), void *private_data)
36 {
37         struct dirent *entry;
38         int ret;
39         DIR *dir = opendir(".");
40
41         if (!dir)
42                 return -ERRNO_TO_DSS_ERROR(errno);
43         while ((entry = readdir(dir))) {
44                 mode_t m;
45                 struct stat s;
46
47                 if (!strcmp(entry->d_name, "."))
48                         continue;
49                 if (!strcmp(entry->d_name, ".."))
50                         continue;
51                 ret = lstat(entry->d_name, &s) == -1;
52                 if (ret == -1) {
53                         ret = -ERRNO_TO_DSS_ERROR(errno);
54                         goto out;
55                 }
56                 m = s.st_mode;
57                 if (!S_ISDIR(m))
58                         continue;
59                 ret = func(entry->d_name, private_data);
60                 if (ret < 0)
61                         goto out;
62         }
63         ret = 1;
64 out:
65         closedir(dir);
66         return ret;
67 }
68
69 /**
70  * Set a file descriptor to non-blocking mode.
71  *
72  * \param fd The file descriptor.
73  *
74  * \return Standard.
75  */
76 __must_check int mark_fd_nonblocking(int fd)
77 {
78         int flags = fcntl(fd, F_GETFL);
79         if (flags < 0)
80                 return -ERRNO_TO_DSS_ERROR(errno);
81         flags = fcntl(fd, F_SETFL, ((long)flags) | O_NONBLOCK);
82         if (flags < 0)
83                 return -ERRNO_TO_DSS_ERROR(errno);
84         return 1;
85 }
86
87 /**
88  * dss' wrapper for select(2).
89  *
90  * It calls select(2) (with no exceptfds) and starts over if select() was
91  * interrupted by a signal.
92  *
93  * \param n The highest-numbered descriptor in any of the two sets, plus 1.
94  * \param readfds fds that should be checked for readability.
95  * \param writefds fds that should be checked for writablility.
96  * \param timeout_tv upper bound on the amount of time elapsed before select()
97  * returns.
98  *
99  * \return The return value of the underlying select() call on success, the
100  * negative system error code on errors.
101  *
102  * All arguments are passed verbatim to select(2).
103  * \sa select(2) select_tut(2).
104  */
105 int dss_select(int n, fd_set *readfds, fd_set *writefds,
106                 struct timeval *timeout_tv)
107 {
108         int ret, err;
109         do {
110                 ret = select(n, readfds, writefds, NULL, timeout_tv);
111                 err = errno;
112         } while (ret < 0 && err == EINTR);
113         if (ret < 0)
114                 return -ERRNO_TO_DSS_ERROR(errno);
115         return ret;
116 }