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 };