(root)/
util-linux-2.39/
libmount/
src/
hook_mkdir.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   * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
      14   */
      15  #include "mountP.h"
      16  #include "fileutils.h"
      17  
      18  static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
      19  {
      20  	void *data = NULL;
      21  
      22  	DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
      23  
      24  	/* remove all our hooks and free hook data */
      25  	while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
      26  		if (data)
      27  			free(data);
      28  		data = NULL;
      29  	}
      30  
      31  	return 0;
      32  }
      33  
      34  static int is_mkdir_required(struct libmnt_context *cxt, const char *tgt, mode_t *mode, int *rc)
      35  {
      36  	struct libmnt_optlist *ol;
      37  	struct libmnt_opt *opt;
      38  	const char *mstr = NULL;
      39  
      40  	assert(cxt);
      41  	assert(cxt->map_userspace);
      42  	assert(tgt);
      43  	assert(mode);
      44  	assert(rc);
      45  
      46  	*mode = 0;
      47  	*rc = 0;
      48  
      49  	ol = mnt_context_get_optlist(cxt);
      50  	if (!ol)
      51  		return -ENOMEM;
      52  
      53  	opt = mnt_optlist_get_named(ol, "X-mount.mkdir", cxt->map_userspace);
      54  	if (!opt)
      55  		opt = mnt_optlist_get_named(ol, "x-mount.mkdir", cxt->map_userspace);
      56  	if (!opt)
      57  		return 0;
      58  
      59  	if (mnt_is_path(tgt))
      60  		return 0;
      61  
      62  	mstr = mnt_opt_get_value(opt);
      63  
      64  	if (mstr && *mstr) {
      65  		char *end = NULL;
      66  
      67  		if (*mstr == '"')
      68  			mstr++;
      69  
      70  		errno = 0;
      71  		*mode = strtol(mstr, &end, 8);
      72  
      73  		if (errno || !end || !(*end == '"' || *end == '\0')) {
      74  			DBG(HOOK, ul_debug("failed to parse mkdir mode '%s'", mstr));
      75  			*rc = -MNT_ERR_MOUNTOPT;
      76  			return 0;
      77  		}
      78  	}
      79  
      80  	if (!*mode)
      81  		*mode = S_IRWXU |			/* 0755 */
      82  		       S_IRGRP | S_IXGRP |
      83  		       S_IROTH | S_IXOTH;
      84  
      85  	DBG(HOOK, ul_debug("mkdir %s (%o) wanted", tgt, *mode));
      86  
      87  	return 1;
      88  }
      89  
      90  static int hook_prepare_target(
      91  			struct libmnt_context *cxt,
      92  			const struct libmnt_hookset *hs,
      93  			void *data __attribute__((__unused__)))
      94  {
      95  	int rc = 0;
      96  	mode_t mode = 0;
      97  	const char *tgt;
      98  
      99  	assert(cxt);
     100  
     101  	tgt = mnt_fs_get_target(cxt->fs);
     102  	if (!tgt)
     103  		return 0;
     104  
     105  	if (cxt->action == MNT_ACT_MOUNT
     106  	    && is_mkdir_required(cxt, tgt, &mode, &rc)) {
     107  
     108  		struct libmnt_cache *cache;
     109  
     110  		/* supported only for root or non-suid mount(8) */
     111  		if (!mnt_context_is_restricted(cxt)) {
     112  			rc = ul_mkdir_p(tgt, mode);
     113  			if (rc)
     114  				DBG(HOOK, ul_debugobj(hs, "mkdir %s failed: %m", tgt));
     115  		} else
     116  			rc = -EPERM;
     117  
     118  		if (rc == 0) {
     119  			cache = mnt_context_get_cache(cxt);
     120  			if (cache) {
     121  				char *path = mnt_resolve_path(tgt, cache);
     122  				if (path && strcmp(path, tgt) != 0)
     123  					rc = mnt_fs_set_target(cxt->fs, path);
     124  			}
     125  		}
     126  	}
     127  
     128  	return rc;
     129  }
     130  
     131  const struct libmnt_hookset hookset_mkdir =
     132  {
     133  	.name = "__mkdir",
     134  
     135  	.firststage = MNT_STAGE_PREP_TARGET,
     136  	.firstcall = hook_prepare_target,
     137  
     138  	.deinit = hookset_deinit
     139  };