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

systemd / systemd / 20684862027

03 Jan 2026 10:26PM UTC coverage: 72.702% (+0.03%) from 72.677%
20684862027

push

github

web-flow
core/dynamic-user: two trivial modernizations (#40264)

2 of 4 new or added lines in 1 file covered. (50.0%)

215 existing lines in 37 files now uncovered.

310139 of 426587 relevant lines covered (72.7%)

1143601.25 hits per line

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

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

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

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

26
int mkfs_exists(const char *fstype) {
127✔
27
        const char *mkfs;
127✔
28
        int r;
127✔
29

30
        assert(fstype);
127✔
31

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

35
        mkfs = strjoina("mkfs.", fstype);
635✔
36
        if (!filename_is_valid(mkfs)) /* refuse file system types with slashes and similar */
127✔
37
                return -EINVAL;
38

39
        r = find_executable(mkfs, NULL);
127✔
40
        if (r == -ENOENT)
127✔
41
                return false;
42
        if (r < 0)
127✔
43
                return r;
×
44

45
        return true;
46
}
47

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

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

55
        assert(s);
99✔
56
        assert(max_len > 0);
99✔
57
        assert(ret);
99✔
58

59
        const char *q;
60
        char *ans;
61

62
        for (q = s; *q;) {
1,065✔
63
                int l;
966✔
64

65
                l = utf8_encoded_valid_unichar(q, SIZE_MAX);
966✔
66
                if (l < 0)
966✔
67
                        return l;
68

69
                if ((size_t) (q - s + l) > max_len)
966✔
70
                        break;
71
                q += l;
966✔
72
        }
73

74
        ans = memdup_suffix0(s, q - s);
99✔
75
        if (!ans)
99✔
76
                return -ENOMEM;
77

78
        *ret = ans;
99✔
79
        return 0;
99✔
80
}
81

82
static int mangle_fat_label(const char *s, char **ret) {
36✔
83
        assert(s);
36✔
84

85
        _cleanup_free_ char *q = NULL;
36✔
86
        int r;
36✔
87

88
        r = utf8_to_ascii(s, '_', &q);
36✔
89
        if (r < 0)
36✔
90
                return r;
91

92
        /* Classic FAT only allows 11 character uppercase labels */
93
        strshorten(q, 11);
36✔
94
        ascii_strupper(q);
36✔
95

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

102
        *ret = TAKE_PTR(q);
36✔
103
        return 0;
36✔
104
}
105

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

112
        assert(node);
2✔
113
        assert(root);
2✔
114

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

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

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

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

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

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

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

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

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

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

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

166
                log_error_errno(errno, "Failed to execute mcopy: %m");
×
167

168
                _exit(EXIT_FAILURE);
×
169
        }
170

171
        return 0;
172
}
173

174
typedef struct ProtofileData {
175
        FILE *file;
176
        bool has_filename_with_spaces;
177
        const char *tmpdir;
178
} ProtofileData;
179

180
static int protofile_print_item(
×
181
                RecurseDirEvent event,
182
                const char *path,
183
                int dir_fd,
184
                int inode_fd,
185
                const struct dirent *de,
186
                const struct statx *sx,
187
                void *userdata) {
188

189
        ProtofileData *data = ASSERT_PTR(userdata);
×
190
        _cleanup_free_ char *copy = NULL;
×
191
        int r;
×
192

193
        if (event == RECURSE_DIR_LEAVE) {
×
194
                fputs("$\n", data->file);
×
195
                return 0;
196
        }
197

198
        if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY))
×
199
                return RECURSE_DIR_CONTINUE;
200

201
        char type = S_ISDIR(sx->stx_mode)  ? 'd' :
×
202
                    S_ISREG(sx->stx_mode)  ? '-' :
203
                    S_ISLNK(sx->stx_mode)  ? 'l' :
204
                    S_ISFIFO(sx->stx_mode) ? 'p' :
205
                    S_ISBLK(sx->stx_mode)  ? 'b' :
206
                    S_ISCHR(sx->stx_mode)  ? 'c' : 0;
207
        if (type == 0)
208
                return RECURSE_DIR_CONTINUE;
209

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

216
        if (strchr(de->d_name, ' ')) {
×
217
                copy = strdup(de->d_name);
×
218
                if (!copy)
×
219
                        return log_oom();
×
220

221
                string_replace_char(copy, ' ', '/');
×
222
                data->has_filename_with_spaces = true;
×
223
        }
224

225
        fprintf(data->file, "%s %c%c%c%03o "UID_FMT" "GID_FMT" ",
×
226
                copy ?: de->d_name,
227
                type,
228
                sx->stx_mode & S_ISUID ? 'u' : '-',
229
                sx->stx_mode & S_ISGID ? 'g' : '-',
230
                (unsigned) (sx->stx_mode & 0777),
×
231
                sx->stx_uid, sx->stx_gid);
×
232

233
        if (S_ISREG(sx->stx_mode)) {
×
234
                _cleanup_free_ char *p = NULL;
×
235

236
                /* While we can escape whitespace in the filename, we cannot escape whitespace in the source
237
                 * path, so hack around that by creating a symlink to the path in a temporary directory and
238
                 * using the symlink as the source path instead. */
239

240
                if (strchr(path, ' ')) {
×
241
                        r = tempfn_random_child(data->tmpdir, "mkfs-xfs", &p);
×
242
                        if (r < 0)
×
243
                                return log_error_errno(r, "Failed to generate random child name in %s: %m", data->tmpdir);
×
244

245
                        if (symlink(path, p) < 0)
×
246
                                return log_error_errno(errno, "Failed to symlink %s to %s: %m", p, path);
×
247
                }
248

249
                fputs(p ?: path, data->file);
×
250
        } else if (S_ISLNK(sx->stx_mode)) {
×
251
                _cleanup_free_ char *p = NULL;
×
252

253
                r = readlinkat_malloc(dir_fd, de->d_name, &p);
×
254
                if (r < 0)
×
255
                        return log_error_errno(r, "Failed to read symlink %s: %m", path);
×
256

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

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

263
                fputs(p, data->file);
×
264
        } else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))
×
265
                fprintf(data->file, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor);
×
266

267
        fputc('\n', data->file);
×
268

269
        return RECURSE_DIR_CONTINUE;
270
}
271

272
static int make_protofile(const char *root, char **ret_path, bool *ret_has_filename_with_spaces, char **ret_tmpdir) {
×
273
        _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
×
274
        _cleanup_fclose_ FILE *f = NULL;
×
275
        _cleanup_(unlink_and_freep) char *p = NULL;
×
276
        struct ProtofileData data = {};
×
277
        const char *vt;
×
278
        int r;
×
279

280
        assert(ret_path);
×
281
        assert(ret_has_filename_with_spaces);
×
282
        assert(ret_tmpdir);
×
283

284
        r = var_tmp_dir(&vt);
×
285
        if (r < 0)
×
286
                return log_error_errno(r, "Failed to get persistent temporary directory: %m");
×
287

288
        r = fopen_temporary_child(vt, &f, &p);
×
289
        if (r < 0)
×
290
                return log_error_errno(r, "Failed to open temporary file: %m");
×
291

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

297
        data.file = f;
×
298
        data.tmpdir = tmpdir;
×
299

300
        fputs("/\n"
×
301
              "0 0\n"
302
              "d--755 0 0\n", f);
303

304
        r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID, UINT_MAX,
×
305
                           RECURSE_DIR_SORT, protofile_print_item, &data);
306
        if (r < 0)
×
307
                return log_error_errno(r, "Failed to recurse through %s: %m", root);
×
308

309
        fputs("$\n", f);
×
310

311
        r = fflush_and_check(f);
×
312
        if (r < 0)
×
313
                return log_error_errno(r, "Failed to flush %s: %m", p);
×
314

315
        *ret_path = TAKE_PTR(p);
×
316
        *ret_has_filename_with_spaces = data.has_filename_with_spaces;
×
317
        *ret_tmpdir = TAKE_PTR(tmpdir);
×
318

319
        return 0;
×
320
}
321

322
int make_filesystem(
160✔
323
                const char *node,
324
                const char *fstype,
325
                const char *label,
326
                const char *root,
327
                sd_id128_t uuid,
328
                MakeFileSystemFlags flags,
329
                uint64_t sector_size,
330
                char *compression,
331
                char *compression_level,
332
                char * const *extra_mkfs_args) {
333

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

344
        assert(node);
160✔
345
        assert(fstype);
160✔
346
        assert(label);
160✔
347

348
        if (fstype_is_ro(fstype) && !root)
160✔
349
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
350
                                       "Cannot generate read-only filesystem %s without a source tree.",
351
                                       fstype);
352

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

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

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

390
                mkfs = strjoin("mkfs.", fstype);
127✔
391
                if (!mkfs)
127✔
392
                        return log_oom();
×
393
        }
394

395
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "xfs", "swap")) {
160✔
396
                size_t max_len =
87✔
397
                        streq(fstype, "xfs") ? 12 :
99✔
398
                        streq(fstype, "swap") ? 15 :
99✔
399
                        16;
400

401
                r = mangle_linux_fs_label(label, max_len, &mangled_label);
99✔
402
                if (r < 0)
99✔
403
                        return log_error_errno(r, "Failed to determine volume label from string \"%s\": %m", label);
×
404
                label = mangled_label;
99✔
405

406
        } else if (streq(fstype, "vfat")) {
61✔
407
                r = mangle_fat_label(label, &mangled_label);
36✔
408
                if (r < 0)
36✔
409
                        return log_error_errno(r, "Failed to determine FAT label from string \"%s\": %m", label);
×
410
                label = mangled_label;
36✔
411

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

419
        if (isempty(vol_id))
160✔
420
                assert_se(sd_id128_to_uuid_string(uuid, vol_id));
124✔
421

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

435
                if (root && strv_extend_many(&argv, "-d", root) < 0)
87✔
436
                        return log_oom();
×
437

438
                if (FLAGS_SET(flags, MKFS_QUIET) && strv_extend(&argv, "-q") < 0)
87✔
439
                        return log_oom();
×
440

441
                if (FLAGS_SET(flags, MKFS_FS_VERITY) && strv_extend_many(&argv, "-O", "verity") < 0)
87✔
442
                        return log_oom();
×
443

444
                if (strv_extend(&argv, node) < 0)
87✔
445
                        return log_oom();
×
446

447
                if (sector_size > 0) {
87✔
448
                        if (strv_extend(&env, "MKE2FS_DEVICE_SECTSIZE") < 0)
87✔
449
                                        return log_oom();
×
450

451
                        if (strv_extendf(&env, "%"PRIu64, sector_size) < 0)
87✔
452
                                return log_oom();
×
453
                }
454

455
        } else if (streq(fstype, "btrfs")) {
73✔
456
                argv = strv_new(mkfs,
4✔
457
                                "-L", label,
458
                                "-U", vol_id);
459
                if (!argv)
4✔
460
                        return log_oom();
×
461

462
                if (!FLAGS_SET(flags, MKFS_DISCARD) && strv_extend(&argv, "--nodiscard") < 0)
4✔
463
                        return log_oom();
×
464

465
                if (root && strv_extend_many(&argv, "-r", root) < 0)
4✔
466
                        return log_oom();
×
467

468
                if (FLAGS_SET(flags, MKFS_QUIET) && strv_extend(&argv, "-q") < 0)
4✔
469
                        return log_oom();
×
470

471
                if (compression) {
4✔
472
                        if (!root)
3✔
473
                                log_warning("Btrfs compression setting ignored because no files are being copied. "
×
474
                                            "Compression= can only be applied when CopyFiles= is also specified.");
475
                        else {
476
                                _cleanup_free_ char *c = NULL;
3✔
477

478
                                c = strdup(compression);
3✔
479
                                if (!c)
3✔
480
                                        return log_oom();
×
481

482
                                if (compression_level && !strextend(&c, ":", compression_level))
3✔
483
                                        return log_oom();
×
484

485
                                if (strv_extend_many(&argv, "--compress", c) < 0)
3✔
486
                                        return log_oom();
×
487
                        }
488
                }
489

490
                /* mkfs.btrfs unconditionally warns about several settings changing from v5.15 onwards which
491
                 * isn't silenced by "-q", so let's redirect stdout to /dev/null as well. */
492
                if (FLAGS_SET(flags, MKFS_QUIET))
4✔
493
                        stdio_fds[1] = -EBADF;
×
494

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

499
                if (strv_extend(&argv, node) < 0)
4✔
500
                        return log_oom();
×
501

502
        } else if (streq(fstype, "f2fs")) {
69✔
503
                argv = strv_new(mkfs,
×
504
                                "-g",  /* "default options" */
505
                                "-f",  /* force override, without this it doesn't seem to want to write to an empty partition */
506
                                "-l", label,
507
                                "-U", vol_id,
508
                                "-t", one_zero(FLAGS_SET(flags, MKFS_DISCARD)));
509
                if (!argv)
×
510
                        return log_oom();
×
511

512
                if (FLAGS_SET(flags, MKFS_QUIET) && strv_extend(&argv, "-q") < 0)
×
513
                        return log_oom();
×
514

515
                if (FLAGS_SET(flags, MKFS_FS_VERITY) && strv_extend_many(&argv, "-O", "verity") < 0)
×
516
                        return log_oom();
×
517

518
                if (sector_size > 0) {
×
519
                        if (strv_extend(&argv, "-w") < 0)
×
520
                                return log_oom();
×
521

522
                        if (strv_extendf(&argv, "%"PRIu64, sector_size) < 0)
×
523
                                return log_oom();
×
524
                }
525

526
                if (strv_extend(&argv, node) < 0)
×
527
                        return log_oom();
×
528

529
        } else if (streq(fstype, "xfs")) {
69✔
530
                const char *j;
×
531

532
                j = strjoina("uuid=", vol_id);
×
533

534
                argv = strv_new(mkfs,
×
535
                                "-L", label,
536
                                "-m", j,
537
                                "-m", "reflink=1");
538
                if (!argv)
×
539
                        return log_oom();
×
540

541
                if (!FLAGS_SET(flags, MKFS_DISCARD) && strv_extend(&argv, "-K") < 0)
×
542
                        return log_oom();
×
543

544
                if (root) {
×
545
                        bool has_filename_with_spaces = false;
×
546
                        _cleanup_free_ char *protofile_with_opt = NULL;
×
547

548
                        r = make_protofile(root, &protofile, &has_filename_with_spaces, &protofile_tmpdir);
×
549
                        if (r < 0)
×
550
                                return r;
551

552
                        /* Gross hack to make mkfs.xfs interpret slashes as spaces so we can encode filenames
553
                         * with spaces in the protofile format. */
554
                        if (has_filename_with_spaces)
×
555
                                protofile_with_opt = strjoin("slashes_are_spaces=1,", protofile);
×
556
                        else
557
                                protofile_with_opt = strdup(protofile);
×
558
                        if (!protofile_with_opt)
×
559
                                return -ENOMEM;
560

561
                        if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0)
×
562
                                return log_oom();
×
563
                }
564

565
                if (sector_size > 0) {
×
566
                        if (strv_extend(&argv, "-s") < 0)
×
567
                                return log_oom();
×
568

569
                        if (strv_extendf(&argv, "size=%"PRIu64, sector_size) < 0)
×
570
                                return log_oom();
×
571
                }
572

573
                if (FLAGS_SET(flags, MKFS_QUIET) && strv_extend(&argv, "-q") < 0)
×
574
                        return log_oom();
×
575

576
                if (strv_extend(&argv, node) < 0)
×
577
                        return log_oom();
×
578

579
        } else if (streq(fstype, "vfat")) {
69✔
580

581
                argv = strv_new(mkfs,
36✔
582
                                "-i", vol_id,
583
                                "-n", label,
584
                                "-F", "32");  /* yes, we force FAT32 here */
585
                if (!argv)
36✔
586
                        return log_oom();
×
587

588
                if (sector_size > 0) {
36✔
589
                        if (strv_extend(&argv, "-S") < 0)
36✔
590
                                return log_oom();
×
591

592
                        if (strv_extendf(&argv, "%"PRIu64, sector_size) < 0)
36✔
593
                                return log_oom();
×
594
                }
595

596
                if (strv_extend(&argv, node) < 0)
36✔
597
                        return log_oom();
×
598

599
                /* mkfs.vfat does not have a --quiet option so let's redirect stdout to /dev/null instead. */
600
                if (FLAGS_SET(flags, MKFS_QUIET))
36✔
601
                        stdio_fds[1] = -EBADF;
×
602

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

606
                argv = strv_new(mkfs,
12✔
607
                                "-L", label,
608
                                "-U", vol_id,
609
                                node);
610
                if (!argv)
12✔
611
                        return log_oom();
×
612

613
                if (FLAGS_SET(flags, MKFS_QUIET))
12✔
614
                        stdio_fds[1] = -EBADF;
×
615

616
        } else if (streq(fstype, "squashfs")) {
21✔
617

618
                argv = strv_new(mkfs,
13✔
619
                                root, node, /* mksquashfs expects its arguments before the options. */
620
                                "-noappend");
621
                if (!argv)
13✔
622
                        return log_oom();
×
623

624
                if (compression) {
13✔
625
                        if (strv_extend_many(&argv, "-comp", compression) < 0)
2✔
626
                                return log_oom();
×
627

628
                        if (compression_level && strv_extend_many(&argv, "-Xcompression-level", compression_level) < 0)
2✔
629
                                return log_oom();
×
630
                }
631

632
                /* mksquashfs -quiet option is pretty new so let's redirect stdout to /dev/null instead. */
633
                if (FLAGS_SET(flags, MKFS_QUIET))
13✔
634
                        stdio_fds[1] = -EBADF;
×
635

636
        } else if (streq(fstype, "erofs")) {
8✔
637
                argv = strv_new(mkfs,
8✔
638
                                "-U", vol_id);
639
                if (!argv)
8✔
640
                        return log_oom();
×
641

642
                if (FLAGS_SET(flags, MKFS_QUIET) && strv_extend(&argv, "--quiet") < 0)
8✔
643
                        return log_oom();
×
644

645
                if (compression) {
8✔
646
                        _cleanup_free_ char *c = NULL;
2✔
647

648
                        c = strjoin("-z", compression);
2✔
649
                        if (!c)
2✔
650
                                return log_oom();
×
651

652
                        if (compression_level && !strextend(&c, ",level=", compression_level))
2✔
653
                                return log_oom();
×
654

655
                        if (strv_extend(&argv, c) < 0)
2✔
656
                                return log_oom();
×
657
                }
658

659
                if (strv_extend_many(&argv, node, root) < 0)
8✔
660
                        return log_oom();
×
661

662
        } else {
663
                /* Generic fallback for all other file systems */
664
                argv = strv_new(mkfs, node);
×
665
                if (!argv)
×
666
                        return log_oom();
×
667
        }
668

669
        if (extra_mkfs_args && strv_extend_strv(&argv, extra_mkfs_args, false) < 0)
160✔
670
                return log_oom();
×
671

672
        if (streq(fstype, "btrfs")) {
160✔
673
                struct stat st;
4✔
674

675
                if (stat(node, &st) < 0)
4✔
676
                        return log_error_errno(r, "Failed to stat '%s': %m", node);
×
677

678
                if (S_ISBLK(st.st_mode))
4✔
679
                        fork_flags |= FORK_NEW_MOUNTNS;
2✔
680
        }
681

682
        log_info("Formatting %s as %s", node, fstype);
160✔
683

684
        if (DEBUG_LOGGING) {
160✔
685
                _cleanup_free_ char *j = NULL;
160✔
686

687
                j = strv_join(argv, " ");
160✔
688
                log_debug("Executing mkfs command: %s", strna(j));
160✔
689
        }
690

691
        r = pidref_safe_fork_full(
160✔
692
                        "(mkfs)",
693
                        stdio_fds,
694
                        /* except_fds= */ NULL,
695
                        /* n_except_fds= */ 0,
696
                        fork_flags,
697
                        /* ret= */ NULL);
698
        if (r < 0)
259✔
699
                return r;
700
        if (r == 0) {
259✔
701
                /* Child */
702

703
                STRV_FOREACH_PAIR(k, v, env)
143✔
704
                        if (setenv(*k, *v, /* replace= */ true) < 0) {
43✔
UNCOV
705
                                log_error_errno(r, "Failed to set %s=%s environment variable: %m", *k, *v);
×
706
                                _exit(EXIT_FAILURE);
707
                        }
708

709
                /* mkfs.btrfs refuses to operate on block devices with mounted partitions, even if operating
710
                 * on unformatted free space, so let's trick it and other mkfs tools into thinking no
711
                 * partitions are mounted. See https://github.com/kdave/btrfs-progs/issues/640 for more
712
                 ° information. */
713
                 if (fork_flags & FORK_NEW_MOUNTNS)
100✔
714
                        (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/null", "/proc/self/mounts", NULL, MS_BIND, NULL);
1✔
715

716
                execvp(mkfs, argv);
×
717

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

UNCOV
720
                _exit(EXIT_FAILURE);
×
721
        }
722

723
        if (root && streq(fstype, "vfat")) {
159✔
724
                r = do_mcopy(node, root);
2✔
725
                if (r < 0)
2✔
726
                        return r;
727
        }
728

729
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap"))
159✔
730
                log_info("%s successfully formatted as %s (label \"%s\", uuid %s)",
138✔
731
                         node, fstype, label, vol_id);
732
        else if (streq(fstype, "erofs"))
21✔
733
                log_info("%s successfully formatted as %s (uuid %s, no label)",
8✔
734
                         node, fstype, vol_id);
735
        else
736
                log_info("%s successfully formatted as %s (no label or uuid specified)",
13✔
737
                         node, fstype);
738

739
        return 0;
159✔
740
}
741

742
int mkfs_options_from_env(const char *component, const char *fstype, char ***ret) {
140✔
743
        _cleanup_strv_free_ char **l = NULL;
140✔
744
        const char *e;
140✔
745
        char *n;
140✔
746

747
        assert(component);
140✔
748
        assert(fstype);
140✔
749
        assert(ret);
140✔
750

751
        n = strjoina("SYSTEMD_", component, "_MKFS_OPTIONS_", fstype);
1,260✔
752
        e = getenv(ascii_strupper(n));
140✔
753
        if (e) {
140✔
UNCOV
754
                l = strv_split(e, NULL);
×
UNCOV
755
                if (!l)
×
756
                        return -ENOMEM;
757
        }
758

759
        *ret = TAKE_PTR(l);
140✔
760
        return 0;
140✔
761
}
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