Files
libguestfs/daemon/isoinfo.ml
Richard W.M. Jones 72cfaff5c5 Update copyright dates for 2025
Automated using this command:

perl -pi.bak -e 's/(20[012][0-9])-20[12][01234]/$1-2025/g' `git ls-files`
2025-02-16 17:00:46 +00:00

133 lines
4.9 KiB
OCaml

(* Parse isoinfo or xorriso output.
* Copyright (C) 2009-2025 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 Printf
open Unix
open Std_utils
include Structs
(* We treat ISO "strA" and "strD" formats the same way, simply
* discarding any trailing spaces.
*)
let iso_parse_strA str =
let len = String.length str in
let rec loop len =
if len > 0 && str.[len-1] = ' ' then loop (len-1) else len
in
let len = loop len in
String.sub str 0 len
let iso_parse_strD = iso_parse_strA
(* These always parse the intX_LSB (little endian) version. *)
let iso_parse_int16 s = s |> int_of_le16 |> Int64.to_int32
let iso_parse_int32 s = s |> int_of_le32 |> Int64.to_int32
(* Parse ISO dec-datetime to a Unix time_t. *)
let iso_parse_datetime str =
if String.sub str 0 16 = "0000000000000000" then -1_L
else (
let tm_year = int_of_string (String.sub str 0 4) in
let tm_mon = int_of_string (String.sub str 4 2) in
let tm_mday = int_of_string (String.sub str 6 2) in
let tm_hour = int_of_string (String.sub str 8 2) in
let tm_min = int_of_string (String.sub str 10 2) in
let tm_sec = int_of_string (String.sub str 12 2) in
(* Adjust fields. *)
let tm_year = tm_year - 1900 in
let tm_mon = tm_mon - 1 in
(* Convert to time_t in UTC timezone. *)
let tm = { tm_sec; tm_min; tm_hour; tm_mday; tm_mon; tm_year;
tm_wday = -1; tm_yday = -1; tm_isdst = false } in
let old_TZ = try Some (getenv "TZ") with Not_found -> None in
putenv "TZ" "UTC";
let r = Int64.of_float (fst (mktime tm)) in
Option.iter (putenv "TZ") old_TZ;
(* The final byte is a time zone offset from GMT.
*
* The documentation of this at
* https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor
* is wrong. See the ECMA 119 documentation for a correct
* description.
*
* For a disk image which we know was created
* in BST (GMT+1), this contains 0x4, ie. 4 * 15 mins ahead.
* We have to subtract this from the gmtime above.
*)
let tz = Char.code str.[16] in
let tz = if tz >= 128 then tz - 256 else tz in
r -^ (Int64.of_int (tz * 15 * 60))
)
let isoinfo_device dev =
let r, pvd =
with_openfile dev [O_RDONLY] 0 (
fun fd ->
ignore (lseek fd 32768 SEEK_SET);
let pvd = Bytes.create 2048 in
let r = read fd pvd 0 2048 in
r, Bytes.to_string pvd
) in
let sub = String.sub pvd in
(* Check that it looks like an ISO9660 Primary Volume Descriptor.
* https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor
*)
if r <> 2048 || pvd.[0] <> '\001' || sub 1 5 <> "CD001" then
failwithf "%s: not an ISO file or CD-ROM" dev;
(* Parse out the PVD fields. *)
let iso_system_id = sub 8 32 |> iso_parse_strA in
let iso_volume_id = sub 40 32 |> iso_parse_strA in
let iso_volume_space_size = sub 80 4 |> iso_parse_int32 in
let iso_volume_set_size = sub 120 2 |> iso_parse_int16 in
let iso_volume_sequence_number = sub 124 2 |> iso_parse_int16 in
let iso_logical_block_size = sub 128 2 |> iso_parse_int16 in
let iso_volume_set_id = sub 190 128 |> iso_parse_strD in
let iso_publisher_id = sub 318 128 |> iso_parse_strA in
let iso_data_preparer_id = sub 446 128 |> iso_parse_strA in
let iso_application_id = sub 574 128 |> iso_parse_strA in
let iso_copyright_file_id = sub 702 37 |> iso_parse_strD in
let iso_abstract_file_id = sub 739 37 |> iso_parse_strD in
let iso_bibliographic_file_id = sub 776 37 |> iso_parse_strD in
let iso_volume_creation_t = sub 813 17 |> iso_parse_datetime in
let iso_volume_modification_t = sub 830 17 |> iso_parse_datetime in
let iso_volume_expiration_t = sub 847 17 |> iso_parse_datetime in
let iso_volume_effective_t = sub 864 17 |> iso_parse_datetime in
(* Return the struct. *)
{
iso_system_id; iso_volume_id; iso_volume_space_size;
iso_volume_set_size; iso_volume_sequence_number;
iso_logical_block_size; iso_volume_set_id; iso_publisher_id;
iso_data_preparer_id; iso_application_id; iso_copyright_file_id;
iso_abstract_file_id; iso_bibliographic_file_id;
iso_volume_creation_t; iso_volume_modification_t;
iso_volume_expiration_t; iso_volume_effective_t;
}
let isoinfo file =
let chroot = Chroot.create ~name:(sprintf "isoinfo: %s" file) () in
Chroot.f chroot isoinfo_device file