]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/hooks.c
libmount: make it possible to define order of hooks
[thirdparty/util-linux.git] / libmount / src / hooks.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 *
13 * The "hookset" is a set of callbacks (hooks) that implement some functionality.
14 * The library defines stages where hooks are called (e.g. when preparing source, post
15 * mount(2), etc.). An arbitrary hook can, on the fly, define another hook for the
16 * arbitrary stage. The first hook from the hookset which goes to the game is a
17 * "firstcall" (defined in struct libmnt_hookset). This first hook controls
18 * what will happen in the next stages (usually nothing).
19 *
20 * The library supports two kinds of data for hooksets:
21 *
22 * - global data; accessible for all callbacks. Makes sense for complex
23 * hooksets with more callbacks in more stages. Usually implemented by
24 * locally defined 'struct hookset_data' in hook_*.c.
25 *
26 * - per-hook data; acessible for specific callback
27 * Usually implemented by locally defined 'struct hook_data' in hook_*.c.
28 */
29 #include "mountP.h"
30 #include "mount-api-utils.h"
31
32 /* built-in hooksets */
33 static const struct libmnt_hookset *hooksets[] =
34 {
35 #ifdef __linux__
36 &hookset_loopdev,
37 #ifdef HAVE_CRYPTSETUP
38 &hookset_veritydev,
39 #endif
40 &hookset_mkdir,
41 #ifdef HAVE_LIBSELINUX
42 &hookset_selinux_target,
43 #endif
44 &hookset_subdir,
45 #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
46 &hookset_mount,
47 #endif
48 &hookset_mount_legacy,
49 #ifdef HAVE_MOUNTFD_API
50 &hookset_idmap,
51 #endif
52 &hookset_owner
53 #endif
54 };
55
56 /* hooksets data (this is global list of hookset data) */
57 struct hookset_data {
58 const struct libmnt_hookset *hookset;
59 void *data;
60
61 struct list_head datas;
62 };
63
64 /* individial callback */
65 struct hookset_hook {
66 const struct libmnt_hookset *hookset;
67 int stage;
68 void *data;
69 const char *after;
70
71 int (*func)(struct libmnt_context *, const struct libmnt_hookset *, void *);
72
73 struct list_head hooks;
74 unsigned int executed : 1;
75 };
76
77 static const char *stagenames[] = {
78 /* prepare */
79 [MNT_STAGE_PREP_SOURCE] = "prep-source",
80 [MNT_STAGE_PREP_TARGET] = "prep-target",
81 [MNT_STAGE_PREP_OPTIONS] = "prep-options",
82 [MNT_STAGE_PREP] = "prep",
83
84 /* mount */
85 [MNT_STAGE_MOUNT_PRE] = "pre-mount",
86 [MNT_STAGE_MOUNT] = "mount",
87 [MNT_STAGE_MOUNT_POST] = "post-mount",
88
89 /* post */
90 [MNT_STAGE_POST] = "post",
91 };
92
93 static int call_depend_hooks(struct libmnt_context *cxt, const char *name, int stage);
94
95
96 int mnt_context_deinit_hooksets(struct libmnt_context *cxt)
97 {
98 size_t i;
99 int rc = 0;
100
101 assert(cxt);
102
103 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
104 const struct libmnt_hookset *hs = hooksets[i];
105
106 rc += hs->deinit(cxt, hs);
107 }
108
109 assert(list_empty(&cxt->hooksets_datas));
110 assert(list_empty(&cxt->hooksets_hooks));
111
112 INIT_LIST_HEAD(&cxt->hooksets_datas);
113 INIT_LIST_HEAD(&cxt->hooksets_hooks);
114
115 return rc;
116 }
117
118 const struct libmnt_hookset *mnt_context_get_hookset(
119 struct libmnt_context *cxt, const char *name)
120 {
121 size_t i;
122
123 assert(cxt);
124 assert(name);
125
126 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
127 const struct libmnt_hookset *hs = hooksets[i];
128
129 if (strcmp(name, hs->name) == 0)
130 return hs;
131 }
132
133 return NULL;
134 }
135
136 static struct hookset_data *get_hookset_data(
137 struct libmnt_context *cxt,
138 const struct libmnt_hookset *hs)
139 {
140 struct list_head *p;
141
142 assert(cxt);
143 assert(hs);
144
145 list_for_each(p, &cxt->hooksets_datas) {
146 struct hookset_data *x = list_entry(p, struct hookset_data, datas);
147
148 if (x->hookset == hs)
149 return x;
150 }
151 return 0;
152 }
153
154 int mnt_context_set_hookset_data(struct libmnt_context *cxt,
155 const struct libmnt_hookset *hs,
156 void *data)
157 {
158 struct hookset_data *hd = NULL;
159
160 hd = get_hookset_data(cxt, hs);
161
162 /* deallocate old data */
163 if (data == NULL) {
164 if (hd) {
165 DBG(CXT, ul_debugobj(cxt, " free '%s' data", hs->name));
166 list_del(&hd->datas);
167 free(hd);
168 }
169 return 0;
170 }
171
172 /* create and append new data */
173 if (!hd) {
174 hd = calloc(1, sizeof(*hd));
175 if (!hd)
176 return -ENOMEM;
177
178 DBG(CXT, ul_debugobj(cxt, " alloc '%s' data", hs->name));
179 INIT_LIST_HEAD(&hd->datas);
180 hd->hookset = hs;
181 list_add_tail(&hd->datas, &cxt->hooksets_datas);
182
183 }
184 hd->data = data;
185 return 0;
186 }
187
188 void *mnt_context_get_hookset_data(struct libmnt_context *cxt,
189 const struct libmnt_hookset *hs)
190 {
191 struct hookset_data *hd = get_hookset_data(cxt, hs);
192
193 return hd ? hd->data : NULL;
194 }
195
196 static int append_hook(struct libmnt_context *cxt,
197 const struct libmnt_hookset *hs,
198 int stage,
199 void *data,
200 int (*func)(struct libmnt_context *,
201 const struct libmnt_hookset *,
202 void *),
203 const char *after)
204 {
205 struct hookset_hook *hook;
206
207 assert(cxt);
208 assert(hs);
209 assert(stage);
210
211 hook = calloc(1, sizeof(*hook));
212 if (!hook)
213 return -ENOMEM;
214
215 DBG(CXT, ul_debugobj(cxt, " appending %s hook from %s",
216 stagenames[stage], hs->name));
217
218 INIT_LIST_HEAD(&hook->hooks);
219
220 hook->hookset = hs;
221 hook->data = data;
222 hook->func = func;
223 hook->stage = stage;
224 hook->after = after;
225
226 list_add_tail(&hook->hooks, &cxt->hooksets_hooks);
227 return 0;
228 }
229
230 int mnt_context_append_hook(struct libmnt_context *cxt,
231 const struct libmnt_hookset *hs,
232 int stage,
233 void *data,
234 int (*func)(struct libmnt_context *,
235 const struct libmnt_hookset *,
236 void *))
237 {
238 return append_hook(cxt, hs, stage, data, func, NULL);
239 }
240
241 int mnt_context_insert_hook(struct libmnt_context *cxt,
242 const char *after,
243 const struct libmnt_hookset *hs,
244 int stage,
245 void *data,
246 int (*func)(struct libmnt_context *,
247 const struct libmnt_hookset *,
248 void *))
249 {
250 return append_hook(cxt, hs, stage, data, func, after);
251 }
252
253 static struct hookset_hook *get_hookset_hook(struct libmnt_context *cxt,
254 const struct libmnt_hookset *hs,
255 int stage,
256 void *data)
257 {
258 struct list_head *p, *next;
259
260 assert(cxt);
261
262 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
263 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
264
265 if (hs && x->hookset != hs)
266 continue;
267 if (stage && x->stage != stage)
268 continue;
269 if (data && x->data != data)
270 continue;
271 return x;
272 }
273
274 return NULL;
275 }
276
277 int mnt_context_remove_hook(struct libmnt_context *cxt,
278 const struct libmnt_hookset *hs,
279 int stage,
280 void **data)
281 {
282 struct hookset_hook *hook;
283
284 assert(cxt);
285
286 hook = get_hookset_hook(cxt, hs, stage, NULL);
287 if (hook) {
288 DBG(CXT, ul_debugobj(cxt, " removing %s hook from %s",
289 stagenames[hook->stage], hook->hookset->name));
290
291 if (data)
292 *data = hook->data;
293
294 list_del(&hook->hooks);
295 free(hook);
296 return 0;
297 }
298
299 return 1;
300 }
301
302 int mnt_context_has_hook(struct libmnt_context *cxt,
303 const struct libmnt_hookset *hs,
304 int stage,
305 void *data)
306 {
307 return get_hookset_hook(cxt, hs, stage, data) ? 1 : 0;
308 }
309
310 static int call_hook(struct libmnt_context *cxt, struct hookset_hook *hook)
311 {
312 int rc = hook->func(cxt, hook->hookset, hook->data);
313
314 hook->executed = 1;
315 if (!rc)
316 rc = call_depend_hooks(cxt, hook->hookset->name, hook->stage);
317 return rc;
318 }
319
320 static int call_depend_hooks(struct libmnt_context *cxt, const char *name, int stage)
321 {
322 struct list_head *p = NULL, *next = NULL;
323 int rc = 0;
324
325 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
326 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
327
328 if (x->stage != stage || x->executed ||
329 x->after == NULL || strcmp(x->after, name) != 0)
330 continue;
331
332 DBG(CXT, ul_debugobj(cxt, "calling %s [after]", x->hookset->name));
333 rc = call_hook(cxt, x);
334 if (rc)
335 break;
336 }
337
338 return rc;
339 }
340
341 int mnt_context_call_hooks(struct libmnt_context *cxt, int stage)
342 {
343 struct list_head *p = NULL, *next = NULL;
344 size_t i;
345 int rc = 0;
346
347 DBG(CXT, ul_debugobj(cxt, "---> stage:%s", stagenames[stage]));
348
349 /* call initial hooks */
350 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
351 const struct libmnt_hookset *hs = hooksets[i];
352
353 if (hs->firststage != stage)
354 continue;
355
356 DBG(CXT, ul_debugobj(cxt, "calling %s [first]", hs->name));
357
358 rc = hs->firstcall(cxt, hs, NULL);
359 if (!rc)
360 rc = call_depend_hooks(cxt, hs->name, stage);
361 if (rc < 0)
362 goto done;
363 }
364
365 /* call already active hooks */
366 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
367 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
368
369 if (x->stage != stage || x->executed)
370 continue;
371
372 DBG(CXT, ul_debugobj(cxt, "calling %s [active]", x->hookset->name));
373 rc = call_hook(cxt, x);
374 if (rc < 0)
375 goto done;
376 }
377
378 done:
379 /* zeroize status */
380 p = next = NULL;
381 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
382 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
383
384 if (x->stage != stage)
385 continue;
386 x->executed = 0;
387 }
388
389 DBG(CXT, ul_debugobj(cxt, "<--- stage:%s [rc=%d status=%d]",
390 stagenames[stage], rc, cxt->syscall_status));
391 return rc;
392 }