(root)/
util-linux-2.39/
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,
      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  }