Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/io.h"
7 :
8 : #include <dirent.h>
9 : #include <errno.h>
10 : #include <fcntl.h>
11 : #include <stdio.h>
12 : #include <stdlib.h>
13 : #include <string.h>
14 : #include <sys/file.h>
15 : #include <sys/socket.h>
16 : #include <sys/stat.h>
17 : #include <unistd.h>
18 :
19 : #include "bpfilter/dynbuf.h"
20 : #include "bpfilter/helper.h"
21 : #include "bpfilter/logger.h"
22 : #include "bpfilter/request.h"
23 : #include "bpfilter/response.h"
24 :
25 : #define BF_PERM_755 (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
26 : #define BF_MSG_BUF_SIZE 1024U
27 :
28 0 : static int _bf_recv_in_buff(int fd, struct bf_dynbuf *buf)
29 : {
30 0 : size_t remaining = 1;
31 :
32 0 : bf_assert(buf);
33 :
34 0 : while (remaining > 0) {
35 : ssize_t r;
36 : uint8_t tmpbuf[BF_MSG_BUF_SIZE];
37 :
38 0 : struct iovec iov[2] = {
39 : {
40 : .iov_base = &remaining,
41 : .iov_len = sizeof(remaining),
42 : },
43 : {
44 : .iov_base = tmpbuf,
45 : .iov_len = BF_MSG_BUF_SIZE,
46 : },
47 : };
48 :
49 0 : struct msghdr msg = {
50 : .msg_iov = iov,
51 : .msg_iovlen = ARRAY_SIZE(iov),
52 : .msg_name = NULL,
53 : .msg_namelen = 0,
54 : .msg_control = NULL,
55 : .msg_controllen = 0,
56 : };
57 :
58 0 : r = recvmsg(fd, &msg, 0);
59 0 : if (r < 0)
60 0 : return bf_err_r(-errno, "failed to receive data");
61 0 : if ((size_t)r < sizeof(remaining))
62 0 : return bf_err_r(-EIO, "received partial data");
63 :
64 0 : r = bf_dynbuf_write(buf, tmpbuf, r - sizeof(remaining));
65 0 : if (r) {
66 0 : return bf_err_r((int)r,
67 : "failed to write received data to dynamic buffer");
68 : }
69 : }
70 :
71 : return 0;
72 : }
73 :
74 0 : static int _bf_send_from_buff(int fd, void *buf, size_t buf_len)
75 : {
76 : size_t sent = 0;
77 :
78 0 : bf_assert(buf);
79 :
80 0 : while (buf_len > 0) {
81 0 : size_t send_size = bf_min(BF_MSG_BUF_SIZE, buf_len);
82 : ssize_t r;
83 0 : size_t rem = buf_len - send_size;
84 :
85 0 : struct iovec iov[2] = {
86 : {
87 : .iov_base = &rem,
88 : .iov_len = sizeof(buf_len),
89 : },
90 : {
91 0 : .iov_base = buf + sent,
92 : .iov_len = send_size,
93 : },
94 : };
95 :
96 0 : struct msghdr msg = {
97 : .msg_iov = iov,
98 : .msg_iovlen = ARRAY_SIZE(iov),
99 : .msg_name = NULL,
100 : .msg_namelen = 0,
101 : .msg_control = NULL,
102 : .msg_controllen = 0,
103 : };
104 :
105 0 : r = sendmsg(fd, &msg, MSG_NOSIGNAL);
106 0 : if (r < 0)
107 0 : return bf_err_r(-errno, "failed to send data from buff");
108 0 : if ((size_t)r != send_size + sizeof(buf_len))
109 0 : return bf_err_r(-EIO, "sent partial data");
110 :
111 0 : sent += (size_t)r - sizeof(buf_len);
112 0 : buf_len -= (size_t)r - sizeof(buf_len);
113 : }
114 :
115 : return 0;
116 : }
117 :
118 0 : int bf_send_request(int fd, const struct bf_request *request)
119 : {
120 : int r;
121 :
122 0 : bf_assert(request);
123 :
124 0 : r = _bf_send_from_buff(fd, (void *)request, bf_request_size(request));
125 0 : if (r < 0)
126 0 : return bf_err_r(r, "failed to send request");
127 :
128 : return 0;
129 : }
130 :
131 0 : int bf_recv_request(int fd, struct bf_request **request)
132 : {
133 0 : _clean_bf_dynbuf_ struct bf_dynbuf dynbuf = bf_dynbuf_default();
134 : int r;
135 :
136 0 : bf_assert(request);
137 :
138 0 : r = _bf_recv_in_buff(fd, &dynbuf);
139 0 : if (r)
140 0 : return bf_err_r(r, "failed to receive request");
141 :
142 0 : r = bf_request_new_from_dynbuf(request, &dynbuf);
143 0 : if (r)
144 0 : return bf_err_r((int)r, "failed to create request from buffer");
145 :
146 : return 0;
147 : }
148 :
149 0 : int bf_send_response(int fd, struct bf_response *response)
150 : {
151 : int r;
152 :
153 0 : bf_assert(response);
154 :
155 0 : r = _bf_send_from_buff(fd, (void *)response, bf_response_size(response));
156 0 : if (r < 0)
157 0 : return bf_err_r(r, "failed to send response");
158 :
159 : return 0;
160 : }
161 :
162 0 : int bf_recv_response(int fd, struct bf_response **response)
163 : {
164 0 : _clean_bf_dynbuf_ struct bf_dynbuf dynbuf = bf_dynbuf_default();
165 : int r;
166 :
167 0 : bf_assert(response);
168 :
169 0 : r = _bf_recv_in_buff(fd, &dynbuf);
170 0 : if (r)
171 0 : return bf_err_r((int)r, "failed to receive response");
172 :
173 0 : r = bf_response_new_from_dynbuf(response, &dynbuf);
174 0 : if (r)
175 0 : return bf_err_r((int)r, "failed to create response from buffer");
176 :
177 : return 0;
178 : }
179 :
180 0 : int bf_ensure_dir(const char *dir)
181 : {
182 : struct stat stats;
183 : int r;
184 :
185 0 : bf_assert(dir);
186 :
187 0 : r = access(dir, R_OK | W_OK);
188 0 : if (r && errno == ENOENT) {
189 0 : if (mkdir(dir, BF_PERM_755) == 0)
190 : return 0;
191 :
192 0 : return bf_err_r(errno, "failed to create directory '%s'", dir);
193 : }
194 : if (r)
195 0 : return bf_err_r(errno, "no R/W permissions on '%s'", dir);
196 :
197 0 : if (stat(dir, &stats) != 0 || !S_ISDIR(stats.st_mode))
198 0 : return bf_err_r(-EINVAL, "'%s' is not a valid directory", dir);
199 :
200 : return 0;
201 : }
202 :
203 0 : int bf_opendir(const char *path)
204 : {
205 0 : _cleanup_close_ int fd = -1;
206 :
207 0 : bf_assert(path);
208 :
209 0 : fd = open(path, O_DIRECTORY);
210 0 : if (fd < 0)
211 0 : return -errno;
212 :
213 0 : return TAKE_FD(fd);
214 : }
215 :
216 0 : int bf_opendir_at(int parent_fd, const char *dir_name, bool mkdir_if_missing)
217 : {
218 0 : _cleanup_close_ int fd = -1;
219 : int r;
220 :
221 0 : bf_assert(dir_name);
222 :
223 0 : retry:
224 0 : fd = openat(parent_fd, dir_name, O_DIRECTORY);
225 0 : if (fd < 0) {
226 0 : if (errno != ENOENT || !mkdir_if_missing)
227 0 : return -errno;
228 :
229 0 : r = mkdirat(parent_fd, dir_name, BF_PERM_755);
230 0 : if (r)
231 0 : return -errno;
232 :
233 0 : goto retry;
234 : }
235 :
236 0 : return TAKE_FD(fd);
237 : }
238 :
239 0 : static void bf_free_dir(DIR **dir)
240 : {
241 0 : if (!*dir)
242 : return;
243 :
244 0 : closedir(*dir);
245 0 : *dir = NULL;
246 : }
247 :
248 : #define _free_dir_ __attribute__((__cleanup__(bf_free_dir)))
249 :
250 0 : int bf_rmdir_at(int parent_fd, const char *dir_name, bool recursive)
251 : {
252 : int r;
253 :
254 0 : bf_assert(dir_name);
255 :
256 0 : if (recursive) {
257 0 : _cleanup_close_ int child_fd = -1;
258 0 : _free_dir_ DIR *dir = NULL;
259 : struct dirent *entry;
260 :
261 0 : child_fd = openat(parent_fd, dir_name, O_DIRECTORY);
262 0 : if (child_fd < 0) {
263 0 : return bf_err_r(errno,
264 : "failed to open child directory for removal");
265 : }
266 :
267 0 : dir = fdopendir(child_fd);
268 0 : if (!dir)
269 0 : return bf_err_r(errno, "failed to open DIR from file descriptor");
270 :
271 0 : while ((entry = readdir(dir))) {
272 : struct stat stat;
273 :
274 0 : if (bf_streq(entry->d_name, ".") || bf_streq(entry->d_name, ".."))
275 0 : continue;
276 :
277 0 : if (fstatat(child_fd, entry->d_name, &stat, 0) < 0) {
278 0 : return bf_err_r(errno,
279 : "failed to fstatat() file '%s' for removal",
280 : entry->d_name);
281 : }
282 :
283 0 : if (S_ISDIR(stat.st_mode))
284 0 : r = bf_rmdir_at(child_fd, entry->d_name, true);
285 : else
286 0 : r = unlinkat(child_fd, entry->d_name, 0) == 0 ? 0 : -errno;
287 0 : if (r)
288 0 : return bf_err_r(r, "failed to remove '%s'", entry->d_name);
289 : }
290 : }
291 :
292 0 : r = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
293 0 : if (r)
294 0 : return -errno;
295 :
296 : return 0;
297 : }
298 :
299 0 : int bf_acquire_lock(const char *path)
300 : {
301 0 : _cleanup_close_ int fd = -1;
302 :
303 0 : bf_assert(path);
304 :
305 0 : fd = open(path, O_CREAT | O_RDWR, BF_PERM_755);
306 0 : if (fd < 0)
307 0 : return -errno;
308 :
309 0 : if (flock(fd, LOCK_EX | LOCK_NB) < 0)
310 0 : return -errno;
311 :
312 0 : return TAKE_FD(fd);
313 : }
314 :
315 0 : int bf_send_fd(int sock_fd, int fd)
316 : {
317 0 : char dummy = 'X';
318 : struct cmsghdr *cmsg;
319 0 : struct msghdr msg = {0};
320 : char buf[CMSG_SPACE(sizeof(int))];
321 0 : struct iovec iov = {.iov_base = &dummy, .iov_len = 1};
322 : ssize_t r;
323 :
324 0 : msg.msg_iov = &iov;
325 0 : msg.msg_iovlen = 1;
326 0 : msg.msg_control = buf;
327 0 : msg.msg_controllen = sizeof(buf);
328 :
329 : cmsg = CMSG_FIRSTHDR(&msg);
330 0 : cmsg->cmsg_level = SOL_SOCKET;
331 0 : cmsg->cmsg_type = SCM_RIGHTS;
332 0 : cmsg->cmsg_len = CMSG_LEN(sizeof(int));
333 :
334 0 : memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
335 :
336 0 : r = sendmsg(sock_fd, &msg, 0);
337 0 : if (r < 0)
338 0 : return bf_err_r(errno, "failed to send file descriptor");
339 :
340 : return 0;
341 : }
342 :
343 0 : int bf_recv_fd(int sock_fd)
344 : {
345 : int fd;
346 : char dummy;
347 : struct cmsghdr *cmsg;
348 0 : struct msghdr msg = {0};
349 : char buf[CMSG_SPACE(sizeof(int))];
350 0 : struct iovec iov = {.iov_base = &dummy, .iov_len = 1};
351 : ssize_t r;
352 :
353 0 : msg.msg_iov = &iov;
354 0 : msg.msg_iovlen = 1;
355 0 : msg.msg_control = buf;
356 0 : msg.msg_controllen = sizeof(buf);
357 :
358 0 : r = recvmsg(sock_fd, &msg, 0);
359 0 : if (r < 0)
360 0 : return bf_err_r(errno, "failed to receive file descriptor");
361 :
362 0 : cmsg = CMSG_FIRSTHDR(&msg);
363 0 : if (!cmsg)
364 0 : return bf_err_r(-ENOENT, "no control message received");
365 0 : if (cmsg->cmsg_level != SOL_SOCKET)
366 0 : return bf_err_r(-EINVAL, "invalid control message level");
367 0 : if (cmsg->cmsg_type != SCM_RIGHTS)
368 0 : return bf_err_r(-EINVAL, "invalid control message type");
369 :
370 0 : memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
371 :
372 0 : return fd;
373 : }
|