LCOV - code coverage report
Current view: top level - libbpfilter - io.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 0.0 % 157 0
Test Date: 2025-09-16 14:46:44 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 "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              : }
        

Generated by: LCOV version 2.0-1