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 : 6951 : int bf_opendir(const char *path)
44 : : {
45 : 6951 : _cleanup_close_ int fd = -1;
46 : :
47 : : assert(path);
48 : :
49 : 6951 : fd = open(path, O_DIRECTORY);
50 [ + + ]: 6951 : if (fd < 0)
51 : 1 : return -errno;
52 : :
53 : 6950 : return TAKE_FD(fd);
54 : : }
55 : :
56 : 6952 : int bf_opendir_at(int parent_fd, const char *dir_name, bool mkdir_if_missing)
57 : : {
58 : 13904 : _cleanup_close_ int fd = -1;
59 : : int r;
60 : :
61 : : assert(dir_name);
62 : :
63 : 7027 : retry:
64 : 7027 : fd = openat(parent_fd, dir_name, O_DIRECTORY);
65 [ + + ]: 7027 : if (fd < 0) {
66 [ + - + + ]: 76 : if (errno != ENOENT || !mkdir_if_missing)
67 : 1 : return -errno;
68 : :
69 : 75 : r = mkdirat(parent_fd, dir_name, BF_PERM_755);
70 [ - + ]: 75 : if (r)
71 : 0 : return -errno;
72 : :
73 : 75 : goto retry;
74 : : }
75 : :
76 : 6951 : 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 : 5 : int bf_rmdir_at(int parent_fd, const char *dir_name, bool recursive)
91 : : {
92 : : int r;
93 : :
94 : : assert(dir_name);
95 : :
96 [ + + ]: 5 : if (recursive) {
97 : 3 : _cleanup_close_ int child_fd = -1;
98 : : _free_dir_ DIR *dir = NULL;
99 : : struct dirent *entry;
100 : : int dir_fd;
101 : :
102 : 3 : child_fd = openat(parent_fd, dir_name, O_DIRECTORY);
103 [ + + ]: 3 : if (child_fd < 0) {
104 [ + - ]: 1 : return bf_err_r(errno,
105 : : "failed to open child directory for removal");
106 : : }
107 : :
108 : 2 : dir = fdopendir(child_fd);
109 [ - + ]: 2 : if (!dir)
110 [ # # ]: 0 : return bf_err_r(errno, "failed to open DIR from file descriptor");
111 : : /* fdopendir takes ownership of the FD, let's prevent double-close in
112 : : * case of multithreading and FD reuse */
113 : 2 : TAKE_FD(child_fd);
114 : :
115 : 2 : dir_fd = dirfd(dir);
116 [ - + ]: 2 : if (dir_fd < 0)
117 [ # # ]: 0 : return bf_err_r(errno, "failed to retrieve FD after fdopendir");
118 : :
119 [ + + ]: 7 : while ((entry = readdir(dir))) {
120 : : struct stat stat;
121 : :
122 [ + + + + ]: 5 : if (bf_streq(entry->d_name, ".") || bf_streq(entry->d_name, ".."))
123 : 4 : continue;
124 : :
125 [ - + ]: 1 : if (fstatat(dir_fd, entry->d_name, &stat, 0) < 0) {
126 [ # # ]: 0 : return bf_err_r(errno,
127 : : "failed to fstatat() file '%s' for removal",
128 : : entry->d_name);
129 : : }
130 : :
131 [ + - ]: 1 : if (S_ISDIR(stat.st_mode))
132 : 1 : r = bf_rmdir_at(dir_fd, entry->d_name, true);
133 : : else
134 [ # # ]: 0 : r = unlinkat(dir_fd, entry->d_name, 0) == 0 ? 0 : -errno;
135 [ - + ]: 1 : if (r)
136 [ # # ]: 0 : return bf_err_r(r, "failed to remove '%s'", entry->d_name);
137 : : }
138 : : }
139 : :
140 : 4 : r = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
141 [ + + ]: 4 : if (r)
142 : 2 : return -errno;
143 : :
144 : : return 0;
145 : : }
146 : :
147 : 2 : int bf_acquire_lock(const char *path)
148 : : {
149 : 2 : _cleanup_close_ int fd = -1;
150 : :
151 : : assert(path);
152 : :
153 : 2 : fd = open(path, O_CREAT | O_RDWR, BF_PERM_755);
154 [ - + ]: 2 : if (fd < 0)
155 : 0 : return -errno;
156 : :
157 [ + + ]: 2 : if (flock(fd, LOCK_EX | LOCK_NB) < 0)
158 : 1 : return -errno;
159 : :
160 : 1 : return TAKE_FD(fd);
161 : : }
|