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,
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 if (list_empty(&cxt->hooksets_datas) &&
104 list_empty(&cxt->hooksets_hooks))
105 return 0;
106
107 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
108 const struct libmnt_hookset *hs = hooksets[i];
109
110 rc += hs->deinit(cxt, hs);
111 }
112
113 assert(list_empty(&cxt->hooksets_datas));
114 assert(list_empty(&cxt->hooksets_hooks));
115
116 INIT_LIST_HEAD(&cxt->hooksets_datas);
117 INIT_LIST_HEAD(&cxt->hooksets_hooks);
118
119 return rc;
120 }
121
122 const struct libmnt_hookset *mnt_context_get_hookset(
123 struct libmnt_context *cxt, const char *name)
124 {
125 size_t i;
126
127 assert(cxt);
128 assert(name);
129
130 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
131 const struct libmnt_hookset *hs = hooksets[i];
132
133 if (strcmp(name, hs->name) == 0)
134 return hs;
135 }
136
137 return NULL;
138 }
139
140 static struct hookset_data *get_hookset_data(
141 struct libmnt_context *cxt,
142 const struct libmnt_hookset *hs)
143 {
144 struct list_head *p;
145
146 assert(cxt);
147 assert(hs);
148
149 list_for_each(p, &cxt->hooksets_datas) {
150 struct hookset_data *x = list_entry(p, struct hookset_data, datas);
151
152 if (x->hookset == hs)
153 return x;
154 }
155 return 0;
156 }
157
158 int mnt_context_set_hookset_data(struct libmnt_context *cxt,
159 const struct libmnt_hookset *hs,
160 void *data)
161 {
162 struct hookset_data *hd = NULL;
163
164 hd = get_hookset_data(cxt, hs);
165
166 /* deallocate old data */
167 if (data == NULL) {
168 if (hd) {
169 DBG(CXT, ul_debugobj(cxt, " free '%s' data", hs->name));
170 list_del(&hd->datas);
171 free(hd);
172 }
173 return 0;
174 }
175
176 /* create and append new data */
177 if (!hd) {
178 hd = calloc(1, sizeof(*hd));
179 if (!hd)
180 return -ENOMEM;
181
182 DBG(CXT, ul_debugobj(cxt, " alloc '%s' data", hs->name));
183 INIT_LIST_HEAD(&hd->datas);
184 hd->hookset = hs;
185 list_add_tail(&hd->datas, &cxt->hooksets_datas);
186
187 }
188 hd->data = data;
189 return 0;
190 }
191
192 void *mnt_context_get_hookset_data(struct libmnt_context *cxt,
193 const struct libmnt_hookset *hs)
194 {
195 struct hookset_data *hd = get_hookset_data(cxt, hs);
196
197 return hd ? hd->data : NULL;
198 }
199
200 static int append_hook(struct libmnt_context *cxt,
201 const struct libmnt_hookset *hs,
202 int stage,
203 void *data,
204 int (*func)(struct libmnt_context *,
205 const struct libmnt_hookset *,
206 void *),
207 const char *after)
208 {
209 struct hookset_hook *hook;
210
211 assert(cxt);
212 assert(hs);
213 assert(stage);
214
215 hook = calloc(1, sizeof(*hook));
216 if (!hook)
217 return -ENOMEM;
218
219 DBG(CXT, ul_debugobj(cxt, " appending %s hook from %s",
220 stagenames[stage], hs->name));
221
222 INIT_LIST_HEAD(&hook->hooks);
223
224 hook->hookset = hs;
225 hook->data = data;
226 hook->func = func;
227 hook->stage = stage;
228 hook->after = after;
229
230 list_add_tail(&hook->hooks, &cxt->hooksets_hooks);
231 return 0;
232 }
233
234 int mnt_context_append_hook(struct libmnt_context *cxt,
235 const struct libmnt_hookset *hs,
236 int stage,
237 void *data,
238 int (*func)(struct libmnt_context *,
239 const struct libmnt_hookset *,
240 void *))
241 {
242 return append_hook(cxt, hs, stage, data, func, NULL);
243 }
244
245 int mnt_context_insert_hook(struct libmnt_context *cxt,
246 const char *after,
247 const struct libmnt_hookset *hs,
248 int stage,
249 void *data,
250 int (*func)(struct libmnt_context *,
251 const struct libmnt_hookset *,
252 void *))
253 {
254 return append_hook(cxt, hs, stage, data, func, after);
255 }
256
257 static struct hookset_hook *get_hookset_hook(struct libmnt_context *cxt,
258 const struct libmnt_hookset *hs,
259 int stage,
260 void *data)
261 {
262 struct list_head *p, *next;
263
264 assert(cxt);
265
266 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
267 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
268
269 if (hs && x->hookset != hs)
270 continue;
271 if (stage && x->stage != stage)
272 continue;
273 if (data && x->data != data)
274 continue;
275 return x;
276 }
277
278 return NULL;
279 }
280
281 int mnt_context_remove_hook(struct libmnt_context *cxt,
282 const struct libmnt_hookset *hs,
283 int stage,
284 void **data)
285 {
286 struct hookset_hook *hook;
287
288 assert(cxt);
289
290 hook = get_hookset_hook(cxt, hs, stage, NULL);
291 if (hook) {
292 DBG(CXT, ul_debugobj(cxt, " removing %s hook from %s",
293 stagenames[hook->stage], hook->hookset->name));
294
295 if (data)
296 *data = hook->data;
297
298 list_del(&hook->hooks);
299 free(hook);
300 return 0;
301 }
302
303 return 1;
304 }
305
306 int mnt_context_has_hook(struct libmnt_context *cxt,
307 const struct libmnt_hookset *hs,
308 int stage,
309 void *data)
310 {
311 return get_hookset_hook(cxt, hs, stage, data) ? 1 : 0;
312 }
313
314 static int call_hook(struct libmnt_context *cxt, struct hookset_hook *hook)
315 {
316 int rc = hook->func(cxt, hook->hookset, hook->data);
317
318 hook->executed = 1;
319 if (!rc)
320 rc = call_depend_hooks(cxt, hook->hookset->name, hook->stage);
321 return rc;
322 }
323
324 static int call_depend_hooks(struct libmnt_context *cxt, const char *name, int stage)
325 {
326 struct list_head *p = NULL, *next = NULL;
327 int rc = 0;
328
329 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
330 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
331
332 if (x->stage != stage || x->executed ||
333 x->after == NULL || strcmp(x->after, name) != 0)
334 continue;
335
336 DBG(CXT, ul_debugobj(cxt, "calling %s [after]", x->hookset->name));
337 rc = call_hook(cxt, x);
338 if (rc)
339 break;
340 }
341
342 return rc;
343 }
344
345 int mnt_context_call_hooks(struct libmnt_context *cxt, int stage)
346 {
347 struct list_head *p = NULL, *next = NULL;
348 size_t i;
349 int rc = 0;
350
351 DBG(CXT, ul_debugobj(cxt, "---> stage:%s", stagenames[stage]));
352
353 /* call initial hooks */
354 for (i = 0; i < ARRAY_SIZE(hooksets); i++) {
355 const struct libmnt_hookset *hs = hooksets[i];
356
357 if (hs->firststage != stage)
358 continue;
359
360 DBG(CXT, ul_debugobj(cxt, "calling %s [first]", hs->name));
361
362 rc = hs->firstcall(cxt, hs, NULL);
363 if (!rc)
364 rc = call_depend_hooks(cxt, hs->name, stage);
365 if (rc < 0)
366 goto done;
367 }
368
369 /* call already active hooks */
370 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
371 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
372
373 if (x->stage != stage || x->executed)
374 continue;
375
376 DBG(CXT, ul_debugobj(cxt, "calling %s [active]", x->hookset->name));
377 rc = call_hook(cxt, x);
378 if (rc < 0)
379 goto done;
380 }
381
382 done:
383 /* zeroize status */
384 p = next = NULL;
385 list_for_each_safe(p, next, &cxt->hooksets_hooks) {
386 struct hookset_hook *x = list_entry(p, struct hookset_hook, hooks);
387
388 if (x->stage != stage)
389 continue;
390 x->executed = 0;
391 }
392
393 DBG(CXT, ul_debugobj(cxt, "<--- stage:%s [rc=%d status=%d]",
394 stagenames[stage], rc, cxt->syscall_status));
395 return rc;
396 }