Line data Source code
1 : /*
2 : * Copyright (C) 2020 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: Vojtech Trefny <vtrefny@redhat.com>
18 : */
19 :
20 : #include <blockdev/utils.h>
21 : #include <check_deps.h>
22 : #include <uuid.h>
23 :
24 : #include "nilfs.h"
25 : #include "fs.h"
26 : #include "common.h"
27 :
28 : static volatile guint avail_deps = 0;
29 : static GMutex deps_check_lock;
30 :
31 : #define DEPS_MKFSNILFS2 0
32 : #define DEPS_MKFSNILFS2_MASK (1 << DEPS_MKFSNILFS2)
33 : #define DEPS_NILFSTUNE 1
34 : #define DEPS_NILFSTUNE_MASK (1 << DEPS_NILFSTUNE)
35 : #define DEPS_NILFSRESIZE 2
36 : #define DEPS_NILFSRESIZE_MASK (1 << DEPS_NILFSRESIZE)
37 :
38 : #define DEPS_LAST 3
39 :
40 : static const UtilDep deps[DEPS_LAST] = {
41 : {"mkfs.nilfs2", NULL, NULL, NULL},
42 : {"nilfs-tune", NULL, NULL, NULL},
43 : {"nilfs-resize", NULL, NULL, NULL},
44 : };
45 :
46 : static guint32 fs_mode_util[BD_FS_MODE_LAST+1] = {
47 : DEPS_MKFSNILFS2_MASK, /* mkfs */
48 : 0, /* wipe */
49 : 0, /* check */
50 : 0, /* repair */
51 : DEPS_NILFSTUNE_MASK, /* set-label */
52 : DEPS_NILFSTUNE_MASK, /* query */
53 : DEPS_NILFSRESIZE_MASK, /* resize */
54 : DEPS_NILFSTUNE_MASK, /* set-uuid */
55 : };
56 :
57 :
58 : #ifdef __clang__
59 : #define ZERO_INIT {}
60 : #else
61 : #define ZERO_INIT {0}
62 : #endif
63 :
64 : /**
65 : * bd_fs_nilfs2_is_tech_avail:
66 : * @tech: the queried tech
67 : * @mode: a bit mask of queried modes of operation (#BDFSTechMode) for @tech
68 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
69 : *
70 : * Returns: whether the @tech-@mode combination is available -- supported by the
71 : * plugin implementation and having all the runtime dependencies available
72 : */
73 : G_GNUC_INTERNAL gboolean
74 99 : bd_fs_nilfs2_is_tech_avail (BDFSTech tech G_GNUC_UNUSED, guint64 mode, GError **error) {
75 99 : guint32 required = 0;
76 99 : guint i = 0;
77 :
78 99 : if (mode & BD_FS_TECH_MODE_CHECK) {
79 1 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_TECH_UNAVAIL,
80 : "NILFS2 doesn't support filesystem check.");
81 1 : return FALSE;
82 : }
83 :
84 98 : if (mode & BD_FS_TECH_MODE_REPAIR) {
85 1 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_TECH_UNAVAIL,
86 : "NILFS2 doesn't support filesystem repair.");
87 1 : return FALSE;
88 : }
89 :
90 873 : for (i = 0; i <= BD_FS_MODE_LAST; i++)
91 776 : if (mode & (1 << i))
92 285 : required |= fs_mode_util[i];
93 :
94 97 : return check_deps (&avail_deps, required, deps, DEPS_LAST, &deps_check_lock, error);
95 : }
96 :
97 : /**
98 : * bd_fs_nilfs2_info_copy: (skip)
99 : *
100 : * Creates a new copy of @data.
101 : */
102 0 : BDFSNILFS2Info* bd_fs_nilfs2_info_copy (BDFSNILFS2Info *data) {
103 0 : if (data == NULL)
104 0 : return NULL;
105 :
106 0 : BDFSNILFS2Info *ret = g_new0 (BDFSNILFS2Info, 1);
107 :
108 0 : ret->label = g_strdup (data->label);
109 0 : ret->uuid = g_strdup (data->uuid);
110 0 : ret->size = data->size;
111 0 : ret->block_size = data->block_size;
112 0 : ret->free_blocks = data->free_blocks;
113 :
114 0 : return ret;
115 : }
116 :
117 : /**
118 : * bd_fs_nilfs2_info_free: (skip)
119 : *
120 : * Frees @data.
121 : */
122 0 : void bd_fs_nilfs2_info_free (BDFSNILFS2Info *data) {
123 0 : if (data == NULL)
124 0 : return;
125 :
126 0 : g_free (data->label);
127 0 : g_free (data->uuid);
128 0 : g_free (data);
129 : }
130 :
131 : G_GNUC_INTERNAL BDExtraArg **
132 8 : bd_fs_nilfs2_mkfs_options (BDFSMkfsOptions *options, const BDExtraArg **extra) {
133 8 : GPtrArray *options_array = g_ptr_array_new ();
134 8 : const BDExtraArg **extra_p = NULL;
135 :
136 8 : if (options->label && g_strcmp0 (options->label, "") != 0)
137 1 : g_ptr_array_add (options_array, bd_extra_arg_new ("-L", options->label));
138 :
139 8 : if (options->dry_run)
140 2 : g_ptr_array_add (options_array, bd_extra_arg_new ("-n", ""));
141 :
142 8 : if (options->no_discard)
143 0 : g_ptr_array_add (options_array, bd_extra_arg_new ("-K", ""));
144 :
145 8 : if (options->force)
146 2 : g_ptr_array_add (options_array, bd_extra_arg_new ("-f", ""));
147 :
148 8 : if (extra) {
149 0 : for (extra_p = extra; *extra_p; extra_p++)
150 0 : g_ptr_array_add (options_array, bd_extra_arg_copy ((BDExtraArg *) *extra_p));
151 : }
152 :
153 8 : g_ptr_array_add (options_array, NULL);
154 :
155 8 : return (BDExtraArg **) g_ptr_array_free (options_array, FALSE);
156 : }
157 :
158 : /**
159 : * bd_fs_nilfs2_mkfs:
160 : * @device: the device to create a new nilfs fs on
161 : * @extra: (nullable) (array zero-terminated=1): extra options for the creation (right now
162 : * passed to the 'mkfs.nilfs2' utility)
163 : * @error: (out) (optional): place to store error (if any)
164 : *
165 : * Returns: whether a new nilfs fs was successfully created on @device or not
166 : *
167 : * Tech category: %BD_FS_TECH_NILFS2-%BD_FS_TECH_MODE_MKFS
168 : */
169 22 : gboolean bd_fs_nilfs2_mkfs (const gchar *device, const BDExtraArg **extra, GError **error) {
170 22 : const gchar *args[4] = {"mkfs.nilfs2", "-q", device, NULL};
171 :
172 22 : if (!check_deps (&avail_deps, DEPS_MKFSNILFS2_MASK, deps, DEPS_LAST, &deps_check_lock, error))
173 0 : return FALSE;
174 :
175 22 : return bd_utils_exec_and_report_error (args, extra, error);
176 : }
177 :
178 : /**
179 : * bd_fs_nilfs2_set_label:
180 : * @device: the device containing the file system to set label for
181 : * @label: label to set
182 : * @error: (out) (optional): place to store error (if any)
183 : *
184 : * Returns: whether the label of nilfs file system on the @device was
185 : * successfully set or not
186 : *
187 : * Tech category: %BD_FS_TECH_NILFS2-%BD_FS_TECH_MODE_SET_LABEL
188 : */
189 5 : gboolean bd_fs_nilfs2_set_label (const gchar *device, const gchar *label, GError **error) {
190 5 : const gchar *args[5] = {"nilfs-tune", "-L", label, device, NULL};
191 :
192 5 : if (!check_deps (&avail_deps, DEPS_NILFSTUNE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
193 0 : return FALSE;
194 :
195 5 : return bd_utils_exec_and_report_error (args, NULL, error);
196 : }
197 :
198 : /**
199 : * bd_fs_nilfs2_check_label:
200 : * @label: label to check
201 : * @error: (out) (optional): place to store error
202 : *
203 : * Returns: whether @label is a valid label for the nilfs2 file system or not
204 : * (reason is provided in @error)
205 : *
206 : * Tech category: always available
207 : */
208 4 : gboolean bd_fs_nilfs2_check_label (const gchar *label, GError **error) {
209 4 : if (strlen (label) > 80) {
210 2 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_LABEL_INVALID,
211 : "Label for nilfs2 filesystem must be at most 80 characters long.");
212 2 : return FALSE;
213 : }
214 :
215 2 : return TRUE;
216 : }
217 :
218 : /**
219 : * bd_fs_nilfs2_set_uuid:
220 : * @device: the device containing the file system to set UUID for
221 : * @uuid: (nullable): UUID to set or %NULL to generate a new one
222 : * @error: (out) (optional): place to store error (if any)
223 : *
224 : * Returns: whether the uuid of nilfs file system on the @device was
225 : * successfully set or not
226 : *
227 : * Tech category: %BD_FS_TECH_NILFS2-%BD_FS_TECH_MODE_SET_UUID
228 : */
229 4 : gboolean bd_fs_nilfs2_set_uuid (const gchar *device, const gchar *uuid, GError **error) {
230 4 : const gchar *args[5] = {"nilfs-tune", "-U", uuid, device, NULL};
231 : uuid_t uu;
232 4 : gchar uuidbuf[37] = {0};
233 :
234 4 : if (!uuid) {
235 2 : uuid_generate (uu);
236 2 : uuid_unparse (uu, uuidbuf);
237 2 : args[2] = uuidbuf;
238 : }
239 :
240 4 : if (!check_deps (&avail_deps, DEPS_NILFSTUNE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
241 0 : return FALSE;
242 :
243 4 : return bd_utils_exec_and_report_error (args, NULL, error);
244 : }
245 :
246 : /**
247 : * bd_fs_nilfs2_check_uuid:
248 : * @uuid: UUID to check
249 : * @error: (out) (optional): place to store error
250 : *
251 : * Returns: whether @uuid is a valid UUID for the nilfs file system or not
252 : * (reason is provided in @error)
253 : *
254 : * Tech category: always available
255 : */
256 3 : gboolean bd_fs_nilfs2_check_uuid (const gchar *uuid, GError **error) {
257 3 : return check_uuid (uuid, error);
258 : }
259 :
260 : /**
261 : * bd_fs_nilfs2_get_info:
262 : * @device: the device containing the file system to get info for
263 : * @error: (out) (optional): place to store error (if any)
264 : *
265 : * Returns: (transfer full): information about the file system on @device or
266 : * %NULL in case of error
267 : *
268 : * Tech category: %BD_FS_TECH_NILFS2-%BD_FS_TECH_MODE_QUERY
269 : */
270 17 : BDFSNILFS2Info* bd_fs_nilfs2_get_info (const gchar *device, GError **error) {
271 17 : const gchar *args[4] = {"nilfs-tune", "-l", device, NULL};
272 17 : gboolean success = FALSE;
273 17 : gchar *output = NULL;
274 17 : BDFSNILFS2Info *ret = NULL;
275 17 : gchar **lines = NULL;
276 17 : gchar **line_p = NULL;
277 17 : gchar *val_start = NULL;
278 :
279 17 : if (!check_deps (&avail_deps, DEPS_NILFSTUNE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
280 0 : return NULL;
281 :
282 17 : ret = g_new0 (BDFSNILFS2Info, 1);
283 :
284 17 : success = get_uuid_label (device, &(ret->uuid), &(ret->label), error);
285 17 : if (!success) {
286 : /* error is already populated */
287 0 : bd_fs_nilfs2_info_free (ret);
288 0 : return NULL;
289 : }
290 :
291 17 : success = bd_utils_exec_and_capture_output (args, NULL, &output, error);
292 17 : if (!success) {
293 : /* error is already populated */
294 0 : bd_fs_nilfs2_info_free (ret);
295 0 : return NULL;
296 : }
297 :
298 17 : lines = g_strsplit (output, "\n", 0);
299 17 : g_free (output);
300 17 : line_p = lines;
301 :
302 136 : while (line_p && *line_p && !g_str_has_prefix (*line_p, "Block size:"))
303 119 : line_p++;
304 17 : if (!line_p || !(*line_p)) {
305 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse NILFS2 file system information");
306 0 : g_strfreev (lines);
307 0 : bd_fs_nilfs2_info_free (ret);
308 0 : return NULL;
309 : }
310 :
311 : /* * extract data from something like this: "Block size: 4096" */
312 17 : val_start = strchr (*line_p, ':');
313 17 : val_start++;
314 17 : ret->block_size = g_ascii_strtoull (val_start, NULL, 0);
315 :
316 17 : line_p = lines;
317 374 : while (line_p && *line_p && !g_str_has_prefix (*line_p, "Device size"))
318 357 : line_p++;
319 17 : if (!line_p || !(*line_p)) {
320 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse NILFS2 file system information");
321 0 : g_strfreev (lines);
322 0 : bd_fs_nilfs2_info_free (ret);
323 0 : return NULL;
324 : }
325 :
326 : /* extract data from something like this: "Device size: 167772160" */
327 17 : val_start = strchr (*line_p, ':');
328 17 : val_start++;
329 17 : ret->size = g_ascii_strtoull (val_start, NULL, 0);
330 :
331 17 : line_p = lines;
332 493 : while (line_p && *line_p && !g_str_has_prefix (*line_p, "Free blocks count"))
333 476 : line_p++;
334 17 : if (!line_p || !(*line_p)) {
335 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse NILFS2 file system information");
336 0 : g_strfreev (lines);
337 0 : bd_fs_nilfs2_info_free (ret);
338 0 : return NULL;
339 : }
340 :
341 : /* extract data from something like this: "Free blocks count: 389120" */
342 17 : val_start = strchr (*line_p, ':');
343 17 : val_start++;
344 17 : ret->free_blocks = g_ascii_strtoull (val_start, NULL, 0);
345 :
346 17 : g_strfreev (lines);
347 :
348 17 : return ret;
349 : }
350 :
351 : /**
352 : * bd_fs_nilfs2_resize:
353 : * @device: the device the file system of which to resize
354 : * @new_size: new requested size for the file system (if 0, the file system is
355 : * adapted to the underlying block device)
356 : * @error: (out) (optional): place to store error (if any)
357 : *
358 : * Returns: whether the file system on @device was successfully resized or not
359 : *
360 : * Note: Filesystem must be mounted for the resize operation.
361 : *
362 : * Tech category: %BD_FS_TECH_NILFS2-%BD_FS_TECH_MODE_RESIZE
363 : */
364 7 : gboolean bd_fs_nilfs2_resize (const gchar *device, guint64 new_size, GError **error) {
365 7 : const gchar *args[5] = {"nilfs-resize", "-y", device, NULL, NULL};
366 7 : gboolean ret = FALSE;
367 :
368 7 : if (!check_deps (&avail_deps, DEPS_NILFSRESIZE_MASK, deps, DEPS_LAST, &deps_check_lock, error))
369 0 : return FALSE;
370 :
371 7 : if (new_size != 0)
372 5 : args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT, new_size);
373 :
374 7 : ret = bd_utils_exec_and_report_error (args, NULL, error);
375 :
376 7 : g_free ((gchar *) args[3]);
377 7 : return ret;
378 : }
|