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