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