• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 25529530918

07 May 2026 07:03PM UTC coverage: 72.624% (-0.03%) from 72.658%
25529530918

push

github

web-flow
Convert resolvectl to option and verb macros (#41978)

This was one quite involved because some preparatory work was needed and
there are three parsers in the same binary.

636 of 1004 new or added lines in 2 files covered. (63.35%)

3865 existing lines in 59 files now uncovered.

326571 of 449671 relevant lines covered (72.62%)

1217500.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

42.79
/src/mstack/mstack-tool.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <unistd.h>
5

6
#include "argv-util.h"
7
#include "build.h"
8
#include "chase.h"
9
#include "errno-util.h"
10
#include "extract-word.h"
11
#include "fd-util.h"
12
#include "format-table.h"
13
#include "help-util.h"
14
#include "image-policy.h"
15
#include "main-func.h"
16
#include "mount-util.h"
17
#include "mountpoint-util.h"
18
#include "mstack.h"
19
#include "options.h"
20
#include "parse-argument.h"
21
#include "string-util.h"
22

23
static enum {
24
        ACTION_INSPECT,
25
        ACTION_MOUNT,
26
        ACTION_UMOUNT,
27
} arg_action = ACTION_INSPECT;
28
static char *arg_what = NULL;
29
static char *arg_where = NULL;
30
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
31
static PagerFlags arg_pager_flags = 0;
32
static int arg_legend = true;
33
static MStackFlags arg_mstack_flags = 0;
34
static bool arg_rmdir = false;
35
static ImagePolicy *arg_image_policy = NULL;
36
static ImageFilter *arg_image_filter = NULL;
37

38
STATIC_DESTRUCTOR_REGISTER(arg_what, freep);
3✔
39
STATIC_DESTRUCTOR_REGISTER(arg_where, freep);
3✔
40
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
3✔
41
STATIC_DESTRUCTOR_REGISTER(arg_image_filter, image_filter_freep);
3✔
42

43
static int help(void) {
×
44
        _cleanup_(table_unrefp) Table *options = NULL, *commands = NULL;
×
45
        int r;
×
46

47
        r = option_parser_get_help_table_ns("systemd-mstack", &options);
×
48
        if (r < 0)
×
49
                return r;
50

51
        r = option_parser_get_help_table_full("systemd-mstack", "Commands", &commands);
×
UNCOV
52
        if (r < 0)
×
53
                return r;
54

UNCOV
55
        (void) table_sync_column_widths(0, options, commands);
×
56

UNCOV
57
        help_cmdline("[OPTIONS...] WHAT");
×
UNCOV
58
        help_cmdline("[OPTIONS...] --mount WHAT WHERE");
×
UNCOV
59
        help_cmdline("[OPTIONS...] --umount WHERE");
×
UNCOV
60
        help_abstract("Inspect or apply mount stack.");
×
61

UNCOV
62
        help_section("Commands");
×
UNCOV
63
        r = table_print_or_warn(commands);
×
UNCOV
64
        if (r < 0)
×
65
                return r;
66

UNCOV
67
        help_section("Options");
×
UNCOV
68
        r = table_print_or_warn(options);
×
UNCOV
69
        if (r < 0)
×
70
                return r;
71

UNCOV
72
        help_man_page_reference("systemd-mstack", "1");
×
73
        return 0;
74
}
75

76
static int parse_argv(int argc, char *argv[]) {
7✔
77
        int r;
7✔
78

79
        assert(argc >= 0);
7✔
80
        assert(argv);
7✔
81

82
        OptionParser opts = { argc, argv, .namespace = "systemd-mstack" };
7✔
83

84
        FOREACH_OPTION_OR_RETURN(c, &opts)
32✔
85
                switch (c) {
11✔
86

87
                OPTION_NAMESPACE("systemd-mstack"): {}
5✔
88

89
                OPTION('r', "read-only", NULL, "Mount read-only"):
5✔
90
                        arg_mstack_flags |= MSTACK_RDONLY;
5✔
91
                        break;
5✔
92

UNCOV
93
                OPTION_LONG("mkdir", NULL, "Make mount directory before mounting, if missing"):
×
UNCOV
94
                        arg_mstack_flags |= MSTACK_MKDIR;
×
UNCOV
95
                        break;
×
96

UNCOV
97
                OPTION_LONG("rmdir", NULL, "Remove mount directory after unmounting"):
×
UNCOV
98
                        arg_rmdir = true;
×
UNCOV
99
                        break;
×
100

UNCOV
101
                OPTION_LONG("image-policy", "POLICY", "Specify image dissection policy"):
×
UNCOV
102
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
UNCOV
103
                        if (r < 0)
×
104
                                return r;
105
                        break;
106

UNCOV
107
                OPTION_LONG("image-filter", "FILTER", "Specify image dissection filter"): {
×
UNCOV
108
                        _cleanup_(image_filter_freep) ImageFilter *f = NULL;
×
UNCOV
109
                        r = image_filter_parse(opts.arg, &f);
×
UNCOV
110
                        if (r < 0)
×
UNCOV
111
                                return log_error_errno(r, "Failed to parse image filter expression: %s", opts.arg);
×
112

UNCOV
113
                        image_filter_free(arg_image_filter);
×
UNCOV
114
                        arg_image_filter = TAKE_PTR(f);
×
UNCOV
115
                        break;
×
116
                }
117

UNCOV
118
                OPTION_COMMON_NO_PAGER:
×
UNCOV
119
                        arg_pager_flags |= PAGER_DISABLE;
×
120
                        break;
×
121

UNCOV
122
                OPTION_COMMON_NO_LEGEND:
×
123
                        arg_legend = false;
×
124
                        break;
×
125

126
                OPTION_COMMON_JSON:
×
127
                        r = parse_json_argument(opts.arg, &arg_json_format_flags);
×
128
                        if (r <= 0)
×
129
                                return r;
130
                        break;
131

132
                OPTION_GROUP("Commands"): {}
×
133

134
                OPTION_COMMON_HELP:
×
135
                        return help();
×
136

UNCOV
137
                OPTION_COMMON_VERSION:
×
UNCOV
138
                        return version();
×
139

UNCOV
140
                OPTION('m', "mount", NULL, "Mount the mstack to the specified directory"):
×
UNCOV
141
                        arg_action = ACTION_MOUNT;
×
UNCOV
142
                        break;
×
143

144
                OPTION_SHORT('M', NULL, "Shortcut for --mount --mkdir"):
5✔
145
                        arg_action = ACTION_MOUNT;
5✔
146
                        arg_mstack_flags |= MSTACK_MKDIR;
5✔
147
                        break;
5✔
148

UNCOV
149
                OPTION('u', "umount", NULL, "Unmount the image from the specified directory"):
×
UNCOV
150
                        arg_action = ACTION_UMOUNT;
×
151
                        break;
×
152

153
                OPTION_SHORT('U', NULL, "Shortcut for --umount --rmdir"):
1✔
154
                        arg_action = ACTION_UMOUNT;
1✔
155
                        arg_rmdir = true;
1✔
156
                        break;
1✔
157
                }
158

159
        char **args = option_parser_get_args(&opts);
7✔
160
        size_t n_args = option_parser_get_n_args(&opts);
7✔
161

162
        switch (arg_action) {
7✔
163

164
        case ACTION_INSPECT:
1✔
165
                if (n_args != 1)
1✔
166
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one argument.");
×
167

168
                r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
1✔
169
                if (r < 0)
1✔
170
                        return r;
×
171

172
                break;
173

174
        case ACTION_MOUNT:
5✔
175
                if (n_args != 2)
5✔
UNCOV
176
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected two arguments.");
×
177

178
                r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
5✔
179
                if (r < 0)
5✔
180
                        return r;
181

182
                r = parse_path_argument(args[1], /* suppress_root= */ false, &arg_where);
5✔
183
                if (r < 0)
5✔
UNCOV
184
                        return r;
×
185

186
                break;
187

188
        case ACTION_UMOUNT:
1✔
189
                if (n_args != 1)
1✔
UNCOV
190
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one argument.");
×
191

192
                r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_where);
1✔
193
                if (r < 0)
1✔
194
                        return r;
×
195

196
                break;
197

UNCOV
198
        default:
×
UNCOV
199
                assert_not_reached();
×
200
        }
201

202
        return 1;
203
}
204

UNCOV
205
static int parse_argv_as_mount_helper(int argc, char *argv[]) {
×
206
        const char *mount_options = NULL;
×
UNCOV
207
        bool fake = false;
×
UNCOV
208
        int r;
×
209

210
        /* Implements util-linux "external helper" command line interface, as per mount(8) man page. */
211

212
        OptionParser opts = { argc, argv, .namespace = "mount.mstack" };
×
213

UNCOV
214
        FOREACH_OPTION_OR_RETURN(c, &opts)
×
UNCOV
215
                switch (c) {
×
216

UNCOV
217
                OPTION_NAMESPACE("mount.mstack"): {}
×
218

UNCOV
219
                OPTION_SHORT('f', NULL, NULL):
×
220
                        fake = true;
×
UNCOV
221
                        break;
×
222

UNCOV
223
                OPTION_SHORT('o', "OPTIONS", NULL):
×
UNCOV
224
                        mount_options = opts.arg;
×
UNCOV
225
                        break;
×
226

UNCOV
227
                OPTION_SHORT('t', "TYPE", NULL):
×
UNCOV
228
                        if (!streq(opts.arg, "mstack"))
×
UNCOV
229
                                log_debug("Unexpected file system type '%s', ignoring.", opts.arg);
×
230
                        break;
231

UNCOV
232
                OPTION_SHORT('s', NULL, NULL): {} /* sloppy mount options, fall-through */
×
UNCOV
233
                OPTION_SHORT('n', NULL, NULL): {} /* aka --no-mtab, fall-through */
×
234
                OPTION_SHORT('v', NULL, NULL):    /* aka --verbose */
×
235
                        log_debug("Ignoring option -%c, not implemented.", opts.opt->short_code);
×
236
                        break;
237

UNCOV
238
                OPTION_SHORT('N', "NAMESPACE", NULL): /* aka --namespace= */
×
UNCOV
239
                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
240
                                               "Option -%c is not implemented, refusing.", opts.opt->short_code);
241
                }
242

243
        char **args = option_parser_get_args(&opts);
×
244
        if (option_parser_get_n_args(&opts) != 2)
×
UNCOV
245
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
246
                                       "Expected an image file path and target directory as arguments.");
247

248
        for (const char *p = mount_options;;) {
×
249
                _cleanup_free_ char *word = NULL;
×
250

251
                r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE);
×
252
                if (r < 0)
×
253
                        return log_error_errno(r, "Failed to extract mount option: %m");
×
UNCOV
254
                if (r == 0)
×
255
                        break;
256

257
                if (streq(word, "ro"))
×
UNCOV
258
                        SET_FLAG(arg_mstack_flags, MSTACK_RDONLY, true);
×
259
                else if (streq(word, "rw"))
×
260
                        SET_FLAG(arg_mstack_flags, MSTACK_RDONLY, false);
×
261
                else
UNCOV
262
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
263
                                               "Unknown mount option '%s'.", word);
264
        }
265

UNCOV
266
        if (fake)
×
267
                return 0;
268

UNCOV
269
        r = parse_path_argument(args[0], /* suppress_root= */ false, &arg_what);
×
UNCOV
270
        if (r < 0)
×
271
                return r;
272

UNCOV
273
        r = parse_path_argument(args[1], /* suppress_root= */ false, &arg_where);
×
UNCOV
274
        if (r < 0)
×
275
                return r;
276

UNCOV
277
        arg_action = ACTION_MOUNT;
×
278
        return 1;
×
279
}
280

281
static int inspect_mstack(void) {
1✔
282
        _cleanup_(mstack_freep) MStack *mstack = NULL;
1✔
283
        int r;
1✔
284

285
        assert(arg_what);
1✔
286

287
        r = mstack_load(arg_what, /* dir_fd= */ -EBADF, &mstack);
1✔
288
        if (r < 0)
1✔
UNCOV
289
                return log_debug_errno(r, "Failed to load .mstack/ directory '%s': %m", arg_what);
×
290

291
        _cleanup_(table_unrefp) Table *t = NULL;
1✔
292

293
        t = table_new("type", "name", "image", "what", "where", "sort");
1✔
294
        if (!t)
1✔
UNCOV
295
                return log_oom();
×
296

297
        table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
1✔
298

299
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
4✔
300
                _cleanup_free_ char *w = NULL;
3✔
301
                r = fd_get_path(m->what_fd, &w);
3✔
302
                if (r < 0)
3✔
303
                        return log_error_errno(r, "Failed to get path of what file descriptor: %m");
×
304

305
                r = table_add_many(
6✔
306
                                t,
307
                                TABLE_STRING, mstack_mount_type_to_string(m->mount_type),
308
                                TABLE_STRING, m->what,
309
                                TABLE_STRING, image_type_to_string(m->image_type),
310
                                TABLE_PATH, w,
311
                                TABLE_PATH, m->where ?: ((mstack->root_mount && mstack->root_mount != m) ? "/usr" : "/"),
312
                                TABLE_STRING, m->sort_key);
313
                if (r < 0)
3✔
UNCOV
314
                        return table_log_add_error(r);
×
315
        }
316

317
        return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
1✔
318
}
319

320
static int mount_mstack(void) {
5✔
321
        int r;
5✔
322

323
        assert(arg_what);
5✔
324
        assert(arg_where);
5✔
325

326
        r = mstack_apply(
5✔
327
                        arg_what,
328
                        /* dir_fd= */ -EBADF,
329
                        arg_where,
330
                        /* temp_mount_dir= */ NULL,  /* auto-create temporary directory */
331
                        /* mountfsd_link= */ NULL,
332
                        /* userns_fd= */ -EBADF,
333
                        arg_image_policy,
334
                        arg_image_filter,
335
                        arg_mstack_flags,
336
                        /* ret_root_fd= */ NULL);
337
         if (r < 0)
1✔
UNCOV
338
                 return log_error_errno(r, "Failed to apply .mstack/ directory '%s': %m", arg_what);
×
339

340
         return 0;
341
}
342

343
static int umount_mstack(void) {
1✔
344
        int r;
1✔
345

346
        assert(arg_where);
1✔
347

348
        _cleanup_free_ char *canonical = NULL;
1✔
349
        _cleanup_close_ int fd = chase_and_open(arg_where, /* root= */ NULL, /* chase_flags= */ 0, O_DIRECTORY, &canonical);
2✔
350
        if (fd == -ENOTDIR)
1✔
UNCOV
351
                return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "'%s' is not a directory", arg_where);
×
352
        if (fd < 0)
1✔
UNCOV
353
                return log_error_errno(fd, "Failed to resolve path '%s': %m", arg_where);
×
354

355
        r = is_mount_point_at(fd, /* path= */ NULL, /* flags= */ 0);
1✔
356
        if (r == 0)
1✔
UNCOV
357
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is not a mount point", canonical);
×
358
        if (r < 0)
1✔
UNCOV
359
                return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical);
×
360

361
        fd = safe_close(fd);
1✔
362

363
        r = umount_recursive(canonical, 0);
1✔
364
        if (r < 0)
1✔
UNCOV
365
                return log_error_errno(r, "Failed to unmount '%s': %m", canonical);
×
366

367
        if (arg_rmdir) {
1✔
368
                r = RET_NERRNO(rmdir(canonical));
1✔
UNCOV
369
                if (r < 0)
×
UNCOV
370
                        return log_error_errno(r, "Failed to remove mount directory '%s': %m", canonical);
×
371
        }
372

373
        return 0;
374
}
375

376
static int run(int argc, char *argv[]) {
7✔
377
        int r;
7✔
378

379
        log_setup();
7✔
380

381
        if (invoked_as(argv, "mount.mstack"))
7✔
UNCOV
382
                r = parse_argv_as_mount_helper(argc, argv);
×
383
        else
384
                r = parse_argv(argc, argv);
7✔
385
        if (r <= 0)
7✔
386
                return r;
387

388
        switch (arg_action) {
7✔
389

390
        case ACTION_INSPECT:
1✔
391
                return inspect_mstack();
1✔
392

393
        case ACTION_MOUNT:
5✔
394
                return mount_mstack();
5✔
395

396
        case ACTION_UMOUNT:
1✔
397
                return umount_mstack();
1✔
398

399
        default:
×
UNCOV
400
                assert_not_reached();
×
401
        }
402
}
403

404
DEFINE_MAIN_FUNCTION(run);
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc