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/stat.h>
15 : #include <unistd.h>
16 :
17 : #include "core/helper.h"
18 : #include "core/logger.h"
19 : #include "core/request.h"
20 : #include "core/response.h"
21 :
22 : #define BF_PERM_755 (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
23 :
24 0 : static ssize_t _bf_recv_in_buff(int fd, void *buf, size_t buf_len)
25 : {
26 : ssize_t bytes_read = 0;
27 : ssize_t r;
28 :
29 0 : bf_assert(buf);
30 :
31 : do {
32 : /// @todo Add a timeout to the socket to prevent blocking forever.
33 0 : r = read(fd, buf + bytes_read, buf_len - bytes_read);
34 0 : if (r < 0) {
35 0 : (void)fprintf(stderr, "can't read from the socket: %s\n",
36 0 : bf_strerror(errno));
37 0 : return -errno;
38 : }
39 :
40 0 : bytes_read += r;
41 0 : } while (r && (size_t)bytes_read != buf_len);
42 :
43 : return bytes_read;
44 : }
45 :
46 0 : static ssize_t _bf_send_from_buff(int fd, void *buf, size_t buf_len)
47 : {
48 : ssize_t bytes_sent = 0;
49 : ssize_t r;
50 :
51 0 : bf_assert(buf);
52 :
53 0 : while ((size_t)bytes_sent < buf_len) {
54 0 : r = write(fd, buf + bytes_sent, buf_len - bytes_sent);
55 0 : if (r < 0) {
56 0 : (void)fprintf(stderr, "can't write to socket: %s\n",
57 0 : bf_strerror(errno));
58 0 : return -errno;
59 : }
60 :
61 0 : bytes_sent += r;
62 : }
63 :
64 : return bytes_sent;
65 : }
66 :
67 0 : int bf_send_request(int fd, const struct bf_request *request)
68 : {
69 : ssize_t r;
70 :
71 0 : bf_assert(request);
72 :
73 0 : r = _bf_send_from_buff(fd, (void *)request, bf_request_size(request));
74 0 : if (r < 0) {
75 0 : (void)fprintf(stderr, "Failed to send request: %s\n",
76 0 : bf_strerror(errno));
77 0 : return -errno;
78 : }
79 :
80 0 : if ((size_t)r != bf_request_size(request)) {
81 0 : (void)fprintf(stderr,
82 : "Failed to send request: %lu bytes sent, %ld expected\n",
83 : (size_t)r, bf_request_size(request));
84 0 : return -EIO;
85 : }
86 :
87 : return 0;
88 : }
89 :
90 0 : int bf_recv_request(int fd, struct bf_request **request)
91 : {
92 : struct bf_request req;
93 0 : _cleanup_bf_request_ struct bf_request *_request = NULL;
94 : ssize_t r;
95 :
96 0 : bf_assert(request);
97 :
98 0 : r = _bf_recv_in_buff(fd, &req, sizeof(req));
99 0 : if (r < 0)
100 0 : return (int)r;
101 :
102 0 : if ((size_t)r != sizeof(req)) {
103 0 : (void)fprintf(stderr,
104 : "failed to read request: %lu bytes read, %lu expected\n",
105 : (size_t)r, sizeof(req));
106 0 : return -EIO;
107 : }
108 :
109 0 : _request = malloc(bf_request_size(&req));
110 0 : if (!_request) {
111 0 : (void)fprintf(stderr, "failed to allocate request: %s\n",
112 0 : bf_strerror(errno));
113 0 : return -errno;
114 : }
115 :
116 0 : memcpy(_request, &req, sizeof(req));
117 :
118 0 : r = _bf_recv_in_buff(fd, _request->data, _request->data_len);
119 0 : if (r < 0)
120 0 : return (int)r;
121 :
122 0 : if ((size_t)r != _request->data_len) {
123 0 : (void)fprintf(stderr,
124 : "failed to read request: %lu bytes read, %lu expected\n",
125 : (size_t)r, _request->data_len);
126 0 : return -EIO;
127 : }
128 :
129 0 : *request = TAKE_PTR(_request);
130 :
131 0 : return 0;
132 : }
133 :
134 0 : int bf_send_response(int fd, struct bf_response *response)
135 : {
136 : ssize_t r;
137 :
138 0 : bf_assert(response);
139 :
140 0 : r = _bf_send_from_buff(fd, (void *)response, bf_response_size(response));
141 0 : if (r < 0) {
142 0 : (void)fprintf(stderr, "Failed to send response: %s\n",
143 0 : bf_strerror(errno));
144 0 : return -errno;
145 : }
146 :
147 0 : if ((size_t)r != bf_response_size(response)) {
148 0 : (void)fprintf(stderr,
149 : "Failed to send response: %lu bytes sent, %ld expected\n",
150 : r, bf_response_size(response));
151 0 : return -EIO;
152 : }
153 :
154 : return 0;
155 : }
156 :
157 0 : int bf_recv_response(int fd, struct bf_response **response)
158 : {
159 : struct bf_response res;
160 0 : _cleanup_bf_response_ struct bf_response *_response = NULL;
161 : ssize_t r;
162 :
163 0 : bf_assert(response);
164 :
165 0 : r = _bf_recv_in_buff(fd, &res, sizeof(res));
166 0 : if (r < 0)
167 0 : return -errno;
168 :
169 0 : if ((size_t)r != sizeof(res)) {
170 0 : (void)fprintf(stderr,
171 : "failed to read response: %lu bytes read, %lu expected\n",
172 : (size_t)r, sizeof(res));
173 0 : return -EIO;
174 : }
175 :
176 0 : _response = malloc(bf_response_size(&res));
177 0 : if (!_response) {
178 0 : (void)fprintf(stderr, "failed to allocate response: %s\n",
179 0 : bf_strerror(errno));
180 0 : return -errno;
181 : }
182 :
183 0 : memcpy(_response, &res, sizeof(res));
184 :
185 0 : r = _bf_recv_in_buff(fd, _response->data, _response->data_len);
186 0 : if (r < 0)
187 0 : return (int)r;
188 :
189 0 : if (_response->type == BF_RES_SUCCESS && (size_t)r != _response->data_len) {
190 0 : (void)fprintf(stderr,
191 : "failed to read response: %lu bytes read, %lu expected\n",
192 : (size_t)r, _response->data_len);
193 0 : return -EIO;
194 : }
195 :
196 0 : *response = TAKE_PTR(_response);
197 :
198 0 : return 0;
199 : }
200 :
201 0 : int bf_ensure_dir(const char *dir)
202 : {
203 : struct stat stats;
204 : int r;
205 :
206 0 : bf_assert(dir);
207 :
208 0 : r = access(dir, R_OK | W_OK);
209 0 : if (r && errno == ENOENT) {
210 0 : if (mkdir(dir, BF_PERM_755) == 0)
211 : return 0;
212 :
213 0 : return bf_err_r(errno, "failed to create directory '%s'", dir);
214 : }
215 : if (r)
216 0 : return bf_err_r(errno, "no R/W permissions on '%s'", dir);
217 :
218 0 : if (stat(dir, &stats) != 0 || !S_ISDIR(stats.st_mode))
219 0 : return bf_err_r(-EINVAL, "'%s' is not a valid directory", dir);
220 :
221 : return 0;
222 : }
223 :
224 0 : int bf_opendir(const char *path)
225 : {
226 0 : _cleanup_close_ int fd = -1;
227 :
228 0 : bf_assert(path);
229 :
230 0 : fd = open(path, O_DIRECTORY);
231 0 : if (fd < 0)
232 0 : return -errno;
233 :
234 0 : return TAKE_FD(fd);
235 : }
236 :
237 0 : int bf_opendir_at(int parent_fd, const char *dir_name, bool mkdir_if_missing)
238 : {
239 0 : _cleanup_close_ int fd = -1;
240 : int r;
241 :
242 0 : bf_assert(dir_name);
243 :
244 0 : retry:
245 0 : fd = openat(parent_fd, dir_name, O_DIRECTORY);
246 0 : if (fd < 0) {
247 0 : if (errno != ENOENT || !mkdir_if_missing)
248 0 : return -errno;
249 :
250 0 : r = mkdirat(parent_fd, dir_name, BF_PERM_755);
251 0 : if (r)
252 0 : return -errno;
253 :
254 0 : goto retry;
255 : }
256 :
257 0 : return TAKE_FD(fd);
258 : }
259 :
260 : static void bf_free_dir(DIR **dir)
261 : {
262 0 : if (!*dir)
263 : return;
264 :
265 0 : closedir(*dir);
266 : *dir = NULL;
267 : }
268 :
269 : #define _free_dir_ __attribute__((__cleanup__(bf_free_dir)))
270 :
271 0 : int bf_rmdir_at(int parent_fd, const char *dir_name, bool recursive)
272 : {
273 : int r;
274 :
275 0 : bf_assert(dir_name);
276 :
277 0 : if (recursive) {
278 0 : _cleanup_close_ int child_fd = -1;
279 : _free_dir_ DIR *dir = NULL;
280 : struct dirent *entry;
281 :
282 0 : child_fd = openat(parent_fd, dir_name, O_DIRECTORY);
283 0 : if (child_fd < 0) {
284 0 : return bf_err_r(errno,
285 : "failed to open child directory for removal");
286 : }
287 :
288 0 : dir = fdopendir(child_fd);
289 0 : if (!dir)
290 0 : return bf_err_r(errno, "failed to open DIR from file descriptor");
291 :
292 0 : while ((entry = readdir(dir))) {
293 : struct stat stat;
294 :
295 0 : if (bf_streq(entry->d_name, ".") || bf_streq(entry->d_name, ".."))
296 0 : continue;
297 :
298 0 : if (fstatat(child_fd, entry->d_name, &stat, 0) < 0) {
299 0 : return bf_err_r(errno,
300 : "failed to fstatat() file '%s' for removal",
301 : entry->d_name);
302 : }
303 :
304 0 : if (S_ISDIR(stat.st_mode))
305 0 : r = bf_rmdir_at(child_fd, entry->d_name, true);
306 : else
307 0 : r = unlinkat(child_fd, entry->d_name, 0) == 0 ? 0 : -errno;
308 0 : if (r)
309 0 : return bf_err_r(r, "failed to remove '%s'", entry->d_name);
310 : }
311 : }
312 :
313 0 : r = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
314 0 : if (r)
315 0 : return -errno;
316 :
317 : return 0;
318 : }
|