(root)/
util-linux-2.39/
libmount/
src/
hook_mount.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 fsconfig/fsopen based mount.
      14   *
      15   * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
      16   *
      17   * Operations: functions and STAGE, all is prepared in hook_prepare():
      18   *
      19   * mount:
      20   *	- fsopen	PRE
      21   *	- fsmount	MOUNT
      22   *	- mount_setattr MOUNT (VFS flags)
      23   *	- mount_move	POST
      24   *	- mount_setattr POST (propagation)
      25   *
      26   * remount:
      27   *	- open_tree	PRE
      28   *	- fsconfig      MOUNT (FS reconfigure)
      29   *	- mount_setattr	MOUNT (VFS flags)
      30   *	- mount_setattr POST (propagation)
      31   *
      32   * propagation-only:
      33   *	- open_tree	PRE
      34   *	- mount_setattr	POST (propagation)
      35   *
      36   * move:
      37   *	- open_tree	PRE
      38   *	- mount_move	POST
      39   *
      40   * bind:
      41   *	- open_tree	PRE (clone)
      42   *	- mount_setattr MOUNT (VFS flags)
      43   *	- mount_move	POST
      44   */
      45  
      46  #include "mountP.h"
      47  #include "fileutils.h"	/* statx() fallback */
      48  #include "mount-api-utils.h"
      49  
      50  #include <inttypes.h>
      51  
      52  #ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
      53  
      54  #define get_sysapi(_cxt) mnt_context_get_sysapi(_cxt)
      55  
      56  static void close_sysapi_fds(struct libmnt_sysapi *api)
      57  {
      58  	if (api->fd_fs >= 0)
      59  		close(api->fd_fs);
      60  	if (api->fd_tree >= 0)
      61  		close(api->fd_tree);
      62  
      63  	api->fd_tree = api->fd_fs = -1;
      64  }
      65  
      66  /*
      67   * This hookset uses 'struct libmnt_sysapi' (mountP.h) as hookset data.
      68   */
      69  static void free_hookset_data(	struct libmnt_context *cxt,
      70  				const struct libmnt_hookset *hs)
      71  {
      72  	struct libmnt_sysapi *api = mnt_context_get_hookset_data(cxt, hs);
      73  
      74  	if (!api)
      75  		return;
      76  
      77  	close_sysapi_fds(api);
      78  
      79  	free(api);
      80  	mnt_context_set_hookset_data(cxt, hs, NULL);
      81  }
      82  
      83  /* global data, used by all callbacks */
      84  static struct libmnt_sysapi *new_hookset_data(
      85  				struct libmnt_context *cxt,
      86  				const struct libmnt_hookset *hs)
      87  {
      88  	struct libmnt_sysapi *api = calloc(1, sizeof(struct libmnt_sysapi));
      89  
      90  	if (!api)
      91  		return NULL;
      92  	api->fd_fs = api->fd_tree = -1;
      93  
      94  	if (mnt_context_set_hookset_data(cxt, hs, api) != 0) {
      95  		/* probably ENOMEM problem */
      96  		free(api);
      97  		api = NULL;
      98  	}
      99  	return api;
     100  }
     101  
     102  /* de-initiallize this module */
     103  static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
     104  {
     105  	DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
     106  
     107  	/* remove all our hooks */
     108  	while (mnt_context_remove_hook(cxt, hs, 0, NULL) == 0);
     109  
     110  	/* free and remove global hookset data */
     111  	free_hookset_data(cxt, hs);
     112  
     113  	return 0;
     114  }
     115  
     116  static inline int fsconfig_set_value(
     117  			struct libmnt_context *cxt,
     118  			const struct libmnt_hookset *hs,
     119  			int fd,
     120  			const char *name, const char *value)
     121  {
     122  	int rc;
     123  
     124  	DBG(HOOK, ul_debugobj(hs, "  fsconfig(name=%s,value=%s)", name,
     125  				value ? : ""));
     126  	if (value)
     127  		rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0);
     128  	else
     129  		rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0);
     130  
     131  	set_syscall_status(cxt, "fsconfig", rc == 0);
     132  	return rc;
     133  }
     134  
     135  static int configure_superblock(struct libmnt_context *cxt,
     136  				const struct libmnt_hookset *hs,
     137  				int fd, int force_rwro)
     138  {
     139  	struct libmnt_optlist *ol;
     140  	struct libmnt_iter itr;
     141  	struct libmnt_opt *opt;
     142  	int rc = 0, has_rwro = 0;
     143  
     144  	DBG(HOOK, ul_debugobj(hs, " config FS"));
     145  
     146  	ol = mnt_context_get_optlist(cxt);
     147  	if (!ol)
     148  		return -ENOMEM;
     149  
     150  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     151  
     152  	while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
     153  		const char *name = mnt_opt_get_name(opt);
     154  		const char *value = mnt_opt_get_value(opt);
     155  		const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
     156  
     157  		if (ent && mnt_opt_get_map(opt) == cxt->map_linux &&
     158  		    ent->id == MS_RDONLY) {
     159  			value = NULL;
     160  			has_rwro = 1;
     161  		} else if (!name || mnt_opt_get_map(opt) || mnt_opt_is_external(opt))
     162  			continue;
     163  
     164  		rc = fsconfig_set_value(cxt, hs, fd, name, value);
     165  		if (rc != 0)
     166  			goto done;
     167  	}
     168  
     169  	if (force_rwro && !has_rwro)
     170  		rc = fsconfig_set_value(cxt, hs, fd, "rw", NULL);
     171  
     172  done:
     173  	DBG(HOOK, ul_debugobj(hs, " config done [rc=%d]", rc));
     174  	return rc != 0 && errno ? -errno : rc;
     175  }
     176  
     177  static int open_fs_configuration_context(struct libmnt_context *cxt,
     178  					 struct libmnt_sysapi *api,
     179  					 const char *type)
     180  {
     181  	DBG(HOOK, ul_debug(" new FS '%s'", type));
     182  
     183  	if (!type)
     184  		return -EINVAL;
     185  
     186  	DBG(HOOK, ul_debug(" fsopen(%s)", type));
     187  
     188  	api->fd_fs = fsopen(type, FSOPEN_CLOEXEC);
     189  	set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
     190  	if (api->fd_fs < 0)
     191  		return -errno;
     192  	api->is_new_fs = 1;
     193  	return api->fd_fs;
     194  }
     195  
     196  static int open_mount_tree(struct libmnt_context *cxt, const char *path, unsigned long mflg)
     197  {
     198  	unsigned long oflg = OPEN_TREE_CLOEXEC;
     199  	int rc = 0, fd = -1;
     200  
     201  	if (mflg == (unsigned long) -1) {
     202  		rc = mnt_optlist_get_flags(cxt->optlist, &mflg, cxt->map_linux, 0);
     203  		if (rc)
     204  			return rc;
     205  	}
     206  	if (!path) {
     207  		path = mnt_fs_get_target(cxt->fs);
     208  		if (!path)
     209  			return -EINVAL;
     210  	}
     211  
     212  	/* Classic -oremount,bind,ro is not bind operation, it's just
     213  	 * VFS flags update only */
     214  	if ((mflg & MS_BIND) && !(mflg & MS_REMOUNT)) {
     215  		oflg |= OPEN_TREE_CLONE;
     216  
     217  		if (mnt_optlist_is_rbind(cxt->optlist))
     218  			oflg |= AT_RECURSIVE;
     219  	}
     220  
     221  	if (cxt->force_clone)
     222  		oflg |= OPEN_TREE_CLONE;
     223  
     224  	DBG(HOOK, ul_debug("open_tree(path=%s%s%s)", path,
     225  				oflg & OPEN_TREE_CLONE ? " clone" : "",
     226  				oflg & AT_RECURSIVE ? " recursive" : ""));
     227  	fd = open_tree(AT_FDCWD, path, oflg);
     228  	set_syscall_status(cxt, "open_tree", fd >= 0);
     229  
     230  	return fd;
     231  }
     232  
     233  static int hook_create_mount(struct libmnt_context *cxt,
     234  			const struct libmnt_hookset *hs,
     235  			void *data __attribute__((__unused__)))
     236  {
     237  	struct libmnt_sysapi *api;
     238  	const char *src;
     239  	int rc = 0;
     240  
     241  	assert(cxt);
     242  	assert(cxt->fs);
     243  
     244  	api = get_sysapi(cxt);
     245  	assert(api);
     246  
     247  	if (api->fd_fs < 0) {
     248  		const char *type = mnt_fs_get_fstype(cxt->fs);
     249  
     250  		rc = open_fs_configuration_context(cxt, api, type);
     251  		if (rc < 0) {
     252  			rc = api->fd_fs;
     253  			goto done;
     254  		}
     255  	}
     256  
     257  	src = mnt_fs_get_srcpath(cxt->fs);
     258  	if (!src)
     259  		return -EINVAL;
     260  
     261  	DBG(HOOK, ul_debugobj(hs, "init FS"));
     262  
     263  	rc = fsconfig(api->fd_fs, FSCONFIG_SET_STRING, "source", src, 0);
     264  	set_syscall_status(cxt, "fsconfig", rc == 0);
     265  
     266  	if (!rc)
     267  		rc = configure_superblock(cxt, hs, api->fd_fs, 0);
     268  	if (!rc) {
     269  		DBG(HOOK, ul_debugobj(hs, "create FS"));
     270  		rc = fsconfig(api->fd_fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
     271  		set_syscall_status(cxt, "fsconfig", rc == 0);
     272  	}
     273  
     274  	if (!rc) {
     275  		api->fd_tree = fsmount(api->fd_fs, FSMOUNT_CLOEXEC, 0);
     276  		set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
     277  		if (api->fd_tree < 0)
     278  			rc = -errno;
     279  	}
     280  
     281  	if (rc)
     282  		/* cleanup after fail (libmount may only try the FS type) */
     283  		close_sysapi_fds(api);
     284  
     285  	if (!rc && cxt->fs) {
     286  		struct statx st;
     287  
     288  		rc = statx(api->fd_tree, "", AT_EMPTY_PATH, STATX_MNT_ID, &st);
     289  		cxt->fs->id = (int) st.stx_mnt_id;
     290  
     291  		if (cxt->update) {
     292  			struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
     293  			if (fs)
     294  				fs->id = cxt->fs->id;
     295  		}
     296  	}
     297  
     298  done:
     299  	DBG(HOOK, ul_debugobj(hs, "create FS done [rc=%d, id=%d]", rc, cxt->fs ? cxt->fs->id : -1));
     300  	return rc;
     301  }
     302  
     303  static int hook_reconfigure_mount(struct libmnt_context *cxt,
     304  			const struct libmnt_hookset *hs,
     305  			void *data __attribute__((__unused__)))
     306  {
     307  	struct libmnt_sysapi *api;
     308  	int rc = 0;
     309  
     310  	assert(cxt);
     311  
     312  	api = get_sysapi(cxt);
     313  	assert(api);
     314  	assert(api->fd_tree >= 0);
     315  
     316  	if (api->fd_fs < 0) {
     317  		api->fd_fs = fspick(api->fd_tree, "", FSPICK_EMPTY_PATH |
     318  						      FSPICK_NO_AUTOMOUNT);
     319  		set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
     320  		if (api->fd_fs < 0)
     321  			return -errno;
     322  	}
     323  
     324  	rc = configure_superblock(cxt, hs, api->fd_fs, 1);
     325  	if (!rc) {
     326  		DBG(HOOK, ul_debugobj(hs, "re-configurate FS"));
     327  		rc = fsconfig(api->fd_fs, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0);
     328  		set_syscall_status(cxt, "fsconfig", rc == 0);
     329  	}
     330  
     331  	DBG(HOOK, ul_debugobj(hs, "reconf FS done [rc=%d]", rc));
     332  	return rc;
     333  }
     334  
     335  static int set_vfsflags(struct libmnt_context *cxt,
     336  			const struct libmnt_hookset *hs,
     337  			uint64_t set, uint64_t clr, int recursive)
     338  {
     339  	struct libmnt_sysapi *api;
     340  	struct mount_attr attr = { .attr_clr = 0 };
     341  	unsigned int callflags = AT_EMPTY_PATH;
     342  	int rc;
     343  
     344  	api = get_sysapi(cxt);
     345  	assert(api);
     346  
     347  	/* fallback only; necessary when init_sysapi() during preparation
     348  	 * cannot open the tree -- for example when we call /sbin/mount.<type> */
     349  	if (api->fd_tree < 0 && mnt_fs_get_target(cxt->fs)) {
     350  		rc = api->fd_tree = open_mount_tree(cxt, NULL, (unsigned long) -1);
     351  		if (rc < 0)
     352  			return rc;
     353  		rc = 0;
     354  	}
     355  
     356  	if (recursive)
     357  		callflags |= AT_RECURSIVE;
     358  
     359  	DBG(HOOK, ul_debugobj(hs,
     360  			"mount_setattr(set=0x%08" PRIx64" clr=0x%08" PRIx64")", set, clr));
     361  	attr.attr_set = set;
     362  	attr.attr_clr = clr;
     363  
     364  	errno = 0;
     365  	rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr));
     366  	set_syscall_status(cxt, "mount_setattr", rc == 0);
     367  
     368  	if (rc && errno == EINVAL)
     369  		return -MNT_ERR_APPLYFLAGS;
     370  
     371  	return rc == 0 ? 0 : -errno;
     372  }
     373  
     374  static int hook_set_vfsflags(struct libmnt_context *cxt,
     375  			const struct libmnt_hookset *hs,
     376  			void *data __attribute__((__unused__)))
     377  {
     378  	struct libmnt_optlist *ol;
     379  	uint64_t set = 0, clr = 0;
     380  	int rc = 0;
     381  
     382  	DBG(HOOK, ul_debugobj(hs, "setting VFS flags"));
     383  
     384  	ol = mnt_context_get_optlist(cxt);
     385  	if (!ol)
     386  		return -ENOMEM;
     387  
     388  	/* normal flags */
     389  	rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_NOREC);
     390  	if (!rc && (set || clr))
     391  		rc = set_vfsflags(cxt, hs, set, clr, 0);
     392  
     393  	/* recursive flags */
     394  	set = clr = 0;
     395  	if (!rc)
     396  		rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_REC);
     397  	if (!rc && (set || clr))
     398  		rc = set_vfsflags(cxt, hs, set, clr, 1);
     399  
     400  	return rc;
     401  }
     402  
     403  static int hook_set_propagation(struct libmnt_context *cxt,
     404  			const struct libmnt_hookset *hs,
     405  			void *data __attribute__((__unused__)))
     406  {
     407  	struct libmnt_sysapi *api;
     408  	struct libmnt_optlist *ol;
     409  	struct libmnt_iter itr;
     410  	struct libmnt_opt *opt;
     411  	int rc = 0;
     412  
     413  	DBG(HOOK, ul_debugobj(hs, "setting propagation"));
     414  
     415  	ol = mnt_context_get_optlist(cxt);
     416  	if (!ol)
     417  		return -ENOMEM;
     418  
     419  	api = get_sysapi(cxt);
     420  	assert(api);
     421  
     422  	/* fallback only; necessary when init_sysapi() during preparation
     423  	 * cannot open the tree -- for example when we call /sbin/mount.<type> */
     424  	if (api->fd_tree < 0 && mnt_fs_get_target(cxt->fs)) {
     425  		rc = api->fd_tree = open_mount_tree(cxt, NULL, (unsigned long) -1);
     426  		if (rc < 0)
     427  			goto done;
     428  		rc = 0;
     429  	}
     430  
     431  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     432  
     433  	while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
     434  		const struct libmnt_optmap *map = mnt_opt_get_map(opt);
     435  		const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
     436  		struct mount_attr attr = { .attr_clr = 0 };
     437  		unsigned int flgs = AT_EMPTY_PATH;
     438  
     439  		if (cxt->map_linux != map)
     440  			continue;
     441  		if (mnt_opt_is_external(opt))
     442  			continue;
     443  		if (!ent || !ent->id || !(ent->id & MS_PROPAGATION))
     444  			continue;
     445  
     446  		attr.propagation = ent->id & MS_PROPAGATION;
     447  		if (ent->id & MS_REC)
     448  			flgs |= AT_RECURSIVE;
     449  
     450  		DBG(HOOK, ul_debugobj(hs,
     451  			"mount_setattr(propagation=0x%08" PRIx64")",
     452  			(uint64_t) attr.propagation));
     453  
     454  		rc = mount_setattr(api->fd_tree, "", flgs, &attr, sizeof(attr));
     455  		set_syscall_status(cxt, "move_setattr", rc == 0);
     456  
     457  		if (rc && errno == EINVAL)
     458  			return -MNT_ERR_APPLYFLAGS;
     459  		if (rc != 0)
     460  			break;
     461  	}
     462  done:
     463  	return rc == 0 ? 0 : -errno;
     464  }
     465  
     466  static int hook_attach_target(struct libmnt_context *cxt,
     467  		const struct libmnt_hookset *hs,
     468  		void *data __attribute__((__unused__)))
     469  {
     470  	struct libmnt_sysapi *api;
     471  	const char *target;
     472  	int rc = 0;
     473  
     474  	target = mnt_fs_get_target(cxt->fs);
     475  	if (!target)
     476  		return -EINVAL;
     477  
     478  	api = get_sysapi(cxt);
     479  	assert(api);
     480  	assert(api->fd_tree >= 0);
     481  
     482  	DBG(HOOK, ul_debugobj(hs, "move_mount(to=%s)", target));
     483  
     484  	/* umount old target if we created a clone */
     485  	if (cxt->force_clone
     486  	    && !api->is_new_fs
     487  	    && !mnt_optlist_is_bind(cxt->optlist)) {
     488  
     489  		DBG(HOOK, ul_debugobj(hs, "remove expired target"));
     490  		umount2(target, MNT_DETACH);
     491  	}
     492  
     493  	rc = move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
     494  	set_syscall_status(cxt, "move_mount", rc == 0);
     495  
     496  	return rc == 0 ? 0 : -errno;
     497  }
     498  
     499  static inline int fsopen_is_supported(void)
     500  {
     501  	int dummy, rc = 1;
     502  
     503  	errno = 0;
     504  	dummy = fsopen(NULL, FSOPEN_CLOEXEC);
     505  
     506  	if (errno == ENOSYS)
     507  		rc = 0;
     508  	if (dummy >= 0)
     509  		close(dummy);
     510  	return rc;
     511  }
     512  
     513  /*
     514   * open_tree() and fsopen()
     515   */
     516  static int init_sysapi(struct libmnt_context *cxt,
     517  		       const struct libmnt_hookset *hs,
     518  		       unsigned long flags)
     519  {
     520  	struct libmnt_sysapi *api;
     521  	const char *path = NULL;
     522  
     523  	assert(cxt);
     524  	assert(hs);
     525  
     526  	DBG(HOOK, ul_debugobj(hs, "initialize API fds"));
     527  
     528  	/* A) tree based operation -- the tree is mount point */
     529  	if ((flags & MS_REMOUNT)
     530  	    || mnt_context_propagation_only(cxt)) {
     531  		DBG(HOOK, ul_debugobj(hs, " REMOUNT/propagation"));
     532  		path = mnt_fs_get_target(cxt->fs);
     533  		if (!path)
     534  			return -EINVAL;
     535  
     536  	/* B) tree based operation -- the tree is mount source */
     537  	} else if ((flags & MS_BIND)
     538  	    || (flags & MS_MOVE)) {
     539  		DBG(HOOK, ul_debugobj(hs, " BIND/MOVE"));
     540  		path = mnt_fs_get_srcpath(cxt->fs);
     541  		if (!path)
     542  			return -EINVAL;
     543  	}
     544  
     545  	api = new_hookset_data(cxt, hs);
     546  	if (!api)
     547  		return -ENOMEM;
     548  
     549  	if (mnt_context_is_fake(cxt))
     550  		goto fake;
     551  
     552  	if (path) {
     553  		api->fd_tree = open_mount_tree(cxt, path, flags);
     554  		if (api->fd_tree < 0)
     555  			goto fail;
     556  
     557  	/* C) FS based operation
     558  	 *
     559  	 *  Note, fstype is optinal and may be specified later if mount by
     560  	 *  list of FS types (mount -t foo,bar,ext4). In this case fsopen()
     561  	 *  is called later in hook_create_mount(). */
     562  	} else {
     563  		const char *type = mnt_fs_get_fstype(cxt->fs);
     564  		int rc = 0;
     565  
     566  		/* fsopen() to create a superblock */
     567  		if (cxt->helper == NULL && type && !strchr(type, ','))
     568  			rc = open_fs_configuration_context(cxt, api, type);
     569  
     570  		/* dummy fsopen() to test if API is available */
     571  		else if (!fsopen_is_supported()) {
     572  			errno = ENOSYS;
     573  			rc = -errno;
     574  			set_syscall_status(cxt, "fsopen", rc == 0);
     575  		}
     576  		if (rc < 0)
     577  			goto fail;
     578  	}
     579  
     580  	return 0;
     581  fail:
     582  	DBG(HOOK, ul_debugobj(hs, "init fs/tree failed [errno=%d %m]", errno));
     583  	return -errno;
     584  fake:
     585  	DBG(CXT, ul_debugobj(cxt, " FAKE (-f)"));
     586  	cxt->syscall_status = 0;
     587  	return 0;
     588  }
     589  
     590  /*
     591   * Analyze library context and register hook to call mount-like syscalls.
     592   *
     593   * Note that this function interprets classic MS_* flags by new Linux mount FD
     594   * based API.
     595   *
     596   * Returns: 0 on success, <0 on error, >0 on recover-able error
     597   */
     598  static int hook_prepare(struct libmnt_context *cxt,
     599  			const struct libmnt_hookset *hs,
     600  			void *data __attribute__((__unused__)))
     601  {
     602  	struct libmnt_optlist *ol;
     603  	unsigned long flags = 0;
     604  	uint64_t set = 0, clr = 0;
     605  	int rc = 0;
     606  
     607  	assert(cxt);
     608  	assert(hs == &hookset_mount);
     609  
     610  	/*
     611  	 * The current kernel btrfs driver does not completely implement
     612  	 * fsconfig() as it does not work with selinux stuff.
     613  	 *
     614  	 * Don't use the new mount API in this situation. Let's hope this issue
     615  	 * is temporary.
     616  	 */
     617  	{
     618  		const char *type = mnt_fs_get_fstype(cxt->fs);
     619  
     620  		if (type && strcmp(type, "btrfs") == 0 && cxt->has_selinux_opt) {
     621  			DBG(HOOK, ul_debugobj(hs, "don't use new API (btrfs issue)"));
     622  			return 0;
     623  		}
     624  	}
     625  
     626  	DBG(HOOK, ul_debugobj(hs, "prepare mount"));
     627  
     628  	ol = mnt_context_get_optlist(cxt);
     629  	if (!ol)
     630  		return -ENOMEM;
     631  
     632  	/* classic MS_* flags (include oprations like MS_REMOUNT, etc) */
     633  	rc = mnt_optlist_get_flags(ol, &flags, cxt->map_linux, 0);
     634  
     635  	/* MOUNT_ATTR_* flags for mount_setattr() */
     636  	if (!rc)
     637  		rc = mnt_optlist_get_attrs(ol, &set, &clr, 0);
     638  
     639  	/* open_tree() or fsopen() */
     640  	if (!rc) {
     641  		rc = init_sysapi(cxt, hs, flags);
     642  		if (rc && cxt->syscall_status == -ENOSYS) {
     643  			/* we need to recover from this error, so hook_mount_legacy.c
     644  			 * can try to continue */
     645  			reset_syscall_status(cxt);
     646  			free_hookset_data(cxt, hs);
     647  			return 1;
     648  		}
     649  	}
     650  
     651  	/* check mutually exclusive operations */
     652  	if (!rc && (flags & MS_BIND) && (flags & MS_MOVE))
     653  		return -EINVAL;
     654  	if (!rc && (flags & MS_MOVE) && (flags & MS_REMOUNT))
     655  		return -EINVAL;
     656  
     657  	/* classic remount (note -oremount,bind,ro is not superblock reconfiguration) */
     658  	if (!rc
     659  	    && cxt->helper == NULL
     660  	    && (flags & MS_REMOUNT)
     661  	    && !(flags & MS_BIND))
     662  		rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
     663  					hook_reconfigure_mount);
     664  
     665  	/* create a new FS instance */
     666  	else if (!rc
     667  	    && cxt->helper == NULL
     668  	    && !(flags & MS_BIND)
     669  	    && !(flags & MS_MOVE)
     670  	    && !(flags & MS_REMOUNT)
     671  	    && !mnt_context_propagation_only(cxt))
     672  		rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
     673  					hook_create_mount);
     674  
     675  	/* call mount_setattr() */
     676  	if (!rc
     677  	    && cxt->helper == NULL
     678  	    && (set != 0 || clr != 0 || (flags & MS_REMOUNT)))
     679  		rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT, NULL,
     680  					hook_set_vfsflags);
     681  
     682  	/* call move_mount() to attach target */
     683  	if (!rc
     684  	    && cxt->helper == NULL
     685  	    && (cxt->force_clone ||
     686  			(!(flags & MS_REMOUNT) && !mnt_context_propagation_only(cxt))))
     687  		rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT_POST, NULL,
     688  					hook_attach_target);
     689  
     690  	/* set propagation (has to be attached to VFS) */
     691  	if (!rc && mnt_optlist_get_propagation(ol))
     692  		rc = mnt_context_append_hook(cxt, hs, MNT_STAGE_MOUNT_POST, NULL,
     693  					hook_set_propagation);
     694  
     695  	DBG(HOOK, ul_debugobj(hs, "prepare mount done [rc=%d]", rc));
     696  	return rc;
     697  }
     698  
     699  const struct libmnt_hookset hookset_mount =
     700  {
     701  	.name = "__mount",
     702  
     703  	.firststage = MNT_STAGE_PREP,
     704  	.firstcall = hook_prepare,
     705  
     706  	.deinit = hookset_deinit
     707  };
     708  #endif /* USE_LIBMOUNT_MOUNTFD_SUPPORT */