LCOV - code coverage report
Current view: top level - core - io.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 0.0 % 184 0
Test Date: 2025-08-19 17:27:08 Functions: 0.0 % 14 0

            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              : }
        

Generated by: LCOV version 2.0-1