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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

47.55
/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 "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 "tmpfile-util.h"
23
#include "utf8.h"
24

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

29
        assert(fstype);
99✔
30

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

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

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

44
        return true;
45
}
46

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

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

54
        assert(s);
89✔
55
        assert(max_len > 0);
89✔
56
        assert(ret);
89✔
57

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

61
        for (q = s; *q;) {
939✔
62
                int l;
850✔
63

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

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

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

77
        *ret = ans;
89✔
78
        return 0;
89✔
79
}
80

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

84
        _cleanup_free_ char *q = NULL;
22✔
85
        int r;
22✔
86

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

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

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

101
        *ret = TAKE_PTR(q);
22✔
102
        return 0;
22✔
103
}
104

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

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

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

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

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

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

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

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

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

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

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

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

154
        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✔
155
        if (r < 0)
4✔
156
                return r;
157
        if (r == 0) {
4✔
158
                /* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling
159
                 * the stricter mcopy checks using MTOOLS_SKIP_CHECK. */
160
                execve(mcopy, argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1", "TZ=UTC", strv_find_prefix(environ, "SOURCE_DATE_EPOCH=")));
×
161

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

164
                _exit(EXIT_FAILURE);
×
165
        }
166

167
        return 0;
168
}
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

265
        return RECURSE_DIR_CONTINUE;
266
}
267

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

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

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

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

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

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

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

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

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

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

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

315
        return 0;
×
316
}
317

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

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

341
        assert(node);
132✔
342
        assert(fstype);
132✔
343
        assert(label);
132✔
344

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

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

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

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

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

392
        if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "xfs", "swap")) {
132✔
393
                size_t max_len =
178✔
394
                        streq(fstype, "xfs") ? 12 :
89✔
395
                        streq(fstype, "swap") ? 15 :
89✔
396
                        16;
397

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

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

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

416
        if (isempty(vol_id))
132✔
417
                assert_se(sd_id128_to_uuid_string(uuid, vol_id));
110✔
418

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

565
        } else if (streq(fstype, "vfat")) {
55✔
566

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

668
        if (DEBUG_LOGGING) {
132✔
669
                _cleanup_free_ char *j = NULL;
132✔
670

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

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

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

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

700
                execvp(mkfs, argv);
×
701

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

704
                _exit(EXIT_FAILURE);
×
705
        }
706

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

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

723
        return 0;
132✔
724
}
725

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

731
        assert(component);
112✔
732
        assert(fstype);
112✔
733
        assert(ret);
112✔
734

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

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