LCOV - code coverage report
Current view: top level - plugins - part.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 60.1 % 1309 787
Test Date: 2026-01-26 13:19:28 Functions: 86.5 % 37 32
Legend: Lines: hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1