diff options
Diffstat (limited to 'libmount/src/hook_owner.c')
-rw-r--r-- | libmount/src/hook_owner.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/libmount/src/hook_owner.c b/libmount/src/hook_owner.c new file mode 100644 index 0000000..11b238c --- /dev/null +++ b/libmount/src/hook_owner.c @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2022 Karel Zak <kzak@redhat.com> + * + * libmount is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * + * This is X-mount.owner=, X-mount.group= and X-mount.mode= implementation. + * + * Please, see the comment in libmount/src/hooks.c to understand how hooks work. + */ +#include <sched.h> + +#include "mountP.h" +#include "fileutils.h" + +struct hook_data { + uid_t owner; + gid_t group; + mode_t mode; +}; + +/* de-initiallize this module */ +static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs) +{ + void *data; + + DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name)); + + /* remove all our hooks and free hook data */ + while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) { + free(data); + data = NULL; + } + + return 0; +} + +static int hook_post( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs __attribute__((__unused__)), + void *data) +{ + struct hook_data *hd = (struct hook_data *) data; + const char *target; + int rc = 0; + + assert(cxt); + + if (!hd || !cxt->fs) + return 0; + + target = mnt_fs_get_target(cxt->fs); + if (!target) + return 0; + + if (hd->owner != (uid_t) -1 || hd->group != (uid_t) -1) { + DBG(CXT, ul_debugobj(cxt, " lchown(%s, %u, %u)", target, hd->owner, hd->group)); + if (lchown(target, hd->owner, hd->group) == -1) + return -MNT_ERR_CHOWN; + } + + if (hd->mode != (mode_t) -1) { + DBG(CXT, ul_debugobj(cxt, " chmod(%s, %04o)", target, hd->mode)); + if (chmod(target, hd->mode) == -1) + return -MNT_ERR_CHMOD; + } + + return rc; +} + +static inline struct hook_data *new_hook_data(void) +{ + struct hook_data *hd = calloc(1, sizeof(*hd)); + + if (!hd) + return NULL; + + hd->owner = (uid_t) -1; + hd->group = (gid_t) -1; + hd->mode = (mode_t) -1; + return hd; +} + +static int hook_prepare_options( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + void *data __attribute__((__unused__))) +{ + struct hook_data *hd = NULL; + struct libmnt_optlist *ol; + struct libmnt_opt *opt; + int rc = 0; + + assert(cxt); + assert(cxt->map_userspace); + + ol = mnt_context_get_optlist(cxt); + if (!ol) + return -ENOMEM; + + opt = mnt_optlist_get_named(ol, "X-mount.owner", cxt->map_userspace); + if (opt) { + const char *value = mnt_opt_get_value(opt); + if (!value) + goto fail; + if (!hd) { + hd = new_hook_data(); + if (!hd) + goto fail; + } + if (mnt_parse_uid(value, strlen(value), &hd->owner)) + goto fail; + } + + opt = mnt_optlist_get_named(ol, "X-mount.group", cxt->map_userspace); + if (opt) { + const char *value = mnt_opt_get_value(opt); + if (!value) + goto fail; + if (!hd) { + hd = new_hook_data(); + if (!hd) + goto fail; + } + if (mnt_parse_gid(value, strlen(value), &hd->group)) + goto fail; + } + + opt = mnt_optlist_get_named(ol, "X-mount.mode", cxt->map_userspace); + if (opt) { + const char *value = mnt_opt_get_value(opt); + if (!value) + goto fail; + if (!hd) { + hd = new_hook_data(); + if (!hd) + goto fail; + } + if (mnt_parse_mode(value, strlen(value), &hd->mode)) + goto fail; + } + + if (hd) { + DBG(CXT, ul_debugobj(cxt, " wanted ownership %d:%d, mode %04o", + hd->owner, hd->group, hd->mode)); + rc = mnt_context_append_hook(cxt, hs, + MNT_STAGE_POST, + hd, hook_post); + if (rc < 0) + goto fail; + } + return 0; +fail: + if (rc == 0) + rc = -MNT_ERR_MOUNTOPT; + free(hd); + return rc; +} + + +const struct libmnt_hookset hookset_owner = +{ + .name = "__owner", + + .firststage = MNT_STAGE_PREP_OPTIONS, + .firstcall = hook_prepare_options, + + .deinit = hookset_deinit +}; |