diff mbox series

[1/2] diskpart_handler: support for GPT PARTUUIDs

Message ID 20211105083803.332678-1-michael.adler@siemens.com
State Accepted
Headers show
Series [1/2] diskpart_handler: support for GPT PARTUUIDs | expand

Commit Message

Michael Adler Nov. 5, 2021, 8:38 a.m. UTC
It is already possible to specify PARTLABELS (using `name=`) in
SWUpdate's diskpart_handler.

This commit makes it possible to explicitly provide the PARTUUID by
setting `partuuid=` in the respective partition's property section.
If omitted, the current behavior of generating an automatic PARTUUID is
preserved, i.e. this is a backwards compatible extension.

An example use-case is to mount a partition by its PARTUUID, i.e. by
mounting /dev/disk/by-partuuid/<PARTUUID> (which is typically a symlink
created by udev). On real hardware, the symlink might point to
/dev/nvme0n1p1, whereas inside a VM, it could refer to /dev/vda1.
Thus, by using PARTUUID, the same mount command can be used in both
settings. This is useful because - unlike the disk UUID - the PARTUUID
can be explicitly set.

Signed-off-by: Michael Adler <michael.adler@siemens.com>
Signed-off-by: Christian Storm <christian.storm@siemens.com>
---
 doc/source/handlers.rst     |  3 ++
 handlers/diskpart_handler.c | 81 ++++++++++++++++++++++---------------
 2 files changed, 52 insertions(+), 32 deletions(-)

Comments

Stefano Babic Nov. 17, 2021, 11:04 a.m. UTC | #1
Hallo Michael,

On 05.11.21 09:38, Michael Adler wrote:
> It is already possible to specify PARTLABELS (using `name=`) in
> SWUpdate's diskpart_handler.
> 
> This commit makes it possible to explicitly provide the PARTUUID by
> setting `partuuid=` in the respective partition's property section.
> If omitted, the current behavior of generating an automatic PARTUUID is
> preserved, i.e. this is a backwards compatible extension.
> 

Fully agree.

> An example use-case is to mount a partition by its PARTUUID, i.e. by
> mounting /dev/disk/by-partuuid/<PARTUUID> (which is typically a symlink
> created by udev). On real hardware, the symlink might point to
> /dev/nvme0n1p1, whereas inside a VM, it could refer to /dev/vda1.
> Thus, by using PARTUUID, the same mount command can be used in both
> settings. This is useful because - unlike the disk UUID - the PARTUUID
> can be explicitly set.

Right.

> 
> Signed-off-by: Michael Adler <michael.adler@siemens.com>
> Signed-off-by: Christian Storm <christian.storm@siemens.com>
> ---
>   doc/source/handlers.rst     |  3 ++
>   handlers/diskpart_handler.c | 81 ++++++++++++++++++++++---------------
>   2 files changed, 52 insertions(+), 32 deletions(-)
> 
> diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst
> index ed811e3..f9c771b 100644
> --- a/doc/source/handlers.rst
> +++ b/doc/source/handlers.rst
> @@ -844,6 +844,9 @@ supported:
>      |             |          | will be created on the corresponding partition.    |
>      |             |          | vfat / ext2 / ext3 /ext4 file system is supported  |
>      +-------------+----------+----------------------------------------------------+
> +   | partuuid    | string   | The partition UUID (GPT only). If omitted, a UUID  |
> +   |             |          | will be generated automatically.			 |
> +   +-------------+----------+----------------------------------------------------+
>   
>   
>   
> diff --git a/handlers/diskpart_handler.c b/handlers/diskpart_handler.c
> index e08c10e..21efc8f 100644
> --- a/handlers/diskpart_handler.c
> +++ b/handlers/diskpart_handler.c
> @@ -17,6 +17,7 @@
>   #include <sys/types.h>
>   #include <libfdisk/libfdisk.h>
>   #include <fs_interface.h>
> +#include <uuid/uuid.h>
>   #include "swupdate.h"
>   #include "handler.h"
>   #include "util.h"
> @@ -50,7 +51,8 @@ enum partfield {
>   	PART_TYPE,
>   	PART_NAME,
>   	PART_FSTYPE,
> -	PART_DOSTYPE
> +	PART_DOSTYPE,
> +	PART_UUID,
>   };
>   
>   const char *fields[] = {
> @@ -59,7 +61,8 @@ const char *fields[] = {
>   	[PART_TYPE] = "type",
>   	[PART_NAME] = "name",
>   	[PART_FSTYPE] = "fstype",
> -	[PART_DOSTYPE] = "dostype"
> +	[PART_DOSTYPE] = "dostype",
> +	[PART_UUID] = "partuuid",
>   };
>   
>   struct partition_data {
> @@ -70,6 +73,7 @@ struct partition_data {
>   	char name[SWUPDATE_GENERAL_STRING_SIZE];
>   	char fstype[SWUPDATE_GENERAL_STRING_SIZE];
>   	char dostype[SWUPDATE_GENERAL_STRING_SIZE];
> +	char partuuid[UUID_STR_LEN];
>   	LIST_ENTRY(partition_data) next;
>   };
>   LIST_HEAD(listparts, partition_data);
> @@ -308,6 +312,9 @@ static int diskpart_set_partition(struct fdisk_partition *pa,
>   	if (parttype)
>   		ret |= fdisk_partition_set_type(pa, parttype);
>   
> +	if (strlen(part->partuuid))
> +	      ret |= fdisk_partition_set_uuid(pa, part->partuuid);
> +

I have supposed that the new feature should just make this call, however....

>   	return ret;
>   }
>   
> @@ -424,41 +431,44 @@ static void diskpart_partition_info(struct fdisk_context *cxt, const char *name,
>   /*
>    * Return true if partition differs
>    */
> -static bool diskpart_partition_cmp(struct fdisk_context *cxt, struct fdisk_partition *firstpa,
> -		struct fdisk_partition *secondpa)
> +static bool diskpart_partition_cmp(struct fdisk_partition *firstpa, struct fdisk_partition *secondpa)
>   {
> -	struct fdisk_parttype *type;
> -	const char *lbtype;
> -
>   	if (!firstpa || !secondpa)
>   		return true;
>   
> -	type = fdisk_partition_get_type(firstpa);
> -	if (!type)
> +	if (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> +		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> +			fdisk_partition_cmp_start(firstpa, secondpa)) ||
> +		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa)) {
>   		return true;
> +	}
>   
> -	if (fdisk_parttype_get_string(type))
> -		lbtype = "gpt";
> -	else
> -		lbtype = "dos";
> -
> -	if (firstpa && secondpa && (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> -		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> -		fdisk_partition_cmp_start(firstpa, secondpa)) ||
> -		(!strcmp(lbtype, "gpt") &&
> -			(strcmp(fdisk_parttype_get_string(fdisk_partition_get_type(firstpa)),
> -				fdisk_parttype_get_string(fdisk_partition_get_type(secondpa))) ||
> -			strcmp(fdisk_partition_get_name(firstpa) ? fdisk_partition_get_name(firstpa) : "",
> -		       		fdisk_partition_get_name(secondpa) ? fdisk_partition_get_name(secondpa) : ""))) ||
> -		(!strcmp(lbtype, "dos") &&
> -			fdisk_parttype_get_code(fdisk_partition_get_type(firstpa)) !=
> -			fdisk_parttype_get_code(fdisk_partition_get_type(secondpa))) ||
> -		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa))) {
> -		TRACE("Partition differ:");
> -		diskpart_partition_info(cxt, "Original", firstpa);
> -		diskpart_partition_info(cxt, "New", secondpa);
> +	struct fdisk_parttype *firstpa_type = fdisk_partition_get_type(firstpa);
> +	if (!firstpa_type)
>   		return true;
> +	struct fdisk_parttype *secondpa_type = fdisk_partition_get_type(secondpa);
> +
> +	if (fdisk_parttype_get_string(firstpa_type)) {
> +		/* gpt */
> +		const char *firstpa_name = fdisk_partition_get_name(firstpa);
> +		const char *secondpa_name = fdisk_partition_get_name(secondpa);
> +		if ((secondpa_type && strcmp(fdisk_parttype_get_string(firstpa_type), fdisk_parttype_get_string(secondpa_type))) ||
> +			strcmp(firstpa_name ? firstpa_name : "", secondpa_name ? secondpa_name : "")) {
> +			return true;
> +		}
> +
> +		const char *firstpa_uuid = fdisk_partition_get_uuid(firstpa);
> +		const char *secondpa_uuid = fdisk_partition_get_uuid(secondpa);
> +		if (firstpa_uuid && secondpa_uuid && strcmp(firstpa_uuid, secondpa_uuid)) {
> +			return true;
> +		}
> +	} else {
> +		/* dos */
> +		if (fdisk_parttype_get_code(firstpa_type) != fdisk_parttype_get_code(secondpa_type)) {
> +			return true;
> +		}
>   	}
> +

...it is not clear to me how the changes above have something to do with 
PARTUUID, or which issue should be fixed (and in any case, this should 
be done in a separate patch). Can you explain this ?

Regards,
Stefano

>   	return false;
>   }
>   
> @@ -610,7 +620,10 @@ static int diskpart_table_cmp(struct fdisk_context *cxt, struct fdisk_table *tb,
>   				fdisk_table_next_partition (oldtb, olditr, &pa)) {
>   				TRACE("Partition not found, something went wrong %lu !", i);
>   				ret = -EFAULT;
> -			} else if (diskpart_partition_cmp(cxt, pa, newpa)) {
> +			} else if (diskpart_partition_cmp(pa, newpa)) {
> +				TRACE("Partition differ:");
> +				diskpart_partition_info(cxt, "Original", pa);
> +				diskpart_partition_info(cxt, "New", newpa);
>   				ret = 1;
>   			}
>   
> @@ -845,6 +858,9 @@ static int diskpart(struct img_type *img,
>   						strncpy(part->dostype, equal, sizeof(part->dostype));
>   						hybrid++;
>   						break;
> +					case PART_UUID:
> +						strncpy(part->partuuid, equal, sizeof(part->partuuid));
> +						break;
>   					}
>   				}
>   			}
> @@ -864,12 +880,13 @@ static int diskpart(struct img_type *img,
>   			goto handler_exit;
>   		}
>   
> -		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s",
> +		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s partuuid %s",
>   			part->partno != LIBFDISK_INIT_UNDEF(part->partno) ? part->partno : 0,
>   			strlen(part->name) ? part->name : "UNDEF NAME",
>   			part->size != LIBFDISK_INIT_UNDEF(part->size) ? part->size : 0,
>   			part->start!= LIBFDISK_INIT_UNDEF(part->start) ? part->start : 0,
> -			part->type);
> +			part->type,
> +			strlen(part->partuuid) ? part->partuuid : "automatic");
>   
>   		/*
>   		 * Partitions in sw-description start from 1,
>
Michael Adler Nov. 18, 2021, 10:07 a.m. UTC | #2
Hi Stefano,

> ...it is not clear to me how the changes above have something to do with
> PARTUUID, or which issue should be fixed (and in any case, this should be
> done in a separate patch). Can you explain this ?

the check which compares the existing GPT (on the device) with the GPT described in sw-description needs to be extended
as well. For example, let's assume that previously I did not set an explicit PARTUUID for the devices and thus a random
one has been generated. But now I want all my devices to have the same PARTUUID. diskpart_partition_cmp is used to
determine if the partition tables are different, therefore I had to extend it and check for different PARTUUIDS.

I could have inserted another || clause in the convoluted if-construct but since it's quite hard to read (and rather
inefficient) I decided to simplify it. If you agree with the simplification, I can split the commit and do the
simplification in one step and the additional PARTUUID check in another commit.

Kind regards,
Michael
Stefano Babic Nov. 18, 2021, 10:21 a.m. UTC | #3
Hi Michael,

On 18.11.21 11:07, Michael Adler wrote:
> Hi Stefano,
> 
>> ...it is not clear to me how the changes above have something to do with
>> PARTUUID, or which issue should be fixed (and in any case, this should be
>> done in a separate patch). Can you explain this ?
> 
> the check which compares the existing GPT (on the device) with the GPT described in sw-description needs to be extended
> as well. For example, let's assume that previously I did not set an explicit PARTUUID for the devices and thus a random
> one has been generated. But now I want all my devices to have the same PARTUUID. diskpart_partition_cmp is used to
> determine if the partition tables are different, therefore I had to extend it and check for different PARTUUIDS.
> 

This is clear.

> I could have inserted another || clause in the convoluted if-construct but since it's quite hard to read

maybe yes

> (and rather
> inefficient)

Why inefficient ?

> I decided to simplify it.

At first glance, it appeared to me that different checks are done. I 
will deeply reviewed it.

> If you agree with the simplification, I can split the commit and do the
> simplification in one step and the additional PARTUUID check in another commit.
> 

Best regards,
Stefano

> Kind regards,
> Michael
>
Stefano Babic Nov. 18, 2021, 10:45 a.m. UTC | #4
Hi Michael,

On 05.11.21 09:38, Michael Adler wrote:
> It is already possible to specify PARTLABELS (using `name=`) in
> SWUpdate's diskpart_handler.
> 
> This commit makes it possible to explicitly provide the PARTUUID by
> setting `partuuid=` in the respective partition's property section.
> If omitted, the current behavior of generating an automatic PARTUUID is
> preserved, i.e. this is a backwards compatible extension.
> 
> An example use-case is to mount a partition by its PARTUUID, i.e. by
> mounting /dev/disk/by-partuuid/<PARTUUID> (which is typically a symlink
> created by udev). On real hardware, the symlink might point to
> /dev/nvme0n1p1, whereas inside a VM, it could refer to /dev/vda1.
> Thus, by using PARTUUID, the same mount command can be used in both
> settings. This is useful because - unlike the disk UUID - the PARTUUID
> can be explicitly set.
> 
> Signed-off-by: Michael Adler <michael.adler@siemens.com>
> Signed-off-by: Christian Storm <christian.storm@siemens.com>
> ---
>   doc/source/handlers.rst     |  3 ++
>   handlers/diskpart_handler.c | 81 ++++++++++++++++++++++---------------
>   2 files changed, 52 insertions(+), 32 deletions(-)
> 
> diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst
> index ed811e3..f9c771b 100644
> --- a/doc/source/handlers.rst
> +++ b/doc/source/handlers.rst
> @@ -844,6 +844,9 @@ supported:
>      |             |          | will be created on the corresponding partition.    |
>      |             |          | vfat / ext2 / ext3 /ext4 file system is supported  |
>      +-------------+----------+----------------------------------------------------+
> +   | partuuid    | string   | The partition UUID (GPT only). If omitted, a UUID  |
> +   |             |          | will be generated automatically.			 |
> +   +-------------+----------+----------------------------------------------------+
>   
>   
>   
> diff --git a/handlers/diskpart_handler.c b/handlers/diskpart_handler.c
> index e08c10e..21efc8f 100644
> --- a/handlers/diskpart_handler.c
> +++ b/handlers/diskpart_handler.c
> @@ -17,6 +17,7 @@
>   #include <sys/types.h>
>   #include <libfdisk/libfdisk.h>
>   #include <fs_interface.h>
> +#include <uuid/uuid.h>
>   #include "swupdate.h"
>   #include "handler.h"
>   #include "util.h"
> @@ -50,7 +51,8 @@ enum partfield {
>   	PART_TYPE,
>   	PART_NAME,
>   	PART_FSTYPE,
> -	PART_DOSTYPE
> +	PART_DOSTYPE,
> +	PART_UUID,
>   };
>   
>   const char *fields[] = {
> @@ -59,7 +61,8 @@ const char *fields[] = {
>   	[PART_TYPE] = "type",
>   	[PART_NAME] = "name",
>   	[PART_FSTYPE] = "fstype",
> -	[PART_DOSTYPE] = "dostype"
> +	[PART_DOSTYPE] = "dostype",
> +	[PART_UUID] = "partuuid",
>   };
>   
>   struct partition_data {
> @@ -70,6 +73,7 @@ struct partition_data {
>   	char name[SWUPDATE_GENERAL_STRING_SIZE];
>   	char fstype[SWUPDATE_GENERAL_STRING_SIZE];
>   	char dostype[SWUPDATE_GENERAL_STRING_SIZE];
> +	char partuuid[UUID_STR_LEN];
>   	LIST_ENTRY(partition_data) next;
>   };
>   LIST_HEAD(listparts, partition_data);
> @@ -308,6 +312,9 @@ static int diskpart_set_partition(struct fdisk_partition *pa,
>   	if (parttype)
>   		ret |= fdisk_partition_set_type(pa, parttype);
>   
> +	if (strlen(part->partuuid))
> +	      ret |= fdisk_partition_set_uuid(pa, part->partuuid);
> +
>   	return ret;
>   }
>   
> @@ -424,41 +431,44 @@ static void diskpart_partition_info(struct fdisk_context *cxt, const char *name,
>   /*
>    * Return true if partition differs
>    */
> -static bool diskpart_partition_cmp(struct fdisk_context *cxt, struct fdisk_partition *firstpa,
> -		struct fdisk_partition *secondpa)
> +static bool diskpart_partition_cmp(struct fdisk_partition *firstpa, struct fdisk_partition *secondpa)
>   {
> -	struct fdisk_parttype *type;
> -	const char *lbtype;
> -
>   	if (!firstpa || !secondpa)
>   		return true;
>   
> -	type = fdisk_partition_get_type(firstpa);
> -	if (!type)
> +	if (fdisk_partition_cmp_partno(firstpa, secondpa) ||

fdisk_partition_cmp_partno() is called, but check if the pointers are 
valid was removed. What does it happen ny passing NULL to 
fdisk_partition_cmp_partno ?

> +		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> +			fdisk_partition_cmp_start(firstpa, secondpa)) ||
> +		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa)) {
>   		return true;
> +	}
>   
> -	if (fdisk_parttype_get_string(type))
> -		lbtype = "gpt";
> -	else
> -		lbtype = "dos";
> -
> -	if (firstpa && secondpa && (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> -		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> -		fdisk_partition_cmp_start(firstpa, secondpa)) ||
> -		(!strcmp(lbtype, "gpt") &&
> -			(strcmp(fdisk_parttype_get_string(fdisk_partition_get_type(firstpa)),
> -				fdisk_parttype_get_string(fdisk_partition_get_type(secondpa))) ||
> -			strcmp(fdisk_partition_get_name(firstpa) ? fdisk_partition_get_name(firstpa) : "",
> -		       		fdisk_partition_get_name(secondpa) ? fdisk_partition_get_name(secondpa) : ""))) ||
> -		(!strcmp(lbtype, "dos") &&
> -			fdisk_parttype_get_code(fdisk_partition_get_type(firstpa)) !=
> -			fdisk_parttype_get_code(fdisk_partition_get_type(secondpa))) ||
> -		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa))) {
> -		TRACE("Partition differ:");
> -		diskpart_partition_info(cxt, "Original", firstpa);
> -		diskpart_partition_info(cxt, "New", secondpa);
> +	struct fdisk_parttype *firstpa_type = fdisk_partition_get_type(firstpa);
> +	if (!firstpa_type)
>   		return true;
> +	struct fdisk_parttype *secondpa_type = fdisk_partition_get_type(secondpa);
> +
> +	if (fdisk_parttype_get_string(firstpa_type)) {

Documentation for libfdisk is scarce, but it seems to me that this has 
meaning only in case of GPT. In case of MBR, it could (just because I do 
not see if it is set) mreturns NULL, but it is undocumented in libfdisk. 
But you dropped the check for GPT. Do we rely that 
fdisk_parttype_get_string() returns a not null just in case of GPT ? 
Where is it documented ?

In libfdisk, there is no check about type as well, but it just returns 
the pointer. fdisk_parttype_set_typestr() does not check as well, and 
comment to the function says "Don't use this function for MBR". The way 
thought by libfdisk developers is to check for type, and then access to 
the right set of funtions (as done before your patch). I understand that 
behavior is unpredictable if the wrong set is used.


> +		/* gpt */
> +		const char *firstpa_name = fdisk_partition_get_name(firstpa);
> +		const char *secondpa_name = fdisk_partition_get_name(secondpa);
> +		if ((secondpa_type && strcmp(fdisk_parttype_get_string(firstpa_type), fdisk_parttype_get_string(secondpa_type))) ||
> +			strcmp(firstpa_name ? firstpa_name : "", secondpa_name ? secondpa_name : "")) {
> +			return true;
> +		}
> +
> +		const char *firstpa_uuid = fdisk_partition_get_uuid(firstpa);
> +		const char *secondpa_uuid = fdisk_partition_get_uuid(secondpa);
> +		if (firstpa_uuid && secondpa_uuid && strcmp(firstpa_uuid, secondpa_uuid)) {
> +			return true;

But this means: if one of firstpa_uuid or secondpa_uuid is not set, it 
the check is skipped and it return false (that is, they are identical). 
It looks wrong.


> +		}
> +	} else {
> +		/* dos */
> +		if (fdisk_parttype_get_code(firstpa_type) != fdisk_parttype_get_code(secondpa_type)) {
> +			return true;
> +		}

Ok, and now where is compared the size ?

>   	}
> +
>   	return false;
>   }
>   
> @@ -610,7 +620,10 @@ static int diskpart_table_cmp(struct fdisk_context *cxt, struct fdisk_table *tb,
>   				fdisk_table_next_partition (oldtb, olditr, &pa)) {
>   				TRACE("Partition not found, something went wrong %lu !", i);
>   				ret = -EFAULT;
> -			} else if (diskpart_partition_cmp(cxt, pa, newpa)) {
> +			} else if (diskpart_partition_cmp(pa, newpa)) {
> +				TRACE("Partition differ:");
> +				diskpart_partition_info(cxt, "Original", pa);
> +				diskpart_partition_info(cxt, "New", newpa);
>   				ret = 1;
>   			}
>   
> @@ -845,6 +858,9 @@ static int diskpart(struct img_type *img,
>   						strncpy(part->dostype, equal, sizeof(part->dostype));
>   						hybrid++;
>   						break;
> +					case PART_UUID:
> +						strncpy(part->partuuid, equal, sizeof(part->partuuid));
> +						break;
>   					}
>   				}
>   			}
> @@ -864,12 +880,13 @@ static int diskpart(struct img_type *img,
>   			goto handler_exit;
>   		}
>   
> -		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s",
> +		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s partuuid %s",
>   			part->partno != LIBFDISK_INIT_UNDEF(part->partno) ? part->partno : 0,
>   			strlen(part->name) ? part->name : "UNDEF NAME",
>   			part->size != LIBFDISK_INIT_UNDEF(part->size) ? part->size : 0,
>   			part->start!= LIBFDISK_INIT_UNDEF(part->start) ? part->start : 0,
> -			part->type);
> +			part->type,
> +			strlen(part->partuuid) ? part->partuuid : "automatic");
>   
>   		/*
>   		 * Partitions in sw-description start from 1,
> 

Best regards,
Stefano
Michael Adler Nov. 18, 2021, 11:02 a.m. UTC | #5
> Why inefficient ?

Nothing major and the compiler should optimize most of it away:

* the string variable lbtype is introduced and subsequently used with strcmp
		=> no need to use a string for this (int/enum would suffice)
		=> in fact, there is no need to introduce a variable at all if you break up the if-clause like I did
* at other times, the result of a function call is not cached, e.g.
			strcmp(fdisk_partition_get_name(firstpa) ? fdisk_partition_get_name(firstpa)
		(this happens all over the place in the if-clause)

Like I said, nothing major, it's probably more about clean code than performance here.

> At first glance, it appeared to me that different checks are done. I will
> deeply reviewed it.

Thanks, appreciated! It _should_ behave the same.
Michael Adler Nov. 18, 2021, 12:56 p.m. UTC | #6
> >   	if (!firstpa || !secondpa)
> >   		return true;
> > -	type = fdisk_partition_get_type(firstpa);
> > -	if (!type)
> > +	if (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> 
> fdisk_partition_cmp_partno() is called, but check if the pointers are valid
> was removed. What does it happen ny passing NULL to
> fdisk_partition_cmp_partno ?

There is a NULL check a few lines earlier:

> >   	if (!firstpa || !secondpa)
> >   		return true;


> > +	if (fdisk_parttype_get_string(firstpa_type)) {
> 
> Documentation for libfdisk is scarce, but it seems to me that this has
> meaning only in case of GPT. In case of MBR, it could (just because I do not
> see if it is set) mreturns NULL, but it is undocumented in libfdisk. But you
> dropped the check for GPT. Do we rely that fdisk_parttype_get_string()
> returns a not null just in case of GPT ? Where is it documented ?

I didn't change the logic. Previously, we had:

```
  if (fdisk_parttype_get_string(type))
 		lbtype = "gpt";
 	else
 		lbtype = "dos";

	...

  (!strcmp(lbtype, "gpt") &&
			(strcmp(fdisk_parttype_get_string(fdisk_partition_get_type(firstpa)),
				fdisk_parttype_get_string(fdisk_partition_get_type(secondpa))) ||
```

So basically I inlined this and got rid of the lbtype (string!) variable.

> But this means: if one of firstpa_uuid or secondpa_uuid is not set, it the
> check is skipped and it return false (that is, they are identical). It looks
> wrong.

firstpa_uuid is always set in case of GPT. Either it has been generated randomly or it has been explicitly set by us.
Just in case there is an extra NULL check. The thing is: secondpa_uuid is the value provided in sw-description. If the
value is missing in sw-description, it means we do not care about the PARTUUID and in particular, we do not want to
modify the existing (random) value. The idea is to avoid modifying the GPT unless we really have to.

> Ok, and now where is compared the size ?

The size check is at the beginning of the function (line 442 after applying the patch).

I hope I didn't miss any question. Please let me know if I did or something else is unclear.

Michael
Stefano Babic Nov. 29, 2021, 10:31 a.m. UTC | #7
On 05.11.21 09:38, Michael Adler wrote:
> It is already possible to specify PARTLABELS (using `name=`) in
> SWUpdate's diskpart_handler.
> 
> This commit makes it possible to explicitly provide the PARTUUID by
> setting `partuuid=` in the respective partition's property section.
> If omitted, the current behavior of generating an automatic PARTUUID is
> preserved, i.e. this is a backwards compatible extension.
> 
> An example use-case is to mount a partition by its PARTUUID, i.e. by
> mounting /dev/disk/by-partuuid/<PARTUUID> (which is typically a symlink
> created by udev). On real hardware, the symlink might point to
> /dev/nvme0n1p1, whereas inside a VM, it could refer to /dev/vda1.
> Thus, by using PARTUUID, the same mount command can be used in both
> settings. This is useful because - unlike the disk UUID - the PARTUUID
> can be explicitly set.
> 
> Signed-off-by: Michael Adler <michael.adler@siemens.com>
> Signed-off-by: Christian Storm <christian.storm@siemens.com>
> ---
>   doc/source/handlers.rst     |  3 ++
>   handlers/diskpart_handler.c | 81 ++++++++++++++++++++++---------------
>   2 files changed, 52 insertions(+), 32 deletions(-)
> 
> diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst
> index ed811e3..f9c771b 100644
> --- a/doc/source/handlers.rst
> +++ b/doc/source/handlers.rst
> @@ -844,6 +844,9 @@ supported:
>      |             |          | will be created on the corresponding partition.    |
>      |             |          | vfat / ext2 / ext3 /ext4 file system is supported  |
>      +-------------+----------+----------------------------------------------------+
> +   | partuuid    | string   | The partition UUID (GPT only). If omitted, a UUID  |
> +   |             |          | will be generated automatically.			 |
> +   +-------------+----------+----------------------------------------------------+
>   
>   
>   
> diff --git a/handlers/diskpart_handler.c b/handlers/diskpart_handler.c
> index e08c10e..21efc8f 100644
> --- a/handlers/diskpart_handler.c
> +++ b/handlers/diskpart_handler.c
> @@ -17,6 +17,7 @@
>   #include <sys/types.h>
>   #include <libfdisk/libfdisk.h>
>   #include <fs_interface.h>
> +#include <uuid/uuid.h>
>   #include "swupdate.h"
>   #include "handler.h"
>   #include "util.h"
> @@ -50,7 +51,8 @@ enum partfield {
>   	PART_TYPE,
>   	PART_NAME,
>   	PART_FSTYPE,
> -	PART_DOSTYPE
> +	PART_DOSTYPE,
> +	PART_UUID,
>   };
>   
>   const char *fields[] = {
> @@ -59,7 +61,8 @@ const char *fields[] = {
>   	[PART_TYPE] = "type",
>   	[PART_NAME] = "name",
>   	[PART_FSTYPE] = "fstype",
> -	[PART_DOSTYPE] = "dostype"
> +	[PART_DOSTYPE] = "dostype",
> +	[PART_UUID] = "partuuid",
>   };
>   
>   struct partition_data {
> @@ -70,6 +73,7 @@ struct partition_data {
>   	char name[SWUPDATE_GENERAL_STRING_SIZE];
>   	char fstype[SWUPDATE_GENERAL_STRING_SIZE];
>   	char dostype[SWUPDATE_GENERAL_STRING_SIZE];
> +	char partuuid[UUID_STR_LEN];
>   	LIST_ENTRY(partition_data) next;
>   };
>   LIST_HEAD(listparts, partition_data);
> @@ -308,6 +312,9 @@ static int diskpart_set_partition(struct fdisk_partition *pa,
>   	if (parttype)
>   		ret |= fdisk_partition_set_type(pa, parttype);
>   
> +	if (strlen(part->partuuid))
> +	      ret |= fdisk_partition_set_uuid(pa, part->partuuid);
> +
>   	return ret;
>   }
>   
> @@ -424,41 +431,44 @@ static void diskpart_partition_info(struct fdisk_context *cxt, const char *name,
>   /*
>    * Return true if partition differs
>    */
> -static bool diskpart_partition_cmp(struct fdisk_context *cxt, struct fdisk_partition *firstpa,
> -		struct fdisk_partition *secondpa)
> +static bool diskpart_partition_cmp(struct fdisk_partition *firstpa, struct fdisk_partition *secondpa)
>   {
> -	struct fdisk_parttype *type;
> -	const char *lbtype;
> -
>   	if (!firstpa || !secondpa)
>   		return true;
>   
> -	type = fdisk_partition_get_type(firstpa);
> -	if (!type)
> +	if (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> +		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> +			fdisk_partition_cmp_start(firstpa, secondpa)) ||
> +		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa)) {
>   		return true;
> +	}
>   
> -	if (fdisk_parttype_get_string(type))
> -		lbtype = "gpt";
> -	else
> -		lbtype = "dos";
> -
> -	if (firstpa && secondpa && (fdisk_partition_cmp_partno(firstpa, secondpa) ||
> -		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
> -		fdisk_partition_cmp_start(firstpa, secondpa)) ||
> -		(!strcmp(lbtype, "gpt") &&
> -			(strcmp(fdisk_parttype_get_string(fdisk_partition_get_type(firstpa)),
> -				fdisk_parttype_get_string(fdisk_partition_get_type(secondpa))) ||
> -			strcmp(fdisk_partition_get_name(firstpa) ? fdisk_partition_get_name(firstpa) : "",
> -		       		fdisk_partition_get_name(secondpa) ? fdisk_partition_get_name(secondpa) : ""))) ||
> -		(!strcmp(lbtype, "dos") &&
> -			fdisk_parttype_get_code(fdisk_partition_get_type(firstpa)) !=
> -			fdisk_parttype_get_code(fdisk_partition_get_type(secondpa))) ||
> -		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa))) {
> -		TRACE("Partition differ:");
> -		diskpart_partition_info(cxt, "Original", firstpa);
> -		diskpart_partition_info(cxt, "New", secondpa);
> +	struct fdisk_parttype *firstpa_type = fdisk_partition_get_type(firstpa);
> +	if (!firstpa_type)
>   		return true;
> +	struct fdisk_parttype *secondpa_type = fdisk_partition_get_type(secondpa);
> +
> +	if (fdisk_parttype_get_string(firstpa_type)) {
> +		/* gpt */
> +		const char *firstpa_name = fdisk_partition_get_name(firstpa);
> +		const char *secondpa_name = fdisk_partition_get_name(secondpa);
> +		if ((secondpa_type && strcmp(fdisk_parttype_get_string(firstpa_type), fdisk_parttype_get_string(secondpa_type))) ||
> +			strcmp(firstpa_name ? firstpa_name : "", secondpa_name ? secondpa_name : "")) {
> +			return true;
> +		}
> +
> +		const char *firstpa_uuid = fdisk_partition_get_uuid(firstpa);
> +		const char *secondpa_uuid = fdisk_partition_get_uuid(secondpa);
> +		if (firstpa_uuid && secondpa_uuid && strcmp(firstpa_uuid, secondpa_uuid)) {
> +			return true;
> +		}
> +	} else {
> +		/* dos */
> +		if (fdisk_parttype_get_code(firstpa_type) != fdisk_parttype_get_code(secondpa_type)) {
> +			return true;
> +		}
>   	}
> +
>   	return false;
>   }
>   
> @@ -610,7 +620,10 @@ static int diskpart_table_cmp(struct fdisk_context *cxt, struct fdisk_table *tb,
>   				fdisk_table_next_partition (oldtb, olditr, &pa)) {
>   				TRACE("Partition not found, something went wrong %lu !", i);
>   				ret = -EFAULT;
> -			} else if (diskpart_partition_cmp(cxt, pa, newpa)) {
> +			} else if (diskpart_partition_cmp(pa, newpa)) {
> +				TRACE("Partition differ:");
> +				diskpart_partition_info(cxt, "Original", pa);
> +				diskpart_partition_info(cxt, "New", newpa);
>   				ret = 1;
>   			}
>   
> @@ -845,6 +858,9 @@ static int diskpart(struct img_type *img,
>   						strncpy(part->dostype, equal, sizeof(part->dostype));
>   						hybrid++;
>   						break;
> +					case PART_UUID:
> +						strncpy(part->partuuid, equal, sizeof(part->partuuid));
> +						break;
>   					}
>   				}
>   			}
> @@ -864,12 +880,13 @@ static int diskpart(struct img_type *img,
>   			goto handler_exit;
>   		}
>   
> -		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s",
> +		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s partuuid %s",
>   			part->partno != LIBFDISK_INIT_UNDEF(part->partno) ? part->partno : 0,
>   			strlen(part->name) ? part->name : "UNDEF NAME",
>   			part->size != LIBFDISK_INIT_UNDEF(part->size) ? part->size : 0,
>   			part->start!= LIBFDISK_INIT_UNDEF(part->start) ? part->start : 0,
> -			part->type);
> +			part->type,
> +			strlen(part->partuuid) ? part->partuuid : "automatic");
>   
>   		/*
>   		 * Partitions in sw-description start from 1,
> 

Applied to -master, thanks !

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst
index ed811e3..f9c771b 100644
--- a/doc/source/handlers.rst
+++ b/doc/source/handlers.rst
@@ -844,6 +844,9 @@  supported:
    |             |          | will be created on the corresponding partition.    |
    |             |          | vfat / ext2 / ext3 /ext4 file system is supported  |
    +-------------+----------+----------------------------------------------------+
+   | partuuid    | string   | The partition UUID (GPT only). If omitted, a UUID  |
+   |             |          | will be generated automatically.			 |
+   +-------------+----------+----------------------------------------------------+
 
 
 
diff --git a/handlers/diskpart_handler.c b/handlers/diskpart_handler.c
index e08c10e..21efc8f 100644
--- a/handlers/diskpart_handler.c
+++ b/handlers/diskpart_handler.c
@@ -17,6 +17,7 @@ 
 #include <sys/types.h>
 #include <libfdisk/libfdisk.h>
 #include <fs_interface.h>
+#include <uuid/uuid.h>
 #include "swupdate.h"
 #include "handler.h"
 #include "util.h"
@@ -50,7 +51,8 @@  enum partfield {
 	PART_TYPE,
 	PART_NAME,
 	PART_FSTYPE,
-	PART_DOSTYPE
+	PART_DOSTYPE,
+	PART_UUID,
 };
 
 const char *fields[] = {
@@ -59,7 +61,8 @@  const char *fields[] = {
 	[PART_TYPE] = "type",
 	[PART_NAME] = "name",
 	[PART_FSTYPE] = "fstype",
-	[PART_DOSTYPE] = "dostype"
+	[PART_DOSTYPE] = "dostype",
+	[PART_UUID] = "partuuid",
 };
 
 struct partition_data {
@@ -70,6 +73,7 @@  struct partition_data {
 	char name[SWUPDATE_GENERAL_STRING_SIZE];
 	char fstype[SWUPDATE_GENERAL_STRING_SIZE];
 	char dostype[SWUPDATE_GENERAL_STRING_SIZE];
+	char partuuid[UUID_STR_LEN];
 	LIST_ENTRY(partition_data) next;
 };
 LIST_HEAD(listparts, partition_data);
@@ -308,6 +312,9 @@  static int diskpart_set_partition(struct fdisk_partition *pa,
 	if (parttype)
 		ret |= fdisk_partition_set_type(pa, parttype);
 
+	if (strlen(part->partuuid))
+	      ret |= fdisk_partition_set_uuid(pa, part->partuuid);
+
 	return ret;
 }
 
@@ -424,41 +431,44 @@  static void diskpart_partition_info(struct fdisk_context *cxt, const char *name,
 /*
  * Return true if partition differs
  */
-static bool diskpart_partition_cmp(struct fdisk_context *cxt, struct fdisk_partition *firstpa,
-		struct fdisk_partition *secondpa)
+static bool diskpart_partition_cmp(struct fdisk_partition *firstpa, struct fdisk_partition *secondpa)
 {
-	struct fdisk_parttype *type;
-	const char *lbtype;
-
 	if (!firstpa || !secondpa)
 		return true;
 
-	type = fdisk_partition_get_type(firstpa);
-	if (!type)
+	if (fdisk_partition_cmp_partno(firstpa, secondpa) ||
+		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
+			fdisk_partition_cmp_start(firstpa, secondpa)) ||
+		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa)) {
 		return true;
+	}
 
-	if (fdisk_parttype_get_string(type))
-		lbtype = "gpt";
-	else
-		lbtype = "dos";
-
-	if (firstpa && secondpa && (fdisk_partition_cmp_partno(firstpa, secondpa) ||
-		(!fdisk_partition_start_is_default(firstpa) && !fdisk_partition_start_is_default(secondpa) &&
-		fdisk_partition_cmp_start(firstpa, secondpa)) ||
-		(!strcmp(lbtype, "gpt") &&
-			(strcmp(fdisk_parttype_get_string(fdisk_partition_get_type(firstpa)),
-				fdisk_parttype_get_string(fdisk_partition_get_type(secondpa))) ||
-			strcmp(fdisk_partition_get_name(firstpa) ? fdisk_partition_get_name(firstpa) : "",
-		       		fdisk_partition_get_name(secondpa) ? fdisk_partition_get_name(secondpa) : ""))) ||
-		(!strcmp(lbtype, "dos") &&
-			fdisk_parttype_get_code(fdisk_partition_get_type(firstpa)) !=
-			fdisk_parttype_get_code(fdisk_partition_get_type(secondpa))) ||
-		fdisk_partition_get_size(firstpa) != fdisk_partition_get_size(secondpa))) {
-		TRACE("Partition differ:");
-		diskpart_partition_info(cxt, "Original", firstpa);
-		diskpart_partition_info(cxt, "New", secondpa);
+	struct fdisk_parttype *firstpa_type = fdisk_partition_get_type(firstpa);
+	if (!firstpa_type)
 		return true;
+	struct fdisk_parttype *secondpa_type = fdisk_partition_get_type(secondpa);
+
+	if (fdisk_parttype_get_string(firstpa_type)) {
+		/* gpt */
+		const char *firstpa_name = fdisk_partition_get_name(firstpa);
+		const char *secondpa_name = fdisk_partition_get_name(secondpa);
+		if ((secondpa_type && strcmp(fdisk_parttype_get_string(firstpa_type), fdisk_parttype_get_string(secondpa_type))) ||
+			strcmp(firstpa_name ? firstpa_name : "", secondpa_name ? secondpa_name : "")) {
+			return true;
+		}
+
+		const char *firstpa_uuid = fdisk_partition_get_uuid(firstpa);
+		const char *secondpa_uuid = fdisk_partition_get_uuid(secondpa);
+		if (firstpa_uuid && secondpa_uuid && strcmp(firstpa_uuid, secondpa_uuid)) {
+			return true;
+		}
+	} else {
+		/* dos */
+		if (fdisk_parttype_get_code(firstpa_type) != fdisk_parttype_get_code(secondpa_type)) {
+			return true;
+		}
 	}
+
 	return false;
 }
 
@@ -610,7 +620,10 @@  static int diskpart_table_cmp(struct fdisk_context *cxt, struct fdisk_table *tb,
 				fdisk_table_next_partition (oldtb, olditr, &pa)) {
 				TRACE("Partition not found, something went wrong %lu !", i);
 				ret = -EFAULT;
-			} else if (diskpart_partition_cmp(cxt, pa, newpa)) {
+			} else if (diskpart_partition_cmp(pa, newpa)) {
+				TRACE("Partition differ:");
+				diskpart_partition_info(cxt, "Original", pa);
+				diskpart_partition_info(cxt, "New", newpa);
 				ret = 1;
 			}
 
@@ -845,6 +858,9 @@  static int diskpart(struct img_type *img,
 						strncpy(part->dostype, equal, sizeof(part->dostype));
 						hybrid++;
 						break;
+					case PART_UUID:
+						strncpy(part->partuuid, equal, sizeof(part->partuuid));
+						break;
 					}
 				}
 			}
@@ -864,12 +880,13 @@  static int diskpart(struct img_type *img,
 			goto handler_exit;
 		}
 
-		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s",
+		TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s partuuid %s",
 			part->partno != LIBFDISK_INIT_UNDEF(part->partno) ? part->partno : 0,
 			strlen(part->name) ? part->name : "UNDEF NAME",
 			part->size != LIBFDISK_INIT_UNDEF(part->size) ? part->size : 0,
 			part->start!= LIBFDISK_INIT_UNDEF(part->start) ? part->start : 0,
-			part->type);
+			part->type,
+			strlen(part->partuuid) ? part->partuuid : "automatic");
 
 		/*
 		 * Partitions in sw-description start from 1,