Skip to content

Commit

Permalink
daemon: Reimplement partition GPT functions using sfdisk
Browse files Browse the repository at this point in the history
sfdisk can now do everything with GPT that sgdisk was needed for
before.  In particular we are able to reimplement the following
functions using sfdisk:

- part_set_disk_guid   (replace with sfdisk --disk-id)
- part_get_disk_guid
- part_set_disk_guid_random
- part_set_gpt_attributes           (sfdisk --part-attrs)
- part_get_gpt_attributes
- part_set_gpt_guid                 (sfdisk --part-uuid)
- part_get_gpt_guid
- part_set_gpt_type                 (sfdisk --part-type)
- part_get_gpt_type

This allows us to drop the requirement for gdisk in many cases.

There is only one API remaining which requires gdisk, part_expand_gpt,
which we do not use in our tools.  In a prior commit I already moved
this solitary function to a new source file (daemon/gdisk.c).

Fixes: https://issues.redhat.com/browse/RHEL-35998
  • Loading branch information
rwmjones committed May 10, 2024
1 parent a25f419 commit c6c266a
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 251 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -108,6 +108,7 @@ Makefile.in
/daemon/parted.mli
/daemon/realpath.mli
/daemon/rpm.mli
/daemon/sfdisk.mli
/daemon/stamp-guestfsd.pod
/daemon/statvfs.mli
/daemon/structs-cleanups.c
Expand Down
3 changes: 3 additions & 0 deletions daemon/Makefile.am
Expand Up @@ -59,6 +59,7 @@ generator_built = \
parted.mli \
realpath.mli \
rpm.mli \
sfdisk.mli \
statvfs.mli \
structs.ml \
structs.mli
Expand Down Expand Up @@ -306,6 +307,7 @@ SOURCES_MLI = \
parted.mli \
realpath.mli \
rpm.mli \
sfdisk.mli \
statvfs.mli \
structs.mli \
sysroot.mli \
Expand Down Expand Up @@ -337,6 +339,7 @@ SOURCES_ML = \
md.ml \
mount.ml \
mount_utils.ml \
sfdisk.ml \
parted.ml \
listfs.ml \
realpath.ml \
Expand Down
2 changes: 1 addition & 1 deletion daemon/inspect_fs_windows.ml
Expand Up @@ -419,7 +419,7 @@ and map_registry_disk_blob_gpt partitions blob =
let typ = Parted.part_get_parttype device in
if typ <> "gpt" then false
else (
let guid = Parted.part_get_gpt_guid device partnum in
let guid = Sfdisk.part_get_gpt_guid device partnum in
String.lowercase_ascii guid = blob_guid
)
) partitions in
Expand Down
2 changes: 1 addition & 1 deletion daemon/listfs.ml
Expand Up @@ -114,7 +114,7 @@ and is_partition_can_hold_filesystem partition =
else if is_mbr then
true
else (
let gpt_type = Parted.part_get_gpt_type device partnum in
let gpt_type = Sfdisk.part_get_gpt_type device partnum in
match gpt_type with
(* Windows Logical Disk Manager metadata partition. *)
| "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3"
Expand Down
144 changes: 0 additions & 144 deletions daemon/parted.c
Expand Up @@ -456,58 +456,6 @@ do_part_set_mbr_id (const char *device, int partnum, int idbyte)
return 0;
}

int
do_part_set_gpt_type (const char *device, int partnum, const char *guid)
{
if (partnum <= 0) {
reply_with_error ("partition number must be >= 1");
return -1;
}

CLEANUP_FREE char *typecode = NULL;
if (asprintf (&typecode, "%i:%s", partnum, guid) == -1) {
reply_with_perror ("asprintf");
return -1;
}

CLEANUP_FREE char *err = NULL;
int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
"sgdisk", device, "-t", typecode, NULL);

if (r == -1) {
reply_with_error ("%s %s -t %s: %s", "sgdisk", device, typecode, err);
return -1;
}

return 0;
}

int
do_part_set_gpt_guid (const char *device, int partnum, const char *guid)
{
if (partnum <= 0) {
reply_with_error ("partition number must be >= 1");
return -1;
}

CLEANUP_FREE char *typecode = NULL;
if (asprintf (&typecode, "%i:%s", partnum, guid) == -1) {
reply_with_perror ("asprintf");
return -1;
}

CLEANUP_FREE char *err = NULL;
int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
"sgdisk", device, "-u", typecode, NULL);

if (r == -1) {
reply_with_error ("%s %s -u %s: %s", "sgdisk", device, typecode, err);
return -1;
}

return 0;
}

char *
do_part_get_name (const char *device, int partnum)
{
Expand Down Expand Up @@ -564,95 +512,3 @@ do_part_get_name (const char *device, int partnum)
return NULL;
}
}

static char *
extract_uuid (const char *value)
{
/* The value contains only valid GUID characters */
const size_t value_len = strspn (value, "-0123456789ABCDEF");

char *ret = malloc (value_len + 1);
if (ret == NULL) {
reply_with_perror ("malloc");
return NULL;
}

memcpy (ret, value, value_len);
ret[value_len] = '\0';
return ret;
}

char *
do_part_get_disk_guid (const char *device)
{
const char *pattern = "Disk identifier (GUID):";
size_t i;

CLEANUP_FREE char *err = NULL;
int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
"sgdisk", device, "-p", NULL);
if (r == -1) {
reply_with_error ("%s %s -p: %s", "sgdisk", device, err);
return NULL;
}

CLEANUP_FREE_STRING_LIST char **lines = split_lines (err);
if (lines == NULL) {
reply_with_error ("'%s %s -p' returned no output",
"sgdisk", device);
return NULL;
}

for (i = 0; lines[i] != NULL; ++i) {
if (STRPREFIX (lines[i], pattern)) {
char *value = lines[i] + strlen (pattern);

/* Skip any leading whitespace */
value += strspn (value, " \t");

/* Extract the actual information from the field. */
char *ret = extract_uuid (value);
if (ret == NULL) {
/* The extraction function already sends the error. */
return NULL;
}

return ret;
}
}

/* If we got here it means we didn't find the field */
reply_with_error ("sgdisk output did not contain disk GUID. "
"See LIBGUESTFS_DEBUG output for more details");
return NULL;
}

int
do_part_set_disk_guid (const char *device, const char *guid)
{
CLEANUP_FREE char *err = NULL;
int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
"sgdisk", device, "-U", guid, NULL);

if (r == -1) {
reply_with_error ("%s %s -U %s: %s", "sgdisk", device, guid, err);
return -1;
}

return 0;
}

int
do_part_set_disk_guid_random (const char *device)
{
CLEANUP_FREE char *err = NULL;
int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
"sgdisk", device, "-U", "R", NULL);

if (r == -1) {
reply_with_error ("%s %s -U R: %s", "sgdisk", device, err);
return -1;
}

return 0;
}
92 changes: 1 addition & 91 deletions daemon/parted.ml
Expand Up @@ -25,18 +25,6 @@ open Utils

include Structs

let part_get_mbr_id device partnum =
if partnum <= 0 then
failwith "partition number must be >= 1";

udev_settle ();
let out =
command "sfdisk" ["--part-type"; device; string_of_int partnum] in
udev_settle ();

(* It's printed in hex, possibly with a leading space. *)
sscanf out " %x" identity

(* This is almost equivalent to print_partition_table in the C code. The
* difference is that here we enforce the "BYT;" header internally.
*)
Expand Down Expand Up @@ -110,7 +98,7 @@ let part_get_parttype device =

let part_get_mbr_part_type device partnum =
let parttype = part_get_parttype device in
let mbr_id = part_get_mbr_id device partnum in
let mbr_id = Sfdisk.part_get_mbr_id device partnum in

(* 0x05 - extended partition.
* 0x0f - extended partition using BIOS INT 13h extensions.
Expand All @@ -120,81 +108,3 @@ let part_get_mbr_part_type device partnum =
| "msdos", (1|2|3|4), _ -> "primary"
| "msdos", _, _ -> "logical"
| _, _, _ -> "primary"

let part_set_gpt_attributes device partnum attributes =
if partnum <= 0 then failwith "partition number must be >= 1";

udev_settle ();

let arg = sprintf "%d:=:%LX" partnum attributes in
let r, _, err =
commandr ~fold_stdout_on_stderr:true
"sgdisk" [ device; "-A"; arg ] in
if r <> 0 then
failwithf "sgdisk: %s" err;

udev_settle ()

let extract_guid value =
(* The value contains only valid GUID characters. *)
String.sub value 0 (String.span value "-0123456789ABCDEF")

let extract_hex value =
(* The value contains only valid numeric characters. *)
let str = String.sub value 0 (String.span value "0123456789ABCDEF") in
Int64.of_string ("0x" ^ str)

let sgdisk_info_extract_field device partnum field extractor =
if partnum <= 0 then failwith "partition number must be >= 1";

udev_settle ();

let r, _, err =
commandr ~fold_stdout_on_stderr:true
"sgdisk" [ device; "-i"; string_of_int partnum ] in
if r <> 0 then
failwithf "getting %S: sgdisk: %s" field err;

udev_settle ();

let err = String.trim err in
let lines = String.nsplit "\n" err in

(* Parse the output of sgdisk -i:
* Partition GUID code: 21686148-6449-6E6F-744E-656564454649 (BIOS boot partition)
* Partition unique GUID: 19AEC5FE-D63A-4A15-9D37-6FCBFB873DC0
* First sector: 2048 (at 1024.0 KiB)
* Last sector: 411647 (at 201.0 MiB)
* Partition size: 409600 sectors (200.0 MiB)
* Attribute flags: 0000000000000000
* Partition name: 'EFI System Partition'
*)
let field_len = String.length field in
let rec loop = function
| [] ->
failwithf "%s: sgdisk output did not contain '%s'" device field
| line :: _ when String.is_prefix line field &&
String.length line >= field_len + 2 &&
line.[field_len] = ':' ->
let value =
String.sub line (field_len+1) (String.length line - field_len - 1) in

(* Skip any whitespace after the colon. *)
let value = String.triml value in

(* Extract the value. *)
extractor value

| _ :: lines -> loop lines
in
loop lines

let rec part_get_gpt_type device partnum =
sgdisk_info_extract_field device partnum "Partition GUID code"
extract_guid
and part_get_gpt_guid device partnum =
sgdisk_info_extract_field device partnum "Partition unique GUID"
extract_guid
and part_get_gpt_attributes device partnum =
sgdisk_info_extract_field device partnum "Attribute flags"
extract_hex

0 comments on commit c6c266a

Please sign in to comment.