Branch data 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 <sys/file.h>
12 : : #include <sys/stat.h>
13 : : #include <unistd.h>
14 : :
15 : : #include "bpfilter/helper.h"
16 : : #include "bpfilter/logger.h"
17 : :
18 : : #define BF_PERM_755 (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
19 : :
20 : 4 : int bf_ensure_dir(const char *dir)
21 : : {
22 : : struct stat stats;
23 : : int r;
24 : :
25 : : assert(dir);
26 : :
27 : 4 : r = access(dir, R_OK | W_OK);
28 [ + + + - ]: 4 : if (r && errno == ENOENT) {
29 [ + - ]: 1 : if (mkdir(dir, BF_PERM_755) == 0)
30 : : return 0;
31 : :
32 [ # # ]: 0 : return bf_err_r(errno, "failed to create directory '%s'", dir);
33 : : }
34 : : if (r)
35 [ # # ]: 0 : return bf_err_r(errno, "no R/W permissions on '%s'", dir);
36 : :
37 [ + - + + ]: 3 : if (stat(dir, &stats) != 0 || !S_ISDIR(stats.st_mode))
38 [ + - ]: 1 : return bf_err_r(-EINVAL, "'%s' is not a valid directory", dir);
39 : :
40 : : return 0;
41 : : }
42 : :
43 : 4853 : int bf_opendir(const char *path)
44 : : {
45 : 4853 : _cleanup_close_ int fd = -1;
46 : :
47 : : assert(path);
48 : :
49 : 4853 : fd = open(path, O_DIRECTORY);
50 [ + + ]: 4853 : if (fd < 0)
51 : 1 : return -errno;
52 : :
53 : 4852 : return TAKE_FD(fd);
54 : : }
55 : :
56 : 7528 : int bf_opendir_at(int parent_fd, const char *dir_name, bool mkdir_if_missing)
57 : : {
58 : 15056 : _cleanup_close_ int fd = -1;
59 : : int r;
60 : :
61 : : assert(dir_name);
62 : :
63 : 8732 : retry:
64 : 8732 : fd = openat(parent_fd, dir_name, O_DIRECTORY);
65 [ + + ]: 8732 : if (fd < 0) {
66 [ + - + + ]: 1205 : if (errno != ENOENT || !mkdir_if_missing)
67 : 1 : return -errno;
68 : :
69 : 1204 : r = mkdirat(parent_fd, dir_name, BF_PERM_755);
70 [ - + ]: 1204 : if (r)
71 : 0 : return -errno;
72 : :
73 : 1204 : goto retry;
74 : : }
75 : :
76 : 7527 : return TAKE_FD(fd);
77 : : }
78 : :
79 : : static void bf_free_dir(DIR **dir)
80 : : {
81 [ # # ]: 0 : if (!*dir)
82 : : return;
83 : :
84 : 2 : closedir(*dir);
85 : : *dir = NULL;
86 : : }
87 : :
88 : : #define _free_dir_ __attribute__((__cleanup__(bf_free_dir)))
89 : :
90 : 1500 : int bf_rmdir_at(int parent_fd, const char *dir_name, bool recursive)
91 : : {
92 : : int r;
93 : :
94 : : assert(dir_name);
95 : :
96 [ + + ]: 1500 : if (recursive) {
97 : 3 : _cleanup_close_ int child_fd = -1;
98 : : _free_dir_ DIR *dir = NULL;
99 : : struct dirent *entry;
100 : :
101 : 3 : child_fd = openat(parent_fd, dir_name, O_DIRECTORY);
102 [ + + ]: 3 : if (child_fd < 0) {
103 [ + - ]: 1 : return bf_err_r(errno,
104 : : "failed to open child directory for removal");
105 : : }
106 : :
107 : 2 : dir = fdopendir(child_fd);
108 [ - + ]: 2 : if (!dir)
109 [ # # ]: 0 : return bf_err_r(errno, "failed to open DIR from file descriptor");
110 : :
111 [ + + ]: 7 : while ((entry = readdir(dir))) {
112 : : struct stat stat;
113 : :
114 [ + + + + ]: 5 : if (bf_streq(entry->d_name, ".") || bf_streq(entry->d_name, ".."))
115 : 4 : continue;
116 : :
117 [ - + ]: 1 : if (fstatat(child_fd, entry->d_name, &stat, 0) < 0) {
118 [ # # ]: 0 : return bf_err_r(errno,
119 : : "failed to fstatat() file '%s' for removal",
120 : : entry->d_name);
121 : : }
122 : :
123 [ + - ]: 1 : if (S_ISDIR(stat.st_mode))
124 : 1 : r = bf_rmdir_at(child_fd, entry->d_name, true);
125 : : else
126 [ # # ]: 0 : r = unlinkat(child_fd, entry->d_name, 0) == 0 ? 0 : -errno;
127 [ - + ]: 1 : if (r)
128 [ # # ]: 0 : return bf_err_r(r, "failed to remove '%s'", entry->d_name);
129 : : }
130 : : }
131 : :
132 : 1499 : r = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
133 [ + + ]: 1499 : if (r)
134 : 341 : return -errno;
135 : :
136 : : return 0;
137 : : }
138 : :
139 : 2 : int bf_acquire_lock(const char *path)
140 : : {
141 : 2 : _cleanup_close_ int fd = -1;
142 : :
143 : : assert(path);
144 : :
145 : 2 : fd = open(path, O_CREAT | O_RDWR, BF_PERM_755);
146 [ - + ]: 2 : if (fd < 0)
147 : 0 : return -errno;
148 : :
149 [ + + ]: 2 : if (flock(fd, LOCK_EX | LOCK_NB) < 0)
150 : 1 : return -errno;
151 : :
152 : 1 : return TAKE_FD(fd);
153 : : }
|