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 935e59eab..62dcabf3f 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -5309,7 +5309,7 @@ See also C." }; { 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 ( @@ -8136,7 +8136,7 @@ group with GUID C." }; { 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"]]), []; @@ -8158,8 +8158,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"; @@ -8174,8 +8173,7 @@ Return the type GUID of numbered GPT partition C." }; { 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"; @@ -8194,8 +8192,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"; @@ -8995,7 +8992,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"]]), []; @@ -9014,8 +9011,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"; @@ -9214,7 +9210,7 @@ This is the internal call which implements C." }; { 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"]]), []; @@ -9233,7 +9229,7 @@ or if C 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"; @@ -9249,7 +9245,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"]]), [];