You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libguestfs/SOURCES/0001-daemon-Reimplement-par...

658 lines
21 KiB

From 1638cd9e58161147bd2f440b6e28bf7365fc5688 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 10 May 2024 13:27:22 +0100
Subject: [PATCH] daemon: Reimplement partition GPT functions using sfdisk
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
(cherry picked from commit c6c266a85d76dc2db90460202415790c585ac625)
---
.gitignore | 1 +
daemon/Makefile.am | 3 +
daemon/inspect_fs_windows.ml | 2 +-
daemon/listfs.ml | 2 +-
daemon/parted.c | 144 -----------------------------
daemon/parted.ml | 92 +------------------
daemon/sfdisk.ml | 172 +++++++++++++++++++++++++++++++++++
generator/actions_core.ml | 24 ++---
8 files changed, 189 insertions(+), 251 deletions(-)
create mode 100644 daemon/sfdisk.ml
diff --git a/.gitignore b/.gitignore
index 00e59fb37..2fc52e843 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 04370b7cd..bc74b6ef7 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -59,6 +59,7 @@ generator_built = \
parted.mli \
realpath.mli \
rpm.mli \
+ sfdisk.mli \
statvfs.mli \
structs.ml \
structs.mli
@@ -306,6 +307,7 @@ SOURCES_MLI = \
parted.mli \
realpath.mli \
rpm.mli \
+ sfdisk.mli \
statvfs.mli \
structs.mli \
sysroot.mli \
@@ -337,6 +339,7 @@ SOURCES_ML = \
md.ml \
mount.ml \
mount_utils.ml \
+ sfdisk.ml \
parted.ml \
listfs.ml \
realpath.ml \
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
index 5d29c3a46..6537481e1 100644
--- a/daemon/inspect_fs_windows.ml
+++ b/daemon/inspect_fs_windows.ml
@@ -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
diff --git a/daemon/listfs.ml b/daemon/listfs.ml
index 4cc3c437a..93c1e7145 100644
--- a/daemon/listfs.ml
+++ b/daemon/listfs.ml
@@ -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"
diff --git a/daemon/parted.c b/daemon/parted.c
index 9af5556c9..0f19baae5 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -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)
{
@@ -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;
-}
diff --git a/daemon/parted.ml b/daemon/parted.ml
index c9e55890b..f8f142bc5 100644
--- a/daemon/parted.ml
+++ b/daemon/parted.ml
@@ -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.
*)
@@ -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.
@@ -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
diff --git a/daemon/sfdisk.ml b/daemon/sfdisk.ml
new file mode 100644
index 000000000..2aea399aa
--- /dev/null
+++ b/daemon/sfdisk.ml
@@ -0,0 +1,172 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2023 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Scanf
+open Printf
+
+open Std_utils
+
+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
+
+let part_get_gpt_type 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 ();
+
+ String.trimr out
+
+let part_set_gpt_type device partnum typ =
+ if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ udev_settle ();
+ let cmd =
+ sprintf "sfdisk --part-type %s %d %s"
+ (quote device) partnum (quote typ) in
+ if verbose () then eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then failwith "sfdisk --part-type failed";
+ udev_settle ()
+
+let part_get_gpt_guid device partnum =
+ if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ udev_settle ();
+ let out =
+ command "sfdisk" ["--part-uuid"; device; string_of_int partnum] in
+ udev_settle ();
+
+ String.trimr out
+
+let part_set_gpt_guid device partnum guid =
+ if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ udev_settle ();
+ let cmd =
+ sprintf "sfdisk --part-uuid %s %d %s"
+ (quote device) partnum (quote guid) in
+ if verbose () then eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then failwith "sfdisk --part-uuid failed";
+ udev_settle ()
+
+let part_get_disk_guid device =
+ udev_settle ();
+ let out =
+ command "sfdisk" ["--disk-id"; device] in
+ udev_settle ();
+
+ String.trimr out
+
+let part_set_disk_guid device guid =
+ udev_settle ();
+ let cmd =
+ sprintf "sfdisk --disk-id %s %s"
+ (quote device) (quote guid) in
+ if verbose () then eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then failwith "sfdisk --disk-id failed";
+ udev_settle ()
+
+let part_set_disk_guid_random device =
+ let random_uuid = Utils.get_random_uuid () in
+ let random_uuid = String.trimr random_uuid in
+ part_set_disk_guid device random_uuid
+
+let part_get_gpt_attributes device partnum =
+ if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ udev_settle ();
+ let out =
+ command "sfdisk" ["--part-attrs"; device; string_of_int partnum] in
+ udev_settle ();
+
+ (* The output is a whitespace-separated list of:
+ * "RequiredPartition" (equivalent to bit 0)
+ * "NoBlockIOProtocol" (equivalent to bit 1)
+ * "LegacyBIOSBootable" (equivalent to bit 2)
+ * "48", "49", ..., "63"
+ *)
+ let out = String.trimr out in
+ let attrs = String.nsplit " " out in
+ List.fold_left (
+ fun bits attr ->
+ let bit =
+ match attr with
+ | "" -> -1
+ | "RequiredPartition" -> 0
+ | "NoBlockIOProtocol" -> 1
+ | "LegacyBIOSBootable" -> 2
+ | n -> int_of_string n in
+ if bit >= 0 then
+ Int64.logor bits (Int64.shift_left 1_L bit)
+ else
+ bits
+ ) 0_L attrs
+
+let part_set_gpt_attributes device partnum attrs =
+ if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ (* The input to sfdisk --part-attrs is a comma-separated list of
+ * attribute names or bit positions. Note you have to use the
+ * names, you can't use "0", "1" or "2".
+ *)
+ let s = ref [] in
+ let rec loop i =
+ let b = Int64.logand attrs (Int64.shift_left 1_L i) <> Int64.zero in
+ (match i with
+ | 0 -> if b then List.push_front "RequiredPartition" s
+ | 1 -> if b then List.push_front "NoBlockIOProtocol" s
+ | 2 -> if b then List.push_front "LegacyBIOSBootable" s
+ | i when i >= 3 && i <= 47 ->
+ if b then
+ failwith "bits 3..47 are reserved and cannot be set"
+ | i when i >= 48 && i <= 63 ->
+ if b then List.push_front (string_of_int i) s
+ | _ -> assert false
+ );
+ if i < 63 then loop (i+1)
+ in
+ loop 0;
+
+ udev_settle ();
+ let cmd =
+ sprintf "sfdisk --part-attrs %s %d %s"
+ (quote device) partnum (quote (String.concat "," !s)) in
+ if verbose () then eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then failwith "sfdisk --part-attrs failed";
+ udev_settle ()
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 68627078f..46ef1422f 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5302,7 +5302,7 @@ See also C<guestfs_part_set_bootable>." };
{ defaults with
name = "part_get_mbr_id"; added = (1, 3, 2);
style = RInt "idbyte", [String (Device, "device"); Int "partnum"], [];
- impl = OCaml "Parted.part_get_mbr_id";
+ impl = OCaml "Sfdisk.part_get_mbr_id";
fish_output = Some FishOutputHexadecimal;
tests = [
InitEmpty, Always, TestResult (
@@ -8128,7 +8128,7 @@ group with GUID C<diskgroup>." };
{ defaults with
name = "part_set_gpt_type"; added = (1, 21, 1);
style = RErr, [String (Device, "device"); Int "partnum"; String (GUID, "guid")], [];
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_set_gpt_type";
tests = [
InitGPT, Always, TestLastFail (
[["part_set_gpt_type"; "/dev/sda"; "1"; "f"]]), [];
@@ -8150,8 +8150,7 @@ for a useful list of type GUIDs." };
{ defaults with
name = "part_get_gpt_type"; added = (1, 21, 1);
style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], [];
- impl = OCaml "Parted.part_get_gpt_type";
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_get_gpt_type";
tests = [
InitGPT, Always, TestResultString (
[["part_set_gpt_type"; "/dev/sda"; "1";
@@ -8166,8 +8165,7 @@ Return the type GUID of numbered GPT partition C<partnum>." };
{ defaults with
name = "part_set_gpt_attributes"; added = (1, 21, 1);
style = RErr, [String (Device, "device"); Int "partnum"; Int64 "attributes"], [];
- impl = OCaml "Parted.part_set_gpt_attributes";
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_set_gpt_attributes";
tests = [
InitGPT, Always, TestResult (
[["part_set_gpt_attributes"; "/dev/sda"; "1";
@@ -8186,8 +8184,7 @@ for a useful list of partition attributes." };
{ defaults with
name = "part_get_gpt_attributes"; added = (1, 21, 1);
style = RInt64 "attributes", [String (Device, "device"); Int "partnum"], [];
- impl = OCaml "Parted.part_get_gpt_attributes";
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_get_gpt_attributes";
tests = [
InitGPT, Always, TestResult (
[["part_set_gpt_attributes"; "/dev/sda"; "1";
@@ -8987,7 +8984,7 @@ Recover bad superblocks from good copies." };
{ defaults with
name = "part_set_gpt_guid"; added = (1, 29, 25);
style = RErr, [String (Device, "device"); Int "partnum"; String (GUID, "guid")], [];
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_set_gpt_guid";
tests = [
InitGPT, Always, TestLastFail (
[["part_set_gpt_guid"; "/dev/sda"; "1"; "f"]]), [];
@@ -9006,8 +9003,7 @@ valid GUID." };
{ defaults with
name = "part_get_gpt_guid"; added = (1, 29, 25);
style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], [];
- impl = OCaml "Parted.part_get_gpt_guid";
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_get_gpt_guid";
tests = [
InitGPT, Always, TestResultString (
[["part_set_gpt_guid"; "/dev/sda"; "1";
@@ -9206,7 +9202,7 @@ This is the internal call which implements C<guestfs_feature_available>." };
{ defaults with
name = "part_set_disk_guid"; added = (1, 33, 2);
style = RErr, [String (Device, "device"); String (GUID, "guid")], [];
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_set_disk_guid";
tests = [
InitGPT, Always, TestLastFail (
[["part_set_disk_guid"; "/dev/sda"; "f"]]), [];
@@ -9225,7 +9221,7 @@ or if C<guid> is not a valid GUID." };
{ defaults with
name = "part_get_disk_guid"; added = (1, 33, 2);
style = RString (RPlainString, "guid"), [String (Device, "device")], [];
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_get_disk_guid";
tests = [
InitGPT, Always, TestResultString (
[["part_set_disk_guid"; "/dev/sda";
@@ -9241,7 +9237,7 @@ Behaviour is undefined for other partition types." };
{ defaults with
name = "part_set_disk_guid_random"; added = (1, 33, 2);
style = RErr, [String (Device, "device")], [];
- optional = Some "gdisk";
+ impl = OCaml "Sfdisk.part_set_disk_guid_random";
tests = [
InitGPT, Always, TestRun (
[["part_set_disk_guid_random"; "/dev/sda"]]), [];
--
2.43.0