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 <stdlib.h>
7 : : #include <string.h>
8 : : #include <unistd.h>
9 : :
10 : : #include <bpfilter/bpfilter.h>
11 : : #include <bpfilter/ctx.h>
12 : :
13 : : #include "bpfilter/chain.h"
14 : : #include "bpfilter/core/list.h"
15 : : #include "bpfilter/counter.h"
16 : : #include "bpfilter/helper.h"
17 : : #include "bpfilter/hook.h"
18 : : #include "bpfilter/logger.h"
19 : : #include "bpfilter/pack.h"
20 : : #include "bpfilter/set.h"
21 : : #include "cgen/cgen.h"
22 : : #include "cgen/handle.h"
23 : : #include "cgen/prog/link.h"
24 : : #include "cgen/prog/map.h"
25 : : #include "core/lock.h"
26 : :
27 : 162 : static int copy_hookopts(struct bf_hookopts **dest,
28 : : const struct bf_hookopts *src)
29 : : {
30 : : struct bf_hookopts *copy;
31 : :
32 : 162 : copy = bf_memdup(src, sizeof(*src));
33 [ + - ]: 162 : if (!copy)
34 : : return -ENOMEM;
35 : :
36 [ + + ]: 162 : if (src->cgpath) {
37 : 64 : copy->cgpath = strdup(src->cgpath);
38 [ - + ]: 64 : if (!copy->cgpath) {
39 : 0 : free(copy);
40 : 0 : return -ENOMEM;
41 : : }
42 : : }
43 : :
44 : 162 : *dest = copy;
45 : :
46 : 162 : return 0;
47 : : }
48 : :
49 : : /**
50 : : * @brief Unload every chain currently pinned in the pin directory.
51 : : *
52 : : * The caller must already hold a `BF_LOCK_WRITE` lock on the pin directory
53 : : * (typically via `bf_lock_init(BF_LOCK_WRITE)`). For each chain entry, a
54 : : * per-chain `BF_LOCK_WRITE` lock is acquired (so the chain dir is removed
55 : : * by `bf_lock_release_chain` after unload).
56 : : */
57 : 1145 : static int _bf_ruleset_flush(struct bf_lock *lock)
58 : : {
59 : 1145 : _free_bf_list_ bf_list *cgens = NULL;
60 : : int r;
61 : :
62 : : assert(lock);
63 : :
64 : 1145 : r = bf_ctx_get_cgens(lock, &cgens);
65 [ - + ]: 1145 : if (r)
66 [ # # ]: 0 : return bf_err_r(r, "failed to discover chains during flush");
67 : :
68 [ + + + + : 3478 : bf_list_foreach (cgens, cgen_node) {
+ + ]
69 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
70 : :
71 : 594 : r = bf_lock_acquire_chain(lock, cgen->chain->name, BF_LOCK_WRITE,
72 : : false);
73 [ - + ]: 594 : if (r) {
74 [ # # ]: 0 : bf_warn_r(
75 : : r,
76 : : "failed to acquire WRITE lock on chain '%s' during flush, skipping",
77 : : cgen->chain->name);
78 : 0 : continue;
79 : : }
80 : :
81 : 594 : bf_cgen_unload(cgen, lock);
82 : 594 : bf_lock_release_chain(lock);
83 : : }
84 : :
85 : : return 0;
86 : : }
87 : :
88 : 8 : int bf_ruleset_get(bf_list *chains, bf_list *hookopts)
89 : : {
90 : 8 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
91 : 8 : _free_bf_list_ bf_list *cgens = NULL;
92 : 8 : _clean_bf_list_ bf_list _chains = bf_list_default_from(*chains);
93 : 8 : _clean_bf_list_ bf_list _hookopts = bf_list_default_from(*hookopts);
94 : : int r;
95 : :
96 : 8 : r = bf_lock_init(&lock, BF_LOCK_READ);
97 [ - + ]: 8 : if (r)
98 [ # # ]: 0 : return bf_err_r(r, "failed to acquire READ lock for ruleset get");
99 : :
100 : 8 : r = bf_ctx_get_cgens(&lock, &cgens);
101 [ - + ]: 8 : if (r < 0)
102 [ # # ]: 0 : return bf_err_r(r, "failed to get cgen list");
103 : :
104 [ + + + + : 30 : bf_list_foreach (cgens, cgen_node) {
+ + ]
105 : : struct bf_cgen *cgen = bf_list_node_get_data(cgen_node);
106 : 0 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
107 : :
108 : 7 : r = bf_cgen_load_counters(cgen);
109 [ - + ]: 7 : if (r) {
110 [ # # ]: 0 : return bf_err_r(r, "failed to read counters for '%s'",
111 : : cgen->chain->name);
112 : : }
113 : :
114 : : // cgen will be destroyed, we can steal the chain
115 : 7 : r = bf_list_push(&_chains, (void **)&cgen->chain);
116 [ + - ]: 7 : if (r)
117 : : return r;
118 : :
119 [ + + + - ]: 7 : if (cgen->handle->link && cgen->handle->link->hookopts) {
120 : 6 : r = copy_hookopts(&hookopts_copy, cgen->handle->link->hookopts);
121 [ - + ]: 6 : if (r)
122 [ # # ]: 0 : return bf_err_r(r, "failed to copy hookopts");
123 : : }
124 : 7 : r = bf_list_add_tail(&_hookopts, hookopts_copy);
125 [ + - ]: 7 : if (r)
126 : : return r;
127 : 7 : TAKE_PTR(hookopts_copy);
128 : : }
129 : :
130 : 8 : *chains = bf_list_move(_chains);
131 : 8 : *hookopts = bf_list_move(_hookopts);
132 : :
133 : 8 : return 0;
134 : : }
135 : :
136 : 9 : int bf_ruleset_set(bf_list *chains, bf_list *hookopts)
137 : : {
138 [ + + ]: 17 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
139 : : struct bf_list_node *chain_node = bf_list_get_head(chains);
140 : : struct bf_list_node *hookopts_node = bf_list_get_head(hookopts);
141 : : int r;
142 : :
143 [ + + ]: 9 : if (bf_list_size(chains) != bf_list_size(hookopts))
144 : : return -EINVAL;
145 : :
146 : 8 : r = bf_lock_init(&lock, BF_LOCK_WRITE);
147 [ - + ]: 8 : if (r)
148 [ # # ]: 0 : return bf_err_r(r, "failed to acquire WRITE lock for ruleset set");
149 : :
150 : 8 : r = _bf_ruleset_flush(&lock);
151 [ + - ]: 8 : if (r)
152 : : return r;
153 : :
154 [ + + ]: 18 : while (chain_node && hookopts_node) {
155 : 12 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
156 : 12 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
157 : 24 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
158 : : struct bf_chain *chain = bf_list_node_get_data(chain_node);
159 : : struct bf_hookopts *node_hookopts =
160 : : bf_list_node_get_data(hookopts_node);
161 : :
162 : 12 : r = bf_chain_new_from_copy(&chain_copy, chain);
163 [ - + ]: 12 : if (r)
164 : 0 : goto err_load;
165 : :
166 [ + + ]: 12 : if (node_hookopts) {
167 : 10 : r = copy_hookopts(&hookopts_copy, node_hookopts);
168 [ - + ]: 10 : if (r)
169 : 0 : goto err_load;
170 : : }
171 : :
172 : 12 : r = bf_cgen_new(&cgen, &chain_copy);
173 [ - + ]: 12 : if (r)
174 : 0 : goto err_load;
175 : :
176 : 12 : r = bf_lock_acquire_chain(&lock, cgen->chain->name, BF_LOCK_WRITE,
177 : : true);
178 [ - + ]: 12 : if (r) {
179 [ # # ]: 0 : bf_err_r(r, "failed to acquire WRITE lock on chain '%s'",
180 : : cgen->chain->name);
181 : 0 : goto err_load;
182 : : }
183 : :
184 [ + + ]: 14 : r = bf_cgen_set(cgen, hookopts_copy ? &hookopts_copy : NULL, &lock);
185 [ + + ]: 12 : if (r) {
186 [ + - ]: 2 : bf_err_r(r, "failed to set chain '%s'", cgen->chain->name);
187 : 2 : bf_lock_release_chain(&lock);
188 : 2 : goto err_load;
189 : : }
190 : :
191 : 10 : bf_lock_release_chain(&lock);
192 : :
193 : : chain_node = bf_list_node_next(chain_node);
194 : : hookopts_node = bf_list_node_next(hookopts_node);
195 : : }
196 : :
197 : : return 0;
198 : :
199 : : err_load:
200 : 2 : _bf_ruleset_flush(&lock);
201 : 2 : return r;
202 : : }
203 : :
204 : 1135 : int bf_ruleset_flush(void)
205 : : {
206 : 1135 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
207 : : int r;
208 : :
209 : 1135 : r = bf_lock_init(&lock, BF_LOCK_WRITE);
210 [ - + ]: 1135 : if (r)
211 [ # # ]: 0 : return bf_err_r(r, "failed to acquire WRITE lock for ruleset flush");
212 : :
213 : 1135 : return _bf_ruleset_flush(&lock);
214 : : }
215 : :
216 : 1267 : int bf_chain_set(struct bf_chain *chain, struct bf_hookopts *hookopts)
217 : : {
218 : 1267 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
219 : 1267 : _free_bf_cgen_ struct bf_cgen *old_cgen = NULL;
220 : 1267 : _free_bf_cgen_ struct bf_cgen *new_cgen = NULL;
221 : 1267 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
222 : 1267 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
223 : : int r;
224 : :
225 : : assert(chain);
226 : :
227 : : /* bf_chain_set is a namespace mutator: the previous chain (if any) is
228 : : * destroyed and a fresh one is published under the same name. Take
229 : : * pindir WRITE for the whole operation so the flush-then-load
230 : : * sequence is atomic w.r.t. every other libbpfilter caller. */
231 : 1267 : r = bf_lock_init(&lock, BF_LOCK_WRITE);
232 [ + - ]: 1267 : if (r)
233 : : return r;
234 : :
235 : : /* Unload any pre-existing chain under this name, and remove its pindir
236 : : * entry so stage-and-rename can publish the new one. */
237 : 1267 : r = bf_lock_acquire_chain(&lock, chain->name, BF_LOCK_WRITE, false);
238 [ + + ]: 1267 : if (r == 0) {
239 : 638 : r = bf_ctx_get_cgen(&lock, &old_cgen);
240 [ - + ]: 638 : if (r && r != -ENOENT) {
241 : 0 : bf_lock_release_chain(&lock);
242 : 0 : return r;
243 : : }
244 [ + - ]: 638 : if (old_cgen)
245 : 638 : bf_cgen_unload(old_cgen, &lock);
246 : : /* Release drops the chain flock and (because we held WRITE)
247 : : * removes the now-empty chain dir. */
248 : 638 : bf_lock_release_chain(&lock);
249 [ + - ]: 629 : } else if (r != -ENOENT) {
250 : : return r;
251 : : }
252 : :
253 : 1267 : r = bf_chain_new_from_copy(&chain_copy, chain);
254 [ + - ]: 1267 : if (r)
255 : : return r;
256 : :
257 [ + + ]: 1267 : if (hookopts) {
258 : 102 : r = copy_hookopts(&hookopts_copy, hookopts);
259 [ + - ]: 102 : if (r)
260 : : return r;
261 : : }
262 : :
263 : 1267 : r = bf_cgen_new(&new_cgen, &chain_copy);
264 [ + - ]: 1267 : if (r)
265 : : return r;
266 : :
267 : : /* Create the new chain dir via stage-and-rename (I3). */
268 : 1267 : r = bf_lock_acquire_chain(&lock, chain->name, BF_LOCK_WRITE, true);
269 [ + - ]: 1267 : if (r)
270 : : return r;
271 : :
272 [ + + ]: 2432 : return bf_cgen_set(new_cgen, hookopts_copy ? &hookopts_copy : NULL, &lock);
273 : : }
274 : :
275 : 1150 : int bf_chain_get(const char *name, struct bf_chain **chain,
276 : : struct bf_hookopts **hookopts)
277 : : {
278 : 1150 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
279 : 1150 : _free_bf_chain_ struct bf_chain *_chain = NULL;
280 : 1150 : _free_bf_hookopts_ struct bf_hookopts *_hookopts = NULL;
281 : 1150 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
282 : : int r;
283 : :
284 : : assert(name);
285 : : assert(chain);
286 : : assert(hookopts);
287 : :
288 : 1150 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_READ, BF_LOCK_READ, false);
289 [ + + ]: 1150 : if (r)
290 : : return r;
291 : :
292 : 1148 : r = bf_ctx_get_cgen(&lock, &cgen);
293 [ + - ]: 1148 : if (r)
294 : : return r;
295 : :
296 : 1148 : r = bf_cgen_load_counters(cgen);
297 [ - + ]: 1148 : if (r) {
298 [ # # ]: 0 : return bf_err_r(r, "failed to load counters for '%s'",
299 : : cgen->chain->name);
300 : : }
301 : :
302 : : // cgen will be destroyed, we can steal the chain
303 : 1148 : _chain = TAKE_PTR(cgen->chain);
304 : :
305 [ + + + - ]: 1148 : if (cgen->handle->link && cgen->handle->link->hookopts) {
306 : 30 : r = copy_hookopts(&_hookopts, cgen->handle->link->hookopts);
307 [ + - ]: 30 : if (r)
308 : : return r;
309 : : }
310 : :
311 : 1148 : *chain = TAKE_PTR(_chain);
312 : 1148 : *hookopts = TAKE_PTR(_hookopts);
313 : :
314 : 1148 : return 0;
315 : : }
316 : :
317 : 2881 : int bf_chain_prog_fd(const char *name)
318 : : {
319 : 2881 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
320 : 2881 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
321 : : int r;
322 : :
323 : : assert(name);
324 : :
325 : 2881 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_READ, BF_LOCK_READ, false);
326 [ + + ]: 2881 : if (r)
327 : : return r;
328 : :
329 : 2880 : r = bf_ctx_get_cgen(&lock, &cgen);
330 [ + - ]: 2880 : if (r)
331 : : return r;
332 : :
333 [ - + ]: 2880 : if (cgen->handle->prog_fd == -1)
334 [ # # ]: 0 : return bf_err_r(-ENODEV, "chain '%s' has no loaded program", name);
335 : :
336 : 2880 : return dup(cgen->handle->prog_fd);
337 : : }
338 : :
339 : 1 : int bf_chain_logs_fd(const char *name)
340 : : {
341 : 1 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
342 : 1 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
343 : : int r;
344 : :
345 : : assert(name);
346 : :
347 : 1 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_READ, BF_LOCK_READ, false);
348 [ - + ]: 1 : if (r)
349 : : return r;
350 : :
351 : 0 : r = bf_ctx_get_cgen(&lock, &cgen);
352 [ # # ]: 0 : if (r)
353 : : return r;
354 : :
355 [ # # ]: 0 : if (!cgen->handle->lmap)
356 [ # # ]: 0 : return bf_err_r(-ENOENT, "chain '%s' has no logs buffer", name);
357 : :
358 : 0 : return dup(cgen->handle->lmap->fd);
359 : : }
360 : :
361 : 17 : int bf_chain_load(struct bf_chain *chain)
362 : : {
363 : 17 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
364 : 17 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
365 : 17 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
366 : : int r;
367 : :
368 : : assert(chain);
369 : :
370 : : /* chain_load is a namespace mutator: pindir WRITE + chain WRITE on the
371 : : * staged inode. Stage-and-rename (I3) returns `-EEXIST` if another
372 : : * creator already claimed the name, which replaces the former
373 : : * check-then-create sequence atomically. */
374 : 17 : r = bf_lock_init_for_chain(&lock, chain->name, BF_LOCK_WRITE, BF_LOCK_WRITE,
375 : : true);
376 [ + - ]: 17 : if (r)
377 : : return r;
378 : :
379 : 17 : r = bf_chain_new_from_copy(&chain_copy, chain);
380 [ + - ]: 17 : if (r)
381 : : return r;
382 : :
383 : 17 : r = bf_cgen_new(&cgen, &chain_copy);
384 [ + - ]: 17 : if (r)
385 : : return r;
386 : :
387 : 17 : return bf_cgen_load(cgen, &lock);
388 : : }
389 : :
390 : 16 : int bf_chain_attach(const char *name, const struct bf_hookopts *hookopts)
391 : : {
392 : 16 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
393 : 16 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
394 : 16 : _free_bf_hookopts_ struct bf_hookopts *hookopts_copy = NULL;
395 : : int r;
396 : :
397 : : assert(name);
398 : : assert(hookopts);
399 : :
400 : 16 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_READ, BF_LOCK_WRITE, false);
401 [ + + ]: 16 : if (r)
402 : : return r;
403 : :
404 : 15 : r = bf_ctx_get_cgen(&lock, &cgen);
405 [ + - ]: 15 : if (r)
406 : : return r;
407 : :
408 [ - + ]: 15 : if (cgen->handle->link)
409 [ # # ]: 0 : return bf_err_r(-EBUSY, "chain '%s' is already linked to a hook", name);
410 : :
411 : 15 : r = bf_hookopts_validate(hookopts, cgen->chain->hook);
412 [ + + ]: 15 : if (r)
413 [ + - ]: 1 : return bf_err_r(r, "failed to validate hook options");
414 : :
415 : 14 : r = copy_hookopts(&hookopts_copy, hookopts);
416 [ + - ]: 14 : if (r)
417 : : return r;
418 : :
419 : 14 : r = bf_cgen_attach(cgen, &hookopts_copy, &lock);
420 [ + + ]: 14 : if (r)
421 [ + - ]: 2 : return bf_err_r(r, "failed to attach codegen to hook");
422 : :
423 : : return 0;
424 : : }
425 : :
426 : 8 : int bf_chain_update(const struct bf_chain *chain)
427 : : {
428 : 8 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
429 : 8 : _free_bf_chain_ struct bf_chain *chain_copy = NULL;
430 : 8 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
431 : : int r;
432 : :
433 : : assert(chain);
434 : :
435 : 8 : r = bf_lock_init_for_chain(&lock, chain->name, BF_LOCK_READ, BF_LOCK_WRITE,
436 : : false);
437 [ + + ]: 8 : if (r)
438 : : return r;
439 : :
440 : 6 : r = bf_ctx_get_cgen(&lock, &cgen);
441 [ + - ]: 6 : if (r)
442 : : return r;
443 : :
444 : 6 : r = bf_chain_new_from_copy(&chain_copy, chain);
445 [ + - ]: 6 : if (r)
446 : : return r;
447 : :
448 : 6 : return bf_cgen_update(cgen, &chain_copy, 0, &lock);
449 : : }
450 : :
451 : 16 : static int copy_set(struct bf_set **dest, const struct bf_set *src)
452 : : {
453 : 16 : _free_bf_wpack_ bf_wpack_t *wpack = NULL;
454 : 16 : _free_bf_rpack_ bf_rpack_t *rpack = NULL;
455 : : const void *data;
456 : : size_t data_len;
457 : : int r;
458 : :
459 : 16 : r = bf_wpack_new(&wpack);
460 [ + - ]: 16 : if (r)
461 : : return r;
462 : :
463 : 16 : bf_wpack_open_object(wpack, "set");
464 : 16 : r = bf_set_pack(src, wpack);
465 [ + - ]: 16 : if (r)
466 : : return r;
467 : 16 : bf_wpack_close_object(wpack);
468 : :
469 : 16 : r = bf_wpack_get_data(wpack, &data, &data_len);
470 [ + - ]: 16 : if (r)
471 : : return r;
472 : :
473 : 16 : r = bf_rpack_new(&rpack, data, data_len);
474 [ + - ]: 16 : if (r)
475 : : return r;
476 : :
477 : : bf_rpack_node_t child;
478 : 16 : r = bf_rpack_kv_obj(bf_rpack_root(rpack), "set", &child);
479 [ + - ]: 16 : if (r)
480 : : return r;
481 : :
482 : 16 : return bf_set_new_from_pack(dest, child);
483 : : }
484 : :
485 : 9 : int bf_chain_update_set(const char *name, const struct bf_set *to_add,
486 : : const struct bf_set *to_remove)
487 : : {
488 : 9 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
489 : 9 : _free_bf_chain_ struct bf_chain *new_chain = NULL;
490 : : struct bf_set *dest_set = NULL;
491 : 9 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
492 : 9 : _free_bf_set_ struct bf_set *add_copy = NULL;
493 : 9 : _free_bf_set_ struct bf_set *remove_copy = NULL;
494 : : int r;
495 : :
496 : : assert(name);
497 : : assert(to_add);
498 : : assert(to_remove);
499 : :
500 [ - + ]: 9 : if (!bf_streq(to_add->name, to_remove->name))
501 [ # # ]: 0 : return bf_err_r(-EINVAL, "to_add->name must match to_remove->name");
502 : :
503 : 9 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_READ, BF_LOCK_WRITE, false);
504 [ + + ]: 9 : if (r)
505 : : return r;
506 : :
507 : 8 : r = bf_ctx_get_cgen(&lock, &cgen);
508 [ + - ]: 8 : if (r)
509 : : return r;
510 : :
511 : 8 : r = bf_chain_new_from_copy(&new_chain, cgen->chain);
512 [ + - ]: 8 : if (r)
513 : : return r;
514 : :
515 : 8 : dest_set = bf_chain_get_set_by_name(new_chain, to_add->name);
516 [ - + ]: 8 : if (!dest_set)
517 [ # # ]: 0 : return bf_err_r(-ENOENT, "set '%s' does not exist", to_add->name);
518 : :
519 : 8 : r = copy_set(&add_copy, to_add);
520 [ + - ]: 8 : if (r)
521 : : return r;
522 : :
523 : 8 : r = copy_set(&remove_copy, to_remove);
524 [ + - ]: 8 : if (r)
525 : : return r;
526 : :
527 : 8 : r = bf_set_add_many(dest_set, &add_copy);
528 [ - + ]: 8 : if (r)
529 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set union");
530 : :
531 : 8 : r = bf_set_remove_many(dest_set, &remove_copy);
532 [ - + ]: 8 : if (r)
533 [ # # ]: 0 : return bf_err_r(r, "failed to calculate set difference");
534 : :
535 : 8 : r = bf_cgen_update(cgen, &new_chain,
536 : : BF_FLAG(BF_CGEN_UPDATE_PRESERVE_COUNTERS), &lock);
537 [ - + ]: 8 : if (r)
538 [ # # ]: 0 : return bf_err_r(r, "failed to update chain with new set data");
539 : :
540 : : return 0;
541 : : }
542 : :
543 : 32 : int bf_chain_flush(const char *name)
544 : : {
545 : 32 : _clean_bf_lock_ struct bf_lock lock = bf_lock_default();
546 : 32 : _free_bf_cgen_ struct bf_cgen *cgen = NULL;
547 : : int r;
548 : :
549 : : assert(name);
550 : :
551 : : /* chain_flush removes the chain dir from the pindir namespace, so it
552 : : * needs pindir WRITE (I2). */
553 : 32 : r = bf_lock_init_for_chain(&lock, name, BF_LOCK_WRITE, BF_LOCK_WRITE,
554 : : false);
555 [ + + ]: 32 : if (r)
556 : : return r;
557 : :
558 : 31 : r = bf_ctx_get_cgen(&lock, &cgen);
559 [ + - ]: 31 : if (r)
560 : : return r;
561 : :
562 : 31 : bf_cgen_unload(cgen, &lock);
563 : :
564 : 31 : return 0;
565 : : }
|