(root)/
util-linux-2.39/
libmount/
src/
hook_owner.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 X-mount.owner=, X-mount.group= and X-mount.mode= implementation.
      14   *
      15   * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
      16   */
      17  #include <sched.h>
      18  
      19  #include "mountP.h"
      20  #include "fileutils.h"
      21  
      22  struct hook_data {
      23  	uid_t owner;
      24  	gid_t group;
      25  	mode_t mode;
      26  };
      27  
      28  /* de-initiallize this module */
      29  static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
      30  {
      31  	void *data;
      32  
      33  	DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
      34  
      35  	/* remove all our hooks and free hook data */
      36  	while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) {
      37  		free(data);
      38  		data = NULL;
      39  	}
      40  
      41  	return 0;
      42  }
      43  
      44  static int hook_post(
      45  			struct libmnt_context *cxt,
      46  			const struct libmnt_hookset *hs __attribute__((__unused__)),
      47  			void *data)
      48  {
      49  	struct hook_data *hd = (struct hook_data *) data;
      50  	const char *target;
      51  	int rc = 0;
      52  
      53  	assert(cxt);
      54  
      55  	if (!hd || !cxt->fs)
      56  		return 0;
      57  
      58  	target = mnt_fs_get_target(cxt->fs);
      59  	if (!target)
      60  		return 0;
      61  
      62  	if (hd->owner != (uid_t) -1 || hd->group != (uid_t) -1) {
      63  		DBG(CXT, ul_debugobj(cxt, " lchown(%s, %u, %u)", target, hd->owner, hd->group));
      64  		if (lchown(target, hd->owner, hd->group) == -1)
      65  			return -MNT_ERR_CHOWN;
      66  	}
      67  
      68  	if (hd->mode != (mode_t) -1) {
      69  		DBG(CXT, ul_debugobj(cxt, " chmod(%s, %04o)", target, hd->mode));
      70  		if (chmod(target, hd->mode) == -1)
      71  			return -MNT_ERR_CHMOD;
      72  	}
      73  
      74  	return rc;
      75  }
      76  
      77  static inline struct hook_data *new_hook_data(void)
      78  {
      79  	struct hook_data *hd = calloc(1, sizeof(*hd));
      80  
      81  	if (!hd)
      82  		return NULL;
      83  
      84  	hd->owner = (uid_t) -1;
      85  	hd->group = (gid_t) -1;
      86  	hd->mode = (mode_t) -1;
      87  	return hd;
      88  }
      89  
      90  static int hook_prepare_options(
      91  			struct libmnt_context *cxt,
      92  			const struct libmnt_hookset *hs,
      93  			void *data __attribute__((__unused__)))
      94  {
      95  	struct hook_data *hd = NULL;
      96  	struct libmnt_optlist *ol;
      97  	struct libmnt_opt *opt;
      98  	int rc = 0;
      99  
     100  	assert(cxt);
     101  	assert(cxt->map_userspace);
     102  
     103  	ol = mnt_context_get_optlist(cxt);
     104  	if (!ol)
     105  		return -ENOMEM;
     106  
     107  	opt = mnt_optlist_get_named(ol, "X-mount.owner", cxt->map_userspace);
     108  	if (opt) {
     109  		const char *value = mnt_opt_get_value(opt);
     110  		if (!value)
     111  			goto fail;
     112  		if (!hd) {
     113  			hd = new_hook_data();
     114  			if (!hd)
     115  				goto fail;
     116  		}
     117  		if (mnt_parse_uid(value, strlen(value), &hd->owner))
     118  			goto fail;
     119  	}
     120  
     121  	opt = mnt_optlist_get_named(ol, "X-mount.group", cxt->map_userspace);
     122  	if (opt) {
     123  		const char *value = mnt_opt_get_value(opt);
     124  		if (!value)
     125  			goto fail;
     126  		if (!hd) {
     127  			hd = new_hook_data();
     128  			if (!hd)
     129  				goto fail;
     130  		}
     131  		if (mnt_parse_gid(value, strlen(value), &hd->group))
     132  			goto fail;
     133  	}
     134  
     135  	opt = mnt_optlist_get_named(ol, "X-mount.mode", cxt->map_userspace);
     136  	if (opt) {
     137  		const char *value = mnt_opt_get_value(opt);
     138  		if (!value)
     139  			goto fail;
     140  		if (!hd) {
     141  			hd = new_hook_data();
     142  			if (!hd)
     143  				goto fail;
     144  		}
     145  		if (mnt_parse_mode(value, strlen(value), &hd->mode))
     146  			goto fail;
     147  	}
     148  
     149  	if (hd) {
     150  		DBG(CXT, ul_debugobj(cxt, " wanted ownership %d:%d, mode %04o",
     151  					hd->owner, hd->group, hd->mode));
     152  		rc = mnt_context_append_hook(cxt, hs,
     153  				MNT_STAGE_POST,
     154  				hd, hook_post);
     155  		if (rc < 0)
     156  			goto fail;
     157  	}
     158  	return 0;
     159  fail:
     160  	if (rc == 0)
     161  		rc = -MNT_ERR_MOUNTOPT;
     162  	free(hd);
     163  	return rc;
     164  }
     165  
     166  
     167  const struct libmnt_hookset hookset_owner =
     168  {
     169  	.name = "__owner",
     170  
     171  	.firststage = MNT_STAGE_PREP_OPTIONS,
     172  	.firstcall = hook_prepare_options,
     173  
     174  	.deinit = hookset_deinit
     175  };