(root)/
util-linux-2.39/
libmount/
src/
hook_mount_legacy.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   * This is classic mount(2) based mount.
      14   *
      15   * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
      16   */
      17  
      18  #include "mountP.h"
      19  
      20  /* mount(2) flags for additional propagation changes etc. */
      21  struct hook_data {
      22  	unsigned long flags;
      23  };
      24  
      25  static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
      26  {
      27  	void *data = NULL;
      28  
      29  	DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
      30  
      31  	/* remove all our hooks and free hook data */
      32  	while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
      33  		if (data)
      34  			free(data);
      35  		data = NULL;
      36  	}
      37  
      38  	return 0;
      39  }
      40  
      41  static struct hook_data *new_hook_data(void)
      42  {
      43  	return calloc(1, sizeof(struct hook_data));
      44  }
      45  
      46  /* call mount(2) for propagation flags */
      47  static int hook_propagation(struct libmnt_context *cxt,
      48  			    const struct libmnt_hookset *hs,
      49  			    void *data)
      50  {
      51  	int rc;
      52  	struct hook_data *hd = (struct hook_data *) data;
      53  	unsigned long extra = 0;
      54  
      55  	assert(hd);
      56  	assert(cxt);
      57  	assert(cxt->fs);
      58  	assert(cxt->optlist);
      59  
      60  	DBG(HOOK, ul_debugobj(hs, " calling mount(2) for propagation: 0x%08lx %s",
      61  				hd->flags,
      62  				hd->flags & MS_REC ? " (recursive)" : ""));
      63  
      64  	if (mnt_context_is_fake(cxt)) {
      65  		DBG(CXT, ul_debugobj(cxt, "  FAKE (-f)"));
      66  		cxt->syscall_status = 0;
      67  		return 0;
      68  	}
      69  
      70  	/*
      71  	 * hd->flags are propagation flags as set in prepare_propagation()
      72  	 *
      73  	 * @cxt contains global mount flags, may be modified after preparation
      74  	 * stage (for example when libmount blindly tries FS type then it uses
      75  	 * MS_SILENT)
      76  	 */
      77  	if (mnt_optlist_is_silent(cxt->optlist))
      78  		extra |= MS_SILENT;
      79  
      80  	rc = mount("none", mnt_fs_get_target(cxt->fs), NULL,
      81  			hd->flags | extra, NULL);
      82  
      83  	if (rc) {
      84  		/* Update global syscall status if only this function called */
      85  		if (mnt_context_propagation_only(cxt)) {
      86  			cxt->syscall_status = -errno;
      87  			cxt->syscall_name = "mount";
      88  		}
      89  
      90  		DBG(HOOK, ul_debugobj(hs, "  mount(2) failed [errno=%d %m]", errno));
      91  		rc = -MNT_ERR_APPLYFLAGS;
      92  	}
      93  	return rc;
      94  }
      95  
      96  /*
      97   * add additional mount(2) syscalls to set propagation flags after regular mount(2).
      98   */
      99  static int prepare_propagation(struct libmnt_context *cxt,
     100  				const struct libmnt_hookset *hs)
     101  {
     102  	struct libmnt_optlist *ol;
     103  	struct libmnt_iter itr;
     104  	struct libmnt_opt *opt;
     105  
     106  	assert(cxt);
     107  	assert(cxt->fs);
     108  
     109  	ol = mnt_context_get_optlist(cxt);
     110  	if (!ol)
     111  		return -ENOMEM;
     112  
     113  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     114  
     115  	while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
     116  		int rc;
     117  		struct hook_data *data;
     118  		const struct libmnt_optmap *map = mnt_opt_get_map(opt);
     119  		const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
     120  
     121  		if (!map || map != cxt->map_linux)
     122  			continue;
     123  		if (!(ent->id & MS_PROPAGATION))
     124  			continue;
     125  
     126  		data = new_hook_data();
     127  		if (!data)
     128  			return -ENOMEM;
     129  
     130  		data->flags = ent->id;
     131  
     132  		DBG(HOOK, ul_debugobj(hs, " adding mount(2) call for %s", ent->name));
     133  		rc = mnt_context_append_hook(cxt, hs,
     134  					MNT_STAGE_MOUNT_POST,
     135  					data,
     136  					hook_propagation);
     137  		if (rc)
     138  			return rc;
     139  
     140  		DBG(HOOK, ul_debugobj(hs, " removing '%s' flag from primary mount(2)", ent->name));
     141  		mnt_optlist_remove_opt(ol, opt);
     142  	}
     143  
     144  	return 0;
     145  }
     146  
     147  /* call mount(2) for bind,remount */
     148  static int hook_bindremount(struct libmnt_context *cxt,
     149  			    const struct libmnt_hookset *hs, void *data)
     150  {
     151  	int rc;
     152  	struct hook_data *hd = (struct hook_data *) data;
     153  	unsigned long extra = 0;
     154  
     155  	DBG(HOOK, ul_debugobj(hs, " mount(2) for bind-remount: 0x%08lx %s",
     156  				hd->flags,
     157  				hd->flags & MS_REC ? " (recursive)" : ""));
     158  
     159  	if (mnt_context_is_fake(cxt)) {
     160  		DBG(CXT, ul_debugobj(cxt, "  FAKE (-f)"));
     161  		cxt->syscall_status = 0;
     162  		return 0;
     163  	}
     164  
     165  	if (mnt_optlist_is_silent(cxt->optlist))
     166  		extra |= MS_SILENT;
     167  
     168  	/* for the flags see comment in hook_propagation() */
     169  	rc = mount("none", mnt_fs_get_target(cxt->fs), NULL,
     170  			hd->flags | extra, NULL);
     171  
     172  	if (rc)
     173  		DBG(HOOK, ul_debugobj(hs, "  mount(2) failed"
     174  				  " [rc=%d errno=%d %m]", rc, errno));
     175  	return rc;
     176  }
     177  
     178  /*
     179   * add additional mount(2) syscall request to implement "bind,<flags>", the first regular
     180   * mount(2) is the "bind" operation, the second is "remount,bind,<flags>" call.
     181   */
     182  static int prepare_bindremount(struct libmnt_context *cxt,
     183  				const struct libmnt_hookset *hs)
     184  {
     185  	struct hook_data *data;
     186  	int rc;
     187  
     188  	assert(cxt);
     189  
     190  	DBG(HOOK, ul_debugobj(hs, " adding mount(2) call for bint-remount"));
     191  
     192  	data = new_hook_data();
     193  	if (!data)
     194  		return -ENOMEM;
     195  
     196  	mnt_context_get_mflags(cxt, &data->flags);
     197  
     198  	assert(data->flags & MS_BIND);
     199  	assert(!(data->flags & MS_REMOUNT));
     200  
     201  	data->flags |= (MS_REMOUNT | MS_BIND);
     202  
     203  	rc = mnt_context_append_hook(cxt, hs,
     204  				MNT_STAGE_MOUNT_POST, data, hook_bindremount);
     205  	return rc;
     206  }
     207  
     208  
     209  
     210  
     211  /* call mount(2) for regular FS mount, mount flags and options are read from
     212   * library context struct. There are no private hook data.
     213   */
     214  static int hook_mount(struct libmnt_context *cxt,
     215  		      const struct libmnt_hookset *hs,
     216  		      void *data __attribute__((__unused__)))
     217  {
     218  	int rc = 0;
     219  	unsigned long flags = 0;
     220  	struct libmnt_optlist *ol = NULL;
     221  	const char *src, *target, *type, *options = NULL;
     222  
     223  	src = mnt_fs_get_srcpath(cxt->fs);
     224  	target = mnt_fs_get_target(cxt->fs);
     225  	type = mnt_fs_get_fstype(cxt->fs);
     226  
     227  	ol = mnt_context_get_optlist(cxt);
     228  	if (!ol)
     229  		return -ENOMEM;
     230  	if (!target)
     231  		return -EINVAL;
     232  	if (!src)
     233  		src = "none";
     234  
     235  	/* FS specific mount options/data */
     236  	if (cxt->flags & MNT_FL_MOUNTDATA)
     237  		options = cxt->mountdata;
     238  	else
     239  		rc = mnt_optlist_get_optstr(ol, &options,
     240  				NULL, MNT_OL_FLTR_UNKNOWN);
     241  	/* mount flags */
     242  	if (!rc)
     243  		rc = mnt_optlist_get_flags(ol, &flags,
     244  				mnt_get_builtin_optmap(MNT_LINUX_MAP), 0);
     245  	if (rc)
     246  		return rc;
     247  
     248  	DBG(HOOK, ul_debugobj(hs, "  mount(2) "
     249  		"[source=%s, target=%s, type=%s, flags=0x%08lx, options=%s]",
     250  		src, target, type, flags,
     251  		options ? (cxt->flags & MNT_FL_MOUNTDATA) ? "binary" :
     252  			  options : "<none>"));
     253  
     254  	if (mnt_context_is_fake(cxt)) {
     255  		DBG(HOOK, ul_debugobj(hs, " FAKE (-f)"));
     256  		cxt->syscall_status = 0;
     257  		return 0;
     258  	}
     259  
     260  	if (mount(src, target, type, flags, options)) {
     261  		cxt->syscall_status = -errno;
     262  		cxt->syscall_name = "mount";
     263  		DBG(HOOK, ul_debugobj(hs, "  mount(2) failed [errno=%d %m]",
     264  					-cxt->syscall_status));
     265  		rc = -cxt->syscall_status;
     266  		return rc;
     267  	}
     268  
     269  	cxt->syscall_status = 0;
     270  	return rc;
     271  }
     272  
     273  /*
     274   * analyze library context and register hooks to call one or more mount(2) syscalls
     275   */
     276  static int hook_prepare(struct libmnt_context *cxt,
     277  			const struct libmnt_hookset *hs,
     278  			void *data __attribute__((__unused__)))
     279  {
     280  	int rc = 0;
     281  	unsigned long flags = 0;
     282  
     283  	assert(cxt);
     284  	assert(hs == &hookset_mount_legacy);
     285  
     286  #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
     287  	/* do nothing when a new __mount succesfully registred */
     288  	if (mnt_context_has_hook(cxt, &hookset_mount, 0, NULL))
     289  		return 0;
     290  #endif
     291  	/* append regular FS mount(2) */
     292  	if (!mnt_context_propagation_only(cxt) && !cxt->helper)
     293  		rc = mnt_context_append_hook(cxt, hs,
     294  				MNT_STAGE_MOUNT, NULL, hook_mount);
     295  
     296  	if (!rc)
     297  		rc = mnt_context_get_mflags(cxt, &flags);
     298  	if (rc)
     299  		return rc;
     300  
     301  	/* add extra mount(2) calls for each propagation flag  */
     302  	if (flags & MS_PROPAGATION) {
     303  		rc = prepare_propagation(cxt, hs);
     304  		if (rc)
     305  			return rc;
     306  	}
     307  
     308  	/* add extra mount(2) call to implement "bind,remount,<flags>" */
     309  	if ((flags & MS_BIND)
     310  	    && (flags & MNT_BIND_SETTABLE)
     311  	    && !(flags & MS_REMOUNT)) {
     312  		rc = prepare_bindremount(cxt, hs);
     313  		if (rc)
     314  			return rc;
     315  	}
     316  
     317  	return rc;
     318  }
     319  
     320  const struct libmnt_hookset hookset_mount_legacy =
     321  {
     322  	.name = "__legacy-mount",
     323  
     324  	.firststage = MNT_STAGE_PREP,
     325  	.firstcall = hook_prepare,
     326  
     327  	.deinit = hookset_deinit
     328  };