Line data Source code
1 : /*
2 : * Copyright (C) 2017 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Vratislav Podzimek <vpodzime@redhat.com>
18 : */
19 :
20 : #include <ext2fs.h>
21 : #include <e2p.h>
22 :
23 : #include <blockdev/utils.h>
24 : #include <check_deps.h>
25 :
26 : #include "common.h"
27 : #include "fs.h"
28 : #include "ext.h"
29 :
30 : #define EXT2 "ext2"
31 : #define EXT3 "ext3"
32 : #define EXT4 "ext4"
33 :
34 : static volatile guint avail_deps = 0;
35 : static GMutex deps_check_lock;
36 :
37 : #define DEPS_MKE2FS 0
38 : #define DEPS_MKE2FS_MASK (1 << DEPS_MKE2FS)
39 : #define DEPS_E2FSCK 1
40 : #define DEPS_E2FSCK_MASK (1 << DEPS_E2FSCK)
41 : #define DEPS_TUNE2FS 2
42 : #define DEPS_TUNE2FS_MASK (1 << DEPS_TUNE2FS)
43 : #define DEPS_RESIZE2FS 3
44 : #define DEPS_RESIZE2FS_MASK (1 << DEPS_RESIZE2FS)
45 :
46 : #define DEPS_LAST 4
47 :
48 : static const UtilDep deps[DEPS_LAST] = {
49 : {"mke2fs", NULL, NULL, NULL},
50 : {"e2fsck", NULL, NULL, NULL},
51 : {"tune2fs", NULL, NULL, NULL},
52 : {"resize2fs", NULL, NULL, NULL},
53 : };
54 :
55 : static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
56 : DEPS_MKE2FS_MASK, /* mkfs */
57 : 0, /* wipe */
58 : DEPS_E2FSCK_MASK, /* check */
59 : DEPS_E2FSCK_MASK, /* repair */
60 : DEPS_TUNE2FS_MASK, /* set-label */
61 : 0, /* query */
62 : DEPS_RESIZE2FS_MASK, /* resize */
63 : DEPS_TUNE2FS_MASK /* set-uuid */
64 : };
65 :
66 :
67 272 : static gint8 compute_percents (guint8 pass_cur, guint8 pass_total, gint val_cur, gint val_total) {
68 : gint perc;
69 : gint one_pass;
70 : /* first get a percentage in the current pass/stage */
71 272 : perc = (val_cur * 100) / val_total;
72 :
73 : /* now map it to the total progress, splitting the stages equally */
74 272 : one_pass = 100 / pass_total;
75 272 : perc = ((pass_cur - 1) * one_pass) + (perc / pass_total);
76 :
77 272 : return perc;
78 : }
79 :
80 : /**
81 : * filter_line_fsck: (skip)
82 : * Filter one line - decide what to do with it.
83 : *
84 : * Returns: Zero or positive number as a percentage, -1 if not a percentage, -2 on an error
85 : */
86 279 : static gint8 filter_line_fsck (const gchar * line, guint8 total_stages) {
87 : static GRegex *output_regex = NULL;
88 : GMatchInfo *match_info;
89 279 : gint8 perc = -1;
90 279 : GError *l_error = NULL;
91 :
92 279 : if (output_regex == NULL) {
93 : /* Compile regular expression that matches to e2fsck progress output */
94 1 : output_regex = g_regex_new ("^([0-9][0-9]*) ([0-9][0-9]*) ([0-9][0-9]*) (/.*)", 0, 0, &l_error);
95 1 : if (output_regex == NULL) {
96 0 : bd_utils_log_format (BD_UTILS_LOG_ERR,
97 0 : "Failed to create regex for parsing progress: %s", l_error->message);
98 0 : g_clear_error (&l_error);
99 0 : return -2;
100 : }
101 : }
102 :
103 : /* Execute regular expression */
104 279 : if (g_regex_match (output_regex, line, 0, &match_info)) {
105 : guint8 stage;
106 : gint64 val_cur;
107 : gint64 val_total;
108 : gchar *s;
109 :
110 : /* The output_regex ensures we have a number in these matches, so we can skip
111 : * tests for conversion errors.
112 : */
113 272 : s = g_match_info_fetch (match_info, 1);
114 272 : stage = (guint8) g_ascii_strtoull (s, (char **)NULL, 10);
115 272 : g_free (s);
116 :
117 272 : s = g_match_info_fetch (match_info, 2);
118 272 : val_cur = g_ascii_strtoll (s, (char **)NULL, 10);
119 272 : g_free (s);
120 :
121 272 : s = g_match_info_fetch (match_info, 3);
122 272 : val_total = g_ascii_strtoll (s, (char **)NULL, 10);
123 272 : g_free (s);
124 :
125 272 : perc = compute_percents (stage, total_stages, val_cur, val_total);
126 : } else {
127 7 : g_match_info_free (match_info);
128 7 : bd_utils_log_format (BD_UTILS_LOG_DEBUG,
129 : "Failed to parse progress from: %s", line);
130 7 : return -1;
131 : }
132 272 : g_match_info_free (match_info);
133 272 : return perc;
134 : }
135 :
136 279 : static gboolean extract_e2fsck_progress (const gchar *line, guint8 *completion) {
137 : /* A magic number 5, e2fsck has 5 stages, but this can't be read from the output in advance. */
138 : gint8 perc;
139 :
140 279 : perc = filter_line_fsck (line, 5);
141 279 : if (perc < 0)
142 7 : return FALSE;
143 :
144 272 : *completion = perc;
145 272 : return TRUE;
146 : }
147 :
148 :
149 : /**
150 : * bd_fs_ext_is_tech_avail:
151 : * @tech: the queried tech
152 : * @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
153 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
154 : *
155 : * Returns: whether the @tech-@mode combination is available -- supported by the
156 : * plugin implementation and having all the runtime dependencies available
157 : */
158 : G_GNUC_INTERNAL gboolean
159 21 : bd_fs_ext_is_tech_avail (BDFSTech tech G_GNUC_UNUSED, guint64 mode, GError **error) {
160 21 : guint32 required = 0;
161 21 : guint i = 0;
162 189 : for (i = 0; i <= BD_FS_MODE_LAST; i++)
163 168 : if (mode & (1 << i))
164 39 : required |= fs_mode_util[i];
165 :
166 21 : return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
167 : }
168 :
169 : /**
170 : * bd_fs_ext2_info_copy: (skip)
171 : *
172 : * Creates a new copy of @data.
173 : */
174 0 : BDFSExt2Info* bd_fs_ext2_info_copy (BDFSExt2Info *data) {
175 0 : if (data == NULL)
176 0 : return NULL;
177 :
178 0 : BDFSExt2Info *ret = g_new0 (BDFSExt2Info, 1);
179 :
180 0 : ret->label = g_strdup (data->label);
181 0 : ret->uuid = g_strdup (data->uuid);
182 0 : ret->state = g_strdup (data->state);
183 0 : ret->block_size = data->block_size;
184 0 : ret->block_count = data->block_count;
185 0 : ret->free_blocks = data->free_blocks;
186 :
187 0 : return ret;
188 : }
189 :
190 : /**
191 : * bd_fs_ext3_info_copy: (skip)
192 : *
193 : * Creates a new copy of @data.
194 : */
195 0 : BDFSExt3Info* bd_fs_ext3_info_copy (BDFSExt3Info *data) {
196 0 : return (BDFSExt3Info*) bd_fs_ext2_info_copy (data);
197 : }
198 :
199 : /**
200 : * bd_fs_ext4_info_copy: (skip)
201 : *
202 : * Creates a new copy of @data.
203 : */
204 0 : BDFSExt4Info* bd_fs_ext4_info_copy (BDFSExt4Info *data) {
205 0 : return (BDFSExt4Info*) bd_fs_ext2_info_copy (data);
206 : }
207 :
208 : /**
209 : * bd_fs_ext2_info_free: (skip)
210 : *
211 : * Frees @data.
212 : */
213 0 : void bd_fs_ext2_info_free (BDFSExt2Info *data) {
214 0 : if (data == NULL)
215 0 : return;
216 :
217 0 : g_free (data->label);
218 0 : g_free (data->uuid);
219 0 : g_free (data->state);
220 0 : g_free (data);
221 : }
222 :
223 : /**
224 : * bd_fs_ext3_info_free: (skip)
225 : *
226 : * Frees @data.
227 : */
228 0 : void bd_fs_ext3_info_free (BDFSExt3Info *data) {
229 0 : bd_fs_ext2_info_free ((BDFSExt2Info*) data);
230 0 : }
231 :
232 : /**
233 : * bd_fs_ext4_info_free: (skip)
234 : *
235 : * Frees @data.
236 : */
237 0 : void bd_fs_ext4_info_free (BDFSExt4Info *data) {
238 0 : bd_fs_ext2_info_free ((BDFSExt2Info*) data);
239 0 : }
240 :
241 13 : static BDExtraArg **ext_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
242 13 : GPtrArray *options_array = g_ptr_array_new ();
243 13 : const BDExtraArg **extra_p = NULL;
244 :
245 13 : if (options->label && g_strcmp0 (options->label, "") != 0)
246 4 : g_ptr_array_add (options_array, bd_extra_arg_new ("-L", options->label));
247 :
248 13 : if (options->uuid && g_strcmp0 (options->uuid, "") != 0)
249 4 : g_ptr_array_add (options_array, bd_extra_arg_new ("-U", options->uuid));
250 :
251 13 : if (options->dry_run)
252 5 : g_ptr_array_add (options_array, bd_extra_arg_new ("-n", ""));
253 :
254 13 : if (options->no_discard)
255 0 : g_ptr_array_add (options_array, bd_extra_arg_new ("-E", "nodiscard"));
256 :
257 13 : if (options->force)
258 1 : g_ptr_array_add (options_array, bd_extra_arg_new ("-F", ""));
259 :
260 13 : if (extra) {
261 2 : for (extra_p = extra; *extra_p; extra_p++)
262 1 : g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
263 : }
264 :
265 13 : g_ptr_array_add (options_array, NULL);
266 :
267 13 : return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
268 : }
269 :
270 : G_GNUC_INTERNAL BDExtraArg **
271 5 : bd_fs_ext2_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
272 5 : return ext_mkfs_options (options, extra);
273 : }
274 :
275 : G_GNUC_INTERNAL BDExtraArg **
276 4 : bd_fs_ext3_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
277 4 : return ext_mkfs_options (options, extra);
278 : }
279 :
280 : G_GNUC_INTERNAL BDExtraArg **
281 4 : bd_fs_ext4_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
282 4 : return ext_mkfs_options (options, extra);
283 : }
284 :
285 55 : static gboolean ext_mkfs (const gchar *device, const BDExtraArg **extra, const gchar *ext_version, GError **error) {
286 55 : const gchar *args[5] = {"mke2fs", "-t", ext_version, device, NULL};
287 :
288 55 : if (!check_deps (&avail_deps, DEPS_MKE2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
289 0 : return FALSE;
290 :
291 55 : return bd_utils_exec_and_report_error (args, extra, error);
292 : }
293 :
294 : /**
295 : * bd_fs_ext2_mkfs:
296 : * @device: the device to create a new ext2 fs on
297 : * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
298 : * passed to the 'mke2fs' utility)
299 : * @error: (out) (optional): place to store error (if any)
300 : *
301 : * Returns: whether a new ext2 fs was successfully created on @device or not
302 : *
303 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_MKFS
304 : */
305 17 : gboolean bd_fs_ext2_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
306 17 : return ext_mkfs (device, extra, EXT2, error);
307 : }
308 :
309 : /**
310 : * bd_fs_ext3_mkfs:
311 : * @device: the device to create a new ext3 fs on
312 : * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
313 : * passed to the 'mke2fs' utility)
314 : * @error: (out) (optional): place to store error (if any)
315 : *
316 : * Returns: whether a new ext3 fs was successfully created on @device or not
317 : *
318 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_MKFS
319 : */
320 15 : gboolean bd_fs_ext3_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
321 15 : return ext_mkfs (device, extra, EXT3, error);
322 : }
323 :
324 : /**
325 : * bd_fs_ext4_mkfs:
326 : * @device: the device to create a new ext4 fs on
327 : * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
328 : * passed to the 'mkfs.ext4' utility)
329 : * @error: (out) (optional): place to store error (if any)
330 : *
331 : * Returns: whether a new ext4 fs was successfully created on @device or not
332 : *
333 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_MKFS
334 : */
335 23 : gboolean bd_fs_ext4_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
336 23 : return ext_mkfs (device, extra, EXT4, error);
337 : }
338 :
339 6 : static gboolean ext_check (const gchar *device, const BDExtraArg **extra, GError **error) {
340 : /* Force checking even if the file system seems clean. AND
341 : * Open the filesystem read-only, and assume an answer of no to all
342 : * questions. */
343 6 : const gchar *args_progress[7] = {"e2fsck", "-f", "-n", "-C", "1", device, NULL};
344 6 : const gchar *args[5] = {"e2fsck", "-f", "-n", device, NULL};
345 6 : gint status = 0;
346 6 : gboolean ret = FALSE;
347 :
348 6 : if (!check_deps (&avail_deps, DEPS_E2FSCK_MASK, deps, DEPS_LAST, &deps_check_lock, error))
349 0 : return FALSE;
350 :
351 6 : if (bd_utils_prog_reporting_initialized ()) {
352 1 : ret = bd_utils_exec_and_report_progress (args_progress, extra, extract_e2fsck_progress, &status, error);
353 : } else {
354 5 : ret = bd_utils_exec_and_report_status_error (args, extra, &status, error);
355 : }
356 :
357 6 : if (!ret && (status == 4)) {
358 : /* no error should be reported for exit code 4 - File system errors left uncorrected */
359 0 : g_clear_error (error);
360 : }
361 6 : return ret;
362 : }
363 :
364 : /**
365 : * bd_fs_ext2_check:
366 : * @device: the device the file system on which to check
367 : * @extra: (nullable) (array zero-terminated=1): extra options for the check (right now
368 : * passed to the 'e2fsck' utility)
369 : * @error: (out) (optional): place to store error (if any)
370 : *
371 : * Returns: whether an ext2 file system on the @device is clean or not
372 : *
373 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_CHECK
374 : */
375 1 : gboolean bd_fs_ext2_check (const gchar *device, const BDExtraArg **extra, GError **error) {
376 1 : return ext_check (device, extra, error);
377 : }
378 :
379 : /**
380 : * bd_fs_ext3_check:
381 : * @device: the device the file system on which to check
382 : * @extra: (nullable) (array zero-terminated=1): extra options for the check (right now
383 : * passed to the 'e2fsck' utility)
384 : * @error: (out) (optional): place to store error (if any)
385 : *
386 : * Returns: whether an ext3 file system on the @device is clean or not
387 : *
388 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_CHECK
389 : */
390 1 : gboolean bd_fs_ext3_check (const gchar *device, const BDExtraArg **extra, GError **error) {
391 1 : return ext_check (device, extra, error);
392 : }
393 :
394 : /**
395 : * bd_fs_ext4_check:
396 : * @device: the device the file system on which to check
397 : * @extra: (nullable) (array zero-terminated=1): extra options for the check (right now
398 : * passed to the 'e2fsck' utility)
399 : * @error: (out) (optional): place to store error (if any)
400 : *
401 : * Returns: whether an ext4 file system on the @device is clean or not
402 : *
403 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_CHECK
404 : */
405 4 : gboolean bd_fs_ext4_check (const gchar *device, const BDExtraArg **extra, GError **error) {
406 4 : return ext_check (device, extra, error);
407 : }
408 :
409 11 : static gboolean ext_repair (const gchar *device, gboolean unsafe, const BDExtraArg **extra, GError **error) {
410 : /* Force checking even if the file system seems clean. AND
411 : * Automatically repair what can be safely repaired. OR
412 : * Assume an answer of `yes' to all questions. */
413 11 : const gchar *args_progress[7] = {"e2fsck", "-f", unsafe ? "-y" : "-p", "-C", "1", device, NULL};
414 11 : const gchar *args[5] = {"e2fsck", "-f", unsafe ? "-y" : "-p", device, NULL};
415 11 : gint status = 0;
416 11 : gboolean ret = FALSE;
417 :
418 11 : if (!check_deps (&avail_deps, DEPS_E2FSCK_MASK, deps, DEPS_LAST, &deps_check_lock, error))
419 0 : return FALSE;
420 :
421 11 : if (bd_utils_prog_reporting_initialized ()) {
422 0 : ret = bd_utils_exec_and_report_progress (args_progress, extra, extract_e2fsck_progress, &status, error);
423 : } else {
424 11 : ret = bd_utils_exec_and_report_status_error (args, extra, &status, error);
425 : }
426 :
427 11 : if (!ret) {
428 0 : if (status == 1) {
429 : /* no error should be reported for exit code 1 - File system errors corrected */
430 0 : g_clear_error (error);
431 0 : ret = TRUE;
432 0 : } else if (status == 2) {
433 : /* no error should be reported for exit code 2 - File system errors corrected, system should be rebooted */
434 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING,
435 : "File system errors on %s were successfully corrected, but system reboot is advised.",
436 : device);
437 0 : g_clear_error (error);
438 0 : ret = TRUE;
439 : }
440 : }
441 11 : return ret;
442 : }
443 :
444 : /**
445 : * bd_fs_ext2_repair:
446 : * @device: the device the file system on which to repair
447 : * @unsafe: whether to do unsafe operations too
448 : * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
449 : * passed to the 'e2fsck' utility)
450 : * @error: (out) (optional): place to store error (if any)
451 : *
452 : * Returns: whether an ext2 file system on the @device was successfully repaired
453 : * (if needed) or not (error is set in that case)
454 : *
455 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_REPAIR
456 : */
457 3 : gboolean bd_fs_ext2_repair (const gchar *device, gboolean unsafe, const BDExtraArg **extra, GError **error) {
458 3 : return ext_repair (device, unsafe, extra, error);
459 : }
460 :
461 : /**
462 : * bd_fs_ext3_repair:
463 : * @device: the device the file system on which to repair
464 : * @unsafe: whether to do unsafe operations too
465 : * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
466 : * passed to the 'e2fsck' utility)
467 : * @error: (out) (optional): place to store error (if any)
468 : *
469 : * Returns: whether an ext3 file system on the @device was successfully repaired
470 : * (if needed) or not (error is set in that case)
471 : *
472 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_REPAIR
473 : */
474 3 : gboolean bd_fs_ext3_repair (const gchar *device, gboolean unsafe, const BDExtraArg **extra, GError **error) {
475 3 : return ext_repair (device, unsafe, extra, error);
476 : }
477 :
478 : /**
479 : * bd_fs_ext4_repair:
480 : * @device: the device the file system on which to repair
481 : * @unsafe: whether to do unsafe operations too
482 : * @extra: (nullable) (array zero-terminated=1): extra options for the repair (right now
483 : * passed to the 'e2fsck' utility)
484 : * @error: (out) (optional): place to store error (if any)
485 : *
486 : * Returns: whether an ext4 file system on the @device was successfully repaired
487 : * (if needed) or not (error is set in that case)
488 : *
489 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_REPAIR
490 : */
491 5 : gboolean bd_fs_ext4_repair (const gchar *device, gboolean unsafe, const BDExtraArg **extra, GError **error) {
492 5 : return ext_repair (device, unsafe, extra, error);
493 : }
494 :
495 11 : static gboolean ext_set_label (const gchar *device, const gchar *label, GError **error) {
496 11 : const gchar *args[5] = {"tune2fs", "-L", label, device, NULL};
497 :
498 11 : if (!check_deps (&avail_deps, DEPS_TUNE2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
499 0 : return FALSE;
500 :
501 11 : return bd_utils_exec_and_report_error (args, NULL, error);
502 : }
503 :
504 : /**
505 : * bd_fs_ext2_set_label:
506 : * @device: the device the file system on which to set label for
507 : * @label: label to set
508 : * @error: (out) (optional): place to store error (if any)
509 : *
510 : * Returns: whether the label of ext2 file system on the @device was
511 : * successfully set or not
512 : *
513 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_SET_LABEL
514 : */
515 3 : gboolean bd_fs_ext2_set_label (const gchar *device, const gchar *label, GError **error) {
516 3 : return ext_set_label (device, label, error);
517 : }
518 :
519 : /**
520 : * bd_fs_ext3_set_label:
521 : * @device: the device the file system on which to set label for
522 : * @label: label to set
523 : * @error: (out) (optional): place to store error (if any)
524 : *
525 : * Returns: whether the label of ext3 file system on the @device was
526 : * successfully set or not
527 : *
528 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_SET_LABEL
529 : */
530 3 : gboolean bd_fs_ext3_set_label (const gchar *device, const gchar *label, GError **error) {
531 3 : return ext_set_label (device, label, error);
532 : }
533 :
534 : /**
535 : * bd_fs_ext4_set_label:
536 : * @device: the device the file system on which to set label for
537 : * @label: label to set
538 : * @error: (out) (optional): place to store error (if any)
539 : *
540 : * Returns: whether the label of ext4 file system on the @device was
541 : * successfully set or not
542 : *
543 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_SET_LABEL
544 : */
545 5 : gboolean bd_fs_ext4_set_label (const gchar *device, const gchar *label, GError **error) {
546 5 : return ext_set_label (device, label, error);
547 : }
548 :
549 : /**
550 : * bd_fs_ext2_check_label:
551 : * @label: label to check
552 : * @error: (out) (optional): place to store error
553 : *
554 : * Returns: whether @label is a valid label for the ext2 file system or not
555 : * (reason is provided in @error)
556 : *
557 : * Tech category: always available
558 : */
559 7 : gboolean bd_fs_ext2_check_label (const gchar *label, GError **error) {
560 7 : if (strlen (label) > 16) {
561 3 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
562 : "Label for ext filesystem must be at most 16 characters long.");
563 3 : return FALSE;
564 : }
565 :
566 4 : return TRUE;
567 : }
568 :
569 : /**
570 : * bd_fs_ext3_check_label:
571 : * @label: label to check
572 : * @error: (out) (optional): place to store error (if any)
573 : *
574 : * Returns: whether @label is a valid label for the ext3 file system or not
575 : * (reason is provided in @error)
576 : *
577 : * Tech category: always available
578 : */
579 2 : gboolean bd_fs_ext3_check_label (const gchar *label, GError **error) {
580 2 : return bd_fs_ext2_check_label (label, error);
581 : }
582 :
583 : /**
584 : * bd_fs_ext4_check_label:
585 : * @label: label to check
586 : * @error: (out) (optional): place to store error (if any)
587 : *
588 : * Returns: whether @label is a valid label for the ext4 file system or not
589 : * (reason is provided in @error)
590 : *
591 : * Tech category: always available
592 : */
593 3 : gboolean bd_fs_ext4_check_label (const gchar *label, GError **error) {
594 3 : return bd_fs_ext2_check_label (label, error);
595 : }
596 :
597 17 : static gboolean ext_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
598 17 : const gchar *args[5] = {"tune2fs", "-U", NULL, device, NULL};
599 :
600 17 : if (!check_deps (&avail_deps, DEPS_TUNE2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
601 0 : return FALSE;
602 :
603 17 : if (!uuid)
604 4 : args[2] = "random";
605 : else
606 13 : args[2] = uuid;
607 :
608 17 : return bd_utils_exec_and_report_error (args, NULL, error);
609 : }
610 :
611 : /**
612 : * bd_fs_ext2_set_uuid:
613 : * @device: the device the file system on which to set UUID for
614 : * @uuid: (nullable): UUID to set %NULL to generate a new one
615 : * UUID can also be one of "clear", "random" and "time" to clear,
616 : * generate a new random/time-based UUID
617 : * @error: (out) (optional): place to store error (if any)
618 : *
619 : * Returns: whether the UUID of ext2 file system on the @device was
620 : * successfully set or not
621 : *
622 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_SET_UUID
623 : */
624 5 : gboolean bd_fs_ext2_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
625 5 : return ext_set_uuid (device, uuid, error);
626 : }
627 :
628 : /**
629 : * bd_fs_ext3_set_uuid:
630 : * @device: the device the file system on which to set UUID for
631 : * @uuid: (nullable): UUID to set %NULL to generate a new one
632 : * UUID can also be one of "clear", "random" and "time" to clear,
633 : * generate a new random/time-based UUID
634 : * @error: (out) (optional): place to store error (if any)
635 : *
636 : * Returns: whether the UUID of ext3 file system on the @device was
637 : * successfully set or not
638 : *
639 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_SET_UUID
640 : */
641 5 : gboolean bd_fs_ext3_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
642 5 : return ext_set_uuid (device, uuid, error);
643 : }
644 :
645 : /**
646 : * bd_fs_ext4_set_uuid:
647 : * @device: the device the file system on which to set UUID for
648 : * @uuid: (nullable): UUID to set %NULL to generate a new one
649 : * UUID can also be one of "clear", "random" and "time" to clear,
650 : * generate a new random/time-based UUID
651 : * @error: (out) (optional): place to store error (if any)
652 : *
653 : * Returns: whether the UUID of ext4 file system on the @device was
654 : * successfully set or not
655 : *
656 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_SET_UUID
657 : */
658 7 : gboolean bd_fs_ext4_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
659 7 : return ext_set_uuid (device, uuid, error);
660 : }
661 :
662 : /**
663 : * bd_fs_ext2_check_uuid:
664 : * @uuid: UUID to check
665 : * @error: (out) (optional): place to store error
666 : *
667 : * Returns: whether @uuid is a valid UUID for the ext2 file system or not
668 : * (reason is provided in @error)
669 : *
670 : * Tech category: always available
671 : */
672 2 : gboolean bd_fs_ext2_check_uuid (const gchar *uuid, GError **error) {
673 2 : return check_uuid (uuid, error);
674 : }
675 :
676 : /**
677 : * bd_fs_ext3_check_uuid:
678 : * @uuid: UUID to check
679 : * @error: (out) (optional): place to store error
680 : *
681 : * Returns: whether @uuid is a valid UUID for the ext3 file system or not
682 : * (reason is provided in @error)
683 : *
684 : * Tech category: always available
685 : */
686 2 : gboolean bd_fs_ext3_check_uuid (const gchar *uuid, GError **error) {
687 2 : return check_uuid (uuid, error);
688 : }
689 :
690 : /**
691 : * bd_fs_ext4_check_uuid:
692 : * @uuid: UUID to check
693 : * @error: (out) (optional): place to store error
694 : *
695 : * Returns: whether @uuid is a valid UUID for the ext4 file system or not
696 : * (reason is provided in @error)
697 : *
698 : * Tech category: always available
699 : */
700 3 : gboolean bd_fs_ext4_check_uuid (const gchar *uuid, GError **error) {
701 3 : return check_uuid (uuid, error);
702 : }
703 :
704 88 : static gchar *decode_fs_state (unsigned short state) {
705 176 : return g_strdup_printf ("%s%s",
706 88 : (state & EXT2_VALID_FS) ? "clean" : "not clean",
707 88 : (state & EXT2_ERROR_FS) ? " with errors" : "");
708 : }
709 :
710 88 : static gchar *decode_uuid (void *uuid) {
711 88 : const char *str = e2p_uuid2str (uuid);
712 88 : if (g_strcmp0 (str, "<none>") == 0)
713 3 : str = "";
714 88 : return g_strdup (str);
715 : }
716 :
717 88 : static BDFSExtInfo* ext_get_info (const gchar *device, GError **error) {
718 : errcode_t retval;
719 : ext2_filsys fs;
720 : struct ext2_super_block *sb;
721 88 : BDFSExtInfo *ret = NULL;
722 :
723 88 : int flags = (EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES |
724 : EXT2_FLAG_64BITS | EXT2_FLAG_SUPER_ONLY |
725 : EXT2_FLAG_IGNORE_CSUM_ERRORS);
726 :
727 : #ifdef EXT2_FLAG_THREADS
728 88 : flags |= EXT2_FLAG_THREADS;
729 : #endif
730 :
731 88 : retval = ext2fs_open (device,
732 : flags,
733 : 0, /* use_superblock */
734 : 0, /* use_blocksize */
735 : unix_io_manager,
736 : &fs);
737 88 : if (retval) {
738 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL, "Failed to open ext4 file system");
739 0 : return NULL;
740 : }
741 :
742 88 : sb = fs->super;
743 88 : ret = g_new0 (BDFSExtInfo, 1);
744 :
745 88 : ret->label = g_strndup ((gchar *)sb->s_volume_name, sizeof (sb->s_volume_name));
746 88 : ret->uuid = decode_uuid (sb->s_uuid);
747 88 : ret->state = decode_fs_state (sb->s_state);
748 88 : ret->block_size = EXT2_BLOCK_SIZE (sb);
749 88 : ret->block_count = ext2fs_blocks_count (sb);
750 88 : ret->free_blocks = ext2fs_free_blocks_count (sb);
751 :
752 88 : ext2fs_close_free (&fs);
753 88 : return ret;
754 : }
755 :
756 : /**
757 : * bd_fs_ext2_get_info:
758 : * @device: the device the file system of which to get info for
759 : * @error: (out) (optional): place to store error (if any)
760 : *
761 : * Returns: (transfer full): information about the file system on @device or
762 : * %NULL in case of error
763 : *
764 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_QUERY
765 : */
766 19 : BDFSExt2Info* bd_fs_ext2_get_info (const gchar *device, GError **error) {
767 19 : return (BDFSExt2Info*) ext_get_info (device, error);
768 : }
769 :
770 : /**
771 : * bd_fs_ext3_get_info:
772 : * @device: the device the file system of which to get info for
773 : * @error: (out) (optional): place to store error (if any)
774 : *
775 : * Returns: (transfer full): information about the file system on @device or
776 : * %NULL in case of error
777 : *
778 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_QUERY
779 : */
780 18 : BDFSExt3Info* bd_fs_ext3_get_info (const gchar *device, GError **error) {
781 18 : return (BDFSExt3Info*) ext_get_info (device, error);
782 : }
783 :
784 : /**
785 : * bd_fs_ext4_get_info:
786 : * @device: the device the file system of which to get info for
787 : * @error: (out) (optional): place to store error (if any)
788 : *
789 : * Returns: (transfer full): information about the file system on @device or
790 : * %NULL in case of error
791 : *
792 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_QUERY
793 : */
794 45 : BDFSExt4Info* bd_fs_ext4_get_info (const gchar *device, GError **error) {
795 45 : return (BDFSExt4Info*) ext_get_info (device, error);
796 : }
797 :
798 21 : static gboolean ext_resize (const gchar *device, guint64 new_size, const BDExtraArg **extra, GError **error) {
799 21 : const gchar *args[4] = {"resize2fs", device, NULL, NULL};
800 21 : gboolean ret = FALSE;
801 :
802 21 : if (!check_deps (&avail_deps, DEPS_RESIZE2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
803 0 : return FALSE;
804 :
805 21 : if (new_size != 0)
806 : /* resize2fs doesn't understand bytes, just 512B sectors */
807 15 : args[2] = g_strdup_printf ("%"G_GUINT64_FORMAT"s", new_size / 512);
808 21 : ret = bd_utils_exec_and_report_error (args, extra, error);
809 :
810 21 : g_free ((gchar *) args[2]);
811 21 : return ret;
812 : }
813 :
814 : /**
815 : * bd_fs_ext2_resize:
816 : * @device: the device the file system of which to resize
817 : * @new_size: new requested size for the file system (if 0, the file system is
818 : * adapted to the underlying block device)
819 : * @extra: (nullable) (array zero-terminated=1): extra options for the resize (right now
820 : * passed to the 'resize2fs' utility)
821 : * @error: (out) (optional): place to store error (if any)
822 : *
823 : * Returns: whether the file system on @device was successfully resized or not
824 : *
825 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_RESIZE
826 : */
827 5 : gboolean bd_fs_ext2_resize (const gchar *device, guint64 new_size, const BDExtraArg **extra, GError **error) {
828 5 : return ext_resize (device, new_size, extra, error);
829 : }
830 :
831 : /**
832 : * bd_fs_ext3_resize:
833 : * @device: the device the file system of which to resize
834 : * @new_size: new requested size for the file system (if 0, the file system is
835 : * adapted to the underlying block device)
836 : * @extra: (nullable) (array zero-terminated=1): extra options for the resize (right now
837 : * passed to the 'resize2fs' utility)
838 : * @error: (out) (optional): place to store error (if any)
839 : *
840 : * Returns: whether the file system on @device was successfully resized or not
841 : *
842 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_RESIZE
843 : */
844 5 : gboolean bd_fs_ext3_resize (const gchar *device, guint64 new_size, const BDExtraArg **extra, GError **error) {
845 5 : return ext_resize (device, new_size, extra, error);
846 : }
847 :
848 : /**
849 : * bd_fs_ext4_resize:
850 : * @device: the device the file system of which to resize
851 : * @new_size: new requested size for the file system (if 0, the file system is
852 : * adapted to the underlying block device)
853 : * @extra: (nullable) (array zero-terminated=1): extra options for the resize (right now
854 : * passed to the 'resize2fs' utility)
855 : * @error: (out) (optional): place to store error (if any)
856 : *
857 : * Returns: whether the file system on @device was successfully resized or not
858 : *
859 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_RESIZE
860 : */
861 11 : gboolean bd_fs_ext4_resize (const gchar *device, guint64 new_size, const BDExtraArg **extra, GError **error) {
862 11 : return ext_resize (device, new_size, extra, error);
863 : }
864 :
865 6 : static guint64 ext_get_min_size (const gchar *device, GError **error) {
866 6 : const gchar *args[4] = {"resize2fs", "-P", device, NULL};
867 6 : gboolean success = FALSE;
868 6 : gchar *output = NULL;
869 6 : gchar **lines = NULL;
870 6 : gchar **line_p = NULL;
871 6 : guint64 min_size = 0;
872 6 : gchar **key_val = NULL;
873 6 : BDFSExtInfo *info = NULL;
874 :
875 6 : if (!check_deps (&avail_deps, DEPS_RESIZE2FS_MASK, deps, DEPS_LAST, &deps_check_lock, error))
876 0 : return FALSE;
877 :
878 6 : info = ext_get_info (device, error);
879 6 : if (!info)
880 0 : return 0;
881 :
882 6 : success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
883 6 : if (!success) {
884 : /* error is already populated */
885 0 : bd_fs_ext2_info_free (info);
886 0 : return 0;
887 : }
888 :
889 6 : lines = g_strsplit (output, "\n", 0);
890 6 : g_free (output);
891 :
892 6 : for (line_p=lines; *line_p; line_p++) {
893 6 : if (g_str_has_prefix (*line_p, "Estimated minimum size")) {
894 6 : key_val = g_strsplit (*line_p, ":", 2);
895 6 : if (g_strv_length (key_val) == 2) {
896 6 : min_size = g_ascii_strtoull (key_val[1], NULL, 0) * info->block_size;
897 6 : g_strfreev (lines);
898 6 : g_strfreev (key_val);
899 6 : bd_fs_ext2_info_free (info);
900 6 : return min_size;
901 : } else {
902 0 : g_strfreev (key_val);
903 0 : break;
904 : }
905 : }
906 : }
907 :
908 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
909 : "Failed to get minimum size for '%s'", device);
910 0 : g_strfreev (lines);
911 0 : bd_fs_ext2_info_free (info);
912 0 : return 0;
913 : }
914 :
915 : /**
916 : * bd_fs_ext2_get_min_size:
917 : * @device: the device containing the file system to get min size for
918 : * @error: (out) (optional): place to store error (if any)
919 : *
920 : * Returns: smallest shrunken filesystem size as reported by resize2fs
921 : * in case of error 0 is returned and @error is set
922 : *
923 : * Tech category: %BD_FS_TECH_EXT2-%BD_FS_TECH_MODE_RESIZE
924 : */
925 4 : guint64 bd_fs_ext2_get_min_size (const gchar *device, GError **error) {
926 4 : return ext_get_min_size (device, error);
927 : }
928 :
929 : /**
930 : * bd_fs_ext3_get_min_size:
931 : * @device: the device containing the file system to get min size for
932 : * @error: (out) (optional): place to store error (if any)
933 : *
934 : * Returns: smallest shrunken filesystem size as reported by resize2fs
935 : * in case of error 0 is returned and @error is set
936 : *
937 : * Tech category: %BD_FS_TECH_EXT3-%BD_FS_TECH_MODE_RESIZE
938 : */
939 1 : guint64 bd_fs_ext3_get_min_size (const gchar *device, GError **error) {
940 1 : return ext_get_min_size (device, error);
941 : }
942 :
943 : /**
944 : * bd_fs_ext4_get_min_size:
945 : * @device: the device containing the file system to get min size for
946 : * @error: (out) (optional): place to store error (if any)
947 : *
948 : * Returns: smallest shrunken filesystem size as reported by resize2fs
949 : * in case of error 0 is returned and @error is set
950 : *
951 : * Tech category: %BD_FS_TECH_EXT4-%BD_FS_TECH_MODE_RESIZE
952 : */
953 1 : guint64 bd_fs_ext4_get_min_size (const gchar *device, GError **error) {
954 1 : return ext_get_min_size (device, error);
955 : }
|