]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
c95f9a23 | 2 | |
3c9fbb99 ZJS |
3 | #include <unistd.h> |
4 | ||
c95f9a23 LP |
5 | #include "id128-util.h" |
6 | #include "mkfs-util.h" | |
eb43379c | 7 | #include "mountpoint-util.h" |
c95f9a23 LP |
8 | #include "path-util.h" |
9 | #include "process-util.h" | |
0f2b2c48 | 10 | #include "stdio-util.h" |
c95f9a23 | 11 | #include "string-util.h" |
dc91c971 | 12 | #include "utf8.h" |
c95f9a23 LP |
13 | |
14 | int mkfs_exists(const char *fstype) { | |
15 | const char *mkfs; | |
16 | int r; | |
17 | ||
18 | assert(fstype); | |
19 | ||
20 | if (STR_IN_SET(fstype, "auto", "swap")) /* these aren't real file system types, refuse early */ | |
21 | return -EINVAL; | |
22 | ||
23 | mkfs = strjoina("mkfs.", fstype); | |
24 | if (!filename_is_valid(mkfs)) /* refuse file system types with slashes and similar */ | |
25 | return -EINVAL; | |
26 | ||
f7bc0c32 | 27 | r = find_executable(mkfs, NULL); |
c95f9a23 LP |
28 | if (r == -ENOENT) |
29 | return false; | |
30 | if (r < 0) | |
31 | return r; | |
32 | ||
33 | return true; | |
34 | } | |
35 | ||
7ffe593b ZJS |
36 | static int mangle_linux_fs_label(const char *s, size_t max_len, char **ret) { |
37 | /* Not more than max_len bytes (12 or 16) */ | |
38 | ||
39 | assert(s); | |
40 | assert(max_len > 0); | |
41 | assert(ret); | |
42 | ||
43 | const char *q; | |
44 | char *ans; | |
45 | ||
46 | for (q = s; *q;) { | |
47 | int l; | |
48 | ||
49 | l = utf8_encoded_valid_unichar(q, SIZE_MAX); | |
50 | if (l < 0) | |
51 | return l; | |
52 | ||
53 | if ((size_t) (q - s + l) > max_len) | |
54 | break; | |
55 | q += l; | |
56 | } | |
57 | ||
58 | ans = memdup_suffix0(s, q - s); | |
59 | if (!ans) | |
60 | return -ENOMEM; | |
61 | ||
62 | *ret = ans; | |
63 | return 0; | |
64 | } | |
65 | ||
dc91c971 ZJS |
66 | static int mangle_fat_label(const char *s, char **ret) { |
67 | assert(s); | |
68 | ||
69 | _cleanup_free_ char *q = NULL; | |
70 | int r; | |
71 | ||
72 | r = utf8_to_ascii(s, '_', &q); | |
73 | if (r < 0) | |
74 | return r; | |
75 | ||
76 | /* Classic FAT only allows 11 character uppercase labels */ | |
77 | strshorten(q, 11); | |
78 | ascii_strupper(q); | |
79 | ||
80 | /* mkfs.vfat: Labels with characters *?.,;:/\|+=<>[]" are not allowed. | |
81 | * Let's also replace any control chars. */ | |
82 | for (char *p = q; *p; p++) | |
83 | if (strchr("*?.,;:/\\|+=<>[]\"", *p) || char_is_cc(*p)) | |
84 | *p = '_'; | |
85 | ||
86 | *ret = TAKE_PTR(q); | |
87 | return 0; | |
88 | } | |
89 | ||
c95f9a23 LP |
90 | int make_filesystem( |
91 | const char *node, | |
92 | const char *fstype, | |
93 | const char *label, | |
7f55ad77 | 94 | const char *root, |
c95f9a23 LP |
95 | sd_id128_t uuid, |
96 | bool discard) { | |
97 | ||
dc91c971 | 98 | _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL; |
b7416360 | 99 | char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {}; |
c95f9a23 LP |
100 | int r; |
101 | ||
102 | assert(node); | |
103 | assert(fstype); | |
104 | assert(label); | |
105 | ||
eb43379c DDM |
106 | if (fstype_is_ro(fstype) && !root) |
107 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
108 | "Cannot generate read-only filesystem %s without a source tree.", | |
109 | fstype); | |
110 | ||
c95f9a23 | 111 | if (streq(fstype, "swap")) { |
7f55ad77 DDM |
112 | if (root) |
113 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
114 | "A swap filesystem can't be populated, refusing"); | |
f7bc0c32 | 115 | r = find_executable("mkswap", &mkfs); |
c95f9a23 LP |
116 | if (r == -ENOENT) |
117 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkswap binary not available."); | |
118 | if (r < 0) | |
119 | return log_error_errno(r, "Failed to determine whether mkswap binary exists: %m"); | |
7f55ad77 | 120 | } else if (streq(fstype, "squashfs")) { |
7f55ad77 DDM |
121 | r = find_executable("mksquashfs", &mkfs); |
122 | if (r == -ENOENT) | |
123 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available."); | |
124 | if (r < 0) | |
125 | return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m"); | |
c95f9a23 | 126 | } else { |
7f55ad77 DDM |
127 | if (root) |
128 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
eb43379c | 129 | "Populating with source tree is only supported for read-only filesystems"); |
c95f9a23 LP |
130 | r = mkfs_exists(fstype); |
131 | if (r < 0) | |
132 | return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype); | |
133 | if (r == 0) | |
134 | return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for %s is not available.", fstype); | |
135 | ||
136 | mkfs = strjoin("mkfs.", fstype); | |
137 | if (!mkfs) | |
138 | return log_oom(); | |
139 | } | |
140 | ||
8d433a99 ZJS |
141 | if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "xfs", "swap")) { |
142 | size_t max_len = | |
143 | streq(fstype, "xfs") ? 12 : | |
144 | streq(fstype, "swap") ? 15 : | |
145 | 16; | |
146 | ||
147 | r = mangle_linux_fs_label(label, max_len, &mangled_label); | |
7ffe593b | 148 | if (r < 0) |
8d433a99 | 149 | return log_error_errno(r, "Failed to determine volume label from string \"%s\": %m", label); |
7ffe593b ZJS |
150 | label = mangled_label; |
151 | ||
152 | } else if (streq(fstype, "vfat")) { | |
dc91c971 ZJS |
153 | r = mangle_fat_label(label, &mangled_label); |
154 | if (r < 0) | |
155 | return log_error_errno(r, "Failed to determine FAT label from string \"%s\": %m", label); | |
4f05a11c ZJS |
156 | label = mangled_label; |
157 | ||
158 | xsprintf(vol_id, "%08" PRIx32, | |
159 | ((uint32_t) uuid.bytes[0] << 24) | | |
160 | ((uint32_t) uuid.bytes[1] << 16) | | |
161 | ((uint32_t) uuid.bytes[2] << 8) | | |
162 | ((uint32_t) uuid.bytes[3])); /* Take first 32 bytes of UUID */ | |
163 | } | |
164 | ||
165 | if (isempty(vol_id)) | |
b7416360 | 166 | assert_se(sd_id128_to_uuid_string(uuid, vol_id)); |
4f05a11c | 167 | |
c95f9a23 LP |
168 | r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL); |
169 | if (r < 0) | |
170 | return r; | |
171 | if (r == 0) { | |
c95f9a23 | 172 | /* Child */ |
4f05a11c ZJS |
173 | |
174 | /* When changing this conditional, also adjust the log statement below. */ | |
8e93a614 ZJS |
175 | if (streq(fstype, "ext2")) |
176 | (void) execlp(mkfs, mkfs, | |
4f05a11c | 177 | "-q", |
8e93a614 | 178 | "-L", label, |
4f05a11c | 179 | "-U", vol_id, |
8e93a614 ZJS |
180 | "-I", "256", |
181 | "-m", "0", | |
182 | "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1", | |
183 | node, NULL); | |
184 | ||
185 | else if (STR_IN_SET(fstype, "ext3", "ext4")) | |
c95f9a23 | 186 | (void) execlp(mkfs, mkfs, |
4f05a11c | 187 | "-q", |
85b55869 | 188 | "-L", label, |
4f05a11c | 189 | "-U", vol_id, |
85b55869 LP |
190 | "-I", "256", |
191 | "-O", "has_journal", | |
192 | "-m", "0", | |
44cdeb6e | 193 | "-E", discard ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1", |
85b55869 | 194 | node, NULL); |
c95f9a23 LP |
195 | |
196 | else if (streq(fstype, "btrfs")) { | |
44cdeb6e | 197 | (void) execlp(mkfs, mkfs, |
4f05a11c | 198 | "-q", |
44cdeb6e | 199 | "-L", label, |
4f05a11c | 200 | "-U", vol_id, |
44cdeb6e ZJS |
201 | node, |
202 | discard ? NULL : "--nodiscard", | |
203 | NULL); | |
c95f9a23 | 204 | |
2d96440f ZJS |
205 | } else if (streq(fstype, "f2fs")) { |
206 | (void) execlp(mkfs, mkfs, | |
207 | "-q", | |
208 | "-g", /* "default options" */ | |
209 | "-f", /* force override, without this it doesn't seem to want to write to an empty partition */ | |
210 | "-l", label, | |
211 | "-U", vol_id, | |
212 | "-t", one_zero(discard), | |
213 | node, | |
214 | NULL); | |
215 | ||
c95f9a23 LP |
216 | } else if (streq(fstype, "xfs")) { |
217 | const char *j; | |
218 | ||
4f05a11c | 219 | j = strjoina("uuid=", vol_id); |
44cdeb6e ZJS |
220 | |
221 | (void) execlp(mkfs, mkfs, | |
4f05a11c | 222 | "-q", |
44cdeb6e ZJS |
223 | "-L", label, |
224 | "-m", j, | |
225 | "-m", "reflink=1", | |
226 | node, | |
227 | discard ? NULL : "-K", | |
228 | NULL); | |
c95f9a23 | 229 | |
4f05a11c | 230 | } else if (streq(fstype, "vfat")) |
0f2b2c48 LP |
231 | |
232 | (void) execlp(mkfs, mkfs, | |
233 | "-i", vol_id, | |
4f05a11c | 234 | "-n", label, |
0f2b2c48 LP |
235 | "-F", "32", /* yes, we force FAT32 here */ |
236 | node, NULL); | |
237 | ||
4f05a11c ZJS |
238 | else if (streq(fstype, "swap")) |
239 | /* TODO: add --quiet here if | |
240 | * https://github.com/util-linux/util-linux/issues/1499 resolved. */ | |
c95f9a23 LP |
241 | |
242 | (void) execlp(mkfs, mkfs, | |
85b55869 | 243 | "-L", label, |
4f05a11c | 244 | "-U", vol_id, |
85b55869 | 245 | node, NULL); |
c95f9a23 | 246 | |
7f55ad77 DDM |
247 | else if (streq(fstype, "squashfs")) |
248 | ||
249 | (void) execlp(mkfs, mkfs, | |
250 | root, node, | |
251 | "-quiet", | |
252 | "-noappend", | |
253 | NULL); | |
4f05a11c | 254 | else |
c95f9a23 LP |
255 | /* Generic fallback for all other file systems */ |
256 | (void) execlp(mkfs, mkfs, node, NULL); | |
257 | ||
258 | log_error_errno(errno, "Failed to execute %s: %m", mkfs); | |
259 | ||
260 | _exit(EXIT_FAILURE); | |
261 | } | |
262 | ||
2d96440f | 263 | if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap")) |
4f05a11c ZJS |
264 | log_info("%s successfully formatted as %s (label \"%s\", uuid %s)", |
265 | node, fstype, label, vol_id); | |
266 | else | |
267 | log_info("%s successfully formatted as %s (no label or uuid specified)", | |
268 | node, fstype); | |
269 | ||
c95f9a23 LP |
270 | return 0; |
271 | } |