Line data Source code
1 : /*
2 : * Copyright (C) 2016 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 <ctype.h>
21 : #include <sys/file.h>
22 : #include <fcntl.h>
23 : #include <blockdev/utils.h>
24 : #include <libfdisk.h>
25 : #include <locale.h>
26 :
27 : #include "part.h"
28 :
29 : /**
30 : * SECTION: part
31 : * @short_description: plugin for operations with partition tables
32 : * @title: Part
33 : * @include: part.h
34 : *
35 : * A plugin for operations with partition tables. Currently supported table
36 : * (disk label) types are MBR and GPT. See the functions below to get an
37 : * overview of which operations are supported. If there's anything missing,
38 : * please don't hesitate to report it as this plugin (just like all the others)
39 : * is subject to future development and enhancements.
40 : *
41 : * This particular implementation of the part plugin uses libfdisk for
42 : * manipulations of both the MBR and GPT disk label types.
43 : */
44 :
45 : /**
46 : * bd_part_error_quark: (skip)
47 : */
48 0 : GQuark bd_part_error_quark (void)
49 : {
50 0 : return g_quark_from_static_string ("g-bd-part-error-quark");
51 : }
52 :
53 0 : BDPartSpec* bd_part_spec_copy (BDPartSpec *data) {
54 0 : if (data == NULL)
55 0 : return NULL;
56 :
57 0 : BDPartSpec *ret = g_new0 (BDPartSpec, 1);
58 :
59 0 : ret->path = g_strdup (data->path);
60 0 : ret->name = g_strdup (data->name);
61 0 : ret->id = g_strdup (data->id);
62 0 : ret->uuid = g_strdup (data->uuid);
63 0 : ret->type_guid = g_strdup (data->type_guid);
64 0 : ret->type_name = g_strdup (data->type_name);
65 0 : ret->type = data->type;
66 0 : ret->start = data->start;
67 0 : ret->size = data->size;
68 0 : ret->bootable = data->bootable;
69 0 : ret->attrs = data->attrs;
70 :
71 0 : return ret;
72 : }
73 :
74 0 : void bd_part_spec_free (BDPartSpec *data) {
75 0 : if (data == NULL)
76 0 : return;
77 :
78 0 : g_free (data->path);
79 0 : g_free (data->name);
80 0 : g_free (data->uuid);
81 0 : g_free (data->id);
82 0 : g_free (data->type_guid);
83 0 : g_free (data->type_name);
84 0 : g_free (data);
85 : }
86 :
87 0 : BDPartDiskSpec* bd_part_disk_spec_copy (BDPartDiskSpec *data) {
88 0 : if (data == NULL)
89 0 : return NULL;
90 :
91 0 : BDPartDiskSpec *ret = g_new0 (BDPartDiskSpec, 1);
92 :
93 0 : ret->path = g_strdup (data->path);
94 0 : ret->table_type = data->table_type;
95 0 : ret->size = data->size;
96 0 : ret->sector_size = data->sector_size;
97 :
98 0 : return ret;
99 : }
100 :
101 0 : void bd_part_disk_spec_free (BDPartDiskSpec *data) {
102 0 : if (data == NULL)
103 0 : return;
104 :
105 0 : g_free (data->path);
106 0 : g_free (data);
107 : }
108 :
109 : /* "C" locale to get the locale-agnostic error messages */
110 : static locale_t c_locale = (locale_t) 0;
111 :
112 : static int fdisk_version = 0;
113 :
114 : /* base 2 logarithm of x */
115 6 : static gint log2i (guint x) {
116 6 : gint ret = 0;
117 :
118 6 : if (x == 0)
119 1 : return -1;
120 :
121 15 : while (x >>= 1)
122 10 : ret++;
123 :
124 5 : return ret;
125 : }
126 :
127 :
128 : /**
129 : * get_part_num: (skip)
130 : *
131 : * Extract partition number from it's name (e.g. sda1).
132 : *
133 : * Returns: partition number or -1 in case of an error
134 : */
135 157 : static gint get_part_num (const gchar *part, GError **error) {
136 157 : const gchar *part_num_str = NULL;
137 157 : gint part_num = -1;
138 :
139 157 : if (!part || *part == '\0') {
140 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
141 : "Invalid partition path given: '%s'", part);
142 0 : return -1;
143 : }
144 :
145 157 : part_num_str = part + (strlen (part) - 1);
146 314 : while (isdigit (*part_num_str) || (*part_num_str == '-')) {
147 157 : part_num_str--;
148 : }
149 157 : part_num_str++;
150 :
151 157 : part_num = atoi (part_num_str);
152 157 : if (part_num == 0) {
153 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
154 : "Invalid partition path given: '%s'. Cannot extract partition number", part);
155 0 : return -1;
156 157 : } else if (part_num < 0) {
157 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
158 : "Invalid partition path given: '%s'.", part);
159 0 : return -1;
160 : }
161 :
162 157 : return part_num;
163 : }
164 :
165 339 : static int fdisk_ask_callback (struct fdisk_context *cxt G_GNUC_UNUSED, struct fdisk_ask *ask, void *data G_GNUC_UNUSED) {
166 339 : gint type = 0;
167 339 : const gchar *fdisk_msg = NULL;
168 339 : gchar *message = NULL;
169 :
170 339 : type = fdisk_ask_get_type (ask);
171 339 : fdisk_msg = fdisk_ask_print_get_mesg (ask);
172 :
173 339 : switch (type) {
174 339 : case FDISK_ASKTYPE_INFO:
175 339 : message = g_strdup_printf ("[fdisk] %s", fdisk_msg);
176 339 : bd_utils_log (BD_UTILS_LOG_INFO, message);
177 339 : g_free (message);
178 339 : break;
179 0 : case FDISK_ASKTYPE_WARNX:
180 : case FDISK_ASKTYPE_WARN:
181 0 : message = g_strdup_printf ("[fdisk] %s", fdisk_msg);
182 0 : bd_utils_log (BD_UTILS_LOG_WARNING, message);
183 0 : g_free (message);
184 0 : break;
185 0 : default:
186 0 : break;
187 : }
188 :
189 339 : return 0;
190 : }
191 :
192 376 : static struct fdisk_context* get_device_context (const gchar *disk, gboolean read_only, GError **error) {
193 376 : struct fdisk_context *cxt = fdisk_new_context ();
194 376 : gint ret = 0;
195 :
196 376 : if (!cxt) {
197 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
198 : "Failed to create a new context");
199 0 : return NULL;
200 : }
201 :
202 376 : ret = fdisk_assign_device (cxt, disk, read_only);
203 376 : if (ret != 0) {
204 6 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
205 6 : "Failed to assign the new context to disk '%s': %s", disk, strerror_l (-ret, c_locale));
206 6 : fdisk_unref_context (cxt);
207 6 : return NULL;
208 : }
209 :
210 370 : fdisk_disable_dialogs (cxt, 1);
211 370 : fdisk_set_ask (cxt, fdisk_ask_callback, NULL);
212 370 : return cxt;
213 : }
214 :
215 370 : static void close_context (struct fdisk_context *cxt) {
216 370 : gint ret = 0;
217 :
218 370 : ret = fdisk_deassign_device (cxt, 0); /* context, nosync */
219 :
220 370 : if (ret != 0)
221 : /* XXX: should report error here? */
222 0 : bd_utils_log_format (BD_UTILS_LOG_WARNING,
223 : "Failed to close and sync the device: %s",
224 0 : strerror_l (-ret, c_locale));
225 :
226 370 : fdisk_unref_context (cxt);
227 370 : }
228 :
229 148 : static gboolean write_label (struct fdisk_context *cxt, struct fdisk_table *orig, const gchar *disk, gboolean force, GError **error) {
230 148 : gint ret = 0;
231 148 : gint dev_fd = 0;
232 148 : guint num_tries = 1;
233 :
234 : /* XXX: try to grab a lock for the device so that udev doesn't step in
235 : between the two operations we need to perform (see below) with its
236 : BLKRRPART ioctl() call which makes the device busy
237 : see https://systemd.io/BLOCK_DEVICE_LOCKING */
238 148 : dev_fd = open (disk, O_RDONLY|O_CLOEXEC);
239 148 : if (dev_fd >= 0) {
240 148 : ret = flock (dev_fd, LOCK_EX|LOCK_NB);
241 257 : while ((ret != 0) && (num_tries <= 5)) {
242 109 : g_usleep (100 * 1000); /* microseconds */
243 109 : ret = flock (dev_fd, LOCK_EX|LOCK_NB);
244 109 : num_tries++;
245 : }
246 : }
247 :
248 : /* Just continue even in case we don't get the lock, there's still a
249 : chance things will just work. If not, an error will be reported
250 : anyway with no harm. */
251 :
252 148 : ret = fdisk_write_disklabel (cxt);
253 148 : if (ret != 0) {
254 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
255 0 : "Failed to write the new disklabel to disk '%s': %s", disk, strerror_l (-ret, c_locale));
256 0 : if (dev_fd >= 0)
257 0 : close (dev_fd);
258 0 : return FALSE;
259 : }
260 :
261 148 : if (force) {
262 : /* force kernel to re-read entire partition table, not only changed partitions */
263 0 : ret = fdisk_reread_partition_table (cxt);
264 0 : if (ret != 0) {
265 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
266 0 : "Failed to inform kernel about changes on the '%s' device: %s", disk, strerror_l (-ret, c_locale));
267 0 : if (dev_fd >= 0)
268 0 : close (dev_fd);
269 0 : return FALSE;
270 : }
271 148 : } else if (orig) {
272 : /* We have original table layout -- reread changed partitions */
273 94 : ret = fdisk_reread_changes (cxt, orig);
274 94 : if (ret != 0) {
275 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
276 0 : "Failed to inform kernel about changes on the '%s' device: %s", disk, strerror_l (-ret, c_locale));
277 0 : if (dev_fd >= 0)
278 0 : close (dev_fd);
279 0 : return FALSE;
280 : }
281 : }
282 :
283 148 : if (dev_fd >= 0)
284 148 : close (dev_fd);
285 :
286 148 : return TRUE;
287 : }
288 :
289 : /**
290 : * bd_part_init:
291 : *
292 : * Initializes the plugin. **This function is called automatically by the
293 : * library's initialization functions.**
294 : *
295 : */
296 36 : gboolean bd_part_init (void) {
297 36 : c_locale = newlocale (LC_ALL_MASK, "C", c_locale);
298 36 : fdisk_init_debug (0);
299 36 : fdisk_version = fdisk_get_library_version (NULL);
300 36 : return TRUE;
301 : }
302 :
303 : /**
304 : * bd_part_close:
305 : *
306 : * Cleans up after the plugin. **This function is called automatically by the
307 : * library's functions that unload it.**
308 : *
309 : */
310 36 : void bd_part_close (void) {
311 36 : c_locale = (locale_t) 0;
312 36 : }
313 :
314 : /**
315 : * bd_part_is_tech_avail:
316 : * @tech: the queried tech
317 : * @mode: a bit mask of queried modes of operation (#BDPartTechMode) for @tech
318 : * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
319 : *
320 : * Returns: whether the @tech-@mode combination is available -- supported by the
321 : * plugin implementation and having all the runtime dependencies available
322 : */
323 2 : gboolean bd_part_is_tech_avail (BDPartTech tech, guint64 mode G_GNUC_UNUSED, GError **error) {
324 2 : switch (tech) {
325 2 : case BD_PART_TECH_MBR:
326 : case BD_PART_TECH_GPT:
327 : /* all MBR and GPT-mode combinations are supported by this implementation of the
328 : * plugin, nothing extra is needed */
329 2 : return TRUE;
330 0 : default:
331 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_TECH_UNAVAIL, "Unknown technology");
332 0 : return FALSE;
333 : }
334 : }
335 :
336 : static const gchar *table_type_str[BD_PART_TABLE_UNDEF] = {"dos", "gpt"};
337 :
338 : /**
339 : * bd_part_create_table:
340 : * @disk: path of the disk block device to create partition table on
341 : * @type: type of the partition table to create
342 : * @ignore_existing: whether to ignore/overwrite the existing table or not
343 : * (reports an error if %FALSE and there's some table on @disk)
344 : * @error: (out) (optional): place to store error (if any)
345 : *
346 : * Returns: whether the partition table was successfully created or not
347 : *
348 : * Tech category: %BD_PART_TECH_MODE_CREATE_TABLE + the tech according to @type
349 : */
350 50 : gboolean bd_part_create_table (const gchar *disk, BDPartTableType type, gboolean ignore_existing, GError **error) {
351 50 : struct fdisk_context *cxt = NULL;
352 50 : gint ret = 0;
353 50 : guint64 progress_id = 0;
354 50 : gchar *msg = NULL;
355 50 : GError *l_error = NULL;
356 :
357 50 : msg = g_strdup_printf ("Starting creation of a new partition table on '%s'", disk);
358 50 : progress_id = bd_utils_report_started (msg);
359 50 : g_free (msg);
360 :
361 50 : cxt = get_device_context (disk, FALSE, &l_error);
362 50 : if (!cxt) {
363 : /* error is already populated */
364 4 : bd_utils_report_finished (progress_id, l_error->message);
365 4 : g_propagate_error (error, l_error);
366 4 : return FALSE;
367 : }
368 :
369 46 : if (!ignore_existing && fdisk_has_label (cxt)) {
370 2 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_EXISTS,
371 : "Device '%s' already contains a partition table", disk);
372 2 : bd_utils_report_finished (progress_id, l_error->message);
373 2 : g_propagate_error (error, l_error);
374 2 : close_context (cxt);
375 2 : return FALSE;
376 : }
377 :
378 44 : ret = fdisk_create_disklabel (cxt, table_type_str[type]);
379 44 : if (ret != 0) {
380 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
381 0 : "Failed to create a new disklabel for disk '%s': %s", disk, strerror_l (-ret, c_locale));
382 0 : bd_utils_report_finished (progress_id, l_error->message);
383 0 : g_propagate_error (error, l_error);
384 0 : close_context (cxt);
385 0 : return FALSE;
386 : }
387 :
388 44 : if (!write_label (cxt, NULL, disk, FALSE, &l_error)) {
389 0 : bd_utils_report_finished (progress_id, l_error->message);
390 0 : g_propagate_error (error, l_error);
391 0 : close_context (cxt);
392 0 : return FALSE;
393 : }
394 :
395 44 : close_context (cxt);
396 44 : bd_utils_report_finished (progress_id, "Completed");
397 44 : return TRUE;
398 : }
399 :
400 45 : static gchar* get_part_type_guid_and_gpt_flags (const gchar *device, int part_num, guint64 *attrs, char **type_name, GError **error) {
401 45 : struct fdisk_context *cxt = NULL;
402 45 : struct fdisk_label *lb = NULL;
403 45 : struct fdisk_partition *pa = NULL;
404 45 : struct fdisk_parttype *ptype = NULL;
405 45 : const gchar *label_name = NULL;
406 45 : const gchar *ptype_string = NULL;
407 45 : gchar *ret = NULL;
408 45 : gint status = 0;
409 :
410 : /* first partition in fdisk is 0 */
411 45 : part_num--;
412 :
413 45 : cxt = get_device_context (device, TRUE, error);
414 45 : if (!cxt) {
415 : /* error is already populated */
416 0 : return NULL;
417 : }
418 :
419 45 : lb = fdisk_get_label (cxt, NULL);
420 45 : if (!lb) {
421 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
422 : "Failed to read partition table on device '%s'", device);
423 0 : close_context (cxt);
424 0 : return NULL;
425 : }
426 :
427 45 : label_name = fdisk_label_get_name (lb);
428 45 : if (g_strcmp0 (label_name, table_type_str[BD_PART_TABLE_GPT]) != 0) {
429 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
430 : "Setting GPT flags is not supported on '%s' partition table", label_name);
431 0 : close_context (cxt);
432 0 : return NULL;
433 : }
434 :
435 45 : if (attrs) {
436 45 : status = fdisk_gpt_get_partition_attrs (cxt, part_num, attrs);
437 45 : if (status < 0) {
438 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
439 : "Failed to read GPT attributes");
440 0 : close_context (cxt);
441 0 : return NULL;
442 : }
443 : }
444 :
445 45 : status = fdisk_get_partition (cxt, part_num, &pa);
446 45 : if (status != 0) {
447 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
448 : "Failed to get partition %d on device '%s'", part_num, device);
449 0 : close_context (cxt);
450 0 : return NULL;
451 : }
452 :
453 45 : ptype = fdisk_partition_get_type (pa);
454 45 : if (!ptype) {
455 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
456 : "Failed to get partition type for partition %d on device '%s'", part_num, device);
457 0 : fdisk_unref_partition (pa);
458 0 : close_context (cxt);
459 0 : return NULL;
460 : }
461 :
462 : /* part type name -- human readable string, e.g. "Microsoft Reserved Partition" */
463 45 : ptype_string = fdisk_parttype_get_name (ptype);
464 45 : if (!ptype_string) {
465 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
466 : "Failed to get partition type string for partition %d on device '%s'", part_num, device);
467 0 : fdisk_unref_partition (pa);
468 0 : close_context (cxt);
469 0 : return NULL;
470 : }
471 :
472 45 : *type_name = g_strdup (ptype_string);
473 :
474 : /* part type string -- GUID as a string */
475 45 : ptype_string = fdisk_parttype_get_string (ptype);
476 45 : if (!ptype_string) {
477 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
478 : "Failed to get partition type for partition %d on device '%s'", part_num, device);
479 0 : fdisk_unref_partition (pa);
480 0 : close_context (cxt);
481 0 : return NULL;
482 : }
483 :
484 45 : ret = g_strdup (ptype_string);
485 :
486 45 : fdisk_unref_partition (pa);
487 45 : close_context (cxt);
488 45 : return ret;
489 : }
490 :
491 310 : static BDPartSpec* get_part_spec_fdisk (struct fdisk_context *cxt, struct fdisk_partition *pa, GError **error) {
492 310 : struct fdisk_label *lb = NULL;
493 310 : struct fdisk_parttype *ptype = NULL;
494 310 : BDPartSpec *ret = NULL;
495 310 : const gchar *devname = NULL;
496 310 : const gchar *partname = NULL;
497 310 : const gchar *partuuid = NULL;
498 310 : GError *l_error = NULL;
499 :
500 310 : ret = g_new0 (BDPartSpec, 1);
501 :
502 310 : devname = fdisk_get_devname (cxt);
503 :
504 310 : if (fdisk_partition_has_partno (pa)) {
505 205 : if (isdigit (devname[strlen (devname) - 1]))
506 0 : ret->path = g_strdup_printf ("%sp%zu", devname, fdisk_partition_get_partno (pa) + 1);
507 : else
508 205 : ret->path = g_strdup_printf ("%s%zu", devname, fdisk_partition_get_partno (pa) + 1);
509 : }
510 :
511 310 : partname = fdisk_partition_get_name (pa);
512 310 : if (partname)
513 45 : ret->name = g_strdup (partname);
514 :
515 310 : partuuid = fdisk_partition_get_uuid (pa);
516 310 : if (partuuid)
517 45 : ret->uuid = g_strdup (partuuid);
518 :
519 310 : if (fdisk_partition_is_container (pa))
520 18 : ret->type = BD_PART_TYPE_EXTENDED;
521 292 : else if (fdisk_partition_is_nested (pa))
522 79 : ret->type = BD_PART_TYPE_LOGICAL;
523 : else
524 213 : ret->type = BD_PART_TYPE_NORMAL;
525 :
526 310 : if (fdisk_partition_is_freespace (pa))
527 105 : ret->type |= BD_PART_TYPE_FREESPACE;
528 :
529 310 : if (fdisk_partition_has_start (pa))
530 310 : ret->start = (guint64) fdisk_partition_get_start (pa) * fdisk_get_sector_size (cxt);
531 :
532 310 : if (fdisk_partition_has_size (pa))
533 310 : ret->size = (guint64) fdisk_partition_get_size (pa) * fdisk_get_sector_size (cxt);
534 :
535 310 : lb = fdisk_get_label (cxt, NULL);
536 310 : if (!lb) {
537 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
538 : "Failed to read partition table.");
539 0 : bd_part_spec_free (ret);
540 0 : return NULL;
541 : }
542 :
543 310 : if (g_strcmp0 (fdisk_label_get_name (lb), "gpt") == 0) {
544 45 : if (ret->type == BD_PART_TYPE_NORMAL) {
545 : /* only 'normal' partitions have GUIDs */
546 45 : ret->type_guid = get_part_type_guid_and_gpt_flags (devname, fdisk_partition_get_partno (pa) + 1,
547 45 : &(ret->attrs), &(ret->type_name), &l_error);
548 45 : if (!ret->type_guid && l_error) {
549 0 : g_propagate_error (error, l_error);
550 0 : bd_part_spec_free (ret);
551 0 : return NULL;
552 : }
553 : }
554 265 : } else if (g_strcmp0 (fdisk_label_get_name (lb), "dos") == 0) {
555 : /* freespace and extended have no type/ids */
556 265 : if (ret->type == BD_PART_TYPE_NORMAL || ret->type == BD_PART_TYPE_LOGICAL || ret->type == BD_PART_TYPE_EXTENDED) {
557 160 : ptype = fdisk_partition_get_type (pa);
558 160 : if (!ptype) {
559 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
560 : "Failed to get partition type.");
561 0 : bd_part_spec_free (ret);
562 0 : return NULL;
563 : }
564 160 : ret->id = g_strdup_printf ("0x%02x", fdisk_parttype_get_code (ptype));
565 : }
566 265 : if (fdisk_partition_is_bootable (pa) == 1)
567 1 : ret->bootable = TRUE;
568 : }
569 :
570 310 : return ret;
571 : }
572 :
573 : /**
574 : * bd_part_get_part_spec:
575 : * @disk: disk to remove the partition from
576 : * @part: partition to get spec for
577 : * @error: (out) (optional): place to store error (if any)
578 : *
579 : * Returns: (transfer full): spec of the @part partition from @disk or %NULL in case of error
580 : *
581 : * Tech category: %BD_PART_TECH_MODE_QUERY_PART + the tech according to the partition table type
582 : */
583 114 : BDPartSpec* bd_part_get_part_spec (const gchar *disk, const gchar *part, GError **error) {
584 114 : struct fdisk_context *cxt = NULL;
585 114 : struct fdisk_partition *pa = NULL;
586 114 : gint status = 0;
587 114 : gint part_num = 0;
588 114 : BDPartSpec *ret = NULL;
589 :
590 114 : part_num = get_part_num (part, error);
591 114 : if (part_num == -1)
592 0 : return NULL;
593 :
594 : /* first partition in fdisk is 0 */
595 114 : part_num--;
596 :
597 114 : cxt = get_device_context (disk, TRUE, error);
598 114 : if (!cxt) {
599 : /* error is already populated */
600 0 : return NULL;
601 : }
602 :
603 114 : status = fdisk_get_partition (cxt, part_num, &pa);
604 114 : if (status != 0) {
605 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
606 : "Failed to get partition %d on device '%s'", part_num, disk);
607 0 : close_context (cxt);
608 0 : return NULL;
609 : }
610 :
611 114 : ret = get_part_spec_fdisk (cxt, pa, error);
612 :
613 114 : fdisk_unref_partition (pa);
614 114 : close_context (cxt);
615 :
616 114 : return ret;
617 : }
618 :
619 32 : static BDPartSpec** get_disk_parts (const gchar *disk, gboolean parts, gboolean freespaces, gboolean metadata, GError **error) {
620 32 : struct fdisk_context *cxt = NULL;
621 32 : struct fdisk_table *table = NULL;
622 32 : struct fdisk_partition *pa = NULL;
623 32 : struct fdisk_iter *itr = NULL;
624 32 : BDPartSpec *spec = NULL;
625 32 : BDPartSpec *prev_spec = NULL;
626 32 : GPtrArray *array = NULL;
627 32 : gint status = 0;
628 :
629 32 : cxt = get_device_context (disk, TRUE, error);
630 32 : if (!cxt) {
631 : /* error is already populated */
632 0 : return NULL;
633 : }
634 :
635 32 : table = fdisk_new_table ();
636 32 : if (!table) {
637 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
638 : "Failed to create a new table");
639 0 : close_context (cxt);
640 0 : return NULL;
641 : }
642 :
643 32 : itr = fdisk_new_iter (FDISK_ITER_FORWARD);
644 32 : if (!itr) {
645 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
646 : "Failed to create a new iterator");
647 0 : close_context (cxt);
648 0 : return NULL;
649 : }
650 :
651 32 : if (parts) {
652 21 : status = fdisk_get_partitions (cxt, &table);
653 21 : if (status != 0) {
654 1 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
655 : "Failed to get partitions");
656 1 : fdisk_free_iter (itr);
657 1 : fdisk_unref_table (table);
658 1 : close_context (cxt);
659 1 : return NULL;
660 : }
661 : }
662 :
663 31 : if (freespaces) {
664 23 : status = fdisk_get_freespaces (cxt, &table);
665 23 : if (status != 0) {
666 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
667 : "Failed to get free spaces");
668 0 : fdisk_free_iter (itr);
669 0 : fdisk_unref_table (table);
670 0 : close_context (cxt);
671 0 : return NULL;
672 : }
673 : }
674 :
675 : /* sort partitions by start */
676 31 : status = fdisk_table_sort_partitions (table, fdisk_partition_cmp_start);
677 31 : if (status != 0) {
678 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
679 : "Failed to sort partitions");
680 0 : fdisk_free_iter (itr);
681 0 : fdisk_unref_table (table);
682 0 : close_context (cxt);
683 0 : return NULL;
684 : }
685 :
686 31 : array = g_ptr_array_new_with_free_func ((GDestroyNotify) (void *) bd_part_spec_free);
687 :
688 227 : while (fdisk_table_next_partition (table, itr, &pa) == 0) {
689 196 : spec = get_part_spec_fdisk (cxt, pa, error);
690 196 : if (!spec) {
691 0 : g_ptr_array_free (array, TRUE);
692 0 : fdisk_free_iter (itr);
693 0 : fdisk_unref_table (table);
694 0 : close_context (cxt);
695 0 : return NULL;
696 : }
697 :
698 : /* libfdisk doesn't have a special partition for metadata so we need to add
699 : a special metadata partition to the "empty" spaces between partitions
700 : and free spaces to mimic behaviour of parted
701 : metadata partitions should be present in the extended partition in front
702 : of every logical partition */
703 196 : if (prev_spec && metadata) {
704 144 : if ((spec->start > prev_spec->start + prev_spec->size) ||
705 108 : (prev_spec->type == BD_PART_TYPE_EXTENDED && spec->start > prev_spec->start) ) {
706 48 : BDPartSpec *ext_meta = g_new0 (BDPartSpec, 1);
707 48 : ext_meta->name = NULL;
708 48 : ext_meta->path = NULL;
709 :
710 48 : if (prev_spec->type == BD_PART_TYPE_EXTENDED) {
711 12 : ext_meta->start = prev_spec->start;
712 12 : ext_meta->size = spec->start - ext_meta->start;
713 12 : ext_meta->type = BD_PART_TYPE_METADATA | BD_PART_TYPE_LOGICAL;
714 : } else {
715 36 : ext_meta->start = prev_spec->start + prev_spec->size;
716 36 : ext_meta->size = spec->start - ext_meta->start;
717 36 : if (spec->type & BD_PART_TYPE_LOGICAL)
718 36 : ext_meta->type = BD_PART_TYPE_METADATA | BD_PART_TYPE_LOGICAL;
719 : else
720 0 : ext_meta->type = BD_PART_TYPE_METADATA;
721 : }
722 48 : ext_meta->type_guid = NULL;
723 :
724 48 : g_ptr_array_add (array, ext_meta);
725 : }
726 : }
727 :
728 196 : prev_spec = spec;
729 196 : g_ptr_array_add (array, spec);
730 : }
731 :
732 31 : fdisk_free_iter (itr);
733 31 : fdisk_unref_table (table);
734 31 : close_context (cxt);
735 :
736 31 : g_ptr_array_add (array, NULL);
737 31 : return (BDPartSpec **) g_ptr_array_free (array, FALSE);
738 : }
739 :
740 : /**
741 : * bd_part_get_part_by_pos:
742 : * @disk: disk to remove the partition from
743 : * @position: position (in bytes) determining the partition
744 : * @error: (out) (optional): place to store error (if any)
745 : *
746 : * Returns: (transfer full): spec of the partition from @disk spanning over the @position or %NULL if no such
747 : * partition exists or in case of error (@error is set)
748 : *
749 : * Tech category: %BD_PART_TECH_MODE_QUERY_PART + the tech according to the partition table type
750 : */
751 12 : BDPartSpec* bd_part_get_part_by_pos (const gchar *disk, guint64 position, GError **error) {
752 12 : BDPartSpec **parts = NULL;
753 12 : BDPartSpec *ret = NULL;
754 :
755 12 : parts = get_disk_parts (disk, TRUE, TRUE, TRUE, error);
756 12 : if (!parts)
757 0 : return NULL;
758 :
759 116 : for (BDPartSpec **parts_p = parts; *parts_p; parts_p++) {
760 116 : if ((*parts_p)->start <= position && ((*parts_p)->start + (*parts_p)->size) > position) {
761 20 : if ((*parts_p)->type == BD_PART_TYPE_EXTENDED) {
762 : /* we don't want to return extended partition here -- there
763 : is either another logical or free space at this position */
764 8 : continue;
765 : }
766 :
767 12 : ret = *parts_p;
768 12 : break;
769 : }
770 : }
771 :
772 : /* free the array except the selected element */
773 216 : for (BDPartSpec **parts_p = parts; *parts_p; parts_p++)
774 204 : if (*parts_p != ret)
775 192 : bd_part_spec_free (*parts_p);
776 12 : g_free (parts);
777 :
778 12 : return ret;
779 : }
780 :
781 : /**
782 : * bd_part_get_disk_spec:
783 : * @disk: disk to get information about
784 : * @error: (out) (optional): place to store error (if any)
785 : *
786 : * Returns: (transfer full): information about the given @disk or %NULL (in case of error)
787 : *
788 : * Tech category: %BD_PART_TECH_MODE_QUERY_TABLE + the tech according to the partition table type
789 : */
790 8 : BDPartDiskSpec* bd_part_get_disk_spec (const gchar *disk, GError **error) {
791 8 : struct fdisk_context *cxt = NULL;
792 8 : struct fdisk_label *lb = NULL;
793 8 : BDPartDiskSpec *ret = NULL;
794 8 : const gchar *label_name = NULL;
795 8 : BDPartTableType type = BD_PART_TABLE_UNDEF;
796 8 : gboolean found = FALSE;
797 :
798 8 : cxt = get_device_context (disk, TRUE, error);
799 8 : if (!cxt) {
800 : /* error is already populated */
801 2 : return NULL;
802 : }
803 :
804 6 : ret = g_new0 (BDPartDiskSpec, 1);
805 6 : ret->path = g_strdup (fdisk_get_devname (cxt));
806 6 : ret->sector_size = (guint64) fdisk_get_sector_size (cxt);
807 6 : ret->size = fdisk_get_nsectors (cxt) * ret->sector_size;
808 :
809 6 : lb = fdisk_get_label (cxt, NULL);
810 6 : if (lb) {
811 4 : label_name = fdisk_label_get_name (lb);
812 10 : for (type=BD_PART_TABLE_MSDOS; !found && type < BD_PART_TABLE_UNDEF; type++) {
813 6 : if (g_strcmp0 (label_name, table_type_str[type]) == 0) {
814 4 : ret->table_type = type;
815 4 : found = TRUE;
816 : }
817 : }
818 4 : if (!found)
819 0 : ret->table_type = BD_PART_TABLE_UNDEF;
820 : } else
821 2 : ret->table_type = BD_PART_TABLE_UNDEF;
822 :
823 6 : close_context (cxt);
824 :
825 6 : return ret;
826 : }
827 :
828 : /**
829 : * bd_part_get_disk_parts:
830 : * @disk: disk to get information about partitions for
831 : * @error: (out) (optional): place to store error (if any)
832 : *
833 : * Returns: (transfer full) (array zero-terminated=1): specs of the partitions from @disk or %NULL in case of error
834 : *
835 : * Tech category: %BD_PART_TECH_MODE_QUERY_TABLE + the tech according to the partition table type
836 : */
837 9 : BDPartSpec** bd_part_get_disk_parts (const gchar *disk, GError **error) {
838 9 : return get_disk_parts (disk, TRUE, FALSE, FALSE, error);
839 : }
840 :
841 : /**
842 : * bd_part_get_disk_free_regions:
843 : * @disk: disk to get free regions for
844 : * @error: (out) (optional): place to store error (if any)
845 : *
846 : * Returns: (transfer full) (array zero-terminated=1): specs of the free regions from @disk or %NULL in case of error
847 : *
848 : * Tech category: %BD_PART_TECH_MODE_QUERY_TABLE + the tech according to the partition table type
849 : */
850 11 : BDPartSpec** bd_part_get_disk_free_regions (const gchar *disk, GError **error) {
851 11 : return get_disk_parts (disk, FALSE, TRUE, FALSE, error);
852 : }
853 :
854 : /**
855 : * bd_part_get_best_free_region:
856 : * @disk: disk to get the best free region for
857 : * @type: type of the partition that is planned to be added
858 : * @size: size of the partition to be added
859 : * @error: (out) (optional): place to store error (if any)
860 : *
861 : * Returns: (transfer full): spec of the best free region on @disk for a new partition of type @type
862 : * with the size of @size or %NULL if there is none such region or if
863 : * there was an error (@error gets populated)
864 : *
865 : * Note: For the @type %BD_PART_TYPE_NORMAL, the smallest possible space that *is not* in an extended partition
866 : * is found. For the @type %BD_PART_TYPE_LOGICAL, the smallest possible space that *is* in an extended
867 : * partition is found. For %BD_PART_TYPE_EXTENDED, the biggest possible space is found as long as there
868 : * is no other extended partition (there can only be one).
869 : *
870 : * Tech category: %BD_PART_TECH_MODE_QUERY_TABLE + the tech according to the partition table type
871 : */
872 7 : BDPartSpec* bd_part_get_best_free_region (const gchar *disk, BDPartType type, guint64 size, GError **error) {
873 7 : BDPartSpec **free_regs = NULL;
874 7 : BDPartSpec **free_reg_p = NULL;
875 7 : BDPartSpec *ret = NULL;
876 :
877 7 : free_regs = bd_part_get_disk_free_regions (disk, error);
878 7 : if (!free_regs)
879 : /* error should be populated */
880 0 : return NULL;
881 7 : if (!(*free_regs)) {
882 : /* no free regions */
883 0 : g_free (free_regs);
884 0 : return NULL;
885 : }
886 :
887 7 : if (type == BD_PART_TYPE_NORMAL) {
888 18 : for (free_reg_p=free_regs; *free_reg_p; free_reg_p++) {
889 : /* check if it has enough space and is not inside an extended partition */
890 14 : if ((*free_reg_p)->size > size && !((*free_reg_p)->type & BD_PART_TYPE_LOGICAL))
891 : /* if it is the first that would fit or if it is smaller than
892 : what we found earlier, it is a better match */
893 8 : if (!ret || ((*free_reg_p)->size < ret->size))
894 7 : ret = *free_reg_p;
895 : }
896 3 : } else if (type == BD_PART_TYPE_EXTENDED) {
897 3 : for (free_reg_p=free_regs; *free_reg_p; free_reg_p++) {
898 : /* if there already is an extended partition, there cannot be another one */
899 2 : if ((*free_reg_p)->type & BD_PART_TYPE_LOGICAL) {
900 0 : for (free_reg_p=free_regs; *free_reg_p; free_reg_p++)
901 0 : bd_part_spec_free (*free_reg_p);
902 0 : g_free (free_regs);
903 0 : return NULL;
904 : }
905 : /* check if it has enough space */
906 2 : if ((*free_reg_p)->size > size)
907 : /* if it is the first that would fit or if it is bigger than
908 : what we found earlier, it is a better match */
909 2 : if (!ret || ((*free_reg_p)->size > ret->size))
910 2 : ret = *free_reg_p;
911 : }
912 2 : } else if (type == BD_PART_TYPE_LOGICAL) {
913 12 : for (free_reg_p=free_regs; *free_reg_p; free_reg_p++) {
914 : /* check if it has enough space and is inside an extended partition */
915 10 : if ((*free_reg_p)->size > size && ((*free_reg_p)->type & BD_PART_TYPE_LOGICAL))
916 : /* if it is the first that would fit or if it is smaller than
917 : what we found earlier, it is a better match */
918 3 : if (!ret || ((*free_reg_p)->size < ret->size))
919 3 : ret = *free_reg_p;
920 : }
921 : }
922 :
923 : /* free all the other specs and return the best one */
924 33 : for (free_reg_p=free_regs; *free_reg_p; free_reg_p++)
925 26 : if (*free_reg_p != ret)
926 19 : bd_part_spec_free (*free_reg_p);
927 7 : g_free (free_regs);
928 :
929 7 : return ret;
930 : }
931 :
932 : /**
933 : * bd_part_create_part:
934 : * @disk: disk to create partition on
935 : * @type: type of the partition to create (if %BD_PART_TYPE_REQ_NEXT, the
936 : * partition type will be determined automatically based on the existing
937 : * partitions)
938 : * @start: where the partition should start (i.e. offset from the disk start)
939 : * @size: desired size of the partition (if 0, a max-sized partition is created)
940 : * @align: alignment to use for the partition
941 : * @error: (out) (optional): place to store error (if any)
942 : *
943 : * Returns: (transfer full): specification of the created partition or %NULL in case of error
944 : *
945 : * NOTE: The resulting partition may start at a different position than given by
946 : * @start and can have different size than @size due to alignment.
947 : *
948 : * Tech category: %BD_PART_TECH_MODE_MODIFY_TABLE + the tech according to the partition table type
949 : */
950 82 : BDPartSpec* bd_part_create_part (const gchar *disk, BDPartTypeReq type, guint64 start, guint64 size, BDPartAlign align, GError **error) {
951 82 : struct fdisk_context *cxt = NULL;
952 82 : struct fdisk_partition *npa = NULL;
953 82 : gint status = 0;
954 82 : BDPartSpec *ret = NULL;
955 82 : guint64 progress_id = 0;
956 82 : gchar *msg = NULL;
957 82 : guint64 sector_size = 0;
958 82 : guint64 grain_size = 0;
959 82 : guint64 end = 0;
960 82 : struct fdisk_parttype *ptype = NULL;
961 82 : struct fdisk_label *lbl = NULL;
962 82 : struct fdisk_table *table = NULL;
963 82 : struct fdisk_iter *iter = NULL;
964 82 : struct fdisk_partition *pa = NULL;
965 82 : struct fdisk_partition *epa = NULL;
966 82 : struct fdisk_partition *in_pa = NULL;
967 82 : struct fdisk_partition *n_epa = NULL;
968 82 : guint n_parts = 0;
969 82 : gboolean on_gpt = FALSE;
970 82 : size_t partno = 0;
971 82 : gchar *ppath = NULL;
972 82 : gboolean new_extended = FALSE;
973 82 : GError *l_error = NULL;
974 :
975 82 : msg = g_strdup_printf ("Started adding partition to '%s'", disk);
976 82 : progress_id = bd_utils_report_started (msg);
977 82 : g_free (msg);
978 :
979 82 : cxt = get_device_context (disk, FALSE, &l_error);
980 82 : if (!cxt) {
981 : /* error is already populated */
982 0 : bd_utils_report_finished (progress_id, l_error->message);
983 0 : g_propagate_error (error, l_error);
984 0 : return NULL;
985 : }
986 :
987 82 : status = fdisk_get_partitions (cxt, &table);
988 82 : if (status != 0) {
989 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
990 0 : "Failed to get existing partitions on the device: %s", strerror_l (-status, c_locale));
991 0 : fdisk_unref_partition (npa);
992 0 : close_context (cxt);
993 0 : bd_utils_report_finished (progress_id, l_error->message);
994 0 : g_propagate_error (error, l_error);
995 0 : return NULL;
996 : }
997 :
998 82 : npa = fdisk_new_partition ();
999 82 : if (!npa) {
1000 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1001 : "Failed to create new partition object");
1002 0 : fdisk_unref_table (table);
1003 0 : close_context (cxt);
1004 0 : bd_utils_report_finished (progress_id, l_error->message);
1005 0 : g_propagate_error (error, l_error);
1006 0 : return NULL;
1007 : }
1008 :
1009 82 : sector_size = (guint64) fdisk_get_sector_size (cxt);
1010 82 : grain_size = (guint64) fdisk_get_grain_size (cxt);
1011 :
1012 82 : if (align == BD_PART_ALIGN_NONE)
1013 15 : grain_size = sector_size;
1014 67 : else if (align == BD_PART_ALIGN_MINIMAL)
1015 0 : grain_size = (guint64) fdisk_get_minimal_iosize (cxt);
1016 : /* else OPTIMAL or unknown -> nothing to do */
1017 :
1018 82 : status = fdisk_save_user_grain (cxt, grain_size);
1019 82 : if (status != 0) {
1020 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1021 : "Failed to setup alignment");
1022 0 : fdisk_unref_table (table);
1023 0 : close_context (cxt);
1024 0 : bd_utils_report_finished (progress_id, l_error->message);
1025 0 : g_propagate_error (error, l_error);
1026 0 : return NULL;
1027 : }
1028 :
1029 : /* this is needed so that the saved grain size from above becomes
1030 : * effective */
1031 82 : status = fdisk_reset_device_properties (cxt);
1032 82 : if (status != 0) {
1033 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1034 : "Failed to setup alignment");
1035 0 : fdisk_unref_table (table);
1036 0 : close_context (cxt);
1037 0 : bd_utils_report_finished (progress_id, l_error->message);
1038 0 : g_propagate_error (error, l_error);
1039 0 : return NULL;
1040 : }
1041 :
1042 : /* set first usable sector to 1 for none and minimal alignments
1043 : we need to set this here, because fdisk_reset_device_properties */
1044 82 : if (align == BD_PART_ALIGN_NONE || align == BD_PART_ALIGN_MINIMAL)
1045 15 : fdisk_set_first_lba (cxt, 1);
1046 :
1047 82 : grain_size = (guint64) fdisk_get_grain_size (cxt);
1048 :
1049 : /* align start up to sectors, we will align it further based on grain_size
1050 : using libfdisk later */
1051 82 : start = (start + sector_size - 1) / sector_size;
1052 :
1053 : /* start on sector 0 doesn't work with libfdisk alignment -- everything
1054 : else is properly aligned, but zero is aligned to zero */
1055 82 : if (start == 0)
1056 2 : start = 1;
1057 :
1058 82 : start = fdisk_align_lba (cxt, (fdisk_sector_t) start, FDISK_ALIGN_UP);
1059 :
1060 82 : if (size == 0)
1061 : /* no size specified, set the end to default (maximum) */
1062 8 : fdisk_partition_end_follow_default (npa, 1);
1063 : else {
1064 : /* align size down */
1065 74 : size = (size / grain_size) * grain_size;
1066 74 : size = size / sector_size;
1067 :
1068 : /* use libfdisk to align the end sector */
1069 74 : end = start + size;
1070 74 : end = fdisk_align_lba (cxt, (fdisk_sector_t) end, FDISK_ALIGN_DOWN);
1071 74 : size = end - start;
1072 :
1073 74 : if (fdisk_partition_set_size (npa, size) != 0) {
1074 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1075 : "Failed to set partition size");
1076 0 : fdisk_unref_table (table);
1077 0 : fdisk_unref_partition (npa);
1078 0 : close_context (cxt);
1079 0 : bd_utils_report_finished (progress_id, l_error->message);
1080 0 : g_propagate_error (error, l_error);
1081 0 : return NULL;
1082 : }
1083 : }
1084 :
1085 82 : fdisk_partition_partno_follow_default (npa, 1);
1086 82 : lbl = fdisk_get_label (cxt, NULL);
1087 82 : on_gpt = g_strcmp0 (fdisk_label_get_name (lbl), "gpt") == 0;
1088 :
1089 : /* GPT is easy, all partitions are the same (NORMAL) */
1090 82 : if (on_gpt && type == BD_PART_TYPE_REQ_NEXT)
1091 10 : type = BD_PART_TYPE_REQ_NORMAL;
1092 :
1093 82 : if (on_gpt && type != BD_PART_TYPE_REQ_NORMAL) {
1094 2 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1095 : "Only normal partitions are supported on GPT.");
1096 2 : fdisk_unref_table (table);
1097 2 : fdisk_unref_partition (npa);
1098 2 : close_context (cxt);
1099 2 : bd_utils_report_finished (progress_id, l_error->message);
1100 2 : g_propagate_error (error, l_error);
1101 2 : return NULL;
1102 : }
1103 :
1104 : /* on DOS we may have to decide if requested */
1105 80 : if (type == BD_PART_TYPE_REQ_NEXT) {
1106 6 : iter = fdisk_new_iter (FDISK_ITER_FORWARD);
1107 23 : while (fdisk_table_next_partition (table, iter, &pa) == 0) {
1108 17 : if (fdisk_partition_is_freespace (pa))
1109 0 : continue;
1110 17 : if (!epa && fdisk_partition_is_container (pa))
1111 2 : epa = pa;
1112 31 : if (!in_pa && fdisk_partition_has_start (pa) && fdisk_partition_has_size (pa) &&
1113 14 : fdisk_partition_get_start (pa) <= start &&
1114 14 : (start < (fdisk_partition_get_start (pa) + fdisk_partition_get_size (pa))))
1115 2 : in_pa = pa;
1116 17 : n_parts++;
1117 : }
1118 6 : if (in_pa) {
1119 2 : if (epa == in_pa)
1120 : /* creating a partition inside an extended partition -> LOGICAL */
1121 2 : type = BD_PART_TYPE_REQ_LOGICAL;
1122 : else {
1123 : /* trying to create a partition inside an existing one, but not
1124 : an extended one -> error */
1125 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1126 : "Cannot create a partition inside an existing non-extended one");
1127 0 : fdisk_unref_partition (npa);
1128 0 : fdisk_free_iter (iter);
1129 0 : fdisk_unref_table (table);
1130 0 : close_context (cxt);
1131 0 : bd_utils_report_finished (progress_id, l_error->message);
1132 0 : g_propagate_error (error, l_error);
1133 0 : return NULL;
1134 : }
1135 4 : } else if (epa)
1136 : /* there's an extended partition already and we are creating a new
1137 : one outside of it */
1138 0 : type = BD_PART_TYPE_REQ_NORMAL;
1139 4 : else if (n_parts == 3) {
1140 : /* already 3 primary partitions -> create an extended partition of
1141 : the biggest possible size and a logical partition as requested in
1142 : it */
1143 1 : new_extended = TRUE;
1144 1 : n_epa = fdisk_new_partition ();
1145 1 : if (!n_epa) {
1146 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1147 : "Failed to create new partition object");
1148 0 : fdisk_unref_partition (npa);
1149 0 : close_context (cxt);
1150 0 : bd_utils_report_finished (progress_id, l_error->message);
1151 0 : g_propagate_error (error, l_error);
1152 0 : return NULL;
1153 : }
1154 1 : if (fdisk_partition_set_start (n_epa, start) != 0) {
1155 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1156 : "Failed to set partition start");
1157 0 : fdisk_unref_partition (n_epa);
1158 0 : fdisk_unref_partition (npa);
1159 0 : fdisk_free_iter (iter);
1160 0 : fdisk_unref_table (table);
1161 0 : close_context (cxt);
1162 0 : bd_utils_report_finished (progress_id, l_error->message);
1163 0 : g_propagate_error (error, l_error);
1164 0 : return NULL;
1165 : }
1166 :
1167 1 : fdisk_partition_partno_follow_default (n_epa, 1);
1168 :
1169 1 : status = fdisk_partition_next_partno (npa, cxt, &partno);
1170 1 : if (status != 0) {
1171 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1172 : "Failed to get new extended partition number");
1173 0 : fdisk_unref_partition (npa);
1174 0 : close_context (cxt);
1175 0 : bd_utils_report_finished (progress_id, l_error->message);
1176 0 : g_propagate_error (error, l_error);
1177 0 : return NULL;
1178 : }
1179 :
1180 1 : status = fdisk_partition_set_partno (npa, partno);
1181 1 : if (status != 0) {
1182 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1183 : "Failed to set new extended partition number");
1184 0 : fdisk_unref_partition (npa);
1185 0 : close_context (cxt);
1186 0 : bd_utils_report_finished (progress_id, l_error->message);
1187 0 : g_propagate_error (error, l_error);
1188 0 : return NULL;
1189 : }
1190 :
1191 : /* set the end to default (maximum) */
1192 1 : fdisk_partition_end_follow_default (n_epa, 1);
1193 :
1194 : /* "05" for extended partition */
1195 1 : ptype = fdisk_label_parse_parttype (fdisk_get_label (cxt, NULL), "05");
1196 1 : if (fdisk_partition_set_type (n_epa, ptype) != 0) {
1197 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1198 : "Failed to set partition type");
1199 0 : fdisk_unref_partition (n_epa);
1200 0 : fdisk_unref_partition (npa);
1201 0 : fdisk_free_iter (iter);
1202 0 : fdisk_unref_table (table);
1203 0 : close_context (cxt);
1204 0 : bd_utils_report_finished (progress_id, l_error->message);
1205 0 : g_propagate_error (error, l_error);
1206 0 : return NULL;
1207 : }
1208 1 : fdisk_unref_parttype (ptype);
1209 :
1210 1 : status = fdisk_add_partition (cxt, n_epa, NULL);
1211 1 : fdisk_unref_partition (n_epa);
1212 1 : if (status != 0) {
1213 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1214 0 : "Failed to add new partition to the table: %s", strerror_l (-status, c_locale));
1215 0 : fdisk_unref_partition (npa);
1216 0 : fdisk_free_iter (iter);
1217 0 : fdisk_unref_table (table);
1218 0 : close_context (cxt);
1219 0 : bd_utils_report_finished (progress_id, l_error->message);
1220 0 : g_propagate_error (error, l_error);
1221 0 : return NULL;
1222 : }
1223 : /* shift the start 2 MiB further as that's where the first logical
1224 : partition inside an extended partition can start */
1225 1 : start += (2 MiB / sector_size);
1226 1 : type = BD_PART_TYPE_REQ_LOGICAL;
1227 : } else
1228 : /* no extended partition and not 3 primary partitions -> just create
1229 : another primary (NORMAL) partition*/
1230 3 : type = BD_PART_TYPE_REQ_NORMAL;
1231 :
1232 6 : fdisk_free_iter (iter);
1233 : }
1234 :
1235 80 : if (type == BD_PART_TYPE_REQ_EXTENDED) {
1236 9 : new_extended = TRUE;
1237 : /* "05" for extended partition */
1238 9 : ptype = fdisk_label_parse_parttype (fdisk_get_label (cxt, NULL), "05");
1239 9 : if (fdisk_partition_set_type (npa, ptype) != 0) {
1240 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1241 : "Failed to set partition type");
1242 0 : fdisk_unref_table (table);
1243 0 : fdisk_unref_partition (npa);
1244 0 : close_context (cxt);
1245 0 : bd_utils_report_finished (progress_id, l_error->message);
1246 0 : g_propagate_error (error, l_error);
1247 0 : return NULL;
1248 : }
1249 :
1250 9 : fdisk_unref_parttype (ptype);
1251 : }
1252 :
1253 80 : if (fdisk_partition_set_start (npa, start) != 0) {
1254 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1255 : "Failed to set partition start");
1256 0 : fdisk_unref_table (table);
1257 0 : fdisk_unref_partition (npa);
1258 0 : close_context (cxt);
1259 0 : bd_utils_report_finished (progress_id, l_error->message);
1260 0 : g_propagate_error (error, l_error);
1261 0 : return NULL;
1262 : }
1263 :
1264 80 : if (type == BD_PART_TYPE_REQ_LOGICAL) {
1265 : /* next_partno doesn't work for logical partitions, for these the
1266 : current maximal number of partitions supported by the label
1267 : is the next (logical) partition number */
1268 11 : partno = fdisk_get_npartitions (cxt);
1269 :
1270 : } else {
1271 69 : status = fdisk_partition_next_partno (npa, cxt, &partno);
1272 69 : if (status != 0) {
1273 7 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1274 : "Failed to get new partition number");
1275 7 : fdisk_unref_table (table);
1276 7 : fdisk_unref_partition (npa);
1277 7 : close_context (cxt);
1278 7 : bd_utils_report_finished (progress_id, l_error->message);
1279 7 : g_propagate_error (error, l_error);
1280 7 : return NULL;
1281 : }
1282 : }
1283 :
1284 73 : status = fdisk_partition_set_partno (npa, partno);
1285 73 : if (status != 0) {
1286 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1287 : "Failed to set new partition number");
1288 0 : fdisk_unref_table (table);
1289 0 : fdisk_unref_partition (npa);
1290 0 : close_context (cxt);
1291 0 : bd_utils_report_finished (progress_id, l_error->message);
1292 0 : g_propagate_error (error, l_error);
1293 0 : return NULL;
1294 : }
1295 :
1296 73 : status = fdisk_add_partition (cxt, npa, NULL);
1297 73 : if (status != 0) {
1298 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1299 0 : "Failed to add new partition to the table: %s", strerror_l (-status, c_locale));
1300 0 : fdisk_unref_table (table);
1301 0 : fdisk_unref_partition (npa);
1302 0 : close_context (cxt);
1303 0 : bd_utils_report_finished (progress_id, l_error->message);
1304 0 : g_propagate_error (error, l_error);
1305 0 : return NULL;
1306 : }
1307 :
1308 : /* for new extended partition we need to force reread whole partition table with
1309 : libfdisk < 2.36.1 */
1310 73 : if (!write_label (cxt, table, disk, new_extended && fdisk_version < 2361, &l_error)) {
1311 0 : bd_utils_report_finished (progress_id, l_error->message);
1312 0 : g_propagate_error (error, l_error);
1313 0 : fdisk_unref_table (table);
1314 0 : fdisk_unref_partition (npa);
1315 0 : close_context (cxt);
1316 0 : return NULL;
1317 : }
1318 :
1319 73 : if (fdisk_partition_has_partno (npa)) {
1320 73 : if (isdigit (disk[strlen (disk) - 1]))
1321 0 : ppath = g_strdup_printf ("%sp%zu", disk, fdisk_partition_get_partno (npa) + 1);
1322 : else
1323 73 : ppath = g_strdup_printf ("%s%zu", disk, fdisk_partition_get_partno (npa) + 1);
1324 : }
1325 :
1326 : /* close the context now, we no longer need it */
1327 73 : fdisk_unref_table (table);
1328 73 : fdisk_unref_partition (npa);
1329 73 : close_context (cxt);
1330 :
1331 : /* the in-memory model of the new partition is not updated, we need to
1332 : read the spec manually
1333 : if we get NULL and error here, just propagate it further */
1334 73 : ret = bd_part_get_part_spec (disk, ppath, error);
1335 73 : g_free (ppath);
1336 :
1337 73 : bd_utils_report_finished (progress_id, "Completed");
1338 :
1339 73 : return ret;
1340 : }
1341 :
1342 : /**
1343 : * bd_part_delete_part:
1344 : * @disk: disk to remove the partition from
1345 : * @part: partition to remove
1346 : * @error: (out) (optional): place to store error (if any)
1347 : *
1348 : * Returns: whether the @part partition was successfully deleted from @disk
1349 : *
1350 : * Tech category: %BD_PART_TECH_MODE_MODIFY_TABLE + the tech according to the partition table type
1351 : */
1352 5 : gboolean bd_part_delete_part (const gchar *disk, const gchar *part, GError **error) {
1353 5 : gint part_num = 0;
1354 5 : struct fdisk_context *cxt = NULL;
1355 5 : struct fdisk_table *table = NULL;
1356 5 : gint ret = 0;
1357 5 : guint64 progress_id = 0;
1358 5 : gchar *msg = NULL;
1359 5 : GError *l_error = NULL;
1360 :
1361 5 : msg = g_strdup_printf ("Started deleting partition '%s'", part);
1362 5 : progress_id = bd_utils_report_started (msg);
1363 5 : g_free (msg);
1364 :
1365 5 : part_num = get_part_num (part, &l_error);
1366 5 : if (part_num == -1) {
1367 0 : bd_utils_report_finished (progress_id, l_error->message);
1368 0 : g_propagate_error (error, l_error);
1369 0 : return FALSE;
1370 : }
1371 :
1372 : /* /dev/sda1 is the partition number 0 in libfdisk */
1373 5 : part_num--;
1374 5 : cxt = get_device_context (disk, FALSE, &l_error);
1375 5 : if (!cxt) {
1376 : /* error is already populated */
1377 0 : bd_utils_report_finished (progress_id, l_error->message);
1378 0 : g_propagate_error (error, l_error);
1379 0 : return FALSE;
1380 : }
1381 :
1382 5 : ret = fdisk_get_partitions (cxt, &table);
1383 5 : if (ret != 0) {
1384 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1385 0 : "Failed to get existing partitions on the device: %s", strerror_l (-ret, c_locale));
1386 0 : close_context (cxt);
1387 0 : bd_utils_report_finished (progress_id, l_error->message);
1388 0 : g_propagate_error (error, l_error);
1389 0 : return FALSE;
1390 : }
1391 :
1392 5 : ret = fdisk_delete_partition (cxt, (size_t) part_num);
1393 5 : if (ret != 0) {
1394 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1395 0 : "Failed to delete partition '%d' on device '%s': %s", part_num+1, disk, strerror_l (-ret, c_locale));
1396 0 : fdisk_unref_table (table);
1397 0 : close_context (cxt);
1398 0 : bd_utils_report_finished (progress_id, l_error->message);
1399 0 : g_propagate_error (error, l_error);
1400 0 : return FALSE;
1401 : }
1402 :
1403 5 : if (!write_label (cxt, table, disk, FALSE, &l_error)) {
1404 0 : bd_utils_report_finished (progress_id, l_error->message);
1405 0 : g_propagate_error (error, l_error);
1406 0 : fdisk_unref_table (table);
1407 0 : close_context (cxt);
1408 0 : return FALSE;
1409 : }
1410 :
1411 5 : fdisk_unref_table (table);
1412 5 : close_context (cxt);
1413 :
1414 5 : bd_utils_report_finished (progress_id, "Completed");
1415 :
1416 5 : return TRUE;
1417 : }
1418 :
1419 : /* get maximal size for partition when resizing
1420 : * this is a simplified copy of 'resize_get_last_possible' function from
1421 : * libfdisk which is unfortunately not public
1422 : */
1423 26 : static gboolean get_max_part_size (struct fdisk_table *tb, guint partno, guint64 *max_size, GError **error) {
1424 26 : struct fdisk_partition *pa = NULL;
1425 26 : struct fdisk_partition *cur = NULL;
1426 26 : struct fdisk_iter *itr = NULL;
1427 26 : guint64 start = 0;
1428 26 : gboolean found = FALSE;
1429 :
1430 26 : itr = fdisk_new_iter (FDISK_ITER_FORWARD);
1431 26 : if (!itr) {
1432 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1433 : "Failed to create a new iterator");
1434 0 : return FALSE;
1435 : }
1436 :
1437 26 : cur = fdisk_table_get_partition_by_partno (tb, partno);
1438 26 : if (!cur) {
1439 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1440 : "Failed to locate partition '%d' in table.", partno);
1441 0 : fdisk_free_iter (itr);
1442 0 : return FALSE;
1443 : }
1444 :
1445 26 : start = fdisk_partition_get_start (cur);
1446 :
1447 69 : while (!found && fdisk_table_next_partition (tb, itr, &pa) == 0) {
1448 86 : if (!fdisk_partition_has_start (pa) || !fdisk_partition_has_size (pa) ||
1449 43 : (fdisk_partition_is_container (pa) && pa != cur)) {
1450 : /* partition has no size/start or is an extended partition */
1451 0 : continue;
1452 : }
1453 :
1454 43 : if (fdisk_partition_is_nested (pa) && fdisk_partition_is_container (cur)) {
1455 : /* ignore logical partitions inside if we are checking an extended partition */
1456 0 : continue;
1457 : }
1458 :
1459 43 : if (fdisk_partition_is_nested (cur) && !fdisk_partition_is_nested (pa)) {
1460 : /* current partition is nested, we are looking for a nested free space */
1461 0 : continue;
1462 : }
1463 :
1464 43 : if (pa == cur) {
1465 : /* found our current partition */
1466 26 : found = TRUE;
1467 : }
1468 : }
1469 :
1470 26 : if (found && fdisk_table_next_partition (tb, itr, &pa) == 0) {
1471 : /* check next partition after our current we found */
1472 12 : if (fdisk_partition_is_freespace (pa)) {
1473 : /* we found a free space after current partition */
1474 : if (LIBFDISK_MINOR_VERSION <= 32)
1475 : /* XXX: older versions of libfdisk doesn't count free space between
1476 : partitions as usable so we need to do the same here, see
1477 : https://github.com/karelzak/util-linux/commit/2f35c1ead621f42f32f7777232568cb03185b473 */
1478 : *max_size = fdisk_partition_get_size (cur) + fdisk_partition_get_size (pa);
1479 : else
1480 8 : *max_size = fdisk_partition_get_size (pa) - (start - fdisk_partition_get_start (pa));
1481 : }
1482 : }
1483 :
1484 : /* no free space found: set max_size to current size */
1485 26 : if (*max_size == 0)
1486 18 : *max_size = fdisk_partition_get_size (cur);
1487 :
1488 26 : fdisk_free_iter (itr);
1489 26 : return TRUE;
1490 : }
1491 :
1492 : /**
1493 : * bd_part_resize_part:
1494 : * @disk: disk containing the partition
1495 : * @part: partition to resize
1496 : * @size: new partition size, 0 for maximal size
1497 : * @align: alignment to use for the partition end
1498 : * @error: (out) (optional): place to store error (if any)
1499 : *
1500 : * Returns: whether the @part partition was successfully resized on @disk to @size
1501 : *
1502 : * NOTE: The resulting partition may be slightly bigger than requested due to alignment.
1503 : *
1504 : * Tech category: %BD_PART_TECH_MODE_MODIFY_TABLE + the tech according to the partition table type
1505 : */
1506 26 : gboolean bd_part_resize_part (const gchar *disk, const gchar *part, guint64 size, BDPartAlign align, GError **error) {
1507 26 : gint part_num = 0;
1508 26 : struct fdisk_context *cxt = NULL;
1509 26 : struct fdisk_table *table = NULL;
1510 26 : struct fdisk_partition *pa = NULL;
1511 26 : gint ret = 0;
1512 26 : guint64 old_size = 0;
1513 26 : guint64 sector_size = 0;
1514 26 : guint64 grain_size = 0;
1515 26 : guint64 progress_id = 0;
1516 26 : guint64 max_size = 0;
1517 26 : guint64 start = 0;
1518 26 : guint64 end = 0;
1519 26 : gint version = 0;
1520 26 : gchar *msg = NULL;
1521 26 : GError *l_error = NULL;
1522 :
1523 26 : msg = g_strdup_printf ("Started resizing partition '%s'", part);
1524 26 : progress_id = bd_utils_report_started (msg);
1525 26 : g_free (msg);
1526 :
1527 26 : part_num = get_part_num (part, &l_error);
1528 26 : if (part_num == -1) {
1529 0 : bd_utils_report_finished (progress_id, l_error->message);
1530 0 : g_propagate_error (error, l_error);
1531 0 : return FALSE;
1532 : }
1533 :
1534 : /* /dev/sda1 is the partition number 0 in libfdisk */
1535 26 : part_num--;
1536 26 : cxt = get_device_context (disk, FALSE, &l_error);
1537 26 : if (!cxt) {
1538 : /* error is already populated */
1539 0 : bd_utils_report_finished (progress_id, l_error->message);
1540 0 : g_propagate_error (error, l_error);
1541 0 : return FALSE;
1542 : }
1543 :
1544 : /* get existing partitions and free spaces and sort the table */
1545 26 : ret = fdisk_get_partitions (cxt, &table);
1546 26 : if (ret != 0) {
1547 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1548 0 : "Failed to get existing partitions on the device: %s", strerror_l (-ret, c_locale));
1549 0 : fdisk_unref_table (table);
1550 0 : close_context (cxt);
1551 0 : bd_utils_report_finished (progress_id, l_error->message);
1552 0 : g_propagate_error (error, l_error);
1553 0 : return FALSE;
1554 : }
1555 :
1556 26 : ret = fdisk_get_freespaces (cxt, &table);
1557 26 : if (ret != 0) {
1558 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1559 0 : "Failed to get free spaces on the device: %s", strerror_l (-ret, c_locale));
1560 0 : fdisk_unref_table (table);
1561 0 : close_context (cxt);
1562 0 : bd_utils_report_finished (progress_id, l_error->message);
1563 0 : g_propagate_error (error, l_error);
1564 0 : return FALSE;
1565 : }
1566 :
1567 26 : fdisk_table_sort_partitions (table, fdisk_partition_cmp_start);
1568 :
1569 26 : ret = fdisk_get_partition (cxt, part_num, &pa);
1570 26 : if (ret != 0) {
1571 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1572 : "Failed to get partition %d on device '%s'", part_num, disk);
1573 0 : fdisk_unref_table (table);
1574 0 : close_context (cxt);
1575 0 : bd_utils_report_finished (progress_id, l_error->message);
1576 0 : g_propagate_error (error, l_error);
1577 0 : return FALSE;
1578 : }
1579 :
1580 26 : if (fdisk_partition_has_size (pa))
1581 26 : old_size = (guint64) fdisk_partition_get_size (pa);
1582 : else {
1583 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1584 : "Failed to get size for partition %d on device '%s'", part_num, disk);
1585 0 : fdisk_unref_partition (pa);
1586 0 : fdisk_unref_table (table);
1587 0 : close_context (cxt);
1588 0 : bd_utils_report_finished (progress_id, l_error->message);
1589 0 : g_propagate_error (error, l_error);
1590 0 : return FALSE;
1591 : }
1592 :
1593 : /* set grain_size based on user alignment preferences */
1594 26 : sector_size = (guint64) fdisk_get_sector_size (cxt);
1595 26 : grain_size = (guint64) fdisk_get_grain_size (cxt);
1596 :
1597 26 : if (align == BD_PART_ALIGN_NONE)
1598 18 : grain_size = sector_size;
1599 8 : else if (align == BD_PART_ALIGN_MINIMAL)
1600 0 : grain_size = (guint64) fdisk_get_minimal_iosize (cxt);
1601 : /* else OPTIMAL or unknown -> nothing to do */
1602 :
1603 26 : if (!get_max_part_size (table, part_num, &max_size, &l_error)) {
1604 0 : g_prefix_error (&l_error, "Failed to get maximal size for '%s': ", part);
1605 0 : fdisk_unref_table (table);
1606 0 : fdisk_unref_partition (pa);
1607 0 : close_context (cxt);
1608 0 : bd_utils_report_finished (progress_id, l_error->message);
1609 0 : g_propagate_error (error, l_error);
1610 0 : return FALSE;
1611 : }
1612 :
1613 26 : if (size == 0) {
1614 8 : if (max_size == old_size) {
1615 4 : bd_utils_log_format (BD_UTILS_LOG_INFO, "Not resizing, partition '%s' is already at its maximum size.", part);
1616 4 : fdisk_unref_table (table);
1617 4 : fdisk_unref_partition (pa);
1618 4 : close_context (cxt);
1619 4 : bd_utils_report_finished (progress_id, "Completed");
1620 4 : return TRUE;
1621 : }
1622 :
1623 : /* latest libfdisk introduces default end alignment for new partitions, we should
1624 : do the same for resizes where we calculate the size ourselves */
1625 4 : version = fdisk_get_library_version (NULL);
1626 4 : if (version >= 2380 && align != BD_PART_ALIGN_NONE) {
1627 2 : start = fdisk_partition_get_start (pa);
1628 2 : end = start + max_size;
1629 2 : end = fdisk_align_lba_in_range (cxt, end, start, end);
1630 2 : max_size = end - start;
1631 : }
1632 :
1633 4 : if (fdisk_partition_set_size (pa, max_size) != 0) {
1634 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1635 : "Failed to set size for partition %d on device '%s'", part_num, disk);
1636 0 : fdisk_unref_table (table);
1637 0 : fdisk_unref_partition (pa);
1638 0 : close_context (cxt);
1639 0 : bd_utils_report_finished (progress_id, l_error->message);
1640 0 : g_propagate_error (error, l_error);
1641 0 : return FALSE;
1642 : }
1643 : } else {
1644 : /* align size up */
1645 18 : if (size % grain_size != 0)
1646 9 : size = ((size + grain_size) / grain_size) * grain_size;
1647 18 : size = size / sector_size;
1648 :
1649 18 : if (size == old_size) {
1650 4 : bd_utils_log_format (BD_UTILS_LOG_INFO, "Not resizing, new size after alignment is the same as the old size.");
1651 4 : fdisk_unref_table (table);
1652 4 : fdisk_unref_partition (pa);
1653 4 : close_context (cxt);
1654 4 : bd_utils_report_finished (progress_id, "Completed");
1655 4 : return TRUE;
1656 : }
1657 :
1658 14 : if (size > old_size && size > max_size) {
1659 4 : if (size - max_size <= 4 MiB / sector_size) {
1660 2 : bd_utils_log_format (BD_UTILS_LOG_INFO,
1661 : "Requested size %"G_GUINT64_FORMAT" is bigger than max size for partition '%s', adjusting to %"G_GUINT64_FORMAT".",
1662 : size * sector_size, part, max_size * sector_size);
1663 2 : size = max_size;
1664 : } else {
1665 2 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1666 : "Requested size %"G_GUINT64_FORMAT" is bigger than max size (%"G_GUINT64_FORMAT") for partition '%s'",
1667 : size * sector_size, max_size * sector_size, part);
1668 2 : fdisk_unref_table (table);
1669 2 : fdisk_unref_partition (pa);
1670 2 : close_context (cxt);
1671 2 : bd_utils_report_finished (progress_id, l_error->message);
1672 2 : g_propagate_error (error, l_error);
1673 2 : return FALSE;
1674 : }
1675 : }
1676 :
1677 12 : if (fdisk_partition_set_size (pa, size) != 0) {
1678 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1679 : "Failed to set partition size");
1680 0 : fdisk_unref_table (table);
1681 0 : fdisk_unref_partition (pa);
1682 0 : close_context (cxt);
1683 0 : bd_utils_report_finished (progress_id, l_error->message);
1684 0 : g_propagate_error (error, l_error);
1685 0 : return FALSE;
1686 : }
1687 : }
1688 :
1689 16 : ret = fdisk_set_partition (cxt, part_num, pa);
1690 16 : if (ret != 0) {
1691 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1692 0 : "Failed to resize partition '%s': %s", part, strerror_l (-ret, c_locale));
1693 0 : fdisk_unref_table (table);
1694 0 : fdisk_unref_partition (pa);
1695 0 : close_context (cxt);
1696 0 : bd_utils_report_finished (progress_id, l_error->message);
1697 0 : g_propagate_error (error, l_error);
1698 0 : return FALSE;
1699 : }
1700 :
1701 16 : if (!write_label (cxt, table, disk, FALSE, &l_error)) {
1702 0 : bd_utils_report_finished (progress_id, l_error->message);
1703 0 : g_propagate_error (error, l_error);
1704 0 : fdisk_unref_table (table);
1705 0 : fdisk_unref_partition (pa);
1706 0 : close_context (cxt);
1707 0 : return FALSE;
1708 : }
1709 :
1710 16 : fdisk_unref_table (table);
1711 :
1712 : /* XXX: double free in libfdisk, see https://github.com/karelzak/util-linux/pull/822
1713 : fdisk_unref_partition (pa); */
1714 16 : close_context (cxt);
1715 :
1716 16 : bd_utils_report_finished (progress_id, "Completed");
1717 :
1718 16 : return TRUE;
1719 : }
1720 :
1721 6 : static gboolean set_part_type (struct fdisk_context *cxt, gint part_num, const gchar *type_str, BDPartTableType table_type, GError **error) {
1722 6 : struct fdisk_label *lb = NULL;
1723 6 : struct fdisk_partition *pa = NULL;
1724 6 : struct fdisk_parttype *ptype = NULL;
1725 6 : const gchar *label_name = NULL;
1726 6 : gint status = 0;
1727 6 : gint part_id_int = 0;
1728 :
1729 : /* check if part type/id is valid for MBR */
1730 6 : if (table_type == BD_PART_TABLE_MSDOS) {
1731 2 : part_id_int = g_ascii_strtoull (type_str, NULL, 0);
1732 :
1733 2 : if (part_id_int == 0) {
1734 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1735 : "Invalid partition id given: '%s'.", type_str);
1736 0 : return FALSE;
1737 : }
1738 :
1739 2 : if (part_id_int == 0x05 || part_id_int == 0x0f || part_id_int == 0x85) {
1740 1 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1741 : "Cannot change partition id to extended.");
1742 1 : return FALSE;
1743 : }
1744 : }
1745 :
1746 5 : lb = fdisk_get_label (cxt, NULL);
1747 5 : if (!lb) {
1748 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1749 : "Failed to read partition table.");
1750 0 : return FALSE;
1751 : }
1752 :
1753 5 : label_name = fdisk_label_get_name (lb);
1754 5 : if (g_strcmp0 (label_name, table_type_str[table_type]) != 0) {
1755 1 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1756 : "Setting partition type is not supported on '%s' partition table", label_name);
1757 1 : return FALSE;
1758 : }
1759 :
1760 4 : status = fdisk_get_partition (cxt, part_num, &pa);
1761 4 : if (status != 0) {
1762 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1763 : "Failed to get partition %d.", part_num);
1764 0 : return FALSE;
1765 : }
1766 :
1767 4 : ptype = fdisk_label_parse_parttype (lb, type_str);
1768 4 : if (!ptype) {
1769 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1770 : "Failed to parse partition type.");
1771 0 : fdisk_unref_partition (pa);
1772 0 : return FALSE;
1773 : }
1774 :
1775 4 : status = fdisk_set_partition_type (cxt, part_num, ptype);
1776 4 : if (status != 0) {
1777 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1778 : "Failed to set partition type for partition %d.", part_num);
1779 0 : fdisk_unref_parttype (ptype);
1780 0 : fdisk_unref_partition (pa);
1781 0 : return FALSE;
1782 : }
1783 :
1784 4 : fdisk_unref_parttype (ptype);
1785 4 : fdisk_unref_partition (pa);
1786 4 : return TRUE;
1787 : }
1788 :
1789 : /**
1790 : * bd_part_set_part_name:
1791 : * @disk: device the partition belongs to
1792 : * @part: partition the name should be set for
1793 : * @name: name to set
1794 : * @error: (out) (optional): place to store error (if any)
1795 : *
1796 : * Returns: whether the name was successfully set or not
1797 : *
1798 : * Tech category: %BD_PART_TECH_MODE_MODIFY_PART + the tech according to the partition table type
1799 : */
1800 4 : gboolean bd_part_set_part_name (const gchar *disk, const gchar *part, const gchar *name, GError **error) {
1801 4 : struct fdisk_context *cxt = NULL;
1802 4 : struct fdisk_label *lb = NULL;
1803 4 : struct fdisk_partition *pa = NULL;
1804 4 : const gchar *label_name = NULL;
1805 4 : gint part_num = 0;
1806 4 : gint status = 0;
1807 4 : guint64 progress_id = 0;
1808 4 : gchar *msg = NULL;
1809 4 : GError *l_error = NULL;
1810 :
1811 4 : msg = g_strdup_printf ("Started setting name on the partition '%s'", part);
1812 4 : progress_id = bd_utils_report_started (msg);
1813 4 : g_free (msg);
1814 :
1815 4 : cxt = get_device_context (disk, FALSE, error);
1816 4 : if (!cxt) {
1817 : /* error is already populated */
1818 0 : return FALSE;
1819 : }
1820 :
1821 4 : lb = fdisk_get_label (cxt, NULL);
1822 4 : if (!lb) {
1823 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1824 : "Failed to read partition table on device '%s'", disk);
1825 0 : close_context (cxt);
1826 0 : bd_utils_report_finished (progress_id, l_error->message);
1827 0 : g_propagate_error (error, l_error);
1828 0 : return FALSE;
1829 : }
1830 :
1831 4 : label_name = fdisk_label_get_name (lb);
1832 4 : if (g_strcmp0 (label_name, table_type_str[BD_PART_TABLE_GPT]) != 0) {
1833 2 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
1834 : "Partition names unsupported on the device '%s' ('%s')", disk,
1835 : label_name);
1836 2 : close_context (cxt);
1837 2 : bd_utils_report_finished (progress_id, l_error->message);
1838 2 : g_propagate_error (error, l_error);
1839 2 : return FALSE;
1840 : }
1841 :
1842 2 : part_num = get_part_num (part, &l_error);
1843 2 : if (part_num == -1) {
1844 0 : close_context (cxt);
1845 0 : bd_utils_report_finished (progress_id, l_error->message);
1846 0 : g_propagate_error (error, l_error);
1847 0 : return FALSE;
1848 : }
1849 :
1850 : /* /dev/sda1 is the partition number 0 in libfdisk */
1851 2 : part_num--;
1852 :
1853 2 : status = fdisk_get_partition (cxt, part_num, &pa);
1854 2 : if (status != 0) {
1855 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1856 : "Failed to get partition '%s' on device '%s': %s",
1857 0 : part, disk, strerror_l (-status, c_locale));
1858 0 : close_context (cxt);
1859 0 : bd_utils_report_finished (progress_id, l_error->message);
1860 0 : g_propagate_error (error, l_error);
1861 0 : return FALSE;
1862 : }
1863 :
1864 2 : status = fdisk_partition_set_name (pa, name);
1865 2 : if (status != 0) {
1866 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1867 : "Failed to set name on the partition '%s' on device '%s': %s",
1868 0 : part, disk, strerror_l (-status, c_locale));
1869 0 : fdisk_unref_partition (pa);
1870 0 : close_context (cxt);
1871 0 : bd_utils_report_finished (progress_id, l_error->message);
1872 0 : g_propagate_error (error, l_error);
1873 0 : return FALSE;
1874 : }
1875 :
1876 2 : status = fdisk_set_partition (cxt, part_num, pa);
1877 2 : if (status != 0) {
1878 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
1879 : "Failed to set name on the partition '%s' on device '%s': %s",
1880 0 : part, disk, strerror_l (-status, c_locale));
1881 0 : fdisk_unref_partition (pa);
1882 0 : close_context (cxt);
1883 0 : bd_utils_report_finished (progress_id, l_error->message);
1884 0 : g_propagate_error (error, l_error);
1885 0 : return FALSE;
1886 : }
1887 :
1888 2 : fdisk_unref_partition (pa);
1889 :
1890 2 : if (!write_label (cxt, NULL, disk, FALSE, &l_error)) {
1891 0 : bd_utils_report_finished (progress_id, l_error->message);
1892 0 : g_propagate_error (error, l_error);
1893 0 : close_context (cxt);
1894 0 : return FALSE;
1895 : }
1896 :
1897 2 : close_context (cxt);
1898 2 : bd_utils_report_finished (progress_id, "Completed");
1899 2 : return TRUE;
1900 : }
1901 :
1902 : /**
1903 : * bd_part_set_part_type:
1904 : * @disk: device the partition belongs to
1905 : * @part: partition the type should be set for
1906 : * @type_guid: GUID of the type
1907 : * @error: (out) (optional): place to store error (if any)
1908 : *
1909 : * Returns: whether the @type_guid type was successfully set for @part or not
1910 : *
1911 : * Tech category: %BD_PART_TECH_GPT-%BD_PART_TECH_MODE_MODIFY_PART
1912 : */
1913 4 : gboolean bd_part_set_part_type (const gchar *disk, const gchar *part, const gchar *type_guid, GError **error) {
1914 4 : guint64 progress_id = 0;
1915 4 : gchar *msg = NULL;
1916 4 : struct fdisk_context *cxt = NULL;
1917 4 : gint part_num = 0;
1918 4 : GError *l_error = NULL;
1919 :
1920 4 : msg = g_strdup_printf ("Started setting type on the partition '%s'", part);
1921 4 : progress_id = bd_utils_report_started (msg);
1922 4 : g_free (msg);
1923 :
1924 4 : part_num = get_part_num (part, &l_error);
1925 4 : if (part_num == -1) {
1926 0 : bd_utils_report_finished (progress_id, l_error->message);
1927 0 : g_propagate_error (error, l_error);
1928 0 : return FALSE;
1929 : }
1930 :
1931 : /* /dev/sda1 is the partition number 0 in libfdisk */
1932 4 : part_num--;
1933 :
1934 4 : cxt = get_device_context (disk, FALSE, &l_error);
1935 4 : if (!cxt) {
1936 : /* error is already populated */
1937 0 : bd_utils_report_finished (progress_id, l_error->message);
1938 0 : g_propagate_error (error, l_error);
1939 0 : return FALSE;
1940 : }
1941 :
1942 4 : if (!set_part_type (cxt, part_num, type_guid, BD_PART_TABLE_GPT, &l_error)) {
1943 1 : bd_utils_report_finished (progress_id, l_error->message);
1944 1 : g_propagate_error (error, l_error);
1945 1 : close_context (cxt);
1946 1 : return FALSE;
1947 : }
1948 :
1949 3 : if (!write_label (cxt, NULL, disk, FALSE, &l_error)) {
1950 0 : bd_utils_report_finished (progress_id, l_error->message);
1951 0 : g_propagate_error (error, l_error);
1952 0 : close_context (cxt);
1953 0 : return FALSE;
1954 : }
1955 :
1956 3 : close_context (cxt);
1957 3 : bd_utils_report_finished (progress_id, "Completed");
1958 3 : return TRUE;
1959 : }
1960 :
1961 : /**
1962 : * bd_part_set_part_id:
1963 : * @disk: device the partition belongs to
1964 : * @part: partition the ID should be set for
1965 : * @part_id: partition Id
1966 : * @error: (out) (optional): place to store error (if any)
1967 : *
1968 : * Returns: whether the @part_id type was successfully set for @part or not
1969 : *
1970 : * Tech category: %BD_PART_TECH_MBR-%BD_PART_TECH_MODE_MODIFY_PART
1971 : */
1972 2 : gboolean bd_part_set_part_id (const gchar *disk, const gchar *part, const gchar *part_id, GError **error) {
1973 2 : guint64 progress_id = 0;
1974 2 : gchar *msg = NULL;
1975 2 : struct fdisk_context *cxt = NULL;
1976 2 : gint part_num = 0;
1977 2 : GError *l_error = NULL;
1978 :
1979 2 : msg = g_strdup_printf ("Started setting id on the partition '%s'", part);
1980 2 : progress_id = bd_utils_report_started (msg);
1981 2 : g_free (msg);
1982 :
1983 2 : part_num = get_part_num (part, &l_error);
1984 2 : if (part_num == -1) {
1985 0 : bd_utils_report_finished (progress_id, l_error->message);
1986 0 : g_propagate_error (error, l_error);
1987 0 : return FALSE;
1988 : }
1989 :
1990 : /* /dev/sda1 is the partition number 0 in libfdisk */
1991 2 : part_num--;
1992 :
1993 2 : cxt = get_device_context (disk, FALSE, &l_error);
1994 2 : if (!cxt) {
1995 : /* error is already populated */
1996 0 : bd_utils_report_finished (progress_id, l_error->message);
1997 0 : g_propagate_error (error, l_error);
1998 0 : return FALSE;
1999 : }
2000 :
2001 2 : if (!set_part_type (cxt, part_num, part_id, BD_PART_TABLE_MSDOS, &l_error)) {
2002 1 : bd_utils_report_finished (progress_id, l_error->message);
2003 1 : g_propagate_error (error, l_error);
2004 1 : close_context (cxt);
2005 1 : return FALSE;
2006 : }
2007 :
2008 1 : if (!write_label (cxt, NULL, disk, FALSE, &l_error)) {
2009 0 : bd_utils_report_finished (progress_id, l_error->message);
2010 0 : g_propagate_error (error, l_error);
2011 0 : close_context (cxt);
2012 0 : return FALSE;
2013 : }
2014 :
2015 1 : close_context (cxt);
2016 1 : bd_utils_report_finished (progress_id, "Completed");
2017 1 : return TRUE;
2018 : }
2019 :
2020 : /**
2021 : * bd_part_set_part_uuid:
2022 : * @disk: device the partition belongs to
2023 : * @part: partition the UUID should be set for
2024 : * @uuid: partition UUID to set
2025 : * @error: (out) (optional): place to store error (if any)
2026 : *
2027 : * Returns: whether the @uuid type was successfully set for @part or not
2028 : *
2029 : * Tech category: %BD_PART_TECH_GPT-%BD_PART_TECH_MODE_MODIFY_PART
2030 : */
2031 1 : gboolean bd_part_set_part_uuid (const gchar *disk, const gchar *part, const gchar *uuid, GError **error) {
2032 1 : struct fdisk_context *cxt = NULL;
2033 1 : struct fdisk_partition *pa = NULL;
2034 1 : struct fdisk_label *lb = NULL;
2035 1 : const gchar *label_name = NULL;
2036 1 : gint part_num = 0;
2037 1 : gint status = 0;
2038 1 : guint64 progress_id = 0;
2039 1 : gchar *msg = NULL;
2040 1 : GError *l_error = NULL;
2041 :
2042 1 : msg = g_strdup_printf ("Started setting UUID on the partition '%s'", part);
2043 1 : progress_id = bd_utils_report_started (msg);
2044 1 : g_free (msg);
2045 :
2046 1 : cxt = get_device_context (disk, FALSE, error);
2047 1 : if (!cxt) {
2048 : /* error is already populated */
2049 0 : return FALSE;
2050 : }
2051 :
2052 1 : lb = fdisk_get_label (cxt, NULL);
2053 1 : if (!lb) {
2054 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2055 : "Failed to read partition table on device '%s'", disk);
2056 0 : close_context (cxt);
2057 0 : bd_utils_report_finished (progress_id, l_error->message);
2058 0 : g_propagate_error (error, l_error);
2059 0 : return FALSE;
2060 : }
2061 :
2062 1 : label_name = fdisk_label_get_name (lb);
2063 1 : if (g_strcmp0 (label_name, table_type_str[BD_PART_TABLE_GPT]) != 0) {
2064 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
2065 : "Partition UUIDs unsupported on the device '%s' ('%s')", disk,
2066 : label_name);
2067 0 : close_context (cxt);
2068 0 : bd_utils_report_finished (progress_id, l_error->message);
2069 0 : g_propagate_error (error, l_error);
2070 0 : return FALSE;
2071 : }
2072 :
2073 1 : part_num = get_part_num (part, &l_error);
2074 1 : if (part_num == -1) {
2075 0 : close_context (cxt);
2076 0 : bd_utils_report_finished (progress_id, l_error->message);
2077 0 : g_propagate_error (error, l_error);
2078 0 : return FALSE;
2079 : }
2080 :
2081 : /* /dev/sda1 is the partition number 0 in libfdisk */
2082 1 : part_num--;
2083 :
2084 1 : status = fdisk_get_partition (cxt, part_num, &pa);
2085 1 : if (status != 0) {
2086 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2087 : "Failed to get partition '%s' on device '%s': %s",
2088 0 : part, disk, strerror_l (-status, c_locale));
2089 0 : close_context (cxt);
2090 0 : bd_utils_report_finished (progress_id, l_error->message);
2091 0 : g_propagate_error (error, l_error);
2092 0 : return FALSE;
2093 : }
2094 :
2095 1 : status = fdisk_partition_set_uuid (pa, uuid);
2096 1 : if (status != 0) {
2097 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2098 : "Failed to set UUID on the partition '%s' on device '%s': %s",
2099 0 : part, disk, strerror_l (-status, c_locale));
2100 0 : fdisk_unref_partition (pa);
2101 0 : close_context (cxt);
2102 0 : bd_utils_report_finished (progress_id, l_error->message);
2103 0 : g_propagate_error (error, l_error);
2104 0 : return FALSE;
2105 : }
2106 :
2107 1 : status = fdisk_set_partition (cxt, part_num, pa);
2108 1 : if (status != 0) {
2109 0 : g_set_error (&l_error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2110 : "Failed to set UUID on the partition '%s' on device '%s': %s",
2111 0 : part, disk, strerror_l (-status, c_locale));
2112 0 : fdisk_unref_partition (pa);
2113 0 : close_context (cxt);
2114 0 : bd_utils_report_finished (progress_id, l_error->message);
2115 0 : g_propagate_error (error, l_error);
2116 0 : return FALSE;
2117 : }
2118 :
2119 1 : fdisk_unref_partition (pa);
2120 :
2121 1 : if (!write_label (cxt, NULL, disk, FALSE, &l_error)) {
2122 0 : bd_utils_report_finished (progress_id, l_error->message);
2123 0 : g_propagate_error (error, l_error);
2124 0 : close_context (cxt);
2125 0 : return FALSE;
2126 : }
2127 :
2128 1 : close_context (cxt);
2129 1 : bd_utils_report_finished (progress_id, "Completed");
2130 1 : return TRUE;
2131 : }
2132 :
2133 : /**
2134 : * bd_part_set_part_bootable:
2135 : * @disk: device the partition belongs to
2136 : * @part: partition the bootable flag should be set for
2137 : * @bootable: whether to set or unset the bootable flag
2138 : * @error: (out) (optional): place to store error (if any)
2139 : *
2140 : * Returns: whether the @bootable flag was successfully set for @part or not
2141 : *
2142 : * Tech category: %BD_PART_TECH_MBR-%BD_PART_TECH_MODE_MODIFY_PART
2143 : */
2144 2 : gboolean bd_part_set_part_bootable (const gchar *disk, const gchar *part, gboolean bootable, GError **error) {
2145 2 : struct fdisk_context *cxt = NULL;
2146 2 : gint part_num = 0;
2147 2 : struct fdisk_partition *pa = NULL;
2148 2 : gint ret = 0;
2149 :
2150 2 : part_num = get_part_num (part, error);
2151 2 : if (part_num == -1)
2152 0 : return FALSE;
2153 :
2154 : /* /dev/sda1 is the partition number 0 in libfdisk */
2155 2 : part_num--;
2156 :
2157 2 : cxt = get_device_context (disk, FALSE, error);
2158 2 : if (!cxt)
2159 0 : return FALSE;
2160 :
2161 2 : ret = fdisk_get_partition (cxt, part_num, &pa);
2162 2 : if (ret != 0) {
2163 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2164 : "Failed to get partition '%d'.", part_num);
2165 0 : close_context (cxt);
2166 0 : return FALSE;
2167 : }
2168 :
2169 2 : ret = fdisk_partition_is_bootable (pa);
2170 2 : if ((ret == 1 && bootable) || (ret != 1 && !bootable)) {
2171 : /* boot flag is already set as desired, no change needed */
2172 0 : fdisk_unref_partition (pa);
2173 0 : close_context (cxt);
2174 0 : return TRUE;
2175 : }
2176 :
2177 2 : ret = fdisk_toggle_partition_flag (cxt, part_num, DOS_FLAG_ACTIVE);
2178 2 : if (ret != 0) {
2179 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2180 0 : "Failed to set partition bootable flag: %s", strerror_l (-ret, c_locale));
2181 0 : fdisk_unref_partition (pa);
2182 0 : close_context (cxt);
2183 0 : return FALSE;
2184 : }
2185 :
2186 2 : if (!write_label (cxt, NULL, disk, FALSE, error)) {
2187 0 : fdisk_unref_partition (pa);
2188 0 : close_context (cxt);
2189 0 : return FALSE;
2190 : }
2191 :
2192 2 : fdisk_unref_partition (pa);
2193 2 : close_context (cxt);
2194 :
2195 2 : return TRUE;
2196 : }
2197 :
2198 : /**
2199 : * bd_part_set_part_attributes:
2200 : * @disk: device the partition belongs to
2201 : * @part: partition the attributes should be set for
2202 : * @attrs: GPT attributes to set on @part
2203 : * @error: (out) (optional): place to store error (if any)
2204 : *
2205 : * Returns: whether the @attrs GPT attributes were successfully set for @part or not
2206 : *
2207 : * Tech category: %BD_PART_TECH_GPT-%BD_PART_TECH_MODE_MODIFY_PART
2208 : */
2209 1 : gboolean bd_part_set_part_attributes (const gchar *disk, const gchar *part, guint64 attrs, GError **error) {
2210 1 : struct fdisk_context *cxt = NULL;
2211 1 : gint part_num = 0;
2212 1 : gint ret = 0;
2213 :
2214 1 : part_num = get_part_num (part, error);
2215 1 : if (part_num == -1)
2216 0 : return FALSE;
2217 :
2218 : /* /dev/sda1 is the partition number 0 in libfdisk */
2219 1 : part_num--;
2220 :
2221 1 : cxt = get_device_context (disk, FALSE, error);
2222 1 : if (!cxt)
2223 0 : return FALSE;
2224 :
2225 1 : ret = fdisk_gpt_set_partition_attrs (cxt, part_num, attrs);
2226 1 : if (ret < 0) {
2227 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_FAIL,
2228 0 : "Failed to set GPT attributes: %s", strerror_l (-ret, c_locale));
2229 0 : return FALSE;
2230 : }
2231 :
2232 1 : if (!write_label (cxt, NULL, disk, FALSE, error)) {
2233 0 : close_context (cxt);
2234 0 : return FALSE;
2235 : }
2236 :
2237 1 : close_context (cxt);
2238 :
2239 1 : return TRUE;
2240 : }
2241 :
2242 : /**
2243 : * bd_part_get_part_table_type_str:
2244 : * @type: table type to get string representation for
2245 : * @error: (out) (optional): place to store error (if any)
2246 : *
2247 : * Returns: (transfer none): string representation of @table_type
2248 : *
2249 : * Tech category: the tech according to @type
2250 : */
2251 2 : const gchar* bd_part_get_part_table_type_str (BDPartTableType type, GError **error) {
2252 2 : if (type >= BD_PART_TABLE_UNDEF) {
2253 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL,
2254 : "Invalid partition table type given");
2255 0 : return NULL;
2256 : }
2257 :
2258 2 : return table_type_str[type];
2259 : }
2260 :
2261 : /* string for BD_PART_TYPE_PROTECTED is "primary", that's what parted returns... */
2262 : static const gchar* const part_types[6] = { "primary", "logical", "extended", "free", "metadata", "primary" };
2263 :
2264 : /**
2265 : * bd_part_get_type_str:
2266 : * @type: type to get string representation for
2267 : * @error: (out) (optional): place to store error (if any)
2268 : *
2269 : * Returns: (transfer none): string representation of @type
2270 : *
2271 : * Tech category: always available
2272 : */
2273 6 : const gchar* bd_part_get_type_str (BDPartType type, GError **error) {
2274 6 : if (type > BD_PART_TYPE_PROTECTED) {
2275 0 : g_set_error (error, BD_PART_ERROR, BD_PART_ERROR_INVAL, "Invalid partition type given");
2276 0 : return NULL;
2277 : }
2278 :
2279 6 : return part_types[log2i (type) + 1];
2280 : }
|