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

systemd / systemd / 13380515387

17 Feb 2025 09:20PM UTC coverage: 71.822% (+0.1%) from 71.714%
13380515387

push

github

DaanDeMeyer
ukify: print all remaining log-like output to stderr

We want to be able to capture stdout for json and such, so convert
all remaining logging to stderr.

293883 of 409184 relevant lines covered (71.82%)

716959.33 hits per line

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

47.29
/src/shared/mkfs-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/mount.h>
4
#include <unistd.h>
5

6
#include "dirent-util.h"
7
#include "fd-util.h"
8
#include "fileio.h"
9
#include "fs-util.h"
10
#include "id128-util.h"
11
#include "mkfs-util.h"
12
#include "mount-util.h"
13
#include "mountpoint-util.h"
14
#include "path-util.h"
15
#include "process-util.h"
16
#include "recurse-dir.h"
17
#include "rm-rf.h"
18
#include "stat-util.h"
19
#include "stdio-util.h"
20
#include "string-util.h"
21
#include "tmpfile-util.h"
22
#include "utf8.h"
23

24
int mkfs_exists(const char *fstype) {
69✔
25
        const char *mkfs;
69✔
26
        int r;
69✔
27

28
        assert(fstype);
69✔
29

30
        if (STR_IN_SET(fstype, "auto", "swap")) /* these aren't real file system types, refuse early */
69✔
31
                return -EINVAL;
×
32

33
        mkfs = strjoina("mkfs.", fstype);
345✔
34
        if (!filename_is_valid(mkfs)) /* refuse file system types with slashes and similar */
69✔
35
                return -EINVAL;
36

37
        r = find_executable(mkfs, NULL);
69✔
38
        if (r == -ENOENT)
69✔
39
                return false;
40
        if (r < 0)
69✔
41
                return r;
×
42

43
        return true;
44
}
45

46
int mkfs_supports_root_option(const char *fstype) {
118✔
47
        return fstype_is_ro(fstype) || STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "vfat", "xfs");
118✔
48
}
49

50
static int mangle_linux_fs_label(const char *s, size_t max_len, char **ret) {
65✔
51
        /* Not more than max_len bytes (12 or 16) */
52

53
        assert(s);
65✔
54
        assert(max_len > 0);
65✔
55
        assert(ret);
65✔
56

57
        const char *q;
58
        char *ans;
59

60
        for (q = s; *q;) {
737✔
61
                int l;
672✔
62

63
                l = utf8_encoded_valid_unichar(q, SIZE_MAX);
672✔
64
                if (l < 0)
672✔
65
                        return l;
66

67
                if ((size_t) (q - s + l) > max_len)
672✔
68
                        break;
69
                q += l;
672✔
70
        }
71

72
        ans = memdup_suffix0(s, q - s);
65✔
73
        if (!ans)
65✔
74
                return -ENOMEM;
75

76
        *ret = ans;
65✔
77
        return 0;
65✔
78
}
79

80
static int mangle_fat_label(const char *s, char **ret) {
16✔
81
        assert(s);
16✔
82

83
        _cleanup_free_ char *q = NULL;
16✔
84
        int r;
16✔
85

86
        r = utf8_to_ascii(s, '_', &q);
16✔
87
        if (r < 0)
16✔
88
                return r;
89

90
        /* Classic FAT only allows 11 character uppercase labels */
91
        strshorten(q, 11);
16✔
92
        ascii_strupper(q);
16✔
93

94
        /* mkfs.vfat: Labels with characters *?.,;:/\|+=<>[]" are not allowed.
95
         * Let's also replace any control chars. */
96
        for (char *p = q; *p; p++)
130✔
97
                if (strchr("*?.,;:/\\|+=<>[]\"", *p) || char_is_cc(*p))
114✔
98
                        *p = '_';
×
99

100
        *ret = TAKE_PTR(q);
16✔
101
        return 0;
16✔
102
}
103

104
static int do_mcopy(const char *node, const char *root) {
2✔
105
        _cleanup_free_ char *mcopy = NULL;
2✔
106
        _cleanup_strv_free_ char **argv = NULL;
×
107
        _cleanup_free_ DirectoryEntries *de = NULL;
4✔
108
        int r;
2✔
109

110
        assert(node);
2✔
111
        assert(root);
2✔
112

113
        /* Return early if there's nothing to copy. */
114
        if (dir_is_empty(root, /*ignore_hidden_or_backup=*/ false))
2✔
115
                return 0;
116

117
        r = find_executable("mcopy", &mcopy);
2✔
118
        if (r == -ENOENT)
2✔
119
                return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Could not find mcopy binary.");
×
120
        if (r < 0)
2✔
121
                return log_error_errno(r, "Failed to determine whether mcopy binary exists: %m");
×
122

123
        argv = strv_new(mcopy, "-s", "-p", "-Q", "-m", "-i", node);
2✔
124
        if (!argv)
2✔
125
                return log_oom();
×
126

127
        /* mcopy copies the top level directory instead of everything in it so we have to pass all
128
         * the subdirectories to mcopy instead to end up with the correct directory structure. */
129

130
        r = readdir_all_at(AT_FDCWD, root, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE, &de);
2✔
131
        if (r < 0)
2✔
132
                return log_error_errno(r, "Failed to read '%s' contents: %m", root);
×
133

134
        for (size_t i = 0; i < de->n_entries; i++) {
4✔
135
                _cleanup_free_ char *p = NULL;
×
136

137
                p = path_join(root, de->entries[i]->d_name);
2✔
138
                if (!p)
2✔
139
                        return log_oom();
×
140

141
                if (!IN_SET(de->entries[i]->d_type, DT_REG, DT_DIR)) {
2✔
142
                        log_debug("%s is not a file/directory which are the only file types supported by vfat, ignoring", p);
×
143
                        continue;
×
144
                }
145

146
                if (strv_consume(&argv, TAKE_PTR(p)) < 0)
2✔
147
                        return log_oom();
×
148
        }
149

150
        if (strv_extend(&argv, "::") < 0)
2✔
151
                return log_oom();
×
152

153
        r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, NULL);
2✔
154
        if (r < 0)
4✔
155
                return r;
156
        if (r == 0) {
4✔
157
                /* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling
158
                 * the stricter mcopy checks using MTOOLS_SKIP_CHECK. */
159
                execve(mcopy, argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1", "TZ=UTC", strv_find_prefix(environ, "SOURCE_DATE_EPOCH=")));
×
160

161
                log_error_errno(errno, "Failed to execute mcopy: %m");
×
162

163
                _exit(EXIT_FAILURE);
×
164
        }
165

166
        return 0;
167
}
168

169
typedef struct ProtofileData {
170
        FILE *file;
171
        bool has_filename_with_spaces;
172
        const char *tmpdir;
173
} ProtofileData;
174

175
static int protofile_print_item(
×
176
                RecurseDirEvent event,
177
                const char *path,
178
                int dir_fd,
179
                int inode_fd,
180
                const struct dirent *de,
181
                const struct statx *sx,
182
                void *userdata) {
183

184
        ProtofileData *data = ASSERT_PTR(userdata);
×
185
        _cleanup_free_ char *copy = NULL;
×
186
        int r;
×
187

188
        if (event == RECURSE_DIR_LEAVE) {
×
189
                fputs("$\n", data->file);
×
190
                return 0;
191
        }
192

193
        if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
×
194
                return RECURSE_DIR_CONTINUE;
195

196
        char type = S_ISDIR(sx->stx_mode)  ? 'd' :
×
197
                    S_ISREG(sx->stx_mode)  ? '-' :
198
                    S_ISLNK(sx->stx_mode)  ? 'l' :
199
                    S_ISFIFO(sx->stx_mode) ? 'p' :
200
                    S_ISBLK(sx->stx_mode)  ? 'b' :
201
                    S_ISCHR(sx->stx_mode)  ? 'c' : 0;
202
        if (type == 0)
203
                return RECURSE_DIR_CONTINUE;
204

205
        /* The protofile format does not support spaces in filenames as whitespace is used as a token
206
         * delimiter. To work around this limitation, mkfs.xfs allows escaping whitespace by using the /
207
         * character (which isn't allowed in filenames and as such can be used to escape whitespace). See
208
         * https://lore.kernel.org/linux-xfs/20230222090303.h6tujm7y32gjhgal@andromeda/T/#m8066b3e7d62a080ee7434faac4861d944e64493b
209
         * for more information. */
210

211
        if (strchr(de->d_name, ' ')) {
×
212
                copy = strdup(de->d_name);
×
213
                if (!copy)
×
214
                        return log_oom();
×
215

216
                string_replace_char(copy, ' ', '/');
×
217
                data->has_filename_with_spaces = true;
×
218
        }
219

220
        fprintf(data->file, "%s %c%c%c%03o "UID_FMT" "GID_FMT" ",
×
221
                copy ?: de->d_name,
222
                type,
223
                sx->stx_mode & S_ISUID ? 'u' : '-',
224
                sx->stx_mode & S_ISGID ? 'g' : '-',
225
                (unsigned) (sx->stx_mode & 0777),
×
226
                sx->stx_uid, sx->stx_gid);
×
227

228
        if (S_ISREG(sx->stx_mode)) {
×
229
                _cleanup_free_ char *p = NULL;
×
230

231
                /* While we can escape whitespace in the filename, we cannot escape whitespace in the source
232
                 * path, so hack around that by creating a symlink to the path in a temporary directory and
233
                 * using the symlink as the source path instead. */
234

235
                if (strchr(path, ' ')) {
×
236
                        r = tempfn_random_child(data->tmpdir, "mkfs-xfs", &p);
×
237
                        if (r < 0)
×
238
                                return log_error_errno(r, "Failed to generate random child name in %s: %m", data->tmpdir);
×
239

240
                        if (symlink(path, p) < 0)
×
241
                                return log_error_errno(errno, "Failed to symlink %s to %s: %m", p, path);
×
242
                }
243

244
                fputs(p ?: path, data->file);
×
245
        } else if (S_ISLNK(sx->stx_mode)) {
×
246
                _cleanup_free_ char *p = NULL;
×
247

248
                r = readlinkat_malloc(dir_fd, de->d_name, &p);
×
249
                if (r < 0)
×
250
                        return log_error_errno(r, "Failed to read symlink %s: %m", path);
×
251

252
                /* If we have a symlink to a path with whitespace in it, we're out of luck, as there's no way
253
                 * to encode that in the mkfs.xfs protofile format. */
254

255
                if (strchr(p, ' '))
×
256
                        return log_error_errno(r, "Symlinks to paths containing whitespace are not supported by mkfs.xfs: %m");
×
257

258
                fputs(p, data->file);
×
259
        } else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))
×
260
                fprintf(data->file, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor);
×
261

262
        fputc('\n', data->file);
×
263

264
        return RECURSE_DIR_CONTINUE;
265
}
266

267
static int make_protofile(const char *root, char **ret_path, bool *ret_has_filename_with_spaces, char **ret_tmpdir) {
×
268
        _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
×
269
        _cleanup_fclose_ FILE *f = NULL;
×
270
        _cleanup_(unlink_and_freep) char *p = NULL;
×
271
        struct ProtofileData data = {};
×
272
        const char *vt;
×
273
        int r;
×
274

275
        assert(ret_path);
×
276
        assert(ret_has_filename_with_spaces);
×
277
        assert(ret_tmpdir);
×
278

279
        r = var_tmp_dir(&vt);
×
280
        if (r < 0)
×
281
                return log_error_errno(r, "Failed to get persistent temporary directory: %m");
×
282

283
        r = fopen_temporary_child(vt, &f, &p);
×
284
        if (r < 0)
×
285
                return log_error_errno(r, "Failed to open temporary file: %m");
×
286

287
        /* Explicitly use /tmp here because this directory cannot have spaces its path. */
288
        r = mkdtemp_malloc("/tmp/systemd-mkfs-XXXXXX", &tmpdir);
×
289
        if (r < 0)
×
290
                return log_error_errno(r, "Failed to create temporary directory: %m");
×
291

292
        data.file = f;
×
293
        data.tmpdir = tmpdir;
×
294

295
        fputs("/\n"
×
296
              "0 0\n"
297
              "d--755 0 0\n", f);
298

299
        r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID, UINT_MAX,
×
300
                           RECURSE_DIR_SORT, protofile_print_item, &data);
301
        if (r < 0)
×
302
                return log_error_errno(r, "Failed to recurse through %s: %m", root);
×
303

304
        fputs("$\n", f);
×
305

306
        r = fflush_and_check(f);
×
307
        if (r < 0)
×
308
                return log_error_errno(r, "Failed to flush %s: %m", p);
×
309

310
        *ret_path = TAKE_PTR(p);
×
311
        *ret_has_filename_with_spaces = data.has_filename_with_spaces;
×
312
        *ret_tmpdir = TAKE_PTR(tmpdir);
×
313

314
        return 0;
×
315
}
316

317
int make_filesystem(
102✔
318
                const char *node,
319
                const char *fstype,
320
                const char *label,
321
                const char *root,
322
                sd_id128_t uuid,
323
                bool discard,
324
                bool quiet,
325
                uint64_t sector_size,
326
                char *compression,
327
                char *compression_level,
328
                char * const *extra_mkfs_args) {
329

330
        _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
102✔
331
        _cleanup_strv_free_ char **argv = NULL, **env = NULL;
102✔
332
        _cleanup_(rm_rf_physical_and_freep) char *protofile_tmpdir = NULL;
102✔
333
        _cleanup_(unlink_and_freep) char *protofile = NULL;
102✔
334
        char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {};
102✔
335
        int stdio_fds[3] = { -EBADF, STDERR_FILENO, STDERR_FILENO};
102✔
336
        ForkFlags flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|
102✔
337
                        FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG;
338
        int r;
102✔
339

340
        assert(node);
102✔
341
        assert(fstype);
102✔
342
        assert(label);
102✔
343

344
        if (fstype_is_ro(fstype) && !root)
102✔
345
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
346
                                       "Cannot generate read-only filesystem %s without a source tree.",
347
                                       fstype);
348

349
        if (streq(fstype, "swap")) {
102✔
350
                if (root)
12✔
351
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
352
                                               "A swap filesystem can't be populated, refusing");
353
                r = find_executable("mkswap", &mkfs);
12✔
354
                if (r == -ENOENT)
12✔
355
                        return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkswap binary not available.");
×
356
                if (r < 0)
12✔
357
                        return log_error_errno(r, "Failed to determine whether mkswap binary exists: %m");
×
358
        } else if (streq(fstype, "squashfs")) {
90✔
359
                r = find_executable("mksquashfs", &mkfs);
13✔
360
                if (r == -ENOENT)
13✔
361
                        return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available.");
×
362
                if (r < 0)
13✔
363
                        return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m");
×
364

365
        } else if (streq(fstype, "erofs")) {
77✔
366
                r = find_executable("mkfs.erofs", &mkfs);
8✔
367
                if (r == -ENOENT)
8✔
368
                        return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs.erofs binary not available.");
×
369
                if (r < 0)
8✔
370
                        return log_error_errno(r, "Failed to determine whether mkfs.erofs binary exists: %m");
×
371

372
        } else if (fstype_is_ro(fstype)) {
69✔
373
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
374
                                                       "Don't know how to create read-only file system '%s', refusing.",
375
                                                       fstype);
376
        } else {
377
                if (root && !mkfs_supports_root_option(fstype))
69✔
378
                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
379
                                               "Populating with source tree is not supported for %s", fstype);
380
                r = mkfs_exists(fstype);
69✔
381
                if (r < 0)
69✔
382
                        return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype);
×
383
                if (r == 0)
69✔
384
                        return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for %s is not available.", fstype);
×
385

386
                mkfs = strjoin("mkfs.", fstype);
69✔
387
                if (!mkfs)
69✔
388
                        return log_oom();
×
389
        }
390

391
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "xfs", "swap")) {
102✔
392
                size_t max_len =
130✔
393
                        streq(fstype, "xfs") ? 12 :
65✔
394
                        streq(fstype, "swap") ? 15 :
65✔
395
                        16;
396

397
                r = mangle_linux_fs_label(label, max_len, &mangled_label);
65✔
398
                if (r < 0)
65✔
399
                        return log_error_errno(r, "Failed to determine volume label from string \"%s\": %m", label);
×
400
                label = mangled_label;
65✔
401

402
        } else if (streq(fstype, "vfat")) {
37✔
403
                r = mangle_fat_label(label, &mangled_label);
16✔
404
                if (r < 0)
16✔
405
                        return log_error_errno(r, "Failed to determine FAT label from string \"%s\": %m", label);
×
406
                label = mangled_label;
16✔
407

408
                xsprintf(vol_id, "%08" PRIx32,
16✔
409
                         ((uint32_t) uuid.bytes[0] << 24) |
410
                         ((uint32_t) uuid.bytes[1] << 16) |
411
                         ((uint32_t) uuid.bytes[2] << 8) |
412
                         ((uint32_t) uuid.bytes[3])); /* Take first 32 bytes of UUID */
413
        }
414

415
        if (isempty(vol_id))
102✔
416
                assert_se(sd_id128_to_uuid_string(uuid, vol_id));
86✔
417

418
        /* When changing this conditional, also adjust the log statement below. */
419
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4")) {
102✔
420
                argv = strv_new(mkfs,
53✔
421
                                "-L", label,
422
                                "-U", vol_id,
423
                                "-I", "256",
424
                                "-m", "0",
425
                                "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
426
                                "-b", "4096",
427
                                "-T", "default");
428
                if (!argv)
53✔
429
                        return log_oom();
×
430

431
                if (root && strv_extend_many(&argv, "-d", root) < 0)
53✔
432
                        return log_oom();
×
433

434
                if (quiet && strv_extend(&argv, "-q") < 0)
53✔
435
                        return log_oom();
×
436

437
                if (strv_extend(&argv, node) < 0)
53✔
438
                        return log_oom();
×
439

440
                if (sector_size > 0) {
53✔
441
                        if (strv_extend(&env, "MKE2FS_DEVICE_SECTSIZE") < 0)
53✔
442
                                        return log_oom();
×
443

444
                        if (strv_extendf(&env, "%"PRIu64, sector_size) < 0)
53✔
445
                                return log_oom();
×
446
                }
447

448
        } else if (streq(fstype, "btrfs")) {
49✔
449
                argv = strv_new(mkfs,
×
450
                                "-L", label,
451
                                "-U", vol_id);
452
                if (!argv)
×
453
                        return log_oom();
×
454

455
                if (!discard && strv_extend(&argv, "--nodiscard") < 0)
×
456
                        return log_oom();
×
457

458
                if (root && strv_extend_many(&argv, "-r", root) < 0)
×
459
                        return log_oom();
×
460

461
                if (quiet && strv_extend(&argv, "-q") < 0)
×
462
                        return log_oom();
×
463

464
                if (compression) {
×
465
                        _cleanup_free_ char *c = NULL;
×
466

467
                        c = strdup(compression);
×
468
                        if (!c)
×
469
                                return log_oom();
×
470

471
                        if (compression_level && !strextend(&c, ":", compression_level))
×
472
                                return log_oom();
×
473

474
                        if (strv_extend_many(&argv, "--compress", c) < 0)
×
475
                                return log_oom();
×
476
                }
477

478
                /* mkfs.btrfs unconditionally warns about several settings changing from v5.15 onwards which
479
                 * isn't silenced by "-q", so let's redirect stdout to /dev/null as well. */
480
                if (quiet)
×
481
                        stdio_fds[1] = -EBADF;
×
482

483
                /* mkfs.btrfs expects a sector size of at least 4k bytes. */
484
                if (sector_size > 0 && strv_extendf(&argv, "--sectorsize=%"PRIu64, MAX(sector_size, 4 * U64_KB)) < 0)
×
485
                        return log_oom();
×
486

487
                if (strv_extend(&argv, node) < 0)
×
488
                        return log_oom();
×
489

490
        } else if (streq(fstype, "f2fs")) {
49✔
491
                argv = strv_new(mkfs,
×
492
                                "-g",  /* "default options" */
493
                                "-f",  /* force override, without this it doesn't seem to want to write to an empty partition */
494
                                "-l", label,
495
                                "-U", vol_id,
496
                                "-t", one_zero(discard));
497
                if (!argv)
×
498
                        return log_oom();
×
499

500
                if (quiet && strv_extend(&argv, "-q") < 0)
×
501
                        return log_oom();
×
502

503
                if (sector_size > 0) {
×
504
                        if (strv_extend(&argv, "-w") < 0)
×
505
                                return log_oom();
×
506

507
                        if (strv_extendf(&argv, "%"PRIu64, sector_size) < 0)
×
508
                                return log_oom();
×
509
                }
510

511
                if (strv_extend(&argv, node) < 0)
×
512
                        return log_oom();
×
513

514
        } else if (streq(fstype, "xfs")) {
49✔
515
                const char *j;
×
516

517
                j = strjoina("uuid=", vol_id);
×
518

519
                argv = strv_new(mkfs,
×
520
                                "-L", label,
521
                                "-m", j,
522
                                "-m", "reflink=1");
523
                if (!argv)
×
524
                        return log_oom();
×
525

526
                if (!discard && strv_extend(&argv, "-K") < 0)
×
527
                        return log_oom();
×
528

529
                if (root) {
×
530
                        bool has_filename_with_spaces = false;
×
531
                        _cleanup_free_ char *protofile_with_opt = NULL;
×
532

533
                        r = make_protofile(root, &protofile, &has_filename_with_spaces, &protofile_tmpdir);
×
534
                        if (r < 0)
×
535
                                return r;
536

537
                        /* Gross hack to make mkfs.xfs interpret slashes as spaces so we can encode filenames
538
                         * with spaces in the protofile format. */
539
                        if (has_filename_with_spaces)
×
540
                                protofile_with_opt = strjoin("slashes_are_spaces=1,", protofile);
×
541
                        else
542
                                protofile_with_opt = strdup(protofile);
×
543
                        if (!protofile_with_opt)
×
544
                                return -ENOMEM;
545

546
                        if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0)
×
547
                                return log_oom();
×
548
                }
549

550
                if (sector_size > 0) {
×
551
                        if (strv_extend(&argv, "-s") < 0)
×
552
                                return log_oom();
×
553

554
                        if (strv_extendf(&argv, "size=%"PRIu64, sector_size) < 0)
×
555
                                return log_oom();
×
556
                }
557

558
                if (quiet && strv_extend(&argv, "-q") < 0)
×
559
                        return log_oom();
×
560

561
                if (strv_extend(&argv, node) < 0)
×
562
                        return log_oom();
×
563

564
        } else if (streq(fstype, "vfat")) {
49✔
565

566
                argv = strv_new(mkfs,
16✔
567
                                "-i", vol_id,
568
                                "-n", label,
569
                                "-F", "32");  /* yes, we force FAT32 here */
570
                if (!argv)
16✔
571
                        return log_oom();
×
572

573
                if (sector_size > 0) {
16✔
574
                        if (strv_extend(&argv, "-S") < 0)
16✔
575
                                return log_oom();
×
576

577
                        if (strv_extendf(&argv, "%"PRIu64, sector_size) < 0)
16✔
578
                                return log_oom();
×
579
                }
580

581
                if (strv_extend(&argv, node) < 0)
16✔
582
                        return log_oom();
×
583

584
                /* mkfs.vfat does not have a --quiet option so let's redirect stdout to /dev/null instead. */
585
                if (quiet)
16✔
586
                        stdio_fds[1] = -EBADF;
×
587

588
        } else if (streq(fstype, "swap")) {
33✔
589
                /* TODO: add --quiet once util-linux v2.38 is available everywhere. */
590

591
                argv = strv_new(mkfs,
12✔
592
                                "-L", label,
593
                                "-U", vol_id,
594
                                node);
595
                if (!argv)
12✔
596
                        return log_oom();
×
597

598
                if (quiet)
12✔
599
                        stdio_fds[1] = -EBADF;
×
600

601
        } else if (streq(fstype, "squashfs")) {
21✔
602

603
                argv = strv_new(mkfs,
13✔
604
                                root, node, /* mksquashfs expects its arguments before the options. */
605
                                "-noappend");
606
                if (!argv)
13✔
607
                        return log_oom();
×
608

609
                if (compression) {
13✔
610
                        if (strv_extend_many(&argv, "-comp", compression) < 0)
2✔
611
                                return log_oom();
×
612

613
                        if (compression_level && strv_extend_many(&argv, "-Xcompression-level", compression_level) < 0)
2✔
614
                                return log_oom();
×
615
                }
616

617
                /* mksquashfs -quiet option is pretty new so let's redirect stdout to /dev/null instead. */
618
                if (quiet)
13✔
619
                        stdio_fds[1] = -EBADF;
×
620

621
        } else if (streq(fstype, "erofs")) {
8✔
622
                argv = strv_new(mkfs,
8✔
623
                                "-U", vol_id);
624
                if (!argv)
8✔
625
                        return log_oom();
×
626

627
                if (quiet && strv_extend(&argv, "--quiet") < 0)
8✔
628
                        return log_oom();
×
629

630
                if (compression) {
8✔
631
                        _cleanup_free_ char *c = NULL;
2✔
632

633
                        c = strjoin("-z", compression);
2✔
634
                        if (!c)
2✔
635
                                return log_oom();
×
636

637
                        if (compression_level && !strextend(&c, ",level=", compression_level))
2✔
638
                                return log_oom();
×
639

640
                        if (strv_extend(&argv, c) < 0)
2✔
641
                                return log_oom();
×
642
                }
643

644
                if (strv_extend_many(&argv, node, root) < 0)
8✔
645
                        return log_oom();
×
646

647
        } else {
648
                /* Generic fallback for all other file systems */
649
                argv = strv_new(mkfs, node);
×
650
                if (!argv)
×
651
                        return log_oom();
×
652
        }
653

654
        if (extra_mkfs_args && strv_extend_strv(&argv, extra_mkfs_args, false) < 0)
102✔
655
                return log_oom();
×
656

657
        if (streq(fstype, "btrfs")) {
102✔
658
                struct stat st;
×
659

660
                if (stat(node, &st) < 0)
×
661
                        return log_error_errno(r, "Failed to stat '%s': %m", node);
×
662

663
                if (S_ISBLK(st.st_mode))
×
664
                        flags |= FORK_NEW_MOUNTNS;
×
665
        }
666

667
        if (DEBUG_LOGGING) {
102✔
668
                _cleanup_free_ char *j = NULL;
102✔
669

670
                j = strv_join(argv, " ");
102✔
671
                log_debug("Executing mkfs command: %s", strna(j));
102✔
672
        }
673

674
        r = safe_fork_full(
102✔
675
                        "(mkfs)",
676
                        stdio_fds,
677
                        /*except_fds=*/ NULL,
678
                        /*n_except_fds=*/ 0,
679
                        flags,
680
                        /*ret_pid=*/ NULL);
681
        if (r < 0)
171✔
682
                return r;
683
        if (r == 0) {
171✔
684
                /* Child */
685

686
                STRV_FOREACH_PAIR(k, v, env)
99✔
687
                        if (setenv(*k, *v, /* replace = */ true) < 0) {
30✔
688
                                log_error_errno(r, "Failed to set %s=%s environment variable: %m", *k, *v);
×
689
                                _exit(EXIT_FAILURE);
690
                        }
691

692
                /* mkfs.btrfs refuses to operate on block devices with mounted partitions, even if operating
693
                 * on unformatted free space, so let's trick it and other mkfs tools into thinking no
694
                 * partitions are mounted. See https://github.com/kdave/btrfs-progs/issues/640 for more
695
                 ° information. */
696
                 if (flags & FORK_NEW_MOUNTNS)
69✔
697
                        (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/null", "/proc/self/mounts", NULL, MS_BIND, NULL);
×
698

699
                execvp(mkfs, argv);
×
700

701
                log_error_errno(errno, "Failed to execute %s: %m", mkfs);
×
702

703
                _exit(EXIT_FAILURE);
×
704
        }
705

706
        if (root && streq(fstype, "vfat")) {
102✔
707
                r = do_mcopy(node, root);
2✔
708
                if (r < 0)
2✔
709
                        return r;
710
        }
711

712
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap"))
102✔
713
                log_info("%s successfully formatted as %s (label \"%s\", uuid %s)",
81✔
714
                         node, fstype, label, vol_id);
715
        else if (streq(fstype, "erofs"))
21✔
716
                log_info("%s successfully formatted as %s (uuid %s, no label)",
8✔
717
                         node, fstype, vol_id);
718
        else
719
                log_info("%s successfully formatted as %s (no label or uuid specified)",
13✔
720
                         node, fstype);
721

722
        return 0;
102✔
723
}
724

725
int mkfs_options_from_env(const char *component, const char *fstype, char ***ret) {
85✔
726
        _cleanup_strv_free_ char **l = NULL;
85✔
727
        const char *e;
85✔
728
        char *n;
85✔
729

730
        assert(component);
85✔
731
        assert(fstype);
85✔
732
        assert(ret);
85✔
733

734
        n = strjoina("SYSTEMD_", component, "_MKFS_OPTIONS_", fstype);
765✔
735
        e = getenv(ascii_strupper(n));
85✔
736
        if (e) {
85✔
737
                l = strv_split(e, NULL);
×
738
                if (!l)
×
739
                        return -ENOMEM;
740
        }
741

742
        *ret = TAKE_PTR(l);
85✔
743
        return 0;
85✔
744
}
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