Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 00/29] Reimplement inspection in the daemon.
v6 was posted here: https://www.redhat.com/archives/libguestfs/2017-June/msg00103.html and this requires the utilities refactoring posted here: https://www.redhat.com/archives/libguestfs/2017-June/msg00169.html Inspection is now complete[*], although not very well tested. I'm intending to compare the output of many guests using old & new virt-inspector to see if I can find any differences. Rich. [*] Except that "name" labels on disks are ignored. This requires a bit of complicated work to fix because the names are not actually available inside the daemon. So far this only affects a test, it was not something that any of our tools used.
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 01/29] inspection: Deprecate APIs and remove support for inspecting installer CDs.
This just duplicated libosinfo information, and because it was never
tested it didn't work most of the time.
---
docs/C_SOURCE_FILES | 2 -
generator/actions_inspection.ml | 67 ---
generator/actions_inspection_deprecated.ml | 61 +++
inspector/Makefile.am | 11 +-
inspector/example-debian-netinst-cd.xml | 23 -
inspector/example-debian.xml | 1 -
inspector/example-fedora-dvd.xml | 23 -
inspector/example-fedora-netinst-cd.xml | 21 -
inspector/example-fedora.xml | 1 -
inspector/example-rhel-6-dvd.xml | 23 -
inspector/example-rhel-6-netinst-cd.xml | 21 -
inspector/example-rhel-6.xml | 1 -
inspector/example-ubuntu-live-cd.xml | 23 -
inspector/example-ubuntu.xml | 1 -
inspector/example-windows-2003-x64-cd.xml | 24 --
inspector/example-windows-2003-x86-cd.xml | 24 --
inspector/example-windows-xp-cd.xml | 24 --
inspector/example-windows.xml | 1 -
inspector/expected-archlinux.img.xml | 1 -
inspector/expected-coreos.img.xml | 1 -
inspector/expected-debian.img.xml | 1 -
inspector/expected-fedora.img.xml | 1 -
inspector/expected-ubuntu.img.xml | 1 -
inspector/expected-windows.img.xml | 1 -
inspector/inspector.c | 31 +-
inspector/virt-inspector.pod | 22 -
inspector/virt-inspector.rng | 15 -
lib/Makefile.am | 3 -
lib/guestfs-internal.h | 31 --
lib/guestfs.pod | 9 -
lib/inspect-fs-cd.c | 606 --------------------------
lib/inspect-fs.c | 40 --
lib/osinfo.c | 655 -----------------------------
33 files changed, 63 insertions(+), 1707 deletions(-)
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 8ce6b5865..61cdbea38 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -304,7 +304,6 @@ lib/guid.c
lib/handle.c
lib/info.c
lib/inspect-apps.c
-lib/inspect-fs-cd.c
lib/inspect-fs-unix.c
lib/inspect-fs-windows.c
lib/inspect-fs.c
@@ -323,7 +322,6 @@ lib/listfs.c
lib/lpj.c
lib/match.c
lib/mountable.c
-lib/osinfo.c
lib/private-data.c
lib/proto.c
lib/qemu.c
diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml
index b7ea5a4de..cd8b9da18 100644
--- a/generator/actions_inspection.ml
+++ b/generator/actions_inspection.ml
@@ -566,73 +566,6 @@ string C<unknown> is returned.
Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
- name = "inspect_get_format"; added = (1, 9, 4);
- style = RString (RPlainString, "format"), [String (Mountable,
"root")], [];
- shortdesc = "get format of inspected operating system";
- longdesc = "\
-This returns the format of the inspected operating system. You
-can use it to detect install images, live CDs and similar.
-
-Currently defined formats are:
-
-=over 4
-
-=item \"installed\"
-
-This is an installed operating system.
-
-=item \"installer\"
-
-The disk image being inspected is not an installed operating system,
-but a I<bootable> install disk, live CD, or similar.
-
-=item \"unknown\"
-
-The format of this disk image is not known.
-
-=back
-
-Future versions of libguestfs may return other strings here.
-The caller should be prepared to handle any string.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_is_live"; added = (1, 9, 4);
- style = RBool "live", [String (Mountable, "root")], [];
- shortdesc = "get live flag for install disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if a live image
-was detected on the disk.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_is_netinst"; added = (1, 9, 4);
- style = RBool "netinst", [String (Mountable, "root")],
[];
- shortdesc = "get netinst (network installer) flag for install
disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if the disk is
-a network installer, ie. not a self-contained install CD but
-one which is likely to require network access to complete
-the install.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_is_multipart"; added = (1, 9, 4);
- style = RBool "multipart", [String (Mountable,
"root")], [];
- shortdesc = "get multipart flag for install disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if the disk is
-part of a set.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
name = "inspect_get_product_variant"; added = (1, 9, 13);
style = RString (RPlainString, "variant"), [String (Mountable,
"root")], [];
shortdesc = "get product variant of inspected operating system";
diff --git a/generator/actions_inspection_deprecated.ml
b/generator/actions_inspection_deprecated.ml
index 342b0c8cd..7c34cb1a8 100644
--- a/generator/actions_inspection_deprecated.ml
+++ b/generator/actions_inspection_deprecated.ml
@@ -121,4 +121,65 @@ If unavailable this is returned as an empty string
C<\"\">.
Please read L<guestfs(3)/INSPECTION> for more details." };
+ { defaults with
+ name = "inspect_get_format"; added = (1, 9, 4);
+ style = RString (RPlainString, "format"), [String (Mountable,
"root")], [];
+ deprecated_by = Deprecated_no_replacement;
+ shortdesc = "get format of inspected operating system";
+ longdesc = "\
+Before libguestfs 1.38, there was some unreliable support for detecting
+installer CDs. This API would return:
+
+=over 4
+
+=item \"installed\"
+
+This is an installed operating system.
+
+=item \"installer\"
+
+The disk image being inspected is not an installed operating system,
+but a I<bootable> install disk, live CD, or similar.
+
+=item \"unknown\"
+
+The format of this disk image is not known.
+
+=back
+
+In libguestfs E<ge> 1.38, this only returns C<installed>.
+Use libosinfo directly to detect installer CDs.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_is_live"; added = (1, 9, 4);
+ style = RBool "live", [String (Mountable, "root")], [];
+ deprecated_by = Deprecated_no_replacement;
+ shortdesc = "get live flag for install disk";
+ longdesc = "\
+This is deprecated and always returns C<false>.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_is_netinst"; added = (1, 9, 4);
+ style = RBool "netinst", [String (Mountable, "root")],
[];
+ deprecated_by = Deprecated_no_replacement;
+ shortdesc = "get netinst (network installer) flag for install
disk";
+ longdesc = "\
+This is deprecated and always returns C<false>.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_is_multipart"; added = (1, 9, 4);
+ style = RBool "multipart", [String (Mountable,
"root")], [];
+ deprecated_by = Deprecated_no_replacement;
+ shortdesc = "get multipart flag for install disk";
+ longdesc = "\
+This is deprecated and always returns C<false>.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
]
diff --git a/inspector/Makefile.am b/inspector/Makefile.am
index 9402270c9..cd82ae918 100644
--- a/inspector/Makefile.am
+++ b/inspector/Makefile.am
@@ -22,16 +22,7 @@ example_xml = \
example-fedora.xml \
example-rhel-6.xml \
example-ubuntu.xml \
- example-windows.xml \
- example-debian-netinst-cd.xml \
- example-fedora-dvd.xml \
- example-fedora-netinst-cd.xml \
- example-rhel-6-dvd.xml \
- example-rhel-6-netinst-cd.xml \
- example-ubuntu-live-cd.xml \
- example-windows-2003-x64-cd.xml \
- example-windows-2003-x86-cd.xml \
- example-windows-xp-cd.xml
+ example-windows.xml
EXTRA_DIST = \
expected-debian.img.xml \
diff --git a/inspector/example-debian-netinst-cd.xml
b/inspector/example-debian-netinst-cd.xml
deleted file mode 100644
index 570a5bd66..000000000
--- a/inspector/example-debian-netinst-cd.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <distro>debian</distro>
- <product_name>Debian GNU/Linux 5.0.5 "Lenny" -
Official amd64 NETINST Binary-1 20100627-10:37</product_name>
- <major_version>5</major_version>
- <minor_version>0</minor_version>
- <format>installer</format>
- <netinst/>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>Debian 5.0.5 amd64 Bin-1</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-debian.xml b/inspector/example-debian.xml
index eb10d8567..22f391d80 100644
--- a/inspector/example-debian.xml
+++ b/inspector/example-debian.xml
@@ -11,7 +11,6 @@
<package_format>deb</package_format>
<package_management>apt</package_management>
<hostname>debian5x64.home.annexia.org</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint
dev="/dev/debian5x64.home.annexia.org/root">/</mountpoint>
<mountpoint
dev="/dev/debian5x64.home.annexia.org/tmp">/tmp</mountpoint>
diff --git a/inspector/example-fedora-dvd.xml b/inspector/example-fedora-dvd.xml
deleted file mode 100644
index 16a04baa5..000000000
--- a/inspector/example-fedora-dvd.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <arch>x86_64</arch>
- <distro>fedora</distro>
- <major_version>14</major_version>
- <minor_version>0</minor_version>
- <format>installer</format>
- <multipart/>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>Fedora 14 x86_64 DVD</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-fedora-netinst-cd.xml
b/inspector/example-fedora-netinst-cd.xml
deleted file mode 100644
index 654fabbe3..000000000
--- a/inspector/example-fedora-netinst-cd.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <distro>fedora</distro>
- <major_version>14</major_version>
- <minor_version>0</minor_version>
- <format>installer</format>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>Fedora</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-fedora.xml b/inspector/example-fedora.xml
index ca8f56b58..32c88adc5 100644
--- a/inspector/example-fedora.xml
+++ b/inspector/example-fedora.xml
@@ -11,7 +11,6 @@
<package_format>rpm</package_format>
<package_management>yum</package_management>
<hostname>f18x64homeannexiaorg</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/fedora/root">/</mountpoint>
<mountpoint dev="/dev/sda1">/boot</mountpoint>
diff --git a/inspector/example-rhel-6-dvd.xml b/inspector/example-rhel-6-dvd.xml
deleted file mode 100644
index 4004d5a73..000000000
--- a/inspector/example-rhel-6-dvd.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <arch>x86_64</arch>
- <distro>rhel</distro>
- <major_version>6</major_version>
- <minor_version>0</minor_version>
- <format>installer</format>
- <multipart/>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>RHEL_6.0 x86_64 Disc 1</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-rhel-6-netinst-cd.xml
b/inspector/example-rhel-6-netinst-cd.xml
deleted file mode 100644
index bcdeebe53..000000000
--- a/inspector/example-rhel-6-netinst-cd.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <distro>rhel</distro>
- <major_version>6</major_version>
- <minor_version>0</minor_version>
- <format>installer</format>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>RHEL_6.0 x86_64 boot</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-rhel-6.xml b/inspector/example-rhel-6.xml
index 0d338cef6..f11402a0c 100644
--- a/inspector/example-rhel-6.xml
+++ b/inspector/example-rhel-6.xml
@@ -11,7 +11,6 @@
<package_format>rpm</package_format>
<package_management>yum</package_management>
<hostname>rhel6x32.home.annexia.org</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint
dev="/dev/vg_rhel6x32/lv_root">/</mountpoint>
<mountpoint dev="/dev/sda1">/boot</mountpoint>
diff --git a/inspector/example-ubuntu-live-cd.xml
b/inspector/example-ubuntu-live-cd.xml
deleted file mode 100644
index ff5a1cedb..000000000
--- a/inspector/example-ubuntu-live-cd.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <distro>ubuntu</distro>
- <product_name>Ubuntu 10.10 "Maverick Meerkat" -
Release amd64 (20101007)</product_name>
- <major_version>10</major_version>
- <minor_version>10</minor_version>
- <format>installer</format>
- <live/>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>Ubuntu 10.10 amd64</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-ubuntu.xml b/inspector/example-ubuntu.xml
index 0519d3403..a1f78ea78 100644
--- a/inspector/example-ubuntu.xml
+++ b/inspector/example-ubuntu.xml
@@ -11,7 +11,6 @@
<package_format>deb</package_format>
<package_management>apt</package_management>
<hostname>ubuntu1304.home.annexia.org</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda1">/</mountpoint>
</mountpoints>
diff --git a/inspector/example-windows-2003-x64-cd.xml
b/inspector/example-windows-2003-x64-cd.xml
deleted file mode 100644
index e74dcf457..000000000
--- a/inspector/example-windows-2003-x64-cd.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>windows</name>
- <arch>x86_64</arch>
- <distro>windows</distro>
- <product_name>Windows Server 2003 Enterprise x64
Edition</product_name>
- <major_version>5</major_version>
- <minor_version>2</minor_version>
- <windows_systemroot>\WINDOWS</windows_systemroot>
- <format>installer</format>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>CRMEXFPP_EN</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-windows-2003-x86-cd.xml
b/inspector/example-windows-2003-x86-cd.xml
deleted file mode 100644
index d46859273..000000000
--- a/inspector/example-windows-2003-x86-cd.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>windows</name>
- <arch>i386</arch>
- <distro>windows</distro>
- <product_name>Windows Server 2003, Enterprise</product_name>
- <major_version>5</major_version>
- <minor_version>2</minor_version>
- <windows_systemroot>\WINDOWS</windows_systemroot>
- <format>installer</format>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>CRMEFPP_EN</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-windows-xp-cd.xml
b/inspector/example-windows-xp-cd.xml
deleted file mode 100644
index 57ea235fb..000000000
--- a/inspector/example-windows-xp-cd.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-<operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>windows</name>
- <arch>i386</arch>
- <distro>windows</distro>
- <product_name>Windows XP Professional</product_name>
- <major_version>5</major_version>
- <minor_version>1</minor_version>
- <windows_systemroot>\WINDOWS</windows_systemroot>
- <format>installer</format>
- <mountpoints>
- <mountpoint dev="/dev/sda">/</mountpoint>
- </mountpoints>
- <filesystems>
- <filesystem dev="/dev/sda">
- <type>iso9660</type>
- <label>GRTMPFPP_EN</label>
- </filesystem>
- </filesystems>
- <applications/>
- </operatingsystem>
-</operatingsystems>
diff --git a/inspector/example-windows.xml b/inspector/example-windows.xml
index 776026a76..f269affaf 100644
--- a/inspector/example-windows.xml
+++ b/inspector/example-windows.xml
@@ -12,7 +12,6 @@
<windows_systemroot>/Windows</windows_systemroot>
<windows_current_control_set>ControlSet001</windows_current_control_set>
<hostname>WIN-6AOV5N85H2F</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda2">/</mountpoint>
</mountpoints>
diff --git a/inspector/expected-archlinux.img.xml
b/inspector/expected-archlinux.img.xml
index 665ac30da..b6c3b9c92 100644
--- a/inspector/expected-archlinux.img.xml
+++ b/inspector/expected-archlinux.img.xml
@@ -10,7 +10,6 @@
<package_format>pacman</package_format>
<package_management>pacman</package_management>
<hostname>archlinux.test</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda1">/</mountpoint>
</mountpoints>
diff --git a/inspector/expected-coreos.img.xml
b/inspector/expected-coreos.img.xml
index c819a0521..e4a5d1134 100644
--- a/inspector/expected-coreos.img.xml
+++ b/inspector/expected-coreos.img.xml
@@ -8,7 +8,6 @@
<major_version>899</major_version>
<minor_version>13</minor_version>
<hostname>coreos.invalid</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda5">/</mountpoint>
<mountpoint dev="/dev/sda3">/usr</mountpoint>
diff --git a/inspector/expected-debian.img.xml
b/inspector/expected-debian.img.xml
index 6f1d9dfa0..37ecfa049 100644
--- a/inspector/expected-debian.img.xml
+++ b/inspector/expected-debian.img.xml
@@ -11,7 +11,6 @@
<package_format>deb</package_format>
<package_management>apt</package_management>
<hostname>debian.invalid</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/debian/root">/</mountpoint>
<mountpoint dev="/dev/debian/usr">/usr</mountpoint>
diff --git a/inspector/expected-fedora.img.xml
b/inspector/expected-fedora.img.xml
index 5b6f1af6e..8d40e8cb7 100644
--- a/inspector/expected-fedora.img.xml
+++ b/inspector/expected-fedora.img.xml
@@ -11,7 +11,6 @@
<package_format>rpm</package_format>
<package_management>yum</package_management>
<hostname>fedora.invalid</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/VG/Root">/</mountpoint>
<mountpoint dev="/dev/sda1">/boot</mountpoint>
diff --git a/inspector/expected-ubuntu.img.xml
b/inspector/expected-ubuntu.img.xml
index ff9d62226..c19c14cd5 100644
--- a/inspector/expected-ubuntu.img.xml
+++ b/inspector/expected-ubuntu.img.xml
@@ -11,7 +11,6 @@
<package_format>deb</package_format>
<package_management>apt</package_management>
<hostname>ubuntu.invalid</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda2">/</mountpoint>
<mountpoint dev="/dev/sda1">/boot</mountpoint>
diff --git a/inspector/expected-windows.img.xml
b/inspector/expected-windows.img.xml
index ff787f188..26aab3261 100644
--- a/inspector/expected-windows.img.xml
+++ b/inspector/expected-windows.img.xml
@@ -12,7 +12,6 @@
<windows_systemroot>/Windows</windows_systemroot>
<windows_current_control_set>ControlSet001</windows_current_control_set>
<hostname>windows.invalid</hostname>
- <format>installed</format>
<mountpoints>
<mountpoint dev="/dev/sda2">/</mountpoint>
</mountpoints>
diff --git a/inspector/inspector.c b/inspector/inspector.c
index 104310d1f..3583c61df 100644
--- a/inspector/inspector.c
+++ b/inspector/inspector.c
@@ -343,7 +343,7 @@ static void
output_root (xmlTextWriterPtr xo, char *root)
{
char *str;
- int i, r;
+ int i;
char buf[32];
char *canonical_root;
size_t size;
@@ -443,35 +443,6 @@ output_root (xmlTextWriterPtr xo, char *root)
BAD_CAST str));
free (str);
- str = guestfs_inspect_get_format (g, root);
- if (!str) exit (EXIT_FAILURE);
- if (STRNEQ (str, "unknown"))
- XMLERROR (-1,
- xmlTextWriterWriteElement (xo, BAD_CAST "format",
- BAD_CAST str));
- free (str);
-
- r = guestfs_inspect_is_live (g, root);
- if (r > 0) {
- XMLERROR (-1,
- xmlTextWriterStartElement (xo, BAD_CAST "live"));
- XMLERROR (-1, xmlTextWriterEndElement (xo));
- }
-
- r = guestfs_inspect_is_netinst (g, root);
- if (r > 0) {
- XMLERROR (-1,
- xmlTextWriterStartElement (xo, BAD_CAST "netinst"));
- XMLERROR (-1, xmlTextWriterEndElement (xo));
- }
-
- r = guestfs_inspect_is_multipart (g, root);
- if (r > 0) {
- XMLERROR (-1,
- xmlTextWriterStartElement (xo, BAD_CAST "multipart"));
- XMLERROR (-1, xmlTextWriterEndElement (xo));
- }
-
output_mountpoints (xo, root);
output_filesystems (xo, root);
diff --git a/inspector/virt-inspector.pod b/inspector/virt-inspector.pod
index 9d6fbda7c..1cea542c7 100644
--- a/inspector/virt-inspector.pod
+++ b/inspector/virt-inspector.pod
@@ -202,7 +202,6 @@ describe the operating system, its architecture, the
descriptive
<major_version>6</major_version>
<minor_version>1</minor_version>
<windows_systemroot>/Windows</windows_systemroot>
- <format>installed</format>
In brief, E<lt>nameE<gt> is the class of operating system
(something
like C<linux> or C<windows>), E<lt>distroE<gt> is the
distribution
@@ -330,27 +329,6 @@ the conversion back to a PNG file:
base64 -i -d < icon.data > icon.png
-=head2 INSPECTING INSTALL DISKS, LIVE CDs
-
-Virt-inspector can detect some operating system installers on
-install disks, live CDs, bootable USB keys and more.
-
-In this case the E<lt>formatE<gt> tag will contain
C<installer>
-and other fields may be present to indicate a live CD, network
-installer, or one part of a multipart CD. For example:
-
- <operatingsystems>
- <operatingsystem>
- <root>/dev/sda</root>
- <name>linux</name>
- <arch>i386</arch>
- <distro>ubuntu</distro>
- <product_name>Ubuntu 10.10 "Maverick
Meerkat"</product_name>
- <major_version>10</major_version>
- <minor_version>10</minor_version>
- <format>installer</format>
- <live/>
-
=head1 XPATH QUERIES
Virt-inspector includes built in support for running XPath queries.
diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng
index dff46c53f..857a02766 100644
--- a/inspector/virt-inspector.rng
+++ b/inspector/virt-inspector.rng
@@ -38,10 +38,6 @@
<optional><ref
name="ospackageformat"/></optional>
<optional><ref
name="ospackagemanagement"/></optional>
<optional><element
name="hostname"><text/></element></optional>
- <optional><ref
name="osformat"/></optional>
- <optional><element
name="live"><empty/></element></optional>
- <optional><element
name="netinst"><empty/></element></optional>
- <optional><element
name="multipart"><empty/></element></optional>
<ref name="mountpoints"/>
<ref name="filesystems"/>
@@ -152,17 +148,6 @@
</element>
</define>
- <!-- the operating system format -->
- <define name="osformat">
- <element name="format">
- <choice>
- <value>installed</value>
- <value>installer</value>
- <!-- "unknown" is intentionally left out -->
- </choice>
- </element>
- </define>
-
<!-- how filesystems are mounted on mount points -->
<define name="mountpoints">
<element name="mountpoints">
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 9ee02bd71..bf3406b16 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -97,7 +97,6 @@ libguestfs_la_SOURCES = \
inspect.c \
inspect-apps.c \
inspect-fs.c \
- inspect-fs-cd.c \
inspect-fs-unix.c \
inspect-fs-windows.c \
inspect-icon.c \
@@ -113,7 +112,6 @@ libguestfs_la_SOURCES = \
lpj.c \
match.c \
mountable.c \
- osinfo.c \
private-data.c \
proto.c \
qemu.c \
@@ -135,7 +133,6 @@ libguestfs_la_SOURCES = \
libguestfs_la_CPPFLAGS = \
-DGUESTFS_WARN_DEPRECATED=1 \
-DGUESTFS_PRIVATE=1 \
- -DLIBOSINFO_DB_PATH='"$(datadir)/libosinfo/db"' \
-I$(top_srcdir)/common/errnostring -I$(top_builddir)/common/errnostring \
-I$(top_srcdir)/common/protocol -I$(top_builddir)/common/protocol \
-I$(top_srcdir)/common/qemuopts -I$(top_builddir)/common/qemuopts \
diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h
index 589968b29..81755177d 100644
--- a/lib/guestfs-internal.h
+++ b/lib/guestfs-internal.h
@@ -889,10 +889,6 @@ extern char *guestfs_int_case_sensitive_path_silently
(guestfs_h *g, const char
extern char * guestfs_int_get_windows_systemroot (guestfs_h *g);
extern int guestfs_int_check_windows_root (guestfs_h *g, struct inspect_fs *fs,
char *windows_systemroot);
-/* inspect-fs-cd.c */
-extern int guestfs_int_check_installer_root (guestfs_h *g, struct inspect_fs
*fs);
-extern int guestfs_int_check_installer_iso (guestfs_h *g, struct inspect_fs
*fs, const char *device);
-
/* dbdump.c */
typedef int (*guestfs_int_db_dump_callback) (guestfs_h *g, const unsigned char
*key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque);
extern int guestfs_int_read_db_dump (guestfs_h *g, const char *dumpfile, void
*opaque, guestfs_int_db_dump_callback callback);
@@ -910,33 +906,6 @@ extern void guestfs_int_free_fuse (guestfs_h *g);
extern virConnectPtr guestfs_int_open_libvirt_connection (guestfs_h *g, const
char *uri, unsigned int flags);
#endif
-/* osinfo.c */
-struct osinfo {
- /* Data provided by libosinfo database. */
- enum inspect_os_type type;
- enum inspect_os_distro distro;
- char *product_name;
- int major_version;
- int minor_version;
- char *arch;
- int is_live_disk;
- bool is_installer;
-
-#if 0
- /* Not yet available in libosinfo database. */
- char *product_variant;
- int is_netinst_disk;
- int is_multipart_disk;
-#endif
-
- /* The regular expressions used to match ISOs. */
- pcre *re_system_id;
- pcre *re_volume_id;
- pcre *re_publisher_id;
- pcre *re_application_id;
-};
-extern int guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo
*isoinfo, const struct osinfo **osinfo_ret);
-
/* command.c */
struct command;
typedef void (*cmd_stdout_callback) (guestfs_h *g, void *data, const char
*line, size_t len);
diff --git a/lib/guestfs.pod b/lib/guestfs.pod
index f2a54a1fd..cd57cc337 100644
--- a/lib/guestfs.pod
+++ b/lib/guestfs.pod
@@ -939,20 +939,11 @@ documentation for that function for details).
Libguestfs (since 1.9.4) can detect some install disks, install
CDs, live CDs and more.
-Call L</guestfs_inspect_get_format> to return the format of the
-operating system, which currently can be C<installed> (a regular
-operating system) or C<installer> (some sort of install disk).
-
Further information is available about the operating system that can
be installed using the regular inspection APIs like
L</guestfs_inspect_get_product_name>,
L</guestfs_inspect_get_major_version> etc.
-Some additional information specific to installer disks is also
-available from the L</guestfs_inspect_is_live>,
-L</guestfs_inspect_is_netinst> and L</guestfs_inspect_is_multipart>
-calls.
-
=head2 SPECIAL CONSIDERATIONS FOR WINDOWS GUESTS
Libguestfs can mount NTFS partitions. It does this using the
diff --git a/lib/inspect-fs-cd.c b/lib/inspect-fs-cd.c
deleted file mode 100644
index 1cff5606b..000000000
--- a/lib/inspect-fs-cd.c
+++ /dev/null
@@ -1,606 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010-2012 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libintl.h>
-#include <inttypes.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#include "c-ctype.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-
-/* Debian/Ubuntu install disks are easy ...
- *
- * These files are added by the debian-cd program, and it is worth
- * looking at the source code to determine exact values, in
- * particular '/usr/share/debian-cd/tools/start_new_disc'
- *
- * XXX Architecture? We could parse it out of the product name
- * string, but that seems quite hairy. We could look for the names
- * of packages. Also note that some Debian install disks are
- * multiarch.
- */
-static int
-check_debian_installer_root (guestfs_h *g, struct inspect_fs *fs)
-{
- fs->product_name = guestfs_int_first_line_of_file (g,
"/.disk/info");
- if (!fs->product_name)
- return -1;
-
- fs->type = OS_TYPE_LINUX;
- if (STRPREFIX (fs->product_name, "Ubuntu"))
- fs->distro = OS_DISTRO_UBUNTU;
- else if (STRPREFIX (fs->product_name, "Debian"))
- fs->distro = OS_DISTRO_DEBIAN;
-
- (void) guestfs_int_parse_major_minor (g, fs);
-
- if (guestfs_is_file (g, "/.disk/cd_type") > 0) {
- CLEANUP_FREE char *cd_type - guestfs_int_first_line_of_file (g,
"/.disk/cd_type");
- if (!cd_type)
- return -1;
-
- if (STRPREFIX (cd_type, "dvd/single") ||
- STRPREFIX (cd_type, "full_cd/single")) {
- fs->is_multipart_disk = 0;
- fs->is_netinst_disk = 0;
- }
- else if (STRPREFIX (cd_type, "dvd") ||
- STRPREFIX (cd_type, "full_cd")) {
- fs->is_multipart_disk = 1;
- fs->is_netinst_disk = 0;
- }
- else if (STRPREFIX (cd_type, "not_complete")) {
- fs->is_multipart_disk = 0;
- fs->is_netinst_disk = 1;
- }
- }
-
- return 0;
-}
-
-/* Take string which must look like "key = value" and find the value.
- * There may or may not be spaces before and after the equals sign.
- * This function is used by both check_fedora_installer_root and
- * check_w2k3_installer_root.
- */
-static const char *
-find_value (const char *kv)
-{
- const char *p;
-
- p = strchr (kv, '=');
- if (!p)
- abort ();
-
- do {
- ++p;
- } while (c_isspace (*p));
-
- return p;
-}
-
-/* RHEL 5 has a DVD.iso and several CD-sized -discX-ftp.iso alternatives.
- *
- * The DVD.iso contains:
- * /.treeinfo:
- * [general]
- * family = Red Hat Enterprise Linux Server
- * timestamp = 1328200566.61
- * totaldiscs = 1
- * version = 5.8
- * discnum = 1
- * packagedir = Server
- * arch = x86_64
- * [...]
- *
- * /.discinfo:
- * 1328205744.315196
- * Red Hat Enterprise Linux Server 5.8 # product name
- * x86_64 # arch
- * 1 # disk number
- * Server/base
- * Server/RPMS
- * Server/pixmaps
- *
- * The alternative CD-sized ISOs contain:
- *
- * disc1:
- * /.treeinfo:
- * [general]
- * family = Red Hat Enterprise Linux Server
- * timestamp = 1328200566.61
- * totaldiscs = 1
- * version = 5.8
- * discnum = 1
- * packagedir = Server
- * arch = x86_64
- * [...]
- *
- * /.discinfo:
- * 1328205744.315196
- * Red Hat Enterprise Linux Server 5.8 # product name
- * x86_64 # arch
- * 1 # disk number
- * Server/base
- * Server/RPMS
- * Server/pixmaps
- *
- * discN (N > 1):
- * /.discinfo:
- * 1328205744.315196
- * Red Hat Enterprise Linux Server 5.8 # product name
- * x86_64 # arch
- * 2 # disk number
- * Server/base
- * Server/RPMS
- * Server/pixmaps
- */
-
-/* Fedora CDs and DVD (not netinst). The /.treeinfo file contains
- * an initial section somewhat like this:
- *
- * [general]
- * version = 14
- * arch = x86_64
- * family = Fedora
- * variant = Fedora
- * discnum = 1
- * totaldiscs = 1
- */
-static int
-check_fedora_installer_root (guestfs_h *g, struct inspect_fs *fs)
-{
- char *str;
- const char *v;
- int r;
- int discnum = 0, totaldiscs = 0;
-
- fs->type = OS_TYPE_LINUX;
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^family = Fedora$", 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_FEDORA;
- free (str);
- }
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^family = Red Hat Enterprise Linux$",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_RHEL;
- free (str);
- }
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^family = Oracle Linux Server$",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_ORACLE_LINUX;
- free (str);
- }
-
- /* XXX should do major.minor before this */
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^version = [[:digit:]]+", 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- v = find_value (str);
- fs->version.v_major = guestfs_int_parse_unsigned_int_ignore_trailing (g,
v);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^arch = [-_[:alnum:]]+$", 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- v = find_value (str);
- fs->arch = safe_strdup (g, v);
- free (str);
- }
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^discnum = [[:digit:]]+$", 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- v = find_value (str);
- discnum = guestfs_int_parse_unsigned_int (g, v);
- free (str);
- if (discnum == -1)
- return -1;
- }
-
- r = guestfs_int_first_egrep_of_file (g, "/.treeinfo",
- "^totaldiscs = [[:digit:]]+$", 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- v = find_value (str);
- totaldiscs = guestfs_int_parse_unsigned_int (g, v);
- free (str);
- if (totaldiscs == -1)
- return -1;
- }
-
- fs->is_multipart_disk = totaldiscs > 1;
- /* and what about discnum? */
-
- return 0;
-}
-
-/* Linux with /isolinux/isolinux.cfg.
- *
- * This file is not easily parsable so we have to do our best.
- * Look for the "menu title" line which contains:
- * menu title Welcome to Fedora 14! # since at least Fedora 10
- * menu title Welcome to Red Hat Enterprise Linux 6.0!
- * menu title Welcome to RHEL6.2-20111117.0-Workstation-x!
- */
-static int
-check_isolinux_installer_root (guestfs_h *g, struct inspect_fs *fs)
-{
- char *str;
- int r;
-
- fs->type = OS_TYPE_LINUX;
-
- r = guestfs_int_first_egrep_of_file (g, "/isolinux/isolinux.cfg",
- "^menu title Welcome to Fedora [[:digit:]]+",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_FEDORA;
- fs->version.v_major -
guestfs_int_parse_unsigned_int_ignore_trailing (g, &str[29]);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- /* XXX parse major.minor */
- r = guestfs_int_first_egrep_of_file (g, "/isolinux/isolinux.cfg",
- "^menu title Welcome to Red Hat Enterprise Linux
[[:digit:]]+",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_RHEL;
- fs->version.v_major -
guestfs_int_parse_unsigned_int_ignore_trailing (g, &str[47]);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- /* XXX parse major.minor */
- r = guestfs_int_first_egrep_of_file (g, "/isolinux/isolinux.cfg",
- "^menu title Welcome to RHEL[[:digit:]]+",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_RHEL;
- fs->version.v_major -
guestfs_int_parse_unsigned_int_ignore_trailing (g, &str[26]);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- /* XXX parse major.minor */
- r = guestfs_int_first_egrep_of_file (g, "/isolinux/isolinux.cfg",
- "^menu title Welcome to Oracle Linux Server [[:digit:]]+",
- 0, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- fs->distro = OS_DISTRO_ORACLE_LINUX;
- fs->version.v_major -
guestfs_int_parse_unsigned_int_ignore_trailing (g, &str[42]);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- return 0;
-}
-
-/* Windows 2003 and similar versions.
- *
- * NB: txtsetup file contains Windows \r\n line endings, which guestfs_grep
- * does not remove. We have to remove them by hand here.
- */
-static void
-trim_cr (char *str)
-{
- const size_t n = strlen (str);
- if (n > 0 && str[n-1] == '\r')
- str[n-1] = '\0';
-}
-
-static void
-trim_quot (char *str)
-{
- const size_t n = strlen (str);
- if (n > 0 && str[n-1] == '"')
- str[n-1] = '\0';
-}
-
-static int
-check_w2k3_installer_root (guestfs_h *g, struct inspect_fs *fs,
- const char *txtsetup)
-{
- char *str;
- const char *v;
- int r;
-
- fs->type = OS_TYPE_WINDOWS;
- fs->distro = OS_DISTRO_WINDOWS;
-
- r = guestfs_int_first_egrep_of_file (g, txtsetup,
- "^productname[[:space:]]*=[[:space:]]*\"", 1,
&str);
- if (r == -1)
- return -1;
- if (r > 0) {
- trim_cr (str);
- trim_quot (str);
- v = find_value (str);
- fs->product_name = safe_strdup (g, v+1);
- free (str);
- }
-
- r = guestfs_int_first_egrep_of_file (g, txtsetup,
- "^majorversion[[:space:]]*=[[:space:]]*[[:digit:]]+",
- 1, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- trim_cr (str);
- v = find_value (str);
- fs->version.v_major = guestfs_int_parse_unsigned_int_ignore_trailing (g,
v);
- free (str);
- if (fs->version.v_major == -1)
- return -1;
- }
-
- r = guestfs_int_first_egrep_of_file (g, txtsetup,
- "^minorversion[[:space:]]*=[[:space:]]*[[:digit:]]+",
- 1, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- trim_cr (str);
- v = find_value (str);
- fs->version.v_minor = guestfs_int_parse_unsigned_int_ignore_trailing (g,
v);
- free (str);
- if (fs->version.v_minor == -1)
- return -1;
- }
-
- /* This is the windows systemroot that would be chosen on
- * installation by default, although not necessarily the one that
- * the user will finally choose.
- */
- r = guestfs_int_first_egrep_of_file (g, txtsetup,
- "^defaultpath[[:space:]]*=[[:space:]]*",
- 1, &str);
- if (r == -1)
- return -1;
- if (r > 0) {
- trim_cr (str);
- v = find_value (str);
- fs->windows_systemroot = safe_strdup (g, v);
- free (str);
- }
-
- return 0;
-}
-
-/* Read the data from a product.id-like file.
- *
- * This is an old file, mostly used in Mandriva-based systems (still including
- * Mageia). A very minimal documentation for it is:
- * - https://wiki.mageia.org/en/Product_id
- * - http://wiki.mandriva.com/en/Product_id (old URL, defunct)
- */
-static int
-check_product_id_installer_root (guestfs_h *g, struct inspect_fs *fs,
- const char *filename)
-{
- CLEANUP_FREE char *line = NULL;
- const char *elem;
- char *saveptr;
-
- fs->type = OS_TYPE_LINUX;
-
- line = guestfs_int_first_line_of_file (g, filename);
- if (line == NULL)
- return -1;
-
- elem = strtok_r (line, ",", &saveptr);
- while (elem) {
- const char *equal = strchr (elem, '=');
- if (equal == NULL || equal == elem)
- return -1;
-
- const char *value = equal + 1;
-
- if (STRPREFIX (elem, "distribution=")) {
- if (STREQ (value, "Mageia"))
- fs->distro = OS_DISTRO_MAGEIA;
- } else if (STRPREFIX (elem, "version=")) {
- if (guestfs_int_version_from_x_y_or_x (g, &fs->version, value) ==
-1)
- return -1;
- } else if (STRPREFIX (elem, "arch=")) {
- fs->arch = safe_strdup (g, value);
- }
-
- elem = strtok_r (NULL, ",", &saveptr);
- }
-
- /* Not found. */
- return 0;
-}
-
-/* The currently mounted device is very likely to be an installer. */
-int
-guestfs_int_check_installer_root (guestfs_h *g, struct inspect_fs *fs)
-{
- CLEANUP_FREE_STRING_LIST char **paths = NULL;
-
- /* The presence of certain files indicates a live CD.
- *
- * XXX Fedora netinst contains a ~120MB squashfs called
- * /images/install.img. However this is not a live CD (unlike the
- * Fedora live CDs which contain the same, but larger file). We
- * need to unpack this and look inside to tell the difference.
- */
- if (guestfs_is_file (g, "/casper/filesystem.squashfs") > 0 ||
- guestfs_is_file (g, "/live/filesystem.squashfs") > 0 ||
- guestfs_is_file (g, "/mfsroot.gz") > 0)
- fs->is_live_disk = 1;
-
- /* Debian/Ubuntu. */
- if (guestfs_is_file (g, "/.disk/info") > 0) {
- if (check_debian_installer_root (g, fs) == -1)
- return -1;
- }
-
- /* Fedora CDs and DVD (not netinst). */
- else if (guestfs_is_file (g, "/.treeinfo") > 0) {
- if (check_fedora_installer_root (g, fs) == -1)
- return -1;
- }
-
- /* FreeDOS install CD. */
- else if (guestfs_is_file (g, "/freedos/freedos.ico") > 0
&&
- guestfs_is_file (g, "/setup.bat") > 0) {
- fs->type = OS_TYPE_DOS;
- fs->distro = OS_DISTRO_FREEDOS;
- fs->arch = safe_strdup (g, "i386");
- }
-
- /* Linux with /isolinux/isolinux.cfg (note that non-Linux can use
- * ISOLINUX too, eg. FreeDOS).
- */
- else if (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0) {
- if (check_isolinux_installer_root (g, fs) == -1)
- return -1;
- }
-
- /* FreeBSD with /boot/loader.rc. */
- else if (guestfs_is_file (g, "/boot/loader.rc") > 0) {
- fs->type = OS_TYPE_FREEBSD;
- }
-
- /* Windows 2003 64 bit */
- else if (guestfs_is_file (g, "/amd64/txtsetup.sif") > 0) {
- fs->arch = safe_strdup (g, "x86_64");
- if (check_w2k3_installer_root (g, fs, "/amd64/txtsetup.sif") ==
-1)
- return -1;
- }
-
- /* Windows 2003 32 bit */
- else if (guestfs_is_file (g, "/i386/txtsetup.sif") > 0) {
- fs->arch = safe_strdup (g, "i386");
- if (check_w2k3_installer_root (g, fs, "/i386/txtsetup.sif") ==
-1)
- return -1;
- }
-
- /* Linux with /{i586,x86_64,etc}/product.id (typically found in Mandriva
- * and Mageia). Usually there should be just one around, so we use the
- * first one found.
- */
- paths = guestfs_glob_expand (g, "/*/product.id");
- if (paths == NULL)
- return -1;
- if (paths[0] != NULL) {
- if (check_product_id_installer_root (g, fs, paths[0]) == -1)
- return -1;
- }
-
- return 0;
-}
-
-/* This is called for whole block devices. See if the device is an
- * ISO and we are able to read the ISO info from it. In that case,
- * try using libosinfo to map from the volume ID and other strings
- * directly to the operating system type.
- */
-int
-guestfs_int_check_installer_iso (guestfs_h *g, struct inspect_fs *fs,
- const char *device)
-{
- CLEANUP_FREE_ISOINFO struct guestfs_isoinfo *isoinfo = NULL;
- const struct osinfo *osinfo;
- int r;
-
- guestfs_push_error_handler (g, NULL, NULL);
- isoinfo = guestfs_isoinfo_device (g, device);
- guestfs_pop_error_handler (g);
- if (!isoinfo)
- return 0;
-
- r = guestfs_int_osinfo_map (g, isoinfo, &osinfo);
- if (r == -1) /* Fatal error. */
- return -1;
- if (r == 0) /* Could not locate any matching ISO. */
- return 0;
-
- /* Otherwise we matched an ISO, so fill in the fs fields. */
- fs->mountable = safe_strdup (g, device);
- fs->role = OS_ROLE_ROOT;
- if (osinfo->is_installer)
- fs->format = OS_FORMAT_INSTALLER;
- fs->type = osinfo->type;
- fs->distro = osinfo->distro;
- fs->product_name - osinfo->product_name ? safe_strdup (g,
osinfo->product_name) : NULL;
- guestfs_int_version_from_values (&fs->version,
osinfo->major_version,
- osinfo->minor_version, 0);
- fs->arch = osinfo->arch ? safe_strdup (g, osinfo->arch) : NULL;
- fs->is_live_disk = osinfo->is_live_disk;
-
- guestfs_int_check_package_format (g, fs);
- guestfs_int_check_package_management (g, fs);
-
- return 1;
-}
diff --git a/lib/inspect-fs.c b/lib/inspect-fs.c
index 2da73d310..e320b3e78 100644
--- a/lib/inspect-fs.c
+++ b/lib/inspect-fs.c
@@ -87,22 +87,6 @@ guestfs_int_check_for_filesystem_on (guestfs_h *g, const char
*mountable)
}
}
- if (whole_device) {
- extend_fses (g);
- fs = &g->fses[g->nr_fses-1];
-
- r = guestfs_int_check_installer_iso (g, fs, m->im_device);
- if (r == -1) { /* Fatal error. */
- g->nr_fses--;
- return -1;
- }
- if (r > 0) /* Found something. */
- return 0;
-
- /* Didn't find anything. Fall through ... */
- g->nr_fses--;
- }
-
/* Try mounting the device. As above, ignore errors. */
guestfs_push_error_handler (g, NULL, NULL);
if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the
*BSDs. */
@@ -290,30 +274,6 @@ check_filesystem (guestfs_h *g, const char *mountable,
*/
fs->arch = safe_strdup (g, "i386");
}
- /* Install CD/disk?
- *
- * Note that we checked (above) for an install ISO, but there are
- * other types of install image (eg. USB keys) which that check
- * wouldn't have picked up.
- *
- * Skip these checks if it's not a whole device (eg. CD) or the
- * first partition (eg. bootable USB key).
- */
- else if ((whole_device || (partnum == 1 && nr_partitions == 1))
&&
- (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 ||
- guestfs_is_dir (g, "/EFI/BOOT") > 0 ||
- guestfs_is_file (g, "/images/install.img") > 0 ||
- guestfs_is_dir (g, "/.disk") > 0 ||
- guestfs_is_file (g, "/.discinfo") > 0 ||
- guestfs_is_file (g, "/i386/txtsetup.sif") > 0 ||
- guestfs_is_file (g, "/amd64/txtsetup.sif") > 0 ||
- guestfs_is_file (g, "/freedos/freedos.ico") > 0 ||
- guestfs_is_file (g, "/boot/loader.rc") > 0)) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLER;
- if (guestfs_int_check_installer_root (g, fs) == -1)
- return -1;
- }
/* The above code should have set fs->type and fs->distro fields, so
* we can now guess the package management system.
diff --git a/lib/osinfo.c b/lib/osinfo.c
deleted file mode 100644
index ea2a7659a..000000000
--- a/lib/osinfo.c
+++ /dev/null
@@ -1,655 +0,0 @@
-/* libguestfs
- * Copyright (C) 2012 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/* Read libosinfo XML files to parse out just the
- * os/media/iso/system-id and os/media/iso/volume-id fields, which we
- * can then use to map install media to operating systems.
- *
- * Note some assumptions here:
- *
- * (1) Ignore the libosinfo library itself, since we don't care
- * for GObject nonsense. The XML database contains all we need.
- *
- * (2) Ignore os/upgrades and os/derives-from fields. This is
- * safe(-ish) since the media identifiers always change for every
- * release of an OS. We can easily add support for this if it becomes
- * necessary.
- *
- * (3) We have to do some translation of the distro names and versions
- * stored in the libosinfo files and the standard names returned by
- * libguestfs.
- *
- * (4) Media detection is only part of the story. We may still need
- * to inspect inside the image.
- *
- * (5) We only read the XML database files (at most) once per process,
- * and keep them cached. They are only read at all if someone tries
- * to inspect a CD/DVD/ISO.
- *
- * XXX Currently the database is not freed when the program exits /
- * library is unloaded, although we should probably do that.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <libintl.h>
-#include <sys/stat.h>
-
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-
-#include "ignore-value.h"
-#include "glthread/lock.h"
-#include "c-ctype.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-
-gl_lock_define_initialized (static, osinfo_db_lock);
-static ssize_t osinfo_db_size = 0; /* 0 = unread, -1 = error, >= 1 =
#records */
-static struct osinfo *osinfo_db = NULL;
-
-static int read_osinfo_db (guestfs_h *g);
-static void free_osinfo_db_entry (struct osinfo *);
-
-#define XMLSTREQ(a,b) (xmlStrEqual((a),(b)) == 1)
-
-/* Given one or more fields from the header of a CD/DVD/ISO, look up
- * the media in the libosinfo database and return our best guess for
- * the operating system.
- *
- * This returns:
- * -1 => a fatal error ('error' has been called, caller must not
ignore it)
- * 0 => could not locate the OS
- * 1 => matching OS found, the osinfo_ret pointer has been filled in
- */
-int
-guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo *isoinfo,
- const struct osinfo **osinfo_ret)
-{
- size_t i;
-
- /* We only need to lock the database when reading it for the first time. */
- gl_lock_lock (osinfo_db_lock);
- if (osinfo_db_size == 0) {
- if (read_osinfo_db (g) == -1) {
- gl_lock_unlock (osinfo_db_lock);
- return -1;
- }
- }
- gl_lock_unlock (osinfo_db_lock);
-
- if (osinfo_db_size <= 0)
- return 0;
-
- /* Look in the database to see if we can find a match. */
- for (i = 0; i < (size_t) osinfo_db_size; ++i) {
- if (osinfo_db[i].re_system_id) {
- if (!isoinfo->iso_system_id ||
- !match (g, isoinfo->iso_system_id, osinfo_db[i].re_system_id))
- continue;
- }
-
- if (osinfo_db[i].re_volume_id) {
- if (!isoinfo->iso_volume_id ||
- !match (g, isoinfo->iso_volume_id, osinfo_db[i].re_volume_id))
- continue;
- }
-
- if (osinfo_db[i].re_publisher_id) {
- if (!isoinfo->iso_publisher_id ||
- !match (g, isoinfo->iso_publisher_id,
osinfo_db[i].re_publisher_id))
- continue;
- }
-
- if (osinfo_db[i].re_application_id) {
- if (!isoinfo->iso_application_id ||
- !match (g, isoinfo->iso_application_id,
osinfo_db[i].re_application_id))
- continue;
- }
-
- debug (g, "osinfo: mapped disk to database entry %zu", i);
-
- if (osinfo_ret)
- *osinfo_ret = &osinfo_db[i];
- return 1;
- }
-
- debug (g, "osinfo: no mapping found");
-
- return 0;
-}
-
-/* Read the libosinfo XML database files. The lock is held while
- * this is called.
- *
- * Returns:
- * -1 => a fatal error ('error' has been called)
- * 0 => OK
- *
- * Note that failure to find or parse the XML files is *not* a fatal
- * error, since we should fall back silently if these are not
- * available. Although we'll emit some debug if this happens.
- *
- * Try to use the shared osinfo database layout (and location) first:
- * https://gitlab.com/libosinfo/libosinfo/blob/master/docs/database-layout.txt
- */
-static int read_osinfo_db_xml (guestfs_h *g, const char *filename);
-
-static int read_osinfo_db_flat (guestfs_h *g, const char *directory);
-static int read_osinfo_db_three_levels (guestfs_h *g, const char *directory);
-static int read_osinfo_db_directory (guestfs_h *g, const char *directory);
-
-static int
-read_osinfo_db (guestfs_h *g)
-{
- int r;
- size_t i;
-
- assert (osinfo_db_size == 0);
-
- /* (1) Try the shared osinfo directory, using either the
- * $OSINFO_SYSTEM_DIR envvar or its default value.
- */
- {
- const char *path;
- CLEANUP_FREE char *os_path = NULL;
-
- path = getenv ("OSINFO_SYSTEM_DIR");
- if (path == NULL)
- path = "/usr/share/osinfo";
- os_path = safe_asprintf (g, "%s/os", path);
- r = read_osinfo_db_three_levels (g, os_path);
- }
- if (r == -1)
- goto error;
- else if (r == 1)
- return 0;
-
- /* (2) Try the libosinfo directory, using the newer three-directory
- * layout ($LIBOSINFO_DB_PATH / "os" / $group-ID / [file.xml]).
- */
- r = read_osinfo_db_three_levels (g, LIBOSINFO_DB_PATH "/os");
- if (r == -1)
- goto error;
- else if (r == 1)
- return 0;
-
- /* (3) Try the libosinfo directory, using the old flat directory
- * layout ($LIBOSINFO_DB_PATH / "oses" / [file.xml]).
- */
- r = read_osinfo_db_flat (g, LIBOSINFO_DB_PATH "/oses");
- if (r == -1)
- goto error;
- else if (r == 1)
- return 0;
-
- /* Nothing found. */
- return 0;
-
- error:
- /* Fatal error: free any database entries which have been read, and
- * mark the database as having a permanent error.
- */
- if (osinfo_db_size > 0) {
- for (i = 0; i < (size_t) osinfo_db_size; ++i)
- free_osinfo_db_entry (&osinfo_db[i]);
- }
- free (osinfo_db);
- osinfo_db = NULL;
- osinfo_db_size = -1;
-
- return -1;
-}
-
-static int
-read_osinfo_db_flat (guestfs_h *g, const char *directory)
-{
- debug (g, "osinfo: loading flat database from %s", directory);
-
- return read_osinfo_db_directory (g, directory);
-}
-
-static int
-read_osinfo_db_three_levels (guestfs_h *g, const char *directory)
-{
- DIR *dir;
- int r;
-
- dir = opendir (directory);
- if (!dir) {
- debug (g, "osinfo: %s: %s", directory, strerror (errno));
- return 0; /* This is not an error: RHBZ#948324. */
- }
-
- debug (g, "osinfo: loading 3-level-directories database from %s",
directory);
-
- for (;;) {
- struct dirent *d;
- CLEANUP_FREE char *pathname = NULL;
- struct stat sb;
-
- errno = 0;
- d = readdir (dir);
- if (!d) break;
-
- pathname = safe_asprintf (g, "%s/%s", directory, d->d_name);
-
- /* Iterate only on directories. */
- if (stat (pathname, &sb) == 0 && S_ISDIR (sb.st_mode)) {
- r = read_osinfo_db_directory (g, pathname);
- if (r == -1)
- goto error;
- }
- }
-
- /* Check for failure in readdir. */
- if (errno != 0) {
- perrorf (g, "readdir: %s", directory);
- goto error;
- }
-
- /* Close the directory handle. */
- r = closedir (dir);
- dir = NULL;
- if (r == -1) {
- perrorf (g, "closedir: %s", directory);
- goto error;
- }
-
- return 1;
-
- error:
- if (dir)
- closedir (dir);
-
- return -1;
-}
-
-static int
-read_osinfo_db_directory (guestfs_h *g, const char *directory)
-{
- DIR *dir;
- int r;
-
- dir = opendir (directory);
- if (!dir) {
- debug (g, "osinfo: %s: %s", directory, strerror (errno));
- return 0; /* This is not an error: RHBZ#948324. */
- }
-
- for (;;) {
- struct dirent *d;
-
- errno = 0;
- d = readdir (dir);
- if (!d) break;
-
- if (STRSUFFIX (d->d_name, ".xml")) {
- CLEANUP_FREE char *pathname = NULL;
-
- pathname = safe_asprintf (g, "%s/%s", directory, d->d_name);
- r = read_osinfo_db_xml (g, pathname);
- if (r == -1)
- goto error;
- }
- }
-
- /* Check for failure in readdir. */
- if (errno != 0) {
- perrorf (g, "readdir: %s", directory);
- goto error;
- }
-
- /* Close the directory handle. */
- r = closedir (dir);
- dir = NULL;
- if (r == -1) {
- perrorf (g, "closedir: %s", directory);
- goto error;
- }
-
- return 1;
-
- error:
- if (dir)
- closedir (dir);
-
- return -1;
-}
-
-static int read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo
*osinfo);
-static int read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
xmlNodePtr media_node, struct osinfo *osinfo);
-static int read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx, xmlNodePtr
os_node, struct osinfo *osinfo);
-
-/* Read a single XML file from pathname (which is a full path).
- * Only memory allocation failures are fatal errors here.
- */
-static int
-read_osinfo_db_xml (guestfs_h *g, const char *pathname)
-{
- CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL;
- CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL;
- CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpathObj = NULL;
- xmlNodeSetPtr nodes;
- xmlNodePtr iso_node, media_node, os_node;
- struct osinfo *osinfo;
- size_t i;
-
- doc = xmlReadFile (pathname, NULL, XML_PARSE_NONET);
- if (doc == NULL) {
- debug (g, "osinfo: unable to parse XML file %s", pathname);
- return 0;
- }
-
- xpathCtx = xmlXPathNewContext (doc);
- if (xpathCtx == NULL) {
- error (g, _("osinfo: unable to create new XPath context"));
- return -1;
- }
-
- /* Get all <iso> nodes at any depth, then use the parent pointers in
- * order to work back up the tree.
- */
- xpathObj = xmlXPathEvalExpression (BAD_CAST
"/libosinfo/os/media/iso",
- xpathCtx);
- if (xpathObj == NULL) {
- error (g, _("osinfo: %s: unable to evaluate XPath expression"),
- pathname);
- return -1;
- }
-
- nodes = xpathObj->nodesetval;
-
- if (nodes != NULL) {
- for (i = 0; i < (size_t) nodes->nodeNr; ++i) {
- iso_node = nodes->nodeTab[i];
- assert (iso_node != NULL);
- assert (STREQ ((const char *) iso_node->name, "iso"));
- assert (iso_node->type == XML_ELEMENT_NODE);
-
- media_node = iso_node->parent;
- assert (media_node != NULL);
- assert (STREQ ((const char *) media_node->name, "media"));
- assert (media_node->type == XML_ELEMENT_NODE);
-
- os_node = media_node->parent;
- assert (os_node != NULL);
- assert (STREQ ((const char *) os_node->name, "os"));
- assert (os_node->type == XML_ELEMENT_NODE);
-
- /* Allocate an osinfo record. */
- osinfo_db_size++;
- osinfo_db = safe_realloc (g, osinfo_db,
- sizeof (struct osinfo) * osinfo_db_size);
- osinfo = &osinfo_db[osinfo_db_size-1];
- memset (osinfo, 0, sizeof *osinfo);
-
- /* Read XML fields into the new osinfo record. */
- if (read_iso_node (g, iso_node, osinfo) == -1 ||
- read_media_node (g, xpathCtx, media_node, osinfo) == -1 ||
- read_os_node (g, xpathCtx, os_node, osinfo) == -1) {
- free_osinfo_db_entry (osinfo);
- osinfo_db_size--;
- return -1;
- }
-
-#if 0
- debug (g, "osinfo: %s: %s%s%s%s=> arch %s live %s installer %s
product %s type %d distro %d version %d.%d",
- pathname,
- osinfo->re_system_id ? "<system-id/> " :
"",
- osinfo->re_volume_id ? "<volume-id/> " :
"",
- osinfo->re_publisher_id ? "<publisher-id/> " :
"",
- osinfo->re_application_id ? "<application-id/>
" : "",
- osinfo->arch ? osinfo->arch : "(none)",
- osinfo->is_live_disk ? "true" : "false",
- osinfo->is_installer ? "true" : "false",
- osinfo->product_name ? osinfo->product_name :
"(none)",
- (int) osinfo->type, (int) osinfo->distro,
- osinfo->major_version, osinfo->minor_version);
-#endif
- }
- }
-
- return 0;
-}
-
-static int compile_re (guestfs_h *g, xmlNodePtr child, pcre **re);
-
-/* Read the regular expressions under the <iso> node. libosinfo
- * itself uses the glib function 'g_regex_match_simple'. That appears
- * to implement PCRE, however I have not checked in detail.
- */
-static int
-read_iso_node (guestfs_h *g, xmlNodePtr iso_node, struct osinfo *osinfo)
-{
- xmlNodePtr child;
-
- for (child = iso_node->children; child; child = child->next) {
- if (STREQ ((const char *) child->name, "system-id")) {
- if (compile_re (g, child, &osinfo->re_system_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "volume-id")) {
- if (compile_re (g, child, &osinfo->re_volume_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "publisher-id")) {
- if (compile_re (g, child, &osinfo->re_publisher_id) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "application-id"))
{
- if (compile_re (g, child, &osinfo->re_application_id) == -1)
- return -1;
- }
- }
-
- return 0;
-}
-
-static int
-compile_re (guestfs_h *g, xmlNodePtr node, pcre **re)
-{
- const char *err;
- int offset;
- CLEANUP_FREE char *content = (char *) xmlNodeGetContent (node);
-
- if (content) {
- *re = pcre_compile (content, 0, &err, &offset, NULL);
- if (*re == NULL)
- debug (g, "osinfo: could not parse regular expression '%s':
%s (ignored)",
- content, err);
- }
-
- return 0;
-}
-
-/* Read the attributes of the <media/> node. */
-static int
-read_media_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
- xmlNodePtr media_node, struct osinfo *osinfo)
-{
- osinfo->arch = (char *) xmlGetProp (media_node, BAD_CAST
"arch");
-
- osinfo->is_live_disk = 0; /* If no 'live' attr, defaults to false.
*/
- {
- CLEANUP_XMLFREE xmlChar *content = NULL;
- content = xmlGetProp (media_node, BAD_CAST "live");
- if (content)
- osinfo->is_live_disk = XMLSTREQ (content, BAD_CAST "true");
- }
-
- osinfo->is_installer = true; /* If no 'installer' attr, defaults
to true. */
- {
- CLEANUP_XMLFREE xmlChar *content = NULL;
- content = xmlGetProp (media_node, BAD_CAST "installer");
- if (content)
- osinfo->is_installer = XMLSTREQ (content, BAD_CAST "true");
- }
-
- return 0;
-}
-
-static int parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo
*osinfo);
-static int parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
-static int parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo);
-
-/* Read some fields under the <os/> node. */
-static int
-read_os_node (guestfs_h *g, xmlXPathContextPtr xpathCtx,
- xmlNodePtr os_node, struct osinfo *osinfo)
-{
- xmlNodePtr child;
-
- for (child = os_node->children; child; child = child->next) {
- if (STREQ ((const char *) child->name, "name"))
- osinfo->product_name = (char *) xmlNodeGetContent (child);
- else if (STREQ ((const char *) child->name, "version")) {
- if (parse_version (g, child, osinfo) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "family")) {
- if (parse_family (g, child, osinfo) == -1)
- return -1;
- }
- else if (STREQ ((const char *) child->name, "distro")) {
- if (parse_distro (g, child, osinfo) == -1)
- return -1;
- }
- }
-
- return 0;
-}
-
-static int
-parse_version (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- content = (char *) xmlNodeGetContent (node);
- /* We parse either "X.Y" or "X" as version strings, so
try to parse
- * only if the first character is a digit.
- */
- if (content && c_isdigit (content[0])) {
- struct version version;
- const int res = guestfs_int_version_from_x_y_or_x (g, &version,
content);
- if (res < 0)
- return -1;
- else if (res > 0) {
- osinfo->major_version = version.v_major;
- osinfo->minor_version = version.v_minor;
- }
- }
-
- return 0;
-}
-
-static int
-parse_family (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- osinfo->type = OS_TYPE_UNKNOWN;
-
- content = (char *) xmlNodeGetContent (node);
- if (content) {
- if (STREQ (content, "linux"))
- osinfo->type = OS_TYPE_LINUX;
- else if (STRPREFIX (content, "win"))
- osinfo->type = OS_TYPE_WINDOWS;
- else if (STREQ (content, "freebsd"))
- osinfo->type = OS_TYPE_FREEBSD;
- else if (STREQ (content, "netbsd"))
- osinfo->type = OS_TYPE_NETBSD;
- else if (STREQ (content, "msdos"))
- osinfo->type = OS_TYPE_DOS;
- else if (STREQ (content, "openbsd"))
- osinfo->type = OS_TYPE_OPENBSD;
- else
- debug (g, "osinfo: warning: unknown <family>
'%s'", content);
- }
-
- return 0;
-}
-
-static int
-parse_distro (guestfs_h *g, xmlNodePtr node, struct osinfo *osinfo)
-{
- CLEANUP_FREE char *content = NULL;
-
- osinfo->distro = OS_DISTRO_UNKNOWN;
-
- content = (char *) xmlNodeGetContent (node);
- if (content) {
- if (STREQ (content, "altlinux"))
- osinfo->distro = OS_DISTRO_ALTLINUX;
- else if (STREQ (content, "centos"))
- osinfo->distro = OS_DISTRO_CENTOS;
- else if (STREQ (content, "debian"))
- osinfo->distro = OS_DISTRO_DEBIAN;
- else if (STREQ (content, "fedora"))
- osinfo->distro = OS_DISTRO_FEDORA;
- else if (STREQ (content, "freebsd"))
- osinfo->distro = OS_DISTRO_FREEBSD;
- else if (STREQ (content, "mageia"))
- osinfo->distro = OS_DISTRO_MAGEIA;
- else if (STREQ (content, "mandriva"))
- osinfo->distro = OS_DISTRO_MANDRIVA;
- else if (STREQ (content, "netbsd"))
- osinfo->distro = OS_DISTRO_NETBSD;
- else if (STREQ (content, "openbsd"))
- osinfo->distro = OS_DISTRO_OPENBSD;
- else if (STREQ (content, "opensuse"))
- osinfo->distro = OS_DISTRO_OPENSUSE;
- else if (STREQ (content, "rhel"))
- osinfo->distro = OS_DISTRO_RHEL;
- else if (STREQ (content, "sled") || STREQ (content,
"sles"))
- osinfo->distro = OS_DISTRO_SLES;
- else if (STREQ (content, "ubuntu"))
- osinfo->distro = OS_DISTRO_UBUNTU;
- else if (STRPREFIX (content, "win"))
- osinfo->distro = OS_DISTRO_WINDOWS;
- else
- debug (g, "osinfo: warning: unknown <distro>
'%s'", content);
- }
-
- return 0;
-}
-
-static void
-free_osinfo_db_entry (struct osinfo *osinfo)
-{
- free (osinfo->product_name);
- free (osinfo->arch);
-
- if (osinfo->re_system_id)
- pcre_free (osinfo->re_system_id);
- if (osinfo->re_volume_id)
- pcre_free (osinfo->re_volume_id);
- if (osinfo->re_publisher_id)
- pcre_free (osinfo->re_publisher_id);
- if (osinfo->re_application_id)
- pcre_free (osinfo->re_application_id);
-}
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 02/29] daemon: Allow parts of the daemon and APIs to be written in OCaml.
This change allows parts of the daemon to be written in the OCaml
programming language. I am using the ‘Main Program in C’ method along
with ‘-output-obj’ to create an object file from the OCaml code /
runtime, as described here:
https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
Furthermore, change the generator to allow individual APIs to be
implemented in OCaml. This is picked by setting:
impl = OCaml <ocaml_function>;
The generator creates ‘do_function’ (the same one you would have to
write by hand in C), with the function calling the named
‘ocaml_function’ and dealing with marshalling/unmarshalling the OCaml
parameters.
Note that the OCaml compiler (either ocamlc or ocamlopt) is now
required even for building from tarballs.
---
.gitignore | 6 +-
Makefile.am | 2 +-
daemon/Makefile.am | 103 +++++++++++++++++++++++--
daemon/chroot.ml | 85 +++++++++++++++++++++
daemon/chroot.mli | 35 +++++++++
daemon/daemon-c.c | 35 +++++++++
daemon/daemon.ml | 39 ++++++++++
daemon/guestfsd.c | 45 +++++++++++
daemon/sysroot-c.c | 37 +++++++++
daemon/sysroot.ml | 19 +++++
daemon/sysroot.mli | 22 ++++++
daemon/utils.ml | 156 ++++++++++++++++++++++++++++++++++++++
daemon/utils.mli | 65 ++++++++++++++++
docs/guestfs-building.pod | 10 ++-
docs/guestfs-hacking.pod | 7 ++
generator/actions.ml | 5 ++
generator/actions.mli | 4 +
generator/daemon.ml | 187 ++++++++++++++++++++++++++++++++++++++++++++++
generator/daemon.mli | 3 +
generator/main.ml | 6 ++
generator/types.ml | 7 +-
21 files changed, 866 insertions(+), 12 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8afd06d0e..f37ca263a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -164,20 +164,24 @@ Makefile.in
/customize/test-settings-*.sh
/customize/virt-customize
/customize/virt-customize.1
+/daemon/.depend
/daemon/actions.h
+/daemon/callbacks.ml
+/daemon/caml-stubs.c
/daemon/dispatch.c
/daemon/guestfsd
/daemon/guestfsd.8
/daemon/guestfsd.exe
+/daemon/lvm-tokenization.c
/daemon/names.c
/daemon/optgroups.c
/daemon/optgroups.h
-/daemon/lvm-tokenization.c
/daemon/stamp-guestfsd.pod
/daemon/structs-cleanups.c
/daemon/structs-cleanups.h
/daemon/stubs-?.c
/daemon/stubs.h
+/daemon/types.ml
/depcomp
/df/stamp-virt-df.pod
/df/virt-df
diff --git a/Makefile.am b/Makefile.am
index 9122d44ac..1b091044a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -44,6 +44,7 @@ SUBDIRS += common/structs
SUBDIRS += lib docs examples po
# The daemon and the appliance.
+SUBDIRS += common/mlutils
if ENABLE_DAEMON
SUBDIRS += daemon
SUBDIRS += tests/daemon
@@ -155,7 +156,6 @@ SUBDIRS += csharp
# OCaml tools. Note 'common/ml*', 'mllib' and
'customize' contain
# shared code used by other OCaml tools, so these must come first.
if HAVE_OCAML
-SUBDIRS += common/mlutils
SUBDIRS += common/mlprogress
SUBDIRS += common/mlvisit
SUBDIRS += common/mlxml
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index db19594b8..c7375fb87 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -19,6 +19,7 @@ include $(top_srcdir)/subdir-rules.mk
generator_built = \
actions.h \
+ caml-stubs.c \
dispatch.c \
names.c \
lvm-tokenization.c \
@@ -31,13 +32,30 @@ generator_built = \
stubs-4.c \
stubs-5.c \
stubs-6.c \
- stubs.h
+ stubs.h \
+ callbacks.ml \
+ types.ml
BUILT_SOURCES = \
- $(generator_built)
+ actions.h \
+ caml-stubs.c \
+ dispatch.c \
+ names.c \
+ lvm-tokenization.c \
+ structs-cleanups.c \
+ structs-cleanups.h \
+ stubs-0.c \
+ stubs-1.c \
+ stubs-2.c \
+ stubs-3.c \
+ stubs-4.c \
+ stubs-5.c \
+ stubs-6.c \
+ stubs.h
EXTRA_DIST = \
- $(BUILT_SOURCES) \
+ $(generator_built) \
+ $(SOURCES_MLI) $(SOURCES_ML) \
guestfsd.pod
if INSTALL_DAEMON
@@ -61,6 +79,7 @@ guestfsd_SOURCES = \
blkid.c \
blockdev.c \
btrfs.c \
+ caml-stubs.c \
cap.c \
checksum.c \
cleanups.c \
@@ -71,6 +90,7 @@ guestfsd_SOURCES = \
copy.c \
cpio.c \
cpmv.c \
+ daemon-c.c \
daemon.h \
dd.c \
debug.c \
@@ -161,6 +181,7 @@ guestfsd_SOURCES = \
swap.c \
sync.c \
syslinux.c \
+ sysroot-c.c \
tar.c \
tsk.c \
truncate.c \
@@ -176,10 +197,16 @@ guestfsd_SOURCES = \
zero.c \
zerofree.c
+guestfsd_LDFLAGS = \
+ -L$(shell $(OCAMLC) -where) \
+ -L$(shell $(OCAMLC) -where)/hivex \
+ -L../common/mlutils \
+ -L../common/mlstdutils
guestfsd_LDADD = \
../common/errnostring/liberrnostring.la \
../common/protocol/libprotocol.la \
../common/utils/libutils.la \
+ camldaemon.o \
$(ACL_LIBS) \
$(CAP_LIBS) \
$(YAJL_LIBS) \
@@ -198,9 +225,12 @@ guestfsd_LDADD = \
$(PCRE_LIBS) \
$(TSK_LIBS) \
$(RPC_LIBS) \
- $(YARA_LIBS)
+ $(YARA_LIBS) \
+ $(OCAML_LIBS)
guestfsd_CPPFLAGS = \
+ -I$(shell $(OCAMLC) -where) \
+ -I$(shell $(OCAMLC) -where)/hivex \
-I$(top_srcdir)/gnulib/lib \
-I$(top_builddir)/gnulib/lib \
-I$(top_srcdir)/lib \
@@ -220,6 +250,69 @@ guestfsd_CFLAGS = \
$(YAJL_CFLAGS) \
$(PCRE_CFLAGS)
+# Parts of the daemon are written in OCaml. These are linked into a
+# library and then linked to the daemon. See
+# https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
+SOURCES_MLI = \
+ chroot.mli \
+ sysroot.mli \
+ utils.mli
+
+SOURCES_ML = \
+ types.ml \
+ utils.ml \
+ sysroot.ml \
+ chroot.ml \
+ callbacks.ml \
+ daemon.ml
+
+BOBJECTS = $(SOURCES_ML:.ml=.cmo)
+XOBJECTS = $(BOBJECTS:.cmo=.cmx)
+
+OCAMLPACKAGES = \
+ -package str,unix,hivex \
+ -I $(top_srcdir)/common/mlstdutils \
+ -I $(top_srcdir)/common/mlutils
+
+OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_FLAGS)
+
+if !HAVE_OCAMLOPT
+OBJECTS = $(BOBJECTS)
+CAMLRUN = camlrun
+else
+OBJECTS = $(XOBJECTS)
+CAMLRUN = asmrun
+endif
+OCAML_LIBS = \
+ -lmlcutils \
+ -lmlstdutils \
+ -lmlhivex \
+ -lcamlstr \
+ -lunix \
+ -l$(CAMLRUN) -ldl -lm
+
+CLEANFILES += camldaemon.o
+
+camldaemon.o: $(OBJECTS)
+ $(OCAMLFIND) $(BEST) -output-obj -o $@ \
+ $(OCAMLFLAGS) $(OCAMLPACKAGES) \
+ -linkpkg mlcutils.$(MLARCHIVE) mlstdutils.$(MLARCHIVE) \
+ $(OBJECTS)
+
+# OCaml dependencies.
+depend: .depend
+
+.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
+ rm -f $@ $@-t
+ $(OCAMLFIND) ocamldep -I $(abs_srcdir) -I
$(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils $^ |
\
+ $(SED) 's/ *$$//' | \
+ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
+ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
+ sort > $@-t
+ mv $@-t $@
+
+-include .depend
+
# Manual pages and HTML files for the website.
if INSTALL_DAEMON
man_MANS = guestfsd.8
@@ -241,4 +334,4 @@ stamp-guestfsd.pod: guestfsd.pod
$<
touch $@
-.PHONY: force
+.PHONY: depend force
diff --git a/daemon/chroot.ml b/daemon/chroot.ml
new file mode 100644
index 000000000..40dfa1dde
--- /dev/null
+++ b/daemon/chroot.ml
@@ -0,0 +1,85 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+open Unix_utils
+
+type t = {
+ name : string;
+ chroot : string;
+}
+
+let create ?(name = prog) chroot + { name = name; chroot = chroot }
+
+let f t func arg + if verbose () then
+ eprintf "chroot: %s: running ‘%s’\n%!" t.chroot t.name;
+
+ let rfd, wfd = pipe () in
+
+ let pid = fork () in
+ if pid = 0 then (
+ (* Child. *)
+ close rfd;
+
+ chdir t.chroot;
+ chroot t.chroot;
+
+ let ret + try Either (func arg)
+ with exn -> Or exn in
+
+ try
+ let chan = out_channel_of_descr wfd in
+ output_value chan ret;
+ Pervasives.flush chan;
+ Exit._exit 0
+ with
+ exn ->
+ prerr_endline (Printexc.to_string exn);
+ Exit._exit 1
+ );
+
+ (* Parent. *)
+ close wfd;
+
+ let _, status = waitpid [] pid in
+ (match status with
+ | WEXITED 0 -> ()
+ | WEXITED i ->
+ close rfd;
+ failwithf "chroot ‘%s’ exited with non-zero error %d" t.name i
+ | WSIGNALED i ->
+ close rfd;
+ failwithf "chroot ‘%s’ killed by signal %d" t.name i
+ | WSTOPPED i ->
+ close rfd;
+ failwithf "chroot ‘%s’ stopped by signal %d" t.name i
+ );
+
+ let chan = in_channel_of_descr rfd in
+ let ret = input_value chan in
+ close_in chan;
+
+ match ret with
+ | Either ret -> ret
+ | Or exn -> raise exn
diff --git a/daemon/chroot.mli b/daemon/chroot.mli
new file mode 100644
index 000000000..eda3a785f
--- /dev/null
+++ b/daemon/chroot.mli
@@ -0,0 +1,35 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+(** This is a generic module for running functions in a chroot.
+ The function runs in a forked subprocess too so that we can
+ restore the root afterwards.
+
+ It handles passing the parmeter, forking, running the
+ function and marshalling the result or any exceptions. *)
+
+type t
+
+val create : ?name:string -> string -> t
+(** Create a chroot handle. [?name] is an optional name used in
+ debugging and error messages. The string is the chroot
+ directory. *)
+
+val f : t -> ('a -> 'b) -> 'a -> 'b
+(** Run a function in the chroot, returning the result or re-raising
+ any exception thrown. *)
diff --git a/daemon/daemon-c.c b/daemon/daemon-c.c
new file mode 100644
index 000000000..da382bc35
--- /dev/null
+++ b/daemon/daemon-c.c
@@ -0,0 +1,35 @@
+/* guestfs-inspection
+ * Copyright (C) 2017 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <caml/mlvalues.h>
+
+#include "daemon.h"
+
+extern value guestfs_int_daemon_get_verbose_flag (value unitv);
+
+/* NB: This is a "noalloc" call. */
+value
+guestfs_int_daemon_get_verbose_flag (value unitv)
+{
+ return Val_bool (verbose);
+}
diff --git a/daemon/daemon.ml b/daemon/daemon.ml
new file mode 100644
index 000000000..45bac029a
--- /dev/null
+++ b/daemon/daemon.ml
@@ -0,0 +1,39 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+external get_verbose_flag : unit -> bool +
"guestfs_int_daemon_get_verbose_flag" "noalloc"
+
+(* When guestfsd starts up, after initialization but before accepting
+ * messages, it calls 'caml_startup' which runs all initialization code
+ * in the OCaml modules, including this one. Therefore this is where
+ * we can place OCaml initialization code for the daemon.
+ *)
+let () + (* Connect the guestfsd [-v] (verbose) flag into 'verbose ()'
+ * used in OCaml code to print debugging messages.
+ *)
+ if get_verbose_flag () then (
+ Std_utils.set_verbose ();
+ eprintf "OCaml daemon loaded\n%!"
+ );
+
+ (* Register the callbacks which are used to call OCaml code from C. *)
+ Callbacks.init_callbacks ()
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index b3f40628b..05337b31c 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -56,6 +56,10 @@
#include <augeas.h>
+#include <caml/callback.h>
+#include <caml/mlvalues.h>
+#include <caml/unixsupport.h>
+
#include "sockets.h"
#include "c-ctype.h"
#include "ignore-value.h"
@@ -348,6 +352,9 @@ main (int argc, char *argv[])
*/
udev_settle ();
+ /* Initialize the OCaml stubs. */
+ caml_startup (argv);
+
/* Send the magic length message which indicates that
* userspace is up inside the guest.
*/
@@ -1205,3 +1212,41 @@ cleanup_free_mountable (mountable_t *mountable)
free (mountable->volume);
}
}
+
+/* Convert an OCaml exception to a reply_with_error_errno call
+ * as best we can.
+ */
+extern void ocaml_exn_to_reply_with_error (const char *func, value exn);
+
+void
+ocaml_exn_to_reply_with_error (const char *func, value exn)
+{
+ const char *exn_name;
+
+ /* This is not the official way to do this, but I could not get the
+ * official way to work, and this way does work. See
+ *
http://caml.inria.fr/pub/ml-archives/caml-list/2006/05/097f63cfb39a80418f95c70c3c520aa8.en.html
+ *
http://caml.inria.fr/pub/ml-archives/caml-list/2009/06/797e2f797f57b8ea2a2c0e431a2df312.en.html
+ */
+ exn_name = String_val (Field (Field (exn, 0), 0));
+ if (verbose)
+ fprintf (stderr, "ocaml_exn: '%s' raised '%s'
exception\n",
+ func, exn_name);
+
+ if (STREQ (exn_name, "Unix.Unix_error")) {
+ int errcode = code_of_unix_error (Field (exn, 1));
+ reply_with_perror_errno (errcode, "%s: %s",
+ String_val (Field (exn, 2)),
+ String_val (Field (exn, 3)));
+ }
+ else if (STREQ (exn_name, "Failure"))
+ reply_with_error ("%s", String_val (Field (exn, 1)));
+ else if (STREQ (exn_name, "Sys_error"))
+ reply_with_error ("%s", String_val (Field (exn, 1)));
+ else if (STREQ (exn_name, "Invalid_argument"))
+ reply_with_error ("invalid argument: %s",
+ String_val (Field (exn, 1)));
+ else
+ reply_with_error ("internal error: %s: unknown exception thrown:
%s",
+ func, exn_name);
+}
diff --git a/daemon/sysroot-c.c b/daemon/sysroot-c.c
new file mode 100644
index 000000000..ad31d36ee
--- /dev/null
+++ b/daemon/sysroot-c.c
@@ -0,0 +1,37 @@
+/* guestfs-inspection
+ * Copyright (C) 2017 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <caml/alloc.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+#include "daemon.h"
+
+extern value guestfs_int_daemon_sysroot (value unitv);
+
+value
+guestfs_int_daemon_sysroot (value unitv)
+{
+ return caml_copy_string (sysroot);
+}
diff --git a/daemon/sysroot.ml b/daemon/sysroot.ml
new file mode 100644
index 000000000..ecf0d7362
--- /dev/null
+++ b/daemon/sysroot.ml
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+external sysroot : unit -> string = "guestfs_int_daemon_sysroot"
diff --git a/daemon/sysroot.mli b/daemon/sysroot.mli
new file mode 100644
index 000000000..88f976476
--- /dev/null
+++ b/daemon/sysroot.mli
@@ -0,0 +1,22 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val sysroot : unit -> string
+(** Return the current sysroot path where filesystems are mounted.
+ This comes from the daemon command line ([-r] option) or a built
+ in default. *)
diff --git a/daemon/utils.ml b/daemon/utils.ml
new file mode 100644
index 000000000..7630a5534
--- /dev/null
+++ b/daemon/utils.ml
@@ -0,0 +1,156 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Std_utils
+
+let prog_exists prog + try ignore (which prog); true
+ with Executable_not_found _ -> false
+
+let commandr prog args + if verbose () then
+ eprintf "command: %s %s\n%!"
+ prog (String.concat " " args);
+
+ let argv = Array.of_list (prog :: args) in
+
+ let stdout_file, stdout_chan = Filename.open_temp_file "cmd"
".out" in
+ let stderr_file, stderr_chan = Filename.open_temp_file "cmd"
".err" in
+ let stdout_fd = descr_of_out_channel stdout_chan in
+ let stderr_fd = descr_of_out_channel stderr_chan in
+ let stdin_fd = openfile "/dev/null" [O_RDONLY] 0 in
+
+ let pid = fork () in
+ if pid = 0 then (
+ (* Child process. *)
+ dup2 stdin_fd stdin;
+ close stdin_fd;
+ dup2 stdout_fd stdout;
+ close stdout_fd;
+ dup2 stderr_fd stderr;
+ close stderr_fd;
+
+ execvp prog argv
+ );
+
+ (* Parent process. *)
+ close stdin_fd;
+ close stdout_fd;
+ close stderr_fd;
+ let _, status = waitpid [] pid in
+ let r + match status with
+ | WEXITED i -> i
+ | WSIGNALED i ->
+ failwithf "external command ‘%s’ killed by signal %d" prog i
+ | WSTOPPED i ->
+ failwithf "external command ‘%s’ stopped by signal %d" prog i
in
+
+ if verbose () then
+ eprintf "command: %s returned %d\n" prog r;
+
+ let stdout = read_whole_file stdout_file in
+ let stderr = read_whole_file stderr_file in
+
+ if verbose () then (
+ if stdout <> "" then (
+ eprintf "command: %s: stdout:\n%s%!" prog stdout;
+ if not (String.is_suffix stdout "\n") then eprintf
"\n%!"
+ );
+ if stderr <> "" then (
+ eprintf "command: %s: stderr:\n%s%!" prog stderr;
+ if not (String.is_suffix stderr "\n") then eprintf
"\n%!"
+ )
+ );
+
+ (* Strip trailing \n from stderr but NOT from stdout. *)
+ let stderr + let n = String.length stderr in
+ if n > 0 && stderr.[n-1] = '\n' then
+ String.sub stderr 0 (n-1)
+ else
+ stderr in
+
+ (r, stdout, stderr)
+
+let command prog args + let r, stdout, stderr = commandr prog args in
+ if r <> 0 then
+ failwithf "%s exited with status %d: %s" prog r stderr;
+ stdout
+
+let udev_settle ?filename () + let args = ref [] in
+ if verbose () then
+ push_back args "--debug";
+ push_back args "settle";
+ (match filename with
+ | None -> ()
+ | Some filename ->
+ push_back args "-E";
+ push_back args filename
+ );
+ let args = !args in
+ let r, _, err = commandr "udevadm" args in
+ if r <> 0 then
+ eprintf "udevadm settle: %s\n" err
+
+let root_device = lazy ((stat "/").st_dev)
+
+let is_root_device_stat statbuf + statbuf.st_rdev = Lazy.force root_device
+
+let is_root_device device + udev_settle ~filename:device ();
+ try
+ let statbuf = stat device in
+ is_root_device_stat statbuf
+ with
+ Unix_error (err, func, arg) ->
+ eprintf "is_root_device: %s: %s: %s: %s\n"
+ device func arg (error_message err);
+ false
+
+let proc_unmangle_path path + let n = String.length path in
+ let b = Buffer.create n in
+ let rec loop i + if i < n-3 && path.[i] = '\\' then (
+ let to_int c = Char.code c - Char.code '0' in
+ let v + (to_int path.[i+1] lsl 6) lor
+ (to_int path.[i+2] lsl 3) lor
+ to_int path.[i+3] in
+ Buffer.add_char b (Char.chr v);
+ loop (i+4)
+ )
+ else if i < n then (
+ Buffer.add_char b path.[i];
+ loop (i+1)
+ )
+ else
+ Buffer.contents b
+ in
+ loop 0
+
+let is_small_file path + is_regular_file path &&
+ (stat path).st_size <= 2 * 1048 * 1024
diff --git a/daemon/utils.mli b/daemon/utils.mli
new file mode 100644
index 000000000..57f703c6c
--- /dev/null
+++ b/daemon/utils.mli
@@ -0,0 +1,65 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val prog_exists : string -> bool
+(** Return true iff the program is found on [$PATH]. *)
+
+val udev_settle : ?filename:string -> unit -> unit
+(**
+ * LVM and other commands aren't synchronous, especially when udev is
+ * involved. eg. You can create or remove some device, but the
+ * [/dev] device node won't appear until some time later. This means
+ * that you get an error if you run one command followed by another.
+ *
+ * Use [udevadm settle] after certain commands, but don't be too
+ * fussed if it fails.
+ *
+ * The optional [?filename] passes the [udevadm settle -E filename]
+ * option, which means udevadm stops waiting as soon as the named
+ * file is created (or if it exists at the start).
+ *)
+
+val is_root_device : string -> bool
+(** Return true if this is the root (appliance) device. *)
+
+val is_root_device_stat : Unix.stats -> bool
+(** As for {!is_root_device} but operates on a statbuf instead of
+ a device name. *)
+
+val proc_unmangle_path : string -> string
+(** Reverse kernel path escaping done in fs/seq_file.c:mangle_path.
+ This is inconsistently used for /proc fields. *)
+
+val command : string -> string list -> string
+(** Run an external command without using the shell, and collect
+ stdout and stderr separately. Returns stdout if the command
+ runs successfully.
+
+ On failure of the command, this throws an exception containing
+ the stderr from the command. *)
+
+val commandr : string -> string list -> (int * string * string)
+(** Run an external command without using the shell, and collect
+ stdout and stderr separately.
+
+ Returns [status, stdout, stderr]. As with the C function in
+ [daemon/command.c], this strips the trailing [\n] from stderr,
+ but {b not} from stdout. *)
+
+val is_small_file : string -> bool
+(** Return true if the path is a small regular file. *)
diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod
index 0f9ed2893..4e1ff7df4 100644
--- a/docs/guestfs-building.pod
+++ b/docs/guestfs-building.pod
@@ -120,8 +120,7 @@ I<Required>. Part of Perl core.
=item OCaml findlib
-I<Required> if compiling from git.
-Optional (but recommended) if compiling from tarball.
+I<Required>.
=item autoconf
@@ -594,8 +593,11 @@ See L</USING A PREBUILT BINARY APPLIANCE> below.
Disable specific language bindings, even if C<./configure> finds all
the necessary libraries are installed so that they could be compiled.
-Note that disabling OCaml or Perl will have the knock-on effect of
-disabling large numbers of virt tools and parts of the test suite.
+Note that disabling Perl will have the knock-on effect of disabling
+parts of the test suite and some tools.
+
+Disabling OCaml only disables the bindings and several virt tools.
+OCaml is required to build libguestfs.
=item B<--disable-fuse>
diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod
index bfbe4526d..25f4bfa94 100644
--- a/docs/guestfs-hacking.pod
+++ b/docs/guestfs-hacking.pod
@@ -416,6 +416,13 @@ in the C<lib/> directory.
In either case, use another function as an example of what to do.
+=item 3.
+
+As an alternative to step 2: Since libguestfs 1.37.15, daemon actions
+can be implemented in OCaml. You have to set the C<impl = OCaml ...>
+flag in the generator. Take a look at F<daemon/file.ml> for an
+example.
+
=back
After making these changes, use C<make> to compile.
diff --git a/generator/actions.ml b/generator/actions.ml
index a9b3b5906..75742397a 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -185,6 +185,11 @@ let is_fish { visibility = v; style = (_, args, _) }
not (List.exists (function Pointer _ -> true | _ -> false) args)
let fish_functions = List.filter is_fish
+let is_ocaml_function = function
+ | { impl = OCaml _ } -> true
+ | { impl = C } -> false
+let impl_ocaml_functions = List.filter is_ocaml_function
+
(* In some places we want the functions to be displayed sorted
* alphabetically, so this is useful:
*)
diff --git a/generator/actions.mli b/generator/actions.mli
index 0d326b609..82217cbdc 100644
--- a/generator/actions.mli
+++ b/generator/actions.mli
@@ -40,6 +40,10 @@ val internal_functions : Types.action list -> Types.action
list
val fish_functions : Types.action list -> Types.action list
(** Filter {!actions}, returning only functions in guestfish. *)
+val impl_ocaml_functions : Types.action list -> Types.action list
+(** Filter {!actions}, returning only functions implemented
+ in OCaml (in the daemon). *)
+
val documented_functions : Types.action list -> Types.action list
(** Filter {!actions}, returning only functions requiring documentation. *)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 2ae462864..ac410b733 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -471,6 +471,193 @@ let generate_daemon_stubs actions () pr
"}\n\n";
) (actions |> daemon_functions |> sort)
+let generate_daemon_caml_types_ml () + generate_header OCamlStyle GPLv2plus
+
+let generate_daemon_caml_callbacks_ml () + generate_header OCamlStyle
GPLv2plus;
+
+ if actions |> impl_ocaml_functions <> [] then (
+ pr "let init_callbacks () =\n";
+ pr " (* Initialize callbacks to OCaml code. *)\n";
+ List.iter (
+ fun ({ name = name; style = ret, args, optargs } as f) ->
+ let ocaml_function + match f.impl with
+ | OCaml f -> f
+ | C -> assert false in
+
+ pr " Callback.register %S %s;\n" ocaml_function
ocaml_function
+ ) (actions |> impl_ocaml_functions |> sort)
+ )
+ else
+ pr "let init_callbacks () = ()\n"
+
+(* Generate stubs for the functions implemented in OCaml.
+ * Basically we implement the do_<name> function here, and
+ * have it call out to OCaml code.
+ *)
+let generate_daemon_caml_stubs () + generate_header CStyle GPLv2plus;
+
+ pr "\
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <caml/alloc.h>
+#include <caml/callback.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+#include \"daemon.h\"
+#include \"actions.h\"
+
+/* This is not declared in <daemon.h> because we don't want to
+ * include the OCaml headers (to get 'value') for the whole daemon.
+ */
+extern void ocaml_exn_to_reply_with_error (const char *func, value exn);
+
+";
+
+ List.iter (
+ fun ({ name = name; style = ret, args, optargs } as f) ->
+ let ocaml_function + match f.impl with
+ | OCaml f -> f
+ | C -> assert false in
+
+ pr "/* Wrapper for OCaml function ‘%s’. */\n" ocaml_function;
+
+ let args_do_function = args @ args_of_optargs optargs in
+ let args_do_function + List.filter (function
+ | String ((FileIn|FileOut), _) -> false | _ -> true)
+ args_do_function in
+ let style = ret, args_do_function, [] in
+ generate_prototype ~extern:false ~semicolon:false
+ ~single_line:false ~newline:false
+ ~in_daemon:true ~prefix:"do_"
+ name style;
+ pr "\n";
+
+ let add_unit_arg + let args = List.filter
+ (function
+ | String ((FileIn|FileOut), _) -> false | _ ->
true)
+ args in
+ args = [] in
+ let nr_args = List.length args_do_function in
+
+ pr "{\n";
+ pr " static value *cb = NULL;\n";
+ pr " CAMLparam0 ();\n";
+ pr " CAMLlocal2 (v, retv);\n";
+ pr " CAMLlocalN (args, %d);\n"
+ (nr_args + if add_unit_arg then 1 else 0);
+ pr "\n";
+ pr " if (cb == NULL)\n";
+ pr " cb = caml_named_value (\"%s\");\n"
ocaml_function;
+ pr "\n";
+
+ (* Construct the actual call, but note that we want to pass
+ * the optional arguments first in the list.
+ *)
+ let i = ref 0 in
+ List.iter (
+ fun optarg ->
+ let n = name_of_optargt optarg in
+ let uc_n = String.uppercase_ascii n in
+
+ (* optargs are all passed as [None|Some _] *)
+ pr " if ((optargs_bitmask & %s_%s_BITMASK) == 0)\n"
+ f.c_optarg_prefix uc_n;
+ pr " args[%d] = Val_int (0); /* None */\n" !i;
+ pr " else {\n";
+ pr " v = ";
+ (match optarg with
+ | OBool _ ->
+ pr "Val_bool (%s)" n;
+ | OInt _ -> assert false
+ | OInt64 _ -> assert false
+ | OString _ -> assert false
+ | OStringList _ -> assert false
+ );
+ pr ";\n";
+ pr " args[%d] = caml_alloc (1, 0);\n" !i;
+ pr " Store_field (args[%d], 0, v);\n" !i;
+ pr " }\n";
+ incr i
+ ) optargs;
+ List.iter (
+ fun arg ->
+ pr " args[%d] = " !i;
+ (match arg with
+ | Bool n -> pr "Val_bool (%s)" n
+ | Int n -> pr "Val_int (%s)" n
+ | Int64 n -> pr "caml_copy_int64 (%s)" n
+ | String (_, n) -> pr "caml_copy_string (%s)" n
+ | OptString _ -> assert false
+ | StringList _ -> assert false
+ | BufferIn _ -> assert false
+ | Pointer _ -> assert false
+ );
+ pr ";\n";
+ incr i
+ ) args;
+ assert (!i = nr_args);
+
+ (* If there are no non-optional arguments, we add a unit arg. *)
+ if add_unit_arg then
+ pr " args[%d] = Val_unit;\n" !i;
+
+ pr " retv = caml_callbackN_exn (*cb, %d, args);\n"
+ (nr_args + if add_unit_arg then 1 else 0);
+ pr "\n";
+ pr " if (Is_exception_result (retv)) {\n";
+ pr " retv = Extract_exception (retv);\n";
+ pr " ocaml_exn_to_reply_with_error (%S, retv);\n" name;
+ (match errcode_of_ret ret with
+ | `CannotReturnError ->
+ pr " CAMLreturn0;\n"
+ | `ErrorIsMinusOne ->
+ pr " CAMLreturnT (int, -1);\n"
+ | `ErrorIsNULL ->
+ pr " CAMLreturnT (void *, NULL);\n"
+ );
+ pr " }\n";
+ pr "\n";
+
+ (match ret with
+ | RErr -> assert false
+ | RInt _ -> assert false
+ | RInt64 _ -> assert false
+ | RBool _ -> assert false
+ | RConstString _ -> assert false
+ | RConstOptString _ -> assert false
+ | RString _ ->
+ pr " char *ret = strdup (String_val (retv));\n";
+ pr " if (ret == NULL) {\n";
+ pr " reply_with_perror (\"strdup\");\n";
+ pr " CAMLreturnT (char *, NULL);\n";
+ pr " }\n";
+ pr " CAMLreturnT (char *, ret); /* caller frees */\n"
+ | RStringList _ -> assert false
+ | RStruct _ -> assert false
+ | RStructList _ -> assert false
+ | RHashtable _ -> assert false
+ | RBufferOut _ -> assert false
+ );
+ pr "}\n";
+ pr "\n"
+ ) (actions |> impl_ocaml_functions |> sort)
+
let generate_daemon_dispatch () generate_header CStyle GPLv2plus;
diff --git a/generator/daemon.mli b/generator/daemon.mli
index ff008bf85..314a6da8f 100644
--- a/generator/daemon.mli
+++ b/generator/daemon.mli
@@ -19,6 +19,9 @@
val generate_daemon_actions_h : unit -> unit
val generate_daemon_stubs_h : unit -> unit
val generate_daemon_stubs : Types.action list -> unit -> unit
+val generate_daemon_caml_stubs : unit -> unit
+val generate_daemon_caml_callbacks_ml : unit -> unit
+val generate_daemon_caml_types_ml : unit -> unit
val generate_daemon_dispatch : unit -> unit
val generate_daemon_lvm_tokenization : unit -> unit
val generate_daemon_names : unit -> unit
diff --git a/generator/main.ml b/generator/main.ml
index 33fe2b2ee..a6c805e2e 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -133,6 +133,12 @@ Run it from the top source directory using the command
Daemon.generate_daemon_stubs_h;
output_to_subset "daemon/stubs-%d.c"
Daemon.generate_daemon_stubs;
+ output_to "daemon/caml-stubs.c"
+ Daemon.generate_daemon_caml_stubs;
+ output_to "daemon/callbacks.ml"
+ Daemon.generate_daemon_caml_callbacks_ml;
+ output_to "daemon/types.ml"
+ Daemon.generate_daemon_caml_types_ml;
output_to "daemon/dispatch.c"
Daemon.generate_daemon_dispatch;
output_to "daemon/names.c"
diff --git a/generator/types.ml b/generator/types.ml
index 740bc7750..fb6c3bc06 100644
--- a/generator/types.ml
+++ b/generator/types.ml
@@ -379,11 +379,16 @@ type deprecated_by | Replaced_by of string (*
replaced by another function *)
| Deprecated_no_replacement (* deprecated with no replacement *)
+type impl + | C (* implemented in C by
"do_<name>" *)
+ | OCaml of string (* implemented in OCaml by named function *)
+
(* Type of an action as declared in Actions module. *)
type action = {
name : string; (* name, not including "guestfs_"
*)
added : version; (* which version was the API first added *)
style : style; (* args and return value *)
+ impl : impl; (* implementation language (C or OCaml) *)
proc_nr : int option; (* proc number, None for non-daemon *)
tests : c_api_tests; (* C API tests *)
test_excuse : string; (* if there's no tests ... *)
@@ -439,7 +444,7 @@ type action = {
*)
let defaults = { name = "";
added = (-1,-1,-1);
- style = RErr, [], []; proc_nr = None;
+ style = RErr, [], []; impl = C; proc_nr = None;
tests = []; test_excuse = "";
shortdesc = ""; longdesc = "";
protocol_limit_warning = false; fish_alias = [];
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 03/29] daemon: Reimplement ‘file’ API in OCaml.
‘file’ is a small, self-contained API which runs a single command, so
it's a good test case for reimplementing APIs.
---
daemon/Makefile.am | 2 ++
daemon/file.c | 80 -----------------------------------------------
daemon/file.ml | 60 +++++++++++++++++++++++++++++++++++
daemon/file.mli | 19 +++++++++++
generator/actions_core.ml | 1 +
5 files changed, 82 insertions(+), 80 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c7375fb87..c11f51cef 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -256,6 +256,7 @@ guestfsd_CFLAGS = \
SOURCES_MLI = \
chroot.mli \
sysroot.mli \
+ file.mli \
utils.mli
SOURCES_ML = \
@@ -263,6 +264,7 @@ SOURCES_ML = \
utils.ml \
sysroot.ml \
chroot.ml \
+ file.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/file.c b/daemon/file.c
index 84874dc6f..ee79eb507 100644
--- a/daemon/file.c
+++ b/daemon/file.c
@@ -30,7 +30,6 @@
#include "actions.h"
#include "optgroups.h"
-GUESTFSD_EXT_CMD(str_file, file);
GUESTFSD_EXT_CMD(str_zcat, zcat);
GUESTFSD_EXT_CMD(str_bzcat, bzcat);
@@ -449,85 +448,6 @@ do_pwrite_device (const char *device, const char *content,
size_t size,
return pwrite_fd (fd, content, size, offset, device, 1);
}
-/* This runs the 'file' command. */
-char *
-do_file (const char *path)
-{
- CLEANUP_FREE char *buf = NULL;
- const char *display_path = path;
- const int is_dev = STRPREFIX (path, "/dev/");
- struct stat statbuf;
-
- if (!is_dev) {
- buf = sysroot_path (path);
- if (!buf) {
- reply_with_perror ("malloc");
- return NULL;
- }
- path = buf;
-
- /* For non-dev, check this is a regular file, else just return the
- * file type as a string (RHBZ#582484).
- */
- if (lstat (path, &statbuf) == -1) {
- reply_with_perror ("lstat: %s", display_path);
- return NULL;
- }
-
- if (! S_ISREG (statbuf.st_mode)) {
- char *ret;
-
- if (S_ISDIR (statbuf.st_mode))
- ret = strdup ("directory");
- else if (S_ISCHR (statbuf.st_mode))
- ret = strdup ("character device");
- else if (S_ISBLK (statbuf.st_mode))
- ret = strdup ("block device");
- else if (S_ISFIFO (statbuf.st_mode))
- ret = strdup ("FIFO");
- else if (S_ISLNK (statbuf.st_mode))
- ret = strdup ("symbolic link");
- else if (S_ISSOCK (statbuf.st_mode))
- ret = strdup ("socket");
- else
- ret = strdup ("unknown, not regular file");
-
- if (ret == NULL)
- reply_with_perror ("strdup");
- return ret;
- }
- }
-
- /* Which flags to use? For /dev paths, follow links because
- * /dev/VG/LV is a symbolic link.
- */
- const char *flags = is_dev ? "-zbsL" : "-zb";
-
- char *out;
- CLEANUP_FREE char *err = NULL;
- int r = command (&out, &err, str_file, flags, path, NULL);
-
- if (r == -1) {
- free (out);
- reply_with_error ("%s: %s", display_path, err);
- return NULL;
- }
-
- /* We need to remove the trailing \n from output of file(1). */
- size_t len = strlen (out);
- if (len > 0 && out[len-1] == '\n')
- out[--len] = '\0';
-
- /* Some upstream versions of file add a space at the end of the
- * output. This is fixed in the Fedora version, but we might as
- * well fix it here too. (RHBZ#928995).
- */
- if (len > 0 && out[len-1] == ' ')
- out[--len] = '\0';
-
- return out; /* caller frees */
-}
-
/* zcat | file */
char *
do_zfile (const char *method, const char *path)
diff --git a/daemon/file.ml b/daemon/file.ml
new file mode 100644
index 000000000..557de764b
--- /dev/null
+++ b/daemon/file.ml
@@ -0,0 +1,60 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Std_utils
+
+open Utils
+
+(* This runs the [file] command. *)
+let file path + let is_dev = String.is_prefix path "/dev/" in
+
+ (* For non-dev, check this is a regular file, else just return the
+ * file type as a string (RHBZ#582484).
+ *)
+ if not is_dev then (
+ let sysroot = Sysroot.sysroot () in
+ let chroot = Chroot.create sysroot ~name:(sprintf "file: %s"
path) in
+
+ let statbuf = Chroot.f chroot lstat path in
+ match statbuf.st_kind with
+ | S_DIR -> "directory"
+ | S_CHR -> "character device"
+ | S_BLK -> "block device"
+ | S_FIFO -> "FIFO"
+ | S_LNK -> "symbolic link"
+ | S_SOCK -> "socket"
+ | S_REG ->
+ (* Regular file, so now run [file] on it. *)
+ let out = command "file" ["-zb"; sysroot // path] in
+
+ (* We need to remove the trailing \n from output of file(1).
+ *
+ * Some upstream versions of file add a space at the end of the
+ * output. This is fixed in the Fedora version, but we might as
+ * well fix it here too. (RHBZ#928995).
+ *)
+ String.trimr out
+ )
+ else (* it's a device *) (
+ let out = command "file" ["-zbsL"; path] in
+ String.trimr out
+ )
diff --git a/daemon/file.mli b/daemon/file.mli
new file mode 100644
index 000000000..bd49bad0b
--- /dev/null
+++ b/daemon/file.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val file : string -> string
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 0e667eff1..26ed1274e 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -2321,6 +2321,7 @@ and physical volumes." };
{ defaults with
name = "file"; added = (1, 9, 1);
style = RString (RPlainString, "description"), [String
(Dev_or_Path, "path")], [];
+ impl = OCaml "File.file";
tests = [
InitISOFS, Always, TestResultString (
[["file"; "/empty"]], "empty"), [];
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 04/29] daemon: Reimplement ‘vfs_type’ API in OCaml.
This also implements support for String (Mountable, _)
parameters.
---
daemon/Makefile.am | 4 ++++
daemon/blkid.c | 6 ------
daemon/blkid.ml | 40 ++++++++++++++++++++++++++++++++++++++++
daemon/blkid.mli | 19 +++++++++++++++++++
daemon/mountable.ml | 43 +++++++++++++++++++++++++++++++++++++++++++
daemon/mountable.mli | 34 ++++++++++++++++++++++++++++++++++
generator/actions_core.ml | 1 +
generator/daemon.ml | 38 ++++++++++++++++++++++++++++++++++++--
8 files changed, 177 insertions(+), 8 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c11f51cef..a9d7fb9bd 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -254,16 +254,20 @@ guestfsd_CFLAGS = \
# library and then linked to the daemon. See
# https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
SOURCES_MLI = \
+ blkid.mli \
chroot.mli \
sysroot.mli \
file.mli \
+ mountable.mli \
utils.mli
SOURCES_ML = \
types.ml \
utils.ml \
sysroot.ml \
+ mountable.ml \
chroot.ml \
+ blkid.ml \
file.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/blkid.c b/daemon/blkid.c
index 1fe5ff93a..7757b5ad0 100644
--- a/daemon/blkid.c
+++ b/daemon/blkid.c
@@ -69,12 +69,6 @@ get_blkid_tag (const char *device, const char *tag)
}
char *
-do_vfs_type (const mountable_t *mountable)
-{
- return get_blkid_tag (mountable->device, "TYPE");
-}
-
-char *
do_vfs_label (const mountable_t *mountable)
{
CLEANUP_FREE char *type = do_vfs_type (mountable);
diff --git a/daemon/blkid.ml b/daemon/blkid.ml
new file mode 100644
index 000000000..3345f826e
--- /dev/null
+++ b/daemon/blkid.ml
@@ -0,0 +1,40 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+
+let rec vfs_type { Mountable.m_device = device } + get_blkid_tag device
"TYPE"
+
+and get_blkid_tag device tag + let r, out, err + commandr "blkid"
+ [(* Adding -c option kills all caching, even on RHEL 5. *)
+ "-c"; "/dev/null";
+ "-o"; "value"; "-s"; tag; device]
in
+ match r with
+ | 0 -> (* success *)
+ String.trimr out
+
+ | 2 -> (* means tag not found, we return
"" *)
+ ""
+
+ | _ ->
+ failwithf "blkid: %s: %s" tag err
diff --git a/daemon/blkid.mli b/daemon/blkid.mli
new file mode 100644
index 000000000..59a86ac2c
--- /dev/null
+++ b/daemon/blkid.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val vfs_type : Mountable.t -> string
diff --git a/daemon/mountable.ml b/daemon/mountable.ml
new file mode 100644
index 000000000..96dffb80b
--- /dev/null
+++ b/daemon/mountable.ml
@@ -0,0 +1,43 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+type t = {
+ m_type : mountable_type;
+ m_device : string;
+}
+and mountable_type + | MountableDevice
+ | MountablePath
+ | MountableBtrfsVol of string (* volume *)
+
+let to_string { m_type = t; m_device = device } + match t with
+ | MountableDevice | MountablePath -> device
+ | MountableBtrfsVol volume ->
+ sprintf "btrfsvol:%s/%s" device volume
+
+let of_device device + { m_type = MountableDevice; m_device = device }
+
+let of_path path + { m_type = MountablePath; m_device = path }
+
+let of_btrfsvol device volume + { m_type = MountableBtrfsVol volume; m_device
= device }
diff --git a/daemon/mountable.mli b/daemon/mountable.mli
new file mode 100644
index 000000000..52f1ad45b
--- /dev/null
+++ b/daemon/mountable.mli
@@ -0,0 +1,34 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+type t = {
+ m_type : mountable_type;
+ m_device : string;
+}
+and mountable_type + | MountableDevice
+ | MountablePath
+ | MountableBtrfsVol of string (* volume *)
+
+val to_string : t -> string
+(** Convert the mountable back to the string used in the public API. *)
+
+val of_device : string -> t
+val of_path : string -> t
+val of_btrfsvol : string -> string -> t
+(** Create a mountable from various objects. *)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 26ed1274e..a6eb2c273 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -4872,6 +4872,7 @@ See also C<guestfs_realpath>." };
{ defaults with
name = "vfs_type"; added = (1, 0, 75);
style = RString (RPlainString, "fstype"), [String (Mountable,
"mountable")], [];
+ impl = OCaml "Blkid.vfs_type";
tests = [
InitScratchFS, Always, TestResultString (
[["vfs_type"; "/dev/sdb1"]], "ext2"), []
diff --git a/generator/daemon.ml b/generator/daemon.ml
index ac410b733..121634806 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -524,6 +524,35 @@ let generate_daemon_caml_stubs () */
extern void ocaml_exn_to_reply_with_error (const char *func, value exn);
+/* Implement String (Mountable, _) parameter. */
+static value
+copy_mountable (const mountable_t *mountable)
+{
+ CAMLparam0 ();
+ CAMLlocal4 (r, typev, devicev, volumev);
+
+ switch (mountable->type) {
+ case MOUNTABLE_DEVICE:
+ typev = Val_int (0); /* MountableDevice */
+ break;
+ case MOUNTABLE_PATH:
+ typev = Val_int (1); /* MountablePath */
+ break;
+ case MOUNTABLE_BTRFSVOL:
+ volumev = caml_copy_string (mountable->volume);
+ typev = caml_alloc (1, 0); /* MountableBtrfsVol */
+ Store_field (typev, 0, volumev);
+ }
+
+ devicev = caml_copy_string (mountable->device);
+
+ r = caml_alloc_tuple (2);
+ Store_field (r, 0, typev);
+ Store_field (r, 1, devicev);
+
+ CAMLreturn (r);
+}
+
";
List.iter (
@@ -602,7 +631,11 @@ extern void ocaml_exn_to_reply_with_error (const char
*func, value exn);
| Bool n -> pr "Val_bool (%s)" n
| Int n -> pr "Val_int (%s)" n
| Int64 n -> pr "caml_copy_int64 (%s)" n
- | String (_, n) -> pr "caml_copy_string (%s)" n
+ | String ((PlainString|Device|Dev_or_Path), n) ->
+ pr "caml_copy_string (%s)" n
+ | String (Mountable, n) ->
+ pr "copy_mountable (%s)" n
+ | String _ -> assert false
| OptString _ -> assert false
| StringList _ -> assert false
| BufferIn _ -> assert false
@@ -641,13 +674,14 @@ extern void ocaml_exn_to_reply_with_error (const char
*func, value exn);
| RBool _ -> assert false
| RConstString _ -> assert false
| RConstOptString _ -> assert false
- | RString _ ->
+ | RString (RPlainString, _) ->
pr " char *ret = strdup (String_val (retv));\n";
pr " if (ret == NULL) {\n";
pr " reply_with_perror (\"strdup\");\n";
pr " CAMLreturnT (char *, NULL);\n";
pr " }\n";
pr " CAMLreturnT (char *, ret); /* caller frees */\n"
+ | RString _ -> assert false
| RStringList _ -> assert false
| RStruct _ -> assert false
| RStructList _ -> assert false
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 05/29] daemon: Reimplement several devsparts APIs in OCaml.
The reimplemented APIs are:
* list_devices
* list_partitions
* part_to_dev
* part_to_partnum
* is_whole_device
---
daemon/Makefile.am | 2 +
daemon/daemon.h | 3 -
daemon/devsparts.c | 257 ----------------------------------------------
daemon/devsparts.ml | 109 ++++++++++++++++++++
daemon/devsparts.mli | 25 +++++
daemon/guestfsd.c | 75 --------------
daemon/utils.ml | 84 +++++++++++++++
daemon/utils.mli | 15 +++
generator/actions_core.ml | 5 +
generator/daemon.ml | 32 +++++-
10 files changed, 268 insertions(+), 339 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a9d7fb9bd..9e53aef7a 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -257,6 +257,7 @@ SOURCES_MLI = \
blkid.mli \
chroot.mli \
sysroot.mli \
+ devsparts.mli \
file.mli \
mountable.mli \
utils.mli
@@ -268,6 +269,7 @@ SOURCES_ML = \
mountable.ml \
chroot.ml \
blkid.ml \
+ devsparts.ml \
file.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/daemon.h b/daemon/daemon.h
index be7a3bedc..0a92e6cee 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -130,9 +130,6 @@ extern void free_stringsbuf (struct stringsbuf *sb);
extern void sort_strings (char **argv, size_t len);
extern void free_stringslen (char **argv, size_t len);
-extern void sort_device_names (char **argv, size_t len);
-extern int compare_device_names (const char *a, const char *b);
-
extern struct stringsbuf split_lines_sb (char *str);
extern char **split_lines (char *str);
diff --git a/daemon/devsparts.c b/daemon/devsparts.c
index 82467b92f..1aacb8e16 100644
--- a/daemon/devsparts.c
+++ b/daemon/devsparts.c
@@ -33,263 +33,6 @@
#include "daemon.h"
#include "actions.h"
-typedef int (*block_dev_func_t) (const char *dev, struct stringsbuf *r);
-
-/* Execute a given function for each discovered block device */
-static char **
-foreach_block_device (block_dev_func_t func, bool return_md)
-{
- CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (r);
- DIR *dir;
- int err = 0;
- struct dirent *d;
- int fd;
-
- dir = opendir ("/sys/block");
- if (!dir) {
- reply_with_perror ("opendir: /sys/block");
- return NULL;
- }
-
- for (;;) {
- errno = 0;
- d = readdir (dir);
- if (!d) break;
-
- if (STREQLEN (d->d_name, "sd", 2) ||
- STREQLEN (d->d_name, "hd", 2) ||
- STREQLEN (d->d_name, "ubd", 3) ||
- STREQLEN (d->d_name, "vd", 2) ||
- STREQLEN (d->d_name, "sr", 2) ||
- (return_md &&
- STREQLEN (d->d_name, "md", 2) && c_isdigit
(d->d_name[2]))) {
- CLEANUP_FREE char *dev_path = NULL;
- if (asprintf (&dev_path, "/dev/%s", d->d_name) == -1) {
- reply_with_perror ("asprintf");
- closedir (dir);
- return NULL;
- }
-
- /* Ignore the root device. */
- if (is_root_device (dev_path))
- continue;
-
- /* RHBZ#514505: Some versions of qemu <= 0.10 add a
- * CD-ROM device even though we didn't request it. Try to
- * detect this by seeing if the device contains media.
- */
- fd = open (dev_path, O_RDONLY|O_CLOEXEC);
- if (fd == -1) {
- perror (dev_path);
- continue;
- }
- close (fd);
-
- /* Call the map function for this device */
- if ((*func)(d->d_name, &r) != 0) {
- err = 1;
- break;
- }
- }
- }
-
- /* Check readdir didn't fail */
- if (errno != 0) {
- reply_with_perror ("readdir: /sys/block");
- closedir (dir);
- return NULL;
- }
-
- /* Close the directory handle */
- if (closedir (dir) == -1) {
- reply_with_perror ("closedir: /sys/block");
- return NULL;
- }
-
- if (err)
- return NULL;
-
- /* Sort the devices. */
- if (r.size > 0)
- sort_device_names (r.argv, r.size);
-
- /* NULL terminate the list */
- if (end_stringsbuf (&r) == -1) {
- return NULL;
- }
-
- return take_stringsbuf (&r);
-}
-
-/* Add a device to the list of devices */
-static int
-add_device (const char *device, struct stringsbuf *r)
-{
- char *dev_path;
-
- if (asprintf (&dev_path, "/dev/%s", device) == -1) {
- reply_with_perror ("asprintf");
- return -1;
- }
-
- if (add_string_nodup (r, dev_path) == -1)
- return -1;
-
- return 0;
-}
-
-char **
-do_list_devices (void)
-{
- /* For backwards compatibility, don't return MD devices in the list
- * returned by guestfs_list_devices. This is because most API users
- * expect that this list is effectively the same as the list of
- * devices added by guestfs_add_drive.
- *
- * Also, MD devices are special devices - unlike the devices exposed
- * by QEMU, and there is a special API for them,
- * guestfs_list_md_devices.
- */
- return foreach_block_device (add_device, false);
-}
-
-static int
-add_partitions (const char *device, struct stringsbuf *r)
-{
- CLEANUP_FREE char *devdir = NULL;
-
- /* Open the device's directory under /sys/block */
- if (asprintf (&devdir, "/sys/block/%s", device) == -1) {
- reply_with_perror ("asprintf");
- return -1;
- }
-
- DIR *dir = opendir (devdir);
- if (!dir) {
- reply_with_perror ("opendir: %s", devdir);
- return -1;
- }
-
- /* Look in /sys/block/<device>/ for entries starting with
<device>
- * e.g. /sys/block/sda/sda1
- */
- errno = 0;
- struct dirent *d;
- while ((d = readdir (dir)) != NULL) {
- if (STREQLEN (d->d_name, device, strlen (device))) {
- CLEANUP_FREE char *part = NULL;
- if (asprintf (&part, "/dev/%s", d->d_name) == -1) {
- perror ("asprintf");
- closedir (dir);
- return -1;
- }
-
- if (add_string (r, part) == -1) {
- closedir (dir);
- return -1;
- }
- }
- }
-
- /* Check if readdir failed */
- if (0 != errno) {
- reply_with_perror ("readdir: %s", devdir);
- closedir (dir);
- return -1;
- }
-
- /* Close the directory handle */
- if (closedir (dir) == -1) {
- reply_with_perror ("closedir: /sys/block/%s", device);
- return -1;
- }
-
- return 0;
-}
-
-char **
-do_list_partitions (void)
-{
- return foreach_block_device (add_partitions, true);
-}
-
-char *
-do_part_to_dev (const char *part)
-{
- int err = 1;
- size_t n = strlen (part);
-
- while (n >= 1 && c_isdigit (part[n-1])) {
- err = 0;
- n--;
- }
-
- if (err) {
- reply_with_error ("device name is not a partition");
- return NULL;
- }
-
- /* Deal with <device>p<N> partition names such as /dev/md0p1. */
- if (part[n-1] == 'p')
- n--;
-
- char *r = strndup (part, n);
- if (r == NULL) {
- reply_with_perror ("strdup");
- return NULL;
- }
-
- return r;
-}
-
-int
-do_part_to_partnum (const char *part)
-{
- int err = 1;
- size_t n = strlen (part);
-
- while (n >= 1 && c_isdigit (part[n-1])) {
- err = 0;
- n--;
- }
-
- if (err) {
- reply_with_error ("device name is not a partition");
- return -1;
- }
-
- int r;
- if (sscanf (&part[n], "%d", &r) != 1) {
- reply_with_error ("could not parse number");
- return -1;
- }
-
- return r;
-}
-
-int
-do_is_whole_device (const char *device)
-{
- /* A 'whole' block device will have a symlink to the device in its
- * /sys/block directory */
- CLEANUP_FREE char *devpath = NULL;
- if (asprintf (&devpath, "/sys/block/%s/device",
- device + strlen ("/dev/")) == -1) {
- reply_with_perror ("asprintf");
- return -1;
- }
-
- struct stat statbuf;
- if (stat (devpath, &statbuf) == -1) {
- if (errno == ENOENT || errno == ENOTDIR) return 0;
-
- reply_with_perror ("stat");
- return -1;
- }
-
- return 1;
-}
-
int
do_device_index (const char *device)
{
diff --git a/daemon/devsparts.ml b/daemon/devsparts.ml
new file mode 100644
index 000000000..e97ff1267
--- /dev/null
+++ b/daemon/devsparts.ml
@@ -0,0 +1,109 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+open Utils
+
+let map_block_devices ~return_md f + let devs = Sys.readdir
"/sys/block" in
+ let devs = Array.to_list devs in
+ let devs = List.filter (
+ fun dev ->
+ String.is_prefix dev "sd" ||
+ String.is_prefix dev "hd" ||
+ String.is_prefix dev "ubd" ||
+ String.is_prefix dev "vd" ||
+ String.is_prefix dev "sr" ||
+ (return_md && String.is_prefix dev "md" &&
+ String.length dev >= 3 && Char.isdigit dev.[2])
+ ) devs in
+
+ (* Ignore the root device. *)
+ let devs + List.filter (fun dev -> not (is_root_device
("/dev/" ^ dev))) devs in
+
+ (* RHBZ#514505: Some versions of qemu <= 0.10 add a
+ * CD-ROM device even though we didn't request it. Try to
+ * detect this by seeing if the device contains media.
+ *)
+ let devs + List.filter (
+ fun dev ->
+ try close (openfile ("/dev/" ^ dev) [O_RDONLY; O_CLOEXEC] 0);
true
+ with _ -> false
+ ) devs in
+
+ (* Call the map function for the devices left in the list. *)
+ List.map f devs
+
+let list_devices () + (* For backwards compatibility, don't return MD
devices in the list
+ * returned by guestfs_list_devices. This is because most API users
+ * expect that this list is effectively the same as the list of
+ * devices added by guestfs_add_drive.
+ *
+ * Also, MD devices are special devices - unlike the devices exposed
+ * by QEMU, and there is a special API for them,
+ * guestfs_list_md_devices.
+ *)
+ let devices + map_block_devices ~return_md:false (fun dev ->
"/dev/" ^ dev) in
+ sort_device_names devices
+
+let rec list_partitions () + let partitions = map_block_devices
~return_md:true add_partitions in
+ let partitions = List.flatten partitions in
+ sort_device_names partitions
+
+and add_partitions dev + (* Open the device's directory under /sys/block
*)
+ let parts = Sys.readdir ("/sys/block/" ^ dev) in
+ let parts = Array.to_list parts in
+
+ (* Look in /sys/block/<device>/ for entries starting with
+ * <device>, eg. /sys/block/sda/sda1.
+ *)
+ let parts = List.filter (fun part -> String.is_prefix part dev) parts in
+ List.map (fun part -> "/dev/" ^ part) parts
+
+let part_to_dev part + let dev, part = split_device_partition part in
+ if part = 0 then
+ failwithf "device name is not a partition";
+ "/dev/" ^ dev
+
+let part_to_partnum part + let _, part = split_device_partition part in
+ if part = 0 then
+ failwithf "device name is not a partition";
+ part
+
+let is_whole_device device + (* A 'whole' block device will have a
symlink to the device in its
+ * /sys/block directory
+ *)
+ assert (String.is_prefix device "/dev/");
+ let device = String.sub device 5 (String.length device - 5) in
+ let devpath = sprintf "/sys/block/%s/device" device in
+
+ try ignore (stat devpath); true
+ with Unix_error ((ENOENT|ENOTDIR), _, _) -> false
diff --git a/daemon/devsparts.mli b/daemon/devsparts.mli
new file mode 100644
index 000000000..4dfaa86e6
--- /dev/null
+++ b/daemon/devsparts.mli
@@ -0,0 +1,25 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val list_devices : unit -> string list
+val list_partitions : unit -> string list
+
+val part_to_dev : string -> string
+val part_to_partnum : string -> int
+
+val is_whole_device : string -> bool
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index 05337b31c..9704094a6 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -630,81 +630,6 @@ free_stringslen (char **argv, size_t len)
}
/**
- * Compare device names (including partition numbers if present).
- *
- *
L<https://rwmj.wordpress.com/2011/01/09/how-are-linux-drives-named-beyond-drive-26-devsdz/>
- */
-int
-compare_device_names (const char *a, const char *b)
-{
- size_t alen, blen;
- int r;
- int a_partnum, b_partnum;
-
- /* Skip /dev/ prefix if present. */
- if (STRPREFIX (a, "/dev/"))
- a += 5;
- if (STRPREFIX (b, "/dev/"))
- b += 5;
-
- /* Skip sd/hd/ubd/vd. */
- alen = strcspn (a, "d");
- blen = strcspn (b, "d");
- assert (alen > 0 && alen <= 2);
- assert (blen > 0 && blen <= 2);
- a += alen + 1;
- b += blen + 1;
-
- /* Get device name part, that is, just 'a', 'ab' etc. */
- alen = strcspn (a, "0123456789");
- blen = strcspn (b, "0123456789");
-
- /* If device name part is longer, it is always greater, eg.
- * "/dev/sdz" < "/dev/sdaa".
- */
- if (alen != blen)
- return alen - blen;
-
- /* Device name parts are the same length, so do a regular compare. */
- r = strncmp (a, b, alen);
- if (r != 0)
- return r;
-
- /* Compare partitions numbers. */
- a += alen;
- b += alen;
-
- /* If no partition numbers, bail -- the devices are the same. This
- * can happen in one peculiar case: where you have a mix of devices
- * with different interfaces (eg. /dev/sda and /dev/vda).
- * (RHBZ#858128).
- */
- if (!*a && !*b)
- return 0;
-
- r = sscanf (a, "%d", &a_partnum);
- assert (r == 1);
- r = sscanf (b, "%d", &b_partnum);
- assert (r == 1);
-
- return a_partnum - b_partnum;
-}
-
-static int
-compare_device_names_vp (const void *vp1, const void *vp2)
-{
- char * const *p1 = (char * const *) vp1;
- char * const *p2 = (char * const *) vp2;
- return compare_device_names (*p1, *p2);
-}
-
-void
-sort_device_names (char **argv, size_t len)
-{
- qsort (argv, len, sizeof (char *), compare_device_names_vp);
-}
-
-/**
* Split an output string into a NULL-terminated list of lines,
* wrapped into a stringsbuf.
*
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 7630a5534..48f6b9c5c 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -129,6 +129,90 @@ let is_root_device device device func arg
(error_message err);
false
+(* XXX This function is copied from C, but is misconceived. It
+ * cannot by design work for devices like /dev/md0. It would be
+ * better if it checked for the existence of devices and partitions
+ * in /sys/block so we know what the kernel thinks is a device or
+ * partition. The same applies to APIs such as part_to_partnum
+ * and part_to_dev which rely on this function.
+ *)
+let split_device_partition dev + (* Skip /dev/ prefix if present. *)
+ let dev + if String.is_prefix dev "/dev/" then
+ String.sub dev 5 (String.length dev - 5)
+ else dev in
+
+ (* Find the partition number (if present). *)
+ let dev, part + let n = String.length dev in
+ let i = ref n in
+ while !i >= 1 && Char.isdigit dev.[!i-1] do
+ decr i
+ done;
+ let i = !i in
+ if i = n then
+ dev, 0 (* no partition number, whole device *)
+ else
+ String.sub dev 0 i, int_of_string (String.sub dev i (n-i)) in
+
+ (* Deal with device names like /dev/md0p1. *)
+ (* XXX This function is buggy (as was the old C function) when
+ * presented with a whole device like /dev/md0.
+ *)
+ let dev + let n = String.length dev in
+ if n < 2 || dev.[n-1] <> 'p' || not (Char.isdigit
dev.[n-2]) then
+ dev
+ else (
+ let i = ref (n-1) in
+ while !i >= 0 && Char.isdigit dev.[!i] do
+ decr i;
+ done;
+ let i = !i in
+ String.sub dev 0 i
+ ) in
+
+ dev, part
+
+let rec sort_device_names devs + List.sort compare_device_names devs
+
+and compare_device_names a b + (* This takes the device name like
"/dev/sda1" and returns ("sda", 1). *)
+ let dev_a, part_a = split_device_partition a
+ and dev_b, part_b = split_device_partition b in
+
+ (* Skip "sd|hd|ubd..." so that /dev/sda and /dev/vda sort together.
+ * (This is what the old C function did, but it's not clear if it
+ * is still relevant. XXX)
+ *)
+ let skip_prefix dev + let n = String.length dev in
+ if n >= 2 && dev.[1] = 'd' then
+ String.sub dev 2 (String.length dev - 2)
+ else if n >= 3 && dev.[2] = 'd' then
+ String.sub dev 3 (String.length dev - 3)
+ else
+ dev in
+ let dev_a = skip_prefix dev_a
+ and dev_b = skip_prefix dev_b in
+
+ (* If device name part is longer, it is always greater, eg.
+ * "/dev/sdz" < "/dev/sdaa".
+ *)
+ let r = compare (String.length dev_a) (String.length dev_b) in
+ if r <> 0 then r
+ else (
+ (* Device name parts are the same length, so do a regular compare. *)
+ let r = compare dev_a dev_b in
+ if r <> 0 then r
+ else (
+ (* Device names are identical, so compare partition numbers. *)
+ compare part_a part_b
+ )
+ )
+
let proc_unmangle_path path let n = String.length path in
let b = Buffer.create n in
diff --git a/daemon/utils.mli b/daemon/utils.mli
index 57f703c6c..a1f956be3 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -41,6 +41,21 @@ val is_root_device_stat : Unix.stats -> bool
(** As for {!is_root_device} but operates on a statbuf instead of
a device name. *)
+val split_device_partition : string -> string * int
+(** Split a device name like [/dev/sda1] into a device name and
+ partition number, eg. ["sda", 1].
+
+ The [/dev/] prefix is skipped and removed, if present.
+
+ If the partition number is not present (a whole device), 0 is returned.
+
+ This function splits [/dev/md0p1] to ["md0", 1]. *)
+
+val sort_device_names : string list -> string list
+(** Sort device names correctly so that /dev/sdaa appears after /dev/sdz.
+ This also deals with partition numbers, and works whether or not
+ [/dev/] is present. *)
+
val proc_unmangle_path : string -> string
(** Reverse kernel path escaping done in fs/seq_file.c:mangle_path.
This is inconsistently used for /proc fields. *)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index a6eb2c273..94391288f 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -1817,6 +1817,7 @@ is I<not> intended that you try to parse the output
string." };
{ defaults with
name = "list_devices"; added = (0, 0, 4);
style = RStringList (RDevice, "devices"), [], [];
+ impl = OCaml "Devsparts.list_devices";
tests = [
InitEmpty, Always, TestResult (
[["list_devices"]],
@@ -1833,6 +1834,7 @@ See also C<guestfs_list_filesystems>." };
{ defaults with
name = "list_partitions"; added = (0, 0, 4);
style = RStringList (RDevice, "partitions"), [], [];
+ impl = OCaml "Devsparts.list_partitions";
tests = [
InitBasicFS, Always, TestResult (
[["list_partitions"]],
@@ -6086,6 +6088,7 @@ See also C<guestfs_stat>." };
{ defaults with
name = "part_to_dev"; added = (1, 5, 15);
style = RString (RDevice, "device"), [String (Device,
"partition")], [];
+ impl = OCaml "Devsparts.part_to_dev";
tests = [
InitPartition, Always, TestResultDevice (
[["part_to_dev"; "/dev/sda1"]],
"/dev/sda"), [];
@@ -6533,6 +6536,7 @@ as in C<guestfs_compress_out>." };
{ defaults with
name = "part_to_partnum"; added = (1, 13, 25);
style = RInt "partnum", [String (Device, "partition")],
[];
+ impl = OCaml "Devsparts.part_to_partnum";
tests = [
InitPartition, Always, TestResult (
[["part_to_partnum"; "/dev/sda1"]], "ret ==
1"), [];
@@ -8480,6 +8484,7 @@ you are better to use C<guestfs_mv> instead."
};
{ defaults with
name = "is_whole_device"; added = (1, 21, 9);
style = RBool "flag", [String (Device, "device")], [];
+ impl = OCaml "Devsparts.is_whole_device";
tests = [
InitEmpty, Always, TestResultTrue (
[["is_whole_device"; "/dev/sda"]]), [];
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 121634806..3ffe91537 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -553,6 +553,26 @@ copy_mountable (const mountable_t *mountable)
CAMLreturn (r);
}
+/* Implement RStringList. */
+static char **
+return_string_list (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value v;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0);
+ if (add_string (&ret, String_val (v)) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
";
List.iter (
@@ -669,12 +689,14 @@ copy_mountable (const mountable_t *mountable)
(match ret with
| RErr -> assert false
- | RInt _ -> assert false
+ | RInt _ ->
+ pr " CAMLreturnT (int, Int_val (retv));\n"
| RInt64 _ -> assert false
- | RBool _ -> assert false
+ | RBool _ ->
+ pr " CAMLreturnT (int, Bool_val (retv));\n"
| RConstString _ -> assert false
| RConstOptString _ -> assert false
- | RString (RPlainString, _) ->
+ | RString ((RPlainString|RDevice), _) ->
pr " char *ret = strdup (String_val (retv));\n";
pr " if (ret == NULL) {\n";
pr " reply_with_perror (\"strdup\");\n";
@@ -682,7 +704,9 @@ copy_mountable (const mountable_t *mountable)
pr " }\n";
pr " CAMLreturnT (char *, ret); /* caller frees */\n"
| RString _ -> assert false
- | RStringList _ -> assert false
+ | RStringList _ ->
+ pr " char **ret = return_string_list (retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RStruct _ -> assert false
| RStructList _ -> assert false
| RHashtable _ -> assert false
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 06/29] daemon: Add unit tests of the ‘Utils’ module.
---
.gitignore | 1 +
daemon/Makefile.am | 43 ++++++++++++++++++++++++++++++++++++++-
daemon/daemon_utils_tests.ml | 48 ++++++++++++++++++++++++++++++++++++++++++++
daemon/dummy.c | 2 ++
docs/C_SOURCE_FILES | 1 +
5 files changed, 94 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index f37ca263a..29596594a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -168,6 +168,7 @@ Makefile.in
/daemon/actions.h
/daemon/callbacks.ml
/daemon/caml-stubs.c
+/daemon/daemon_utils_tests
/daemon/dispatch.c
/daemon/guestfsd
/daemon/guestfsd.8
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 9e53aef7a..a7cd5d5c6 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -56,6 +56,7 @@ BUILT_SOURCES = \
EXTRA_DIST = \
$(generator_built) \
$(SOURCES_MLI) $(SOURCES_ML) \
+ daemon_utils_tests.ml \
guestfsd.pod
if INSTALL_DAEMON
@@ -280,7 +281,8 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx)
OCAMLPACKAGES = \
-package str,unix,hivex \
-I $(top_srcdir)/common/mlstdutils \
- -I $(top_srcdir)/common/mlutils
+ -I $(top_srcdir)/common/mlutils \
+ -I $(top_builddir)/common/utils/.libs
OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_FLAGS)
@@ -307,6 +309,45 @@ camldaemon.o: $(OBJECTS)
-linkpkg mlcutils.$(MLARCHIVE) mlstdutils.$(MLARCHIVE) \
$(OBJECTS)
+# Unit tests.
+
+check_PROGRAMS = daemon_utils_tests
+TESTS = daemon_utils_tests
+
+daemon_utils_tests_SOURCES = dummy.c
+daemon_utils_tests_CPPFLAGS = \
+ -I. \
+ -I$(top_builddir) \
+ -I$(shell $(OCAMLC) -where) \
+ -I$(top_srcdir)/lib
+daemon_utils_tests_BOBJECTS = \
+ utils.cmo \
+ daemon_utils_tests.cmo
+daemon_utils_tests_XOBJECTS = $(daemon_utils_tests_BOBJECTS:.cmo=.cmx)
+
+if !HAVE_OCAMLOPT
+daemon_utils_tests_THEOBJECTS = $(daemon_utils_tests_BOBJECTS)
+else
+daemon_utils_tests_THEOBJECTS = $(daemon_utils_tests_XOBJECTS)
+endif
+
+OCAMLLINKFLAGS = \
+ mlcutils.$(MLARCHIVE) \
+ mlstdutils.$(MLARCHIVE) \
+ $(LINK_CUSTOM_OCAMLC_ONLY)
+
+daemon_utils_tests_DEPENDENCIES = \
+ $(daemon_utils_tests_THEOBJECTS) \
+ $(top_srcdir)/ocaml-link.sh
+daemon_utils_tests_LINK = \
+ $(top_srcdir)/ocaml-link.sh -- \
+ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \
+ $(OCAMLPACKAGES) \
+ $(daemon_utils_tests_THEOBJECTS) -o $@
+
+check-valgrind:
+ $(MAKE) VG="@VG@" check
+
# OCaml dependencies.
depend: .depend
diff --git a/daemon/daemon_utils_tests.ml b/daemon/daemon_utils_tests.ml
new file mode 100644
index 000000000..892509d89
--- /dev/null
+++ b/daemon/daemon_utils_tests.ml
@@ -0,0 +1,48 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Utils
+
+(* Test prog_exists. *)
+let () + assert (prog_exists "ls");
+ assert (prog_exists "true")
+
+(* Test command, commandr. *)
+let () + ignore (command "true" []);
+
+ let r, _, _ = commandr "false" [] in
+ assert (r = 1)
+
+(* Test split_device_partition. *)
+let () + assert (split_device_partition "/dev/sda1" =
("sda", 1));
+ assert (split_device_partition "/dev/sdb" = ("sdb", 0));
+ assert (split_device_partition "/dev/ubda9" = ("ubda",
9));
+ assert (split_device_partition "/dev/md0p1" = ("md0", 1))
+ (* XXX The function is buggy:
+ assert (split_device_partition "/dev/md0" = ("md0", 0))
*)
+
+(* Test proc_unmangle_path. *)
+let () + assert (proc_unmangle_path "\\040" = " ");
+ assert (proc_unmangle_path "\\040\\040" = " ")
diff --git a/daemon/dummy.c b/daemon/dummy.c
new file mode 100644
index 000000000..ebab6198c
--- /dev/null
+++ b/daemon/dummy.c
@@ -0,0 +1,2 @@
+/* Dummy source, to be used for OCaml-based tools with no C sources. */
+enum { foo = 1 };
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 61cdbea38..e7c457e92 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -93,6 +93,7 @@ daemon/dispatch.c
daemon/dmesg.c
daemon/dropcaches.c
daemon/du.c
+daemon/dummy.c
daemon/echo-daemon.c
daemon/ext2.c
daemon/fallocate.c
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 07/29] daemon: Reimplement ‘is_dir’, ‘is_file’ and ‘is_symlink’ APIs in OCaml.
This also demonstrates usage of optional arguments.
---
daemon/Makefile.am | 2 ++
daemon/is.c | 41 -----------------------------------------
daemon/is.ml | 44 ++++++++++++++++++++++++++++++++++++++++++++
daemon/is.mli | 21 +++++++++++++++++++++
generator/actions_core.ml | 3 +++
generator/daemon.ml | 7 ++++---
6 files changed, 74 insertions(+), 44 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a7cd5d5c6..2fffceffb 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -260,6 +260,7 @@ SOURCES_MLI = \
sysroot.mli \
devsparts.mli \
file.mli \
+ is.mli \
mountable.mli \
utils.mli
@@ -272,6 +273,7 @@ SOURCES_ML = \
blkid.ml \
devsparts.ml \
file.ml \
+ is.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/is.c b/daemon/is.c
index 4d5e911c2..a91dab32b 100644
--- a/daemon/is.c
+++ b/daemon/is.c
@@ -39,36 +39,6 @@ do_exists (const char *path)
/* Takes optional arguments, consult optargs_bitmask. */
int
-do_is_file (const char *path, int followsymlinks)
-{
- mode_t mode;
- int r;
-
- if (!(optargs_bitmask & GUESTFS_IS_FILE_FOLLOWSYMLINKS_BITMASK))
- followsymlinks = 0;
-
- r = get_mode (path, &mode, followsymlinks);
- if (r <= 0) return r;
- return S_ISREG (mode);
-}
-
-/* Takes optional arguments, consult optargs_bitmask. */
-int
-do_is_dir (const char *path, int followsymlinks)
-{
- mode_t mode;
- int r;
-
- if (!(optargs_bitmask & GUESTFS_IS_DIR_FOLLOWSYMLINKS_BITMASK))
- followsymlinks = 0;
-
- r = get_mode (path, &mode, followsymlinks);
- if (r <= 0) return r;
- return S_ISDIR (mode);
-}
-
-/* Takes optional arguments, consult optargs_bitmask. */
-int
do_is_chardev (const char *path, int followsymlinks)
{
mode_t mode;
@@ -112,17 +82,6 @@ do_is_fifo (const char *path, int followsymlinks)
return S_ISFIFO (mode);
}
-int
-do_is_symlink (const char *path)
-{
- mode_t mode;
- int r;
-
- r = get_mode (path, &mode, 0);
- if (r <= 0) return r;
- return S_ISLNK (mode);
-}
-
/* Takes optional arguments, consult optargs_bitmask. */
int
do_is_socket (const char *path, int followsymlinks)
diff --git a/daemon/is.ml b/daemon/is.ml
new file mode 100644
index 000000000..b99215737
--- /dev/null
+++ b/daemon/is.ml
@@ -0,0 +1,44 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+let rec is_file ?(followsymlinks = false) path + let sysroot = Sysroot.sysroot
() in
+ let chroot = Chroot.create sysroot ~name:(sprintf "is_file: %s"
path) in
+ Chroot.f chroot get_kind (path, followsymlinks) = Some S_REG
+
+and is_dir ?(followsymlinks = false) path + let sysroot = Sysroot.sysroot ()
in
+ let chroot = Chroot.create sysroot ~name:(sprintf "is_dir: %s"
path) in
+ Chroot.f chroot get_kind (path, followsymlinks) = Some S_DIR
+
+and is_symlink path + let sysroot = Sysroot.sysroot () in
+ let chroot = Chroot.create sysroot ~name:(sprintf "is_symlink: %s"
path) in
+ Chroot.f chroot get_kind (path, false) = Some S_LNK
+
+and get_kind (path, followsymlinks) + let statfun = if followsymlinks then
stat else lstat in
+ try
+ let statbuf = statfun path in
+ Some statbuf.st_kind
+ with
+ Unix_error ((ENOENT|ENOTDIR), _, _) ->
+ None (* File doesn't exist => return None *)
diff --git a/daemon/is.mli b/daemon/is.mli
new file mode 100644
index 000000000..20622c39f
--- /dev/null
+++ b/daemon/is.mli
@@ -0,0 +1,21 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val is_file : ?followsymlinks:bool -> string -> bool
+val is_dir : ?followsymlinks:bool -> string -> bool
+val is_symlink : string -> bool
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 94391288f..421f3ac6b 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -2114,6 +2114,7 @@ See also C<guestfs_is_file>,
C<guestfs_is_dir>, C<guestfs_stat>." };
{ defaults with
name = "is_file"; added = (0, 0, 8);
style = RBool "fileflag", [String (Pathname, "path")],
[OBool "followsymlinks"];
+ impl = OCaml "Is.is_file";
once_had_no_optargs = true;
tests = [
InitISOFS, Always, TestResultTrue (
@@ -2138,6 +2139,7 @@ See also C<guestfs_stat>." };
{ defaults with
name = "is_dir"; added = (0, 0, 8);
style = RBool "dirflag", [String (Pathname, "path")],
[OBool "followsymlinks"];
+ impl = OCaml "Is.is_dir";
once_had_no_optargs = true;
tests = [
InitISOFS, Always, TestResultFalse (
@@ -6052,6 +6054,7 @@ See also C<guestfs_stat>." };
{ defaults with
name = "is_symlink"; added = (1, 5, 10);
style = RBool "flag", [String (Pathname, "path")], [];
+ impl = OCaml "Is.is_symlink";
tests = [
InitISOFS, Always, TestResultFalse (
[["is_symlink"; "/directory"]]), [];
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 3ffe91537..ef6086bfe 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -577,6 +577,7 @@ return_string_list (value retv)
List.iter (
fun ({ name = name; style = ret, args, optargs } as f) ->
+ let uc_name = String.uppercase_ascii name in
let ocaml_function match f.impl with
| OCaml f -> f
@@ -625,8 +626,8 @@ return_string_list (value retv)
let uc_n = String.uppercase_ascii n in
(* optargs are all passed as [None|Some _] *)
- pr " if ((optargs_bitmask & %s_%s_BITMASK) == 0)\n"
- f.c_optarg_prefix uc_n;
+ pr " if ((optargs_bitmask & GUESTFS_%s_%s_BITMASK) ==
0)\n"
+ uc_name uc_n;
pr " args[%d] = Val_int (0); /* None */\n" !i;
pr " else {\n";
pr " v = ";
@@ -651,7 +652,7 @@ return_string_list (value retv)
| Bool n -> pr "Val_bool (%s)" n
| Int n -> pr "Val_int (%s)" n
| Int64 n -> pr "caml_copy_int64 (%s)" n
- | String ((PlainString|Device|Dev_or_Path), n) ->
+ | String ((PlainString|Device|Pathname|Dev_or_Path), n) ->
pr "caml_copy_string (%s)" n
| String (Mountable, n) ->
pr "copy_mountable (%s)" n
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 08/29] daemon: Reimplement ‘readlink’ API in OCaml.
---
daemon/Makefile.am | 2 ++
daemon/link.c | 16 ----------------
daemon/link.ml | 25 +++++++++++++++++++++++++
daemon/link.mli | 19 +++++++++++++++++++
generator/actions_core.ml | 1 +
5 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 2fffceffb..8171985b4 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -261,6 +261,7 @@ SOURCES_MLI = \
devsparts.mli \
file.mli \
is.mli \
+ link.mli \
mountable.mli \
utils.mli
@@ -274,6 +275,7 @@ SOURCES_ML = \
devsparts.ml \
file.ml \
is.ml \
+ link.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/link.c b/daemon/link.c
index 3ce54fa37..dde61a1c2 100644
--- a/daemon/link.c
+++ b/daemon/link.c
@@ -32,22 +32,6 @@
GUESTFSD_EXT_CMD(str_ln, ln);
-char *
-do_readlink (const char *path)
-{
- char *link;
-
- CHROOT_IN;
- link = areadlink (path);
- CHROOT_OUT;
- if (link == NULL) {
- reply_with_perror ("%s", path);
- return NULL;
- }
-
- return link; /* caller frees */
-}
-
char **
do_internal_readlinklist (const char *path, char *const *names)
{
diff --git a/daemon/link.ml b/daemon/link.ml
new file mode 100644
index 000000000..ba53fd6b5
--- /dev/null
+++ b/daemon/link.ml
@@ -0,0 +1,25 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+let readlink path + let sysroot = Sysroot.sysroot () in
+ let chroot = Chroot.create sysroot ~name:(sprintf "readlink: %s"
path) in
+ Chroot.f chroot readlink path
diff --git a/daemon/link.mli b/daemon/link.mli
new file mode 100644
index 000000000..6ca0283b4
--- /dev/null
+++ b/daemon/link.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val readlink : string -> string
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 421f3ac6b..7d6755fdc 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -4489,6 +4489,7 @@ The I<-f> option removes the link
(C<linkname>) if it exists already." };
{ defaults with
name = "readlink"; added = (1, 0, 66);
style = RString (RPlainString, "link"), [String (Pathname,
"path")], [];
+ impl = OCaml "Link.readlink";
shortdesc = "read the target of a symbolic link";
longdesc = "\
This command reads the target of a symbolic link." };
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 09/29] daemon: Reimplement ‘mount’, ‘mount_ro’, ‘mount_options’, ‘mount_vfs’ APIs in OCaml.
Some of the oldest and most core APIs, reimplemented.
This also moves the strange ‘mount_vfs_nochroot’ function into
btrfs.c.
---
daemon/Makefile.am | 2 +
daemon/btrfs.c | 43 ++++++++++++++++++++
daemon/daemon.h | 6 ---
daemon/mount.c | 99 -----------------------------------------------
daemon/mount.ml | 62 +++++++++++++++++++++++++++++
daemon/mount.mli | 22 +++++++++++
generator/actions_core.ml | 4 ++
generator/daemon.ml | 3 +-
8 files changed, 135 insertions(+), 106 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 8171985b4..d5703b595 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -262,6 +262,7 @@ SOURCES_MLI = \
file.mli \
is.mli \
link.mli \
+ mount.mli \
mountable.mli \
utils.mli
@@ -276,6 +277,7 @@ SOURCES_ML = \
file.ml \
is.ml \
link.ml \
+ mount.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/btrfs.c b/daemon/btrfs.c
index 5f1e5d1d0..4f52b71e8 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -37,6 +37,7 @@ GUESTFSD_EXT_CMD(str_btrfs, btrfs);
GUESTFSD_EXT_CMD(str_btrfstune, btrfstune);
GUESTFSD_EXT_CMD(str_btrfsck, btrfsck);
GUESTFSD_EXT_CMD(str_mkfs_btrfs, mkfs.btrfs);
+GUESTFSD_EXT_CMD(str_mount, mount);
GUESTFSD_EXT_CMD(str_umount, umount);
GUESTFSD_EXT_CMD(str_btrfsimage, btrfs-image);
@@ -387,6 +388,48 @@ do_btrfs_subvolume_create (const char *dest, const char
*qgroupid)
return 0;
}
+static int
+mount_vfs_nochroot (const char *options, const char *vfstype,
+ const mountable_t *mountable,
+ const char *mp, const char *user_mp)
+{
+ CLEANUP_FREE char *options_plus = NULL;
+ const char *device = mountable->device;
+ if (mountable->type == MOUNTABLE_BTRFSVOL) {
+ if (options && strlen (options) > 0) {
+ if (asprintf (&options_plus, "subvol=%s,%s",
+ mountable->volume, options) == -1) {
+ reply_with_perror ("asprintf");
+ return -1;
+ }
+ }
+ else {
+ if (asprintf (&options_plus, "subvol=%s",
mountable->volume) == -1) {
+ reply_with_perror ("asprintf");
+ return -1;
+ }
+ }
+ }
+
+ CLEANUP_FREE char *error = NULL;
+ int r;
+ if (vfstype)
+ r = command (NULL, &error,
+ str_mount, "-o", options_plus ? options_plus :
options,
+ "-t", vfstype, device, mp, NULL);
+ else
+ r = command (NULL, &error,
+ str_mount, "-o", options_plus ? options_plus :
options,
+ device, mp, NULL);
+ if (r == -1) {
+ reply_with_error ("%s on %s (options: '%s'): %s",
+ device, user_mp, options, error);
+ return -1;
+ }
+
+ return 0;
+}
+
static char *
mount (const mountable_t *fs)
{
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 0a92e6cee..62e1211c8 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -94,12 +94,6 @@ extern void cleanup_free_stringsbuf (void *ptr);
#define CLEANUP_FREE_STRINGSBUF
#endif
-/*-- in mount.c --*/
-
-extern int mount_vfs_nochroot (const char *options, const char *vfstype,
- const mountable_t *mountable,
- const char *mp, const char *user_mp);
-
/* Growable strings buffer. */
struct stringsbuf {
char **argv;
diff --git a/daemon/mount.c b/daemon/mount.c
index 0ad9626a7..962b86079 100644
--- a/daemon/mount.c
+++ b/daemon/mount.c
@@ -111,105 +111,6 @@ is_device_mounted (const char *device)
return 0;
}
-/* The "simple mount" call offers no complex options, you can just
- * mount a device on a mountpoint. The variations like mount_ro,
- * mount_options and mount_vfs let you set progressively more things.
- *
- * It's tempting to try a direct mount(2) syscall, but that doesn't
- * do any autodetection, so we are better off calling out to
- * /bin/mount.
- */
-
-int
-do_mount_vfs (const char *options, const char *vfstype,
- const mountable_t *mountable, const char *mountpoint)
-{
- CLEANUP_FREE char *mp = NULL;
- struct stat statbuf;
-
- ABS_PATH (mountpoint, 0, return -1);
-
- mp = sysroot_path (mountpoint);
- if (!mp) {
- reply_with_perror ("malloc");
- return -1;
- }
-
- /* Check the mountpoint exists and is a directory. */
- if (stat (mp, &statbuf) == -1) {
- reply_with_perror ("mount: %s", mountpoint);
- return -1;
- }
- if (!S_ISDIR (statbuf.st_mode)) {
- reply_with_perror ("mount: %s: mount point is not a directory",
mountpoint);
- return -1;
- }
-
- return mount_vfs_nochroot (options, vfstype, mountable, mp, mountpoint);
-}
-
-int
-mount_vfs_nochroot (const char *options, const char *vfstype,
- const mountable_t *mountable,
- const char *mp, const char *user_mp)
-{
- CLEANUP_FREE char *options_plus = NULL;
- const char *device = mountable->device;
- if (mountable->type == MOUNTABLE_BTRFSVOL) {
- if (options && strlen (options) > 0) {
- if (asprintf (&options_plus, "subvol=%s,%s",
- mountable->volume, options) == -1) {
- reply_with_perror ("asprintf");
- return -1;
- }
- }
-
- else {
- if (asprintf (&options_plus, "subvol=%s",
mountable->volume) == -1) {
- reply_with_perror ("asprintf");
- return -1;
- }
- }
- }
-
- CLEANUP_FREE char *error = NULL;
- int r;
- if (vfstype)
- r = command (NULL, &error,
- str_mount, "-o", options_plus ? options_plus :
options,
- "-t", vfstype, device, mp, NULL);
- else
- r = command (NULL, &error,
- str_mount, "-o", options_plus ? options_plus :
options,
- device, mp, NULL);
- if (r == -1) {
- reply_with_error ("%s on %s (options: '%s'): %s",
- device, user_mp, options, error);
- return -1;
- }
-
- return 0;
-}
-
-int
-do_mount (const mountable_t *mountable, const char *mountpoint)
-{
- return do_mount_vfs ("", NULL, mountable, mountpoint);
-}
-
-int
-do_mount_ro (const mountable_t *mountable, const char *mountpoint)
-{
- return do_mount_vfs ("ro", NULL, mountable, mountpoint);
-}
-
-int
-do_mount_options (const char *options, const mountable_t *mountable,
- const char *mountpoint)
-{
- return do_mount_vfs (options, NULL, mountable, mountpoint);
-}
-
/* Takes optional arguments, consult optargs_bitmask. */
int
do_umount (const char *pathordevice,
diff --git a/daemon/mount.ml b/daemon/mount.ml
new file mode 100644
index 000000000..4bb74fb82
--- /dev/null
+++ b/daemon/mount.ml
@@ -0,0 +1,62 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Mountable
+open Utils
+
+let mount_vfs options vfs mountable mountpoint + let mp = Sysroot.sysroot ()
// mountpoint in
+
+ (* Check the mountpoint exists and is a directory. *)
+ if not (is_directory mp) then
+ failwithf "mount: %s: mount point is not a directory" mountpoint;
+
+ let args = ref [] in
+
+ (* -o options *)
+ (match options, mountable.m_type with
+ | (None | Some ""), (MountableDevice | MountablePath) -> ()
+ | Some options, (MountableDevice | MountablePath) ->
+ push_back args "-o";
+ push_back args options
+ | (None | Some ""), MountableBtrfsVol subvol ->
+ push_back args "-o";
+ push_back args ("subvol=" ^ subvol)
+ | Some options, MountableBtrfsVol subvol ->
+ push_back args "-o";
+ push_back args ("subvol=" ^ subvol ^ "," ^ options)
+ );
+
+ (* -t vfs *)
+ (match vfs with
+ | None | Some "" -> ()
+ | Some t ->
+ push_back args "-t";
+ push_back args t
+ );
+
+ push_back args mountable.m_device;
+ push_back args mp;
+
+ ignore (command "mount" !args)
+
+let mount = mount_vfs None None
+let mount_ro = mount_vfs (Some "ro") None
+let mount_options options = mount_vfs (Some options) None
diff --git a/daemon/mount.mli b/daemon/mount.mli
new file mode 100644
index 000000000..e43d97c42
--- /dev/null
+++ b/daemon/mount.mli
@@ -0,0 +1,22 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val mount : Mountable.t -> string -> unit
+val mount_ro : Mountable.t -> string -> unit
+val mount_options : string -> Mountable.t -> string -> unit
+val mount_vfs : string option -> string option -> Mountable.t ->
string -> unit
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 7d6755fdc..f33bc5320 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -1739,6 +1739,7 @@ let daemon_functions = [
{ defaults with
name = "mount"; added = (0, 0, 3);
style = RErr, [String (Mountable, "mountable"); String
(PlainString, "mountpoint")], [];
+ impl = OCaml "Mount.mount";
tests = [
InitEmpty, Always, TestResultString (
[["part_disk"; "/dev/sda"; "mbr"];
@@ -2922,6 +2923,7 @@ If set to true, POSIX ACLs are saved in the output tar.
{ defaults with
name = "mount_ro"; added = (1, 0, 10);
style = RErr, [String (Mountable, "mountable"); String
(PlainString, "mountpoint")], [];
+ impl = OCaml "Mount.mount_ro";
tests = [
InitBasicFS, Always, TestLastFail (
[["umount"; "/"; "false";
"false"];
@@ -2941,6 +2943,7 @@ mounts the filesystem with the read-only (I<-o ro>)
flag." };
{ defaults with
name = "mount_options"; added = (1, 0, 10);
style = RErr, [String (PlainString, "options"); String
(Mountable, "mountable"); String (PlainString,
"mountpoint")], [];
+ impl = OCaml "Mount.mount_options";
shortdesc = "mount a guest disk with mount options";
longdesc = "\
This is the same as the C<guestfs_mount> command, but it
@@ -2954,6 +2957,7 @@ the filesystem uses)." };
{ defaults with
name = "mount_vfs"; added = (1, 0, 10);
style = RErr, [String (PlainString, "options"); String
(PlainString, "vfstype"); String (Mountable, "mountable");
String (PlainString, "mountpoint")], [];
+ impl = OCaml "Mount.mount_vfs";
shortdesc = "mount a guest disk with mount options and vfstype";
longdesc = "\
This is the same as the C<guestfs_mount> command, but it
diff --git a/generator/daemon.ml b/generator/daemon.ml
index ef6086bfe..fd01e5d8a 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -689,7 +689,8 @@ return_string_list (value retv)
pr "\n";
(match ret with
- | RErr -> assert false
+ | RErr ->
+ pr " CAMLreturnT (int, 0);\n"
| RInt _ ->
pr " CAMLreturnT (int, Int_val (retv));\n"
| RInt64 _ -> assert false
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 10/29] daemon: Reimplement ‘part_get_mbr_id’ API in OCaml.
---
daemon/Makefile.am | 2 ++
daemon/parted.c | 42 ------------------------------------
daemon/parted.ml | 55 +++++++++++++++++++++++++++++++++++++++++++++++
daemon/parted.mli | 19 ++++++++++++++++
generator/actions_core.ml | 1 +
5 files changed, 77 insertions(+), 42 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index d5703b595..1035d7ea2 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -264,6 +264,7 @@ SOURCES_MLI = \
link.mli \
mount.mli \
mountable.mli \
+ parted.mli \
utils.mli
SOURCES_ML = \
@@ -278,6 +279,7 @@ SOURCES_ML = \
is.ml \
link.ml \
mount.ml \
+ parted.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/parted.c b/daemon/parted.c
index 03e83cb32..a1e5c81cf 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -521,48 +521,6 @@ test_sfdisk_has_part_type (void)
return tested;
}
-/* Currently we use sfdisk for getting and setting the ID byte. In
- * future, extend parted to provide this functionality. As a result
- * of using sfdisk, this won't work for non-MBR-style partitions, but
- * that limitation is noted in the documentation and we can extend it
- * later without breaking the ABI.
- */
-int
-do_part_get_mbr_id (const char *device, int partnum)
-{
- if (partnum <= 0) {
- reply_with_error ("partition number must be >= 1");
- return -1;
- }
-
- const char *param = test_sfdisk_has_part_type () ? "--part-type" :
"--print-id";
-
- char partnum_str[16];
- snprintf (partnum_str, sizeof partnum_str, "%d", partnum);
-
- CLEANUP_FREE char *out = NULL, *err = NULL;
- int r;
-
- udev_settle ();
-
- r = command (&out, &err, str_sfdisk, param, device, partnum_str,
NULL);
- if (r == -1) {
- reply_with_error ("sfdisk %s: %s", param, err);
- return -1;
- }
-
- udev_settle ();
-
- /* It's printed in hex ... */
- unsigned id;
- if (sscanf (out, "%x", &id) != 1) {
- reply_with_error ("sfdisk --print-id: cannot parse output: %s",
out);
- return -1;
- }
-
- return id;
-}
-
int
do_part_set_mbr_id (const char *device, int partnum, int idbyte)
{
diff --git a/daemon/parted.ml b/daemon/parted.ml
new file mode 100644
index 000000000..6be41cf66
--- /dev/null
+++ b/daemon/parted.ml
@@ -0,0 +1,55 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+
+(* Test if [sfdisk] is recent enough to have [--part-type], to be used
+ * instead of [--print-id] and [--change-id].
+ *)
+let test_sfdisk_has_part_type = lazy (
+ let out = command "sfdisk" ["--help"] in
+ String.find out "--part-type" >= 0
+)
+
+(* Currently we use sfdisk for getting and setting the ID byte. In
+ * future, extend parted to provide this functionality. As a result
+ * of using sfdisk, this won't work for non-MBR-style partitions, but
+ * that limitation is noted in the documentation and we can extend it
+ * later without breaking the ABI.
+ *)
+let part_get_mbr_id device partnum + if partnum <= 0 then
+ failwith "partition number must be >= 1";
+
+ let param + if Lazy.force test_sfdisk_has_part_type then
+ "--part-type"
+ else
+ "--print-id" in
+
+ udev_settle ();
+ let out + command "sfdisk" [param; device; string_of_int
partnum] in
+ udev_settle ();
+
+ (* It's printed in hex, possibly with a leading space. *)
+ sscanf out " %x" identity
diff --git a/daemon/parted.mli b/daemon/parted.mli
new file mode 100644
index 000000000..33eb6d30d
--- /dev/null
+++ b/daemon/parted.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val part_get_mbr_id : string -> int -> int
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index f33bc5320..4bf0c7b70 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5513,6 +5513,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";
fish_output = Some FishOutputHexadecimal;
tests = [
InitEmpty, Always, TestResult (
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 11/29] daemon: Reimplement ‘case_sensitive_path’ API in OCaml.
---
daemon/Makefile.am | 2 +
daemon/realpath.c | 187 ----------------------------------------------
daemon/realpath.ml | 83 ++++++++++++++++++++
daemon/realpath.mli | 19 +++++
generator/actions_core.ml | 1 +
5 files changed, 105 insertions(+), 187 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 1035d7ea2..d56c99123 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -265,6 +265,7 @@ SOURCES_MLI = \
mount.mli \
mountable.mli \
parted.mli \
+ realpath.mli \
utils.mli
SOURCES_ML = \
@@ -280,6 +281,7 @@ SOURCES_ML = \
link.ml \
mount.ml \
parted.ml \
+ realpath.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/realpath.c b/daemon/realpath.c
index 24ab133e2..f9d22d28d 100644
--- a/daemon/realpath.c
+++ b/daemon/realpath.c
@@ -48,190 +48,3 @@ do_realpath (const char *path)
return ret; /* caller frees */
}
-
-static int find_path_element (int fd_cwd, int is_end, const char *name, char
**name_ret);
-
-char *
-do_case_sensitive_path (const char *path)
-{
- size_t next;
- int fd_cwd, fd2, err, is_end;
- char *ret;
-
- ret = strdup ("/");
- if (ret == NULL) {
- reply_with_perror ("strdup");
- return NULL;
- }
- next = 1; /* next position in 'ret' buffer */
-
- /* 'fd_cwd' here is a surrogate for the current working directory, so
- * that we don't have to actually call chdir(2).
- */
- fd_cwd = open (sysroot, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
- if (fd_cwd == -1) {
- reply_with_perror ("%s", sysroot);
- goto error;
- }
-
- /* First character is a '/'. Take each subsequent path element
- * and follow it.
- */
- while (*path) {
- char *t;
- size_t i, len;
- CLEANUP_FREE char *name_in = NULL, *name_out = NULL;
-
- i = strcspn (path, "/");
- if (i == 0) {
- path++;
- continue;
- }
-
- if ((i == 1 && path[0] == '.') ||
- (i == 2 && path[0] == '.' && path[1] ==
'.')) {
- reply_with_error ("path contained . or .. elements");
- goto error;
- }
-
- name_in = strndup (path, i);
- if (name_in == NULL) {
- reply_with_perror ("strdup");
- goto error;
- }
-
- /* Skip to next element in path (for the next loop iteration). */
- path += i;
- is_end = *path == 0;
-
- /* Read the current directory looking (case insensitively) for
- * this element of the path. This replaces 'name' with the
- * correct case version.
- */
- if (find_path_element (fd_cwd, is_end, name_in, &name_out) == -1)
- goto error;
- len = strlen (name_out);
-
- /* Add the real name of this path element to the return value. */
- if (next > 1)
- ret[next++] = '/';
-
- t = realloc (ret, next+len+1);
- if (t == NULL) {
- reply_with_perror ("realloc");
- goto error;
- }
- ret = t;
-
- strcpy (&ret[next], name_out);
- next += len;
-
- /* Is it a directory? Try going into it. */
- fd2 = openat (fd_cwd, name_out, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
- err = errno;
- close (fd_cwd);
- fd_cwd = fd2;
- errno = err;
- if (fd_cwd == -1) {
- /* Some errors are OK provided we've reached the end of the path. */
- if (is_end && (errno == ENOTDIR || errno == ENOENT))
- break;
-
- reply_with_perror ("openat: %s", name_out);
- goto error;
- }
- }
-
- if (fd_cwd >= 0)
- close (fd_cwd);
-
- return ret; /* caller frees */
-
- error:
- if (fd_cwd >= 0)
- close (fd_cwd);
- free (ret);
-
- return NULL;
-}
-
-/* 'fd_cwd' is a file descriptor pointing to an open directory.
- * 'name' is the path element to search for. 'is_end' is a
flag
- * indicating if this is the last path element.
- *
- * We search the directory looking for a path element that case
- * insensitively matches 'name', returning the actual name in
'*name_ret'.
- *
- * If this is successful, return 0. If it fails, reply with an error
- * and return -1.
- */
-static int
-find_path_element (int fd_cwd, int is_end, const char *name, char **name_ret)
-{
- int fd2;
- DIR *dir;
- struct dirent *d;
-
- fd2 = dup_cloexec (fd_cwd); /* because closedir will close it */
- if (fd2 == -1) {
- reply_with_perror ("dup");
- return -1;
- }
- dir = fdopendir (fd2);
- if (dir == NULL) {
- reply_with_perror ("opendir");
- close (fd2);
- return -1;
- }
-
- for (;;) {
- errno = 0;
- d = readdir (dir);
- if (d == NULL)
- break;
- if (STRCASEEQ (d->d_name, name))
- break;
- }
-
- if (d == NULL && errno != 0) {
- reply_with_perror ("readdir");
- closedir (dir);
- return -1;
- }
-
- if (d == NULL && is_end) {
- /* Last path element: return it as-is, assuming that the user will
- * create a new file or directory (RHBZ#840115).
- */
- closedir (dir);
- *name_ret = strdup (name);
- if (*name_ret == NULL) {
- reply_with_perror ("strdup");
- return -1;
- }
- return 0;
- }
-
- if (d == NULL) {
- reply_with_error ("%s: no file or directory found with this
name", name);
- closedir (dir);
- return -1;
- }
-
- *name_ret = strdup (d->d_name);
- if (*name_ret == NULL) {
- reply_with_perror ("strdup");
- closedir (dir);
- return -1;
- }
-
- /* NB: closedir frees the structure associated with 'd', so we must
- * do this last.
- */
- if (closedir (dir) == -1) {
- reply_with_perror ("closedir");
- return -1;
- }
-
- return 0;
-}
diff --git a/daemon/realpath.ml b/daemon/realpath.ml
new file mode 100644
index 000000000..cffe86322
--- /dev/null
+++ b/daemon/realpath.ml
@@ -0,0 +1,83 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+(* The infamous case_sensitive_path function, which works around
+ * the bug in ntfs-3g that all paths are case sensitive even though
+ * the underlying filesystem is case insensitive.
+ *)
+let rec case_sensitive_path path + let elems = String.nsplit "/"
path in
+
+ (* The caller ensures that the first element of [path] is [/],
+ * and therefore the first element of the split list must be
+ * empty.
+ *)
+ assert (List.length elems > 0);
+ assert (List.hd elems = "");
+ let elems = List.tl elems in
+
+ let sysroot = Sysroot.sysroot () in
+ let chroot = Chroot.create sysroot
+ ~name:(sprintf "case_sensitive_path: %s"
path) in
+
+ (* Now we iterate down the tree starting at the sysroot. *)
+ let elems + Chroot.f chroot (
+ fun () ->
+ let rec loop = function
+ | [] -> []
+ | [ "."|".." ] ->
+ failwithf "path contains \".\" or \"..\"
elements"
+ | "" :: elems ->
+ (* For compatibility with C implementation, we ignore
+ * "//" in the middle of the path.
+ *)
+ loop elems
+ | [ file ] ->
+ (* If it's the final element, it's allowed to be missing.
*)
+ (match find_path_element file with
+ | None -> [ file ] (* return the original *)
+ | Some file -> [ file ]
+ );
+ | elem :: elems ->
+ (match find_path_element elem with
+ | None ->
+ failwithf "%s: not found" elem
+ | Some elem ->
+ (* This will fail intentionally if not a directory. *)
+ Unix.chdir elem;
+ elem :: loop elems
+ )
+ in
+ loop elems
+ ) () in
+
+ (* Reconstruct the case sensitive path. *)
+ "/" ^ String.concat "/" elems
+
+and find_path_element name + let dir = Sys.readdir "." in
+ let dir = Array.to_list dir in
+ let lc_name = String.lowercase_ascii name in
+ let cmp n = String.lowercase_ascii n = lc_name in
+ try Some (List.find cmp dir)
+ with Not_found -> None
diff --git a/daemon/realpath.mli b/daemon/realpath.mli
new file mode 100644
index 000000000..371e619fc
--- /dev/null
+++ b/daemon/realpath.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val case_sensitive_path : string -> string
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 4bf0c7b70..54d0a6ca8 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -4797,6 +4797,7 @@ The result list is not sorted.
{ defaults with
name = "case_sensitive_path"; added = (1, 0, 75);
style = RString (RPlainString, "rpath"), [String (Pathname,
"path")], [];
+ impl = OCaml "Realpath.case_sensitive_path";
tests = [
InitISOFS, Always, TestResultString (
[["case_sensitive_path"; "/DIRECTORY"]],
"/directory"), [];
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 12/29] daemon: Reimplement ‘file_architecture’ API in OCaml.
The previously library-side ‘file_architecture’ API is reimplemented
in the daemon, in OCaml.
There are some significant differences compared to the C
implementation:
- The C code used libmagic. That is replaced by calling the ‘file’
command (because that is simpler than using the library).
- The C code had extra cases to deal with compressed files. This is
not necessary since the ‘file’ command supports the ‘-z’ option
which transparently looks inside compressed content (this is a
consequence of the change above).
This commit demonstrates a number of techniques which will be useful
for moving inspection code to the daemon:
- Moving an API from the C library to the OCaml daemon.
- Calling from one OCaml API inside the daemon to another (from
‘Filearch.file_architecture’ to ‘File.file’). This can be done and
is done with C daemon APIs but correct reply_with_error handling is
more difficult in C.
- Use of Str for regular expression matching within the appliance.
---
daemon/Makefile.am | 2 +
daemon/filearch.ml | 137 +++++++++++++++++
daemon/filearch.mli | 19 +++
docs/C_SOURCE_FILES | 4 +-
generator/actions_core.ml | 377 +++++++++++++++++++++++-----------------------
generator/proc_nr.ml | 1 +
lib/MAX_PROC_NR | 2 +-
lib/Makefile.am | 3 +-
lib/filearch.c | 362 --------------------------------------------
po/POTFILES | 1 -
10 files changed, 353 insertions(+), 555 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index d56c99123..e86435c4c 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -260,6 +260,7 @@ SOURCES_MLI = \
sysroot.mli \
devsparts.mli \
file.mli \
+ filearch.mli \
is.mli \
link.mli \
mount.mli \
@@ -277,6 +278,7 @@ SOURCES_ML = \
blkid.ml \
devsparts.ml \
file.ml \
+ filearch.ml \
is.ml \
link.ml \
mount.ml \
diff --git a/daemon/filearch.ml b/daemon/filearch.ml
new file mode 100644
index 000000000..68ddd61ea
--- /dev/null
+++ b/daemon/filearch.ml
@@ -0,0 +1,137 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Std_utils
+
+open Utils
+
+let re_file_elf + Str.regexp "ELF \\([0-9]+\\)-bit
\\(MSB\\|LSB\\).*\\(executable\\|shared object\\|relocatable\\),
\\([^,]+\\),"
+
+let re_file_elf_ppc64 = Str.regexp ".*64.*PowerPC"
+
+let initrd_binaries = [
+ "bin/ls";
+ "bin/rm";
+ "bin/modprobe";
+ "sbin/modprobe";
+ "bin/sh";
+ "bin/bash";
+ "bin/dash";
+ "bin/nash";
+]
+
+let rec file_architecture orig_path + (* Get the output of the
"file" command. Note that because this
+ * is running in the daemon, LANG=C so it's in English.
+ *)
+ let magic = File.file orig_path in
+ file_architecture_of_magic magic orig_path orig_path
+
+and file_architecture_of_magic magic orig_path path + if Str.string_match
re_file_elf magic 0 then (
+ let bits = Str.matched_group 1 magic in
+ let endianness = Str.matched_group 2 magic in
+ let elf_arch = Str.matched_group 4 magic in
+ canonical_elf_arch bits endianness elf_arch
+ )
+ else if String.find magic "PE32 executable" >= 0 then
+ "i386"
+ else if String.find magic "PE32+ executable" >= 0 then
+ "x86_64"
+ else if String.find magic "cpio archive" >= 0 then
+ cpio_arch magic orig_path path
+ else
+ failwithf "unknown architecture: %s" path
+
+(* Convert output from 'file' command on ELF files to the canonical
+ * architecture string. Caller must free the result.
+ *)
+and canonical_elf_arch bits endianness elf_arch + let substr s = String.find
elf_arch s >= 0 in
+ if substr "Intel 80386" || substr "Intel 80486" then
+ "i386"
+ else if substr "x86-64" || substr "AMD x86-64" then
+ "x86_64"
+ else if substr "SPARC32" then
+ "sparc"
+ else if substr "SPARC V9" then
+ "sparc64"
+ else if substr "IA-64" then
+ "ia64"
+ else if Str.string_match re_file_elf_ppc64 elf_arch 0 then (
+ match endianness with
+ | "MSB" -> "ppc64"
+ | "LSB" -> "ppc64le"
+ | _ -> failwithf "unknown endianness '%s'" endianness
+ )
+ else if substr "PowerPC" then
+ "ppc"
+ else if substr "ARM aarch64" then
+ "aarch64"
+ else if substr "ARM" then
+ "arm"
+ else if substr "UCB RISC-V" then
+ sprintf "riscv%s" bits
+ else if substr "IBM S/390" then (
+ match bits with
+ | "32" -> "s390"
+ | "64" -> "s390x"
+ | _ -> failwithf "unknown S/390 bit size: %s" bits
+ )
+ else
+ elf_arch
+
+and cpio_arch magic orig_path path + let sysroot = Sysroot.sysroot () in
+
+ let zcat + if String.find magic "gzip" >= 0 then
"zcat"
+ else if String.find magic "bzip2" >= 0 then "bzcat"
+ else if String.find magic "XZ compressed" >= 0 then
"xzcat"
+ else "cat" in
+
+ let tmpdir = sprintf "/tmp/%s" (String.random8 ()) in
+ mkdir tmpdir 0o700;
+
+ (* Construct a command to extract named binaries from the initrd file. *)
+ let cmd + sprintf "cd %s && %s %s | cpio --quiet -id %s"
+ tmpdir zcat (quote (sysroot // path))
+ (String.concat " " (List.map quote initrd_binaries)) in
+ if verbose () then eprintf "%s\n%!" cmd;
+ if Sys.command cmd <> 0 then
+ failwith "cpio command failed";
+
+ (* See if any of the binaries were present in the output. *)
+ let rec loop = function
+ | bin :: bins ->
+ let bin_path = tmpdir // bin in
+ if is_regular_file bin_path then (
+ let out = command "file" ["-zb"; bin_path] in
+ file_architecture_of_magic out orig_path bin_path
+ )
+ else
+ loop bins
+ | [] ->
+ failwithf "could not determine architecture of cpio archive:
%s" path
+ in
+ loop initrd_binaries
diff --git a/daemon/filearch.mli b/daemon/filearch.mli
new file mode 100644
index 000000000..c4630225b
--- /dev/null
+++ b/daemon/filearch.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val file_architecture : string -> string
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index e7c457e92..9562aed89 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -71,6 +71,7 @@ daemon/blkdiscard.c
daemon/blkid.c
daemon/blockdev.c
daemon/btrfs.c
+daemon/caml-stubs.c
daemon/cap.c
daemon/checksum.c
daemon/cleanups.c
@@ -81,6 +82,7 @@ daemon/compress.c
daemon/copy.c
daemon/cpio.c
daemon/cpmv.c
+daemon/daemon-c.c
daemon/daemon.h
daemon/dd.c
daemon/debug-bmap.c
@@ -172,6 +174,7 @@ daemon/stubs.h
daemon/swap.c
daemon/sync.c
daemon/syslinux.c
+daemon/sysroot-c.c
daemon/tar.c
daemon/truncate.c
daemon/tsk.c
@@ -295,7 +298,6 @@ lib/errors.c
lib/event-string.c
lib/events.c
lib/file.c
-lib/filearch.c
lib/fuse.c
lib/guestfs-internal-actions.h
lib/guestfs-internal-all.h
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 54d0a6ca8..bfd96589e 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -183,194 +183,6 @@ making this an unreliable way to test for features.
Use C<guestfs_available> or C<guestfs_feature_available>
instead." };
{ defaults with
- name = "file_architecture"; added = (1, 5, 3);
- style = RString (RPlainString, "arch"), [String (Pathname,
"filename")], [];
- tests = [
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-aarch64-dynamic"]],
"aarch64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-armv7-dynamic"]],
"arm"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-i586-dynamic"]],
"i386"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-ppc64-dynamic"]],
"ppc64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-ppc64le-dynamic"]],
"ppc64le"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-riscv64-dynamic"]],
"riscv64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-s390x-dynamic"]],
"s390x"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-sparc-dynamic"]],
"sparc"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-win32.exe"]],
"i386"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-win64.exe"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-x86_64-dynamic"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-aarch64.so"]],
"aarch64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-armv7.so"]],
"arm"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-i586.so"]],
"i386"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-ppc64.so"]],
"ppc64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-ppc64le.so"]],
"ppc64le"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-riscv64.so"]],
"riscv64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-s390x.so"]],
"s390x"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-sparc.so"]],
"sparc"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-win32.dll"]],
"i386"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-win64.dll"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-x86_64.so"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/initrd-x86_64.img"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/initrd-x86_64.img.gz"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/bin-x86_64-dynamic.gz"]],
"x86_64"), [];
- InitISOFS, Always, TestResultString (
- [["file_architecture"; "/lib-i586.so.xz"]],
"i386"), [];
- ];
- shortdesc = "detect the architecture of a binary file";
- longdesc = "\
-This detects the architecture of the binary F<filename>,
-and returns it if known.
-
-Currently defined architectures are:
-
-=over 4
-
-=item \"aarch64\"
-
-64 bit ARM.
-
-=item \"arm\"
-
-32 bit ARM.
-
-=item \"i386\"
-
-This string is returned for all 32 bit i386, i486, i586, i686 binaries
-irrespective of the precise processor requirements of the binary.
-
-=item \"ia64\"
-
-Intel Itanium.
-
-=item \"ppc\"
-
-32 bit Power PC.
-
-=item \"ppc64\"
-
-64 bit Power PC (big endian).
-
-=item \"ppc64le\"
-
-64 bit Power PC (little endian).
-
-=item \"riscv32\"
-
-=item \"riscv64\"
-
-=item \"riscv128\"
-
-RISC-V 32-, 64- or 128-bit variants.
-
-=item \"s390\"
-
-31 bit IBM S/390.
-
-=item \"s390x\"
-
-64 bit IBM S/390.
-
-=item \"sparc\"
-
-32 bit SPARC.
-
-=item \"sparc64\"
-
-64 bit SPARC V9 and above.
-
-=item \"x86_64\"
-
-64 bit x86-64.
-
-=back
-
-Libguestfs may return other architecture strings in future.
-
-The function works on at least the following types of files:
-
-=over 4
-
-=item *
-
-many types of Un*x and Linux binary
-
-=item *
-
-many types of Un*x and Linux shared library
-
-=item *
-
-Windows Win32 and Win64 binaries
-
-=item *
-
-Windows Win32 and Win64 DLLs
-
-Win32 binaries and DLLs return C<i386>.
-
-Win64 binaries and DLLs return C<x86_64>.
-
-=item *
-
-Linux kernel modules
-
-=item *
-
-Linux new-style initrd images
-
-=item *
-
-some non-x86 Linux vmlinuz kernels
-
-=back
-
-What it can't do currently:
-
-=over 4
-
-=item *
-
-static libraries (libfoo.a)
-
-=item *
-
-Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
-
-=item *
-
-x86 Linux vmlinuz kernels
-
-x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
-compressed code, and are horribly hard to unpack. If you want to find
-the architecture of a kernel, use the architecture of the associated
-initrd or kernel module(s) instead.
-
-=back" };
-
- { defaults with
name = "mountable_device"; added = (1, 33, 15);
style = RString (RDevice, "device"), [String (Mountable,
"mountable")], [];
shortdesc = "extract the device part of a mountable";
@@ -9628,4 +9440,193 @@ wildcards.
Please note that this API may fail when used to compress directories
with large files, such as the resulting squashfs will be over 3GB big." };
+ { defaults with
+ name = "file_architecture"; added = (1, 5, 3);
+ style = RString (RPlainString, "arch"), [String (Pathname,
"filename")], [];
+ impl = OCaml "Filearch.file_architecture";
+ tests = [
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-aarch64-dynamic"]],
"aarch64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-armv7-dynamic"]],
"arm"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-i586-dynamic"]],
"i386"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-ppc64-dynamic"]],
"ppc64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-ppc64le-dynamic"]],
"ppc64le"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-riscv64-dynamic"]],
"riscv64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-s390x-dynamic"]],
"s390x"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-sparc-dynamic"]],
"sparc"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-win32.exe"]],
"i386"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-win64.exe"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-x86_64-dynamic"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-aarch64.so"]],
"aarch64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-armv7.so"]],
"arm"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-i586.so"]],
"i386"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-ppc64.so"]],
"ppc64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-ppc64le.so"]],
"ppc64le"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-riscv64.so"]],
"riscv64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-s390x.so"]],
"s390x"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-sparc.so"]],
"sparc"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-win32.dll"]],
"i386"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-win64.dll"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-x86_64.so"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/initrd-x86_64.img"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/initrd-x86_64.img.gz"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/bin-x86_64-dynamic.gz"]],
"x86_64"), [];
+ InitISOFS, Always, TestResultString (
+ [["file_architecture"; "/lib-i586.so.xz"]],
"i386"), [];
+ ];
+ shortdesc = "detect the architecture of a binary file";
+ longdesc = "\
+This detects the architecture of the binary F<filename>,
+and returns it if known.
+
+Currently defined architectures are:
+
+=over 4
+
+=item \"aarch64\"
+
+64 bit ARM.
+
+=item \"arm\"
+
+32 bit ARM.
+
+=item \"i386\"
+
+This string is returned for all 32 bit i386, i486, i586, i686 binaries
+irrespective of the precise processor requirements of the binary.
+
+=item \"ia64\"
+
+Intel Itanium.
+
+=item \"ppc\"
+
+32 bit Power PC.
+
+=item \"ppc64\"
+
+64 bit Power PC (big endian).
+
+=item \"ppc64le\"
+
+64 bit Power PC (little endian).
+
+=item \"riscv32\"
+
+=item \"riscv64\"
+
+=item \"riscv128\"
+
+RISC-V 32-, 64- or 128-bit variants.
+
+=item \"s390\"
+
+31 bit IBM S/390.
+
+=item \"s390x\"
+
+64 bit IBM S/390.
+
+=item \"sparc\"
+
+32 bit SPARC.
+
+=item \"sparc64\"
+
+64 bit SPARC V9 and above.
+
+=item \"x86_64\"
+
+64 bit x86-64.
+
+=back
+
+Libguestfs may return other architecture strings in future.
+
+The function works on at least the following types of files:
+
+=over 4
+
+=item *
+
+many types of Un*x and Linux binary
+
+=item *
+
+many types of Un*x and Linux shared library
+
+=item *
+
+Windows Win32 and Win64 binaries
+
+=item *
+
+Windows Win32 and Win64 DLLs
+
+Win32 binaries and DLLs return C<i386>.
+
+Win64 binaries and DLLs return C<x86_64>.
+
+=item *
+
+Linux kernel modules
+
+=item *
+
+Linux new-style initrd images
+
+=item *
+
+some non-x86 Linux vmlinuz kernels
+
+=back
+
+What it can't do currently:
+
+=over 4
+
+=item *
+
+static libraries (libfoo.a)
+
+=item *
+
+Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
+
+=item *
+
+x86 Linux vmlinuz kernels
+
+x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
+compressed code, and are horribly hard to unpack. If you want to find
+the architecture of a kernel, use the architecture of the associated
+initrd or kernel module(s) instead.
+
+=back" };
+
]
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index c7619638a..1b0feae87 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -482,6 +482,7 @@ let proc_nr = [
472, "yara_load";
473, "yara_destroy";
474, "internal_yara_scan";
+475, "file_architecture";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 5f3bb9813..7573eff88 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-474
+475
diff --git a/lib/Makefile.am b/lib/Makefile.am
index bf3406b16..71dc25b9b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -89,7 +89,6 @@ libguestfs_la_SOURCES = \
event-string.c \
events.c \
file.c \
- filearch.c \
fuse.c \
guid.c \
handle.c \
@@ -155,7 +154,7 @@ libguestfs_la_LIBADD = \
../common/qemuopts/libqemuopts.la \
../common/structs/libstructs.la \
../common/utils/libutils.la \
- $(PCRE_LIBS) $(MAGIC_LIBS) \
+ $(PCRE_LIBS) \
$(LIBVIRT_LIBS) $(LIBXML2_LIBS) \
$(SELINUX_LIBS) \
$(YAJL_LIBS) \
diff --git a/lib/filearch.c b/lib/filearch.c
deleted file mode 100644
index e1d3daeef..000000000
--- a/lib/filearch.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <libintl.h>
-
-#include <magic.h>
-
-#include "ignore-value.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-#include "guestfs-internal-actions.h"
-
-# ifdef HAVE_ATTRIBUTE_CLEANUP
-# define CLEANUP_MAGIC_T_FREE __attribute__((cleanup(cleanup_magic_t_free)))
-
-static void
-cleanup_magic_t_free (void *ptr)
-{
- magic_t m = *(magic_t *) ptr;
-
- if (m)
- magic_close (m);
-}
-
-# else
-# define CLEANUP_MAGIC_T_FREE
-# endif
-
-COMPILE_REGEXP (re_file_elf,
- "ELF (\\d+)-bit (MSB|LSB).*(?:executable|shared
object|relocatable), (.+?),", 0)
-COMPILE_REGEXP (re_elf_ppc64, ".*64.*PowerPC", 0)
-
-/* Convert output from 'file' command on ELF files to the canonical
- * architecture string. Caller must free the result.
- */
-static char *
-canonical_elf_arch (guestfs_h *g,
- const char *bits, const char *endianness,
- const char *elf_arch)
-{
- const char *r;
- char *ret;
-
- if (strstr (elf_arch, "Intel 80386") ||
- strstr (elf_arch, "Intel 80486"))
- r = "i386";
- else if (strstr (elf_arch, "x86-64") ||
- strstr (elf_arch, "AMD x86-64"))
- r = "x86_64";
- else if (strstr (elf_arch, "SPARC32"))
- r = "sparc";
- else if (strstr (elf_arch, "SPARC V9"))
- r = "sparc64";
- else if (strstr (elf_arch, "IA-64"))
- r = "ia64";
- else if (match (g, elf_arch, re_elf_ppc64)) {
- if (strstr (endianness, "MSB"))
- r = "ppc64";
- else if (strstr (endianness, "LSB"))
- r = "ppc64le";
- else {
- error (g, "file_architecture: unknown endianness '%s'",
endianness);
- return NULL;
- }
- }
- else if (strstr (elf_arch, "PowerPC"))
- r = "ppc";
- else if (strstr (elf_arch, "ARM aarch64"))
- r = "aarch64";
- else if (strstr (elf_arch, "ARM"))
- r = "arm";
- else if (strstr (elf_arch, "UCB RISC-V")) {
- ret = safe_asprintf (g, "riscv%s", bits);
- goto no_strdup;
- }
- else if (strstr (elf_arch, "IBM S/390")) {
- if (STREQ (bits, "32"))
- r = "s390";
- else if (STREQ (bits, "64"))
- r = "s390x";
- else {
- error (g, "file_architecture: unknown S/390 bit size: %s",
bits);
- return NULL;
- }
- }
- else
- r = elf_arch;
-
- ret = safe_strdup (g, r);
- no_strdup:
- return ret;
-}
-
-static int
-is_regular_file (const char *filename)
-{
- struct stat statbuf;
-
- return lstat (filename, &statbuf) == 0 && S_ISREG
(statbuf.st_mode);
-}
-
-static char *
-magic_for_file (guestfs_h *g, const char *filename, bool *loading_ok,
- bool *matched)
-{
- int flags;
- CLEANUP_MAGIC_T_FREE magic_t m = NULL;
- const char *line;
- CLEANUP_FREE char *bits = NULL;
- CLEANUP_FREE char *elf_arch = NULL;
- CLEANUP_FREE char *endianness = NULL;
-
- flags = g->verbose ? MAGIC_DEBUG : 0;
- flags |= MAGIC_ERROR | MAGIC_RAW;
-
- if (loading_ok)
- *loading_ok = false;
- if (matched)
- *matched = false;
-
- m = magic_open (flags);
- if (m == NULL) {
- perrorf (g, "magic_open");
- return NULL;
- }
-
- if (magic_load (m, NULL) == -1) {
- perrorf (g, "magic_load: default magic database file");
- return NULL;
- }
-
- line = magic_file (m, filename);
- if (line == NULL) {
- perrorf (g, "magic_file: %s", filename);
- return NULL;
- }
-
- if (loading_ok)
- *loading_ok = true;
-
- if (!match3 (g, line, re_file_elf, &bits, &endianness,
&elf_arch)) {
- error (g, "no re_file_elf match in '%s'", line);
- return NULL;
- }
-
- if (matched)
- *matched = true;
-
- return canonical_elf_arch (g, bits, endianness, elf_arch);
-}
-
-/* Download and uncompress the cpio file to find binaries within. */
-static const char *initrd_binaries[] = {
- "bin/ls",
- "bin/rm",
- "bin/modprobe",
- "sbin/modprobe",
- "bin/sh",
- "bin/bash",
- "bin/dash",
- "bin/nash",
- NULL
-};
-
-static char *
-cpio_arch (guestfs_h *g, const char *file, const char *path)
-{
- CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL;
- CLEANUP_FREE char *initrd = NULL;
- CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
- char *ret = NULL;
- const char *method;
- int64_t size;
- int r;
- size_t i;
-
- if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) {
- perrorf (g, "asprintf");
- return NULL;
- }
-
- if (strstr (file, "gzip"))
- method = "zcat";
- else if (strstr (file, "bzip2"))
- method = "bzcat";
- else
- method = "cat";
-
- /* Security: Refuse to download initrd if it is huge. */
- size = guestfs_filesize (g, path);
- if (size == -1 || size > 100000000) {
- error (g, _("size of %s unreasonable (%" PRIi64 "
bytes)"),
- path, size);
- goto out;
- }
-
- if (mkdtemp (dir) == NULL) {
- perrorf (g, "mkdtemp");
- goto out;
- }
-
- initrd = safe_asprintf (g, "%s/initrd", dir);
- if (guestfs_download (g, path, initrd) == -1)
- goto out;
-
- /* Construct a command to extract named binaries from the initrd file. */
- guestfs_int_cmd_add_string_unquoted (cmd, "cd ");
- guestfs_int_cmd_add_string_quoted (cmd, dir);
- guestfs_int_cmd_add_string_unquoted (cmd, " && ");
- guestfs_int_cmd_add_string_unquoted (cmd, method);
- guestfs_int_cmd_add_string_unquoted (cmd, " initrd | cpio --quiet
-id");
- for (i = 0; initrd_binaries[i] != NULL; ++i) {
- guestfs_int_cmd_add_string_unquoted (cmd, " ");
- guestfs_int_cmd_add_string_quoted (cmd, initrd_binaries[i]);
- }
-
- r = guestfs_int_cmd_run (cmd);
- if (r == -1)
- goto out;
- if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
- guestfs_int_external_command_failed (g, r, "cpio", path);
- goto out;
- }
-
- for (i = 0; initrd_binaries[i] != NULL; ++i) {
- CLEANUP_FREE char *bin - safe_asprintf (g, "%s/%s", dir,
initrd_binaries[i]);
-
- if (is_regular_file (bin)) {
- bool loading_ok, matched;
-
- ret = magic_for_file (g, bin, &loading_ok, &matched);
- if (!loading_ok || matched)
- goto out;
- }
- }
- error (g, "file_architecture: could not determine architecture of cpio
archive");
-
- out:
- guestfs_int_recursive_remove_dir (g, dir);
-
- return ret;
-}
-
-static char *
-compressed_file_arch (guestfs_h *g, const char *path, const char *method)
-{
- CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL;
- CLEANUP_FREE char *tempfile = NULL, *tempfile_extracted = NULL;
- CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
- char *ret = NULL;
- int64_t size;
- int r;
- bool matched;
-
- if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) {
- perrorf (g, "asprintf");
- return NULL;
- }
-
- /* Security: Refuse to download file if it is huge. */
- size = guestfs_filesize (g, path);
- if (size == -1 || size > 10000000) {
- error (g, _("size of %s unreasonable (%" PRIi64 "
bytes)"),
- path, size);
- goto out;
- }
-
- if (mkdtemp (dir) == NULL) {
- perrorf (g, "mkdtemp");
- goto out;
- }
-
- tempfile = safe_asprintf (g, "%s/file", dir);
- if (guestfs_download (g, path, tempfile) == -1)
- goto out;
-
- tempfile_extracted = safe_asprintf (g, "%s/file_extracted", dir);
-
- /* Construct a command to extract named binaries from the initrd file. */
- guestfs_int_cmd_add_string_unquoted (cmd, method);
- guestfs_int_cmd_add_string_unquoted (cmd, " ");
- guestfs_int_cmd_add_string_quoted (cmd, tempfile);
- guestfs_int_cmd_add_string_unquoted (cmd, " > ");
- guestfs_int_cmd_add_string_quoted (cmd, tempfile_extracted);
-
- r = guestfs_int_cmd_run (cmd);
- if (r == -1)
- goto out;
- if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
- guestfs_int_external_command_failed (g, r, method, path);
- goto out;
- }
-
- ret = magic_for_file (g, tempfile_extracted, NULL, &matched);
- if (!matched)
- error (g, "file_architecture: could not determine architecture of
compressed file");
-
- out:
- guestfs_int_recursive_remove_dir (g, dir);
-
- return ret;
-}
-
-char *
-guestfs_impl_file_architecture (guestfs_h *g, const char *path)
-{
- CLEANUP_FREE char *file = NULL;
- CLEANUP_FREE char *bits = NULL;
- CLEANUP_FREE char *elf_arch = NULL;
- CLEANUP_FREE char *endianness = NULL;
- char *ret = NULL;
-
- /* Get the output of the "file" command. Note that because this
- * runs in the daemon, LANG=C so it's in English.
- */
- file = guestfs_file (g, path);
- if (file == NULL)
- return NULL;
-
- if ((match3 (g, file, re_file_elf, &bits, &endianness,
&elf_arch)) != 0)
- ret = canonical_elf_arch (g, bits, endianness, elf_arch);
- else if (strstr (file, "PE32 executable"))
- ret = safe_strdup (g, "i386");
- else if (strstr (file, "PE32+ executable"))
- ret = safe_strdup (g, "x86_64");
- else if (strstr (file, "cpio archive"))
- ret = cpio_arch (g, file, path);
- else if (strstr (file, "gzip compressed data"))
- ret = compressed_file_arch (g, path, "zcat");
- else if (strstr (file, "XZ compressed data"))
- ret = compressed_file_arch (g, path, "xzcat");
- else
- error (g, "file_architecture: unknown architecture: %s", path);
-
- return ret; /* caller frees */
-}
diff --git a/po/POTFILES b/po/POTFILES
index 0d8a924b6..1a38e8ed4 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -354,7 +354,6 @@ lib/errors.c
lib/event-string.c
lib/events.c
lib/file.c
-lib/filearch.c
lib/fuse.c
lib/guid.c
lib/handle.c
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 13/29] daemon: Reimplement ‘list_ldm_(volumes|partitions)’ APIs in OCaml.
---
daemon/Makefile.am | 2 ++
daemon/ldm.c | 82 -----------------------------------------------
daemon/ldm.ml | 52 ++++++++++++++++++++++++++++++
daemon/ldm.mli | 20 ++++++++++++
generator/actions_core.ml | 2 ++
5 files changed, 76 insertions(+), 82 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index e86435c4c..5d79dc830 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -262,6 +262,7 @@ SOURCES_MLI = \
file.mli \
filearch.mli \
is.mli \
+ ldm.mli \
link.mli \
mount.mli \
mountable.mli \
@@ -280,6 +281,7 @@ SOURCES_ML = \
file.ml \
filearch.ml \
is.ml \
+ ldm.ml \
link.ml \
mount.ml \
parted.ml \
diff --git a/daemon/ldm.c b/daemon/ldm.c
index 75418e8d3..5106e65f9 100644
--- a/daemon/ldm.c
+++ b/daemon/ldm.c
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <glob.h>
#include <string.h>
#include <yajl/yajl_tree.h>
@@ -47,87 +46,6 @@ optgroup_ldm_available (void)
return prog_exists (str_ldmtool);
}
-static int
-glob_errfunc (const char *epath, int eerrno)
-{
- fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror
(eerrno));
- return 1;
-}
-
-static char **
-get_devices (const char *pattern)
-{
- CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
- glob_t devs;
- int err;
- size_t i;
-
- memset (&devs, 0, sizeof devs);
-
- err = glob (pattern, GLOB_ERR, glob_errfunc, &devs);
- if (err == GLOB_NOSPACE) {
- reply_with_error ("glob: returned GLOB_NOSPACE: "
- "rerun with LIBGUESTFS_DEBUG=1");
- goto error;
- } else if (err == GLOB_ABORTED) {
- reply_with_error ("glob: returned GLOB_ABORTED: "
- "rerun with LIBGUESTFS_DEBUG=1");
- goto error;
- }
-
- for (i = 0; i < devs.gl_pathc; ++i) {
- if (add_string (&ret, devs.gl_pathv[i]) == -1)
- goto error;
- }
-
- if (end_stringsbuf (&ret) == -1) goto error;
-
- globfree (&devs);
- return take_stringsbuf (&ret);
-
- error:
- globfree (&devs);
-
- return NULL;
-}
-
-/* All device mapper devices called /dev/mapper/ldm_vol_*. XXX We
- * could tighten this up in future if ldmtool had a way to read these
- * names back after they have been created.
- */
-char **
-do_list_ldm_volumes (void)
-{
- struct stat buf;
-
- /* If /dev/mapper doesn't exist at all, don't give an error. */
- if (stat ("/dev/mapper", &buf) == -1) {
- if (errno == ENOENT)
- return empty_list ();
- reply_with_perror ("/dev/mapper");
- return NULL;
- }
-
- return get_devices ("/dev/mapper/ldm_vol_*");
-}
-
-/* Same as above but /dev/mapper/ldm_part_*. See comment above. */
-char **
-do_list_ldm_partitions (void)
-{
- struct stat buf;
-
- /* If /dev/mapper doesn't exist at all, don't give an error. */
- if (stat ("/dev/mapper", &buf) == -1) {
- if (errno == ENOENT)
- return empty_list ();
- reply_with_perror ("/dev/mapper");
- return NULL;
- }
-
- return get_devices ("/dev/mapper/ldm_part_*");
-}
-
int
do_ldmtool_create_all (void)
{
diff --git a/daemon/ldm.ml b/daemon/ldm.ml
new file mode 100644
index 000000000..dc7b36f9c
--- /dev/null
+++ b/daemon/ldm.ml
@@ -0,0 +1,52 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+
+(* All device mapper devices are called /dev/mapper/ldm_vol_*. XXX We
+ * could tighten this up in future if ldmtool had a way to read these
+ * names back after they have been created.
+ *)
+let list_ldm_volumes () + (* If /dev/mapper doesn't exist at all,
don't give an error. *)
+ if not (is_directory "/dev/mapper") then
+ []
+ else (
+ let dir = Sys.readdir "/dev/mapper" in
+ let dir = Array.to_list dir in
+ let dir + List.filter (fun d -> String.is_prefix d
"ldm_vol_") dir in
+ let dir = List.map ((^) "/dev/mapper/") dir in
+ List.sort compare dir
+ )
+
+(* Same as above but /dev/mapper/ldm_part_*. *)
+let list_ldm_partitions () + (* If /dev/mapper doesn't exist at all,
don't give an error. *)
+ if not (is_directory "/dev/mapper") then
+ []
+ else (
+ let dir = Sys.readdir "/dev/mapper" in
+ let dir = Array.to_list dir in
+ let dir + List.filter (fun d -> String.is_prefix d
"ldm_part_") dir in
+ let dir = List.map ((^) "/dev/mapper/") dir in
+ List.sort compare dir
+ )
diff --git a/daemon/ldm.mli b/daemon/ldm.mli
new file mode 100644
index 000000000..789abb0b3
--- /dev/null
+++ b/daemon/ldm.mli
@@ -0,0 +1,20 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val list_ldm_volumes : unit -> string list
+val list_ldm_partitions : unit -> string list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index bfd96589e..331a5feb1 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -8114,6 +8114,7 @@ The capabilities set C<cap> should be passed in text
form
{ defaults with
name = "list_ldm_volumes"; added = (1, 20, 0);
style = RStringList (RDevice, "devices"), [], [];
+ impl = OCaml "Ldm.list_ldm_volumes";
optional = Some "ldm";
shortdesc = "list all Windows dynamic disk volumes";
longdesc = "\
@@ -8124,6 +8125,7 @@ device names." };
{ defaults with
name = "list_ldm_partitions"; added = (1, 20, 0);
style = RStringList (RDevice, "devices"), [], [];
+ impl = OCaml "Ldm.list_ldm_partitions";
optional = Some "ldm";
shortdesc = "list all Windows dynamic disk partitions";
longdesc = "\
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 14/29] daemon: Reimplement ‘lvs’ API in OCaml.
---
daemon/Makefile.am | 2 +
daemon/lvm.c | 151 ----------------------------------------------
daemon/lvm.ml | 92 ++++++++++++++++++++++++++++
daemon/lvm.mli | 19 ++++++
generator/actions_core.ml | 1 +
5 files changed, 114 insertions(+), 151 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 5d79dc830..abd45b744 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -264,6 +264,7 @@ SOURCES_MLI = \
is.mli \
ldm.mli \
link.mli \
+ lvm.mli \
mount.mli \
mountable.mli \
parted.mli \
@@ -283,6 +284,7 @@ SOURCES_ML = \
is.ml \
ldm.ml \
link.ml \
+ lvm.ml \
mount.ml \
parted.ml \
realpath.ml \
diff --git a/daemon/lvm.c b/daemon/lvm.c
index 5d12b009f..072bf53b4 100644
--- a/daemon/lvm.c
+++ b/daemon/lvm.c
@@ -103,89 +103,6 @@ convert_lvm_output (char *out, const char *prefix)
return take_stringsbuf (&ret);
}
-/* Filter a colon-separated output of
- * lvs -o lv_attr,vg_name,lv_name
- * removing thin layouts, and building the device path as we expect it.
- *
- * This is used only when lvm has no -S.
- */
-static char **
-filter_convert_old_lvs_output (char *out)
-{
- char *p, *pend;
- CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
-
- p = out;
- while (p) {
- size_t len;
- char *saveptr;
- char *lv_attr, *vg_name, *lv_name;
-
- pend = strchr (p, '\n'); /* Get the next line of output. */
- if (pend) {
- *pend = '\0';
- pend++;
- }
-
- while (*p && c_isspace (*p)) /* Skip any leading whitespace. */
- p++;
-
- /* Sigh, skip trailing whitespace too. "pvs", I'm looking at
you. */
- len = strlen (p)-1;
- while (*p && c_isspace (p[len]))
- p[len--] = '\0';
-
- if (!*p) { /* Empty line? Skip it. */
- skip_line:
- p = pend;
- continue;
- }
-
- lv_attr = strtok_r (p, ":", &saveptr);
- if (!lv_attr)
- goto skip_line;
-
- vg_name = strtok_r (NULL, ":", &saveptr);
- if (!vg_name)
- goto skip_line;
-
- lv_name = strtok_r (NULL, ":", &saveptr);
- if (!lv_name)
- goto skip_line;
-
- /* Ignore thin layouts (RHBZ#1278878). */
- if (lv_attr[0] == 't')
- goto skip_line;
-
- /* Ignore activationskip (RHBZ#1306666). */
- if (strlen (lv_attr) >= 10 && lv_attr[9] == 'k')
- goto skip_line;
-
- /* Ignore "unknown device" message (RHBZ#1054761). */
- if (STRNEQ (p, "unknown device")) {
- char buf[256];
-
- snprintf (buf, sizeof buf, "/dev/%s/%s", vg_name, lv_name);
- if (add_string (&ret, buf) == -1) {
- free (out);
- return NULL;
- }
- }
-
- p = pend;
- }
-
- free (out);
-
- if (ret.size > 0)
- sort_strings (ret.argv, ret.size);
-
- if (end_stringsbuf (&ret) == -1)
- return NULL;
-
- return take_stringsbuf (&ret);
-}
-
char **
do_pvs (void)
{
@@ -222,74 +139,6 @@ do_vgs (void)
return convert_lvm_output (out, NULL);
}
-/* Check whether lvs has -S to filter its output.
- * It is available only in lvm2 >= 2.02.107.
- */
-static int
-test_lvs_has_S_opt (void)
-{
- static int result = -1;
- if (result != -1)
- return result;
-
- CLEANUP_FREE char *out = NULL;
- CLEANUP_FREE char *err = NULL;
-
- int r = command (&out, &err, str_lvm, "lvs",
"--help", NULL);
- if (r == -1) {
- reply_with_error ("lvm lvs --help: %s", err);
- return -1;
- }
-
- if (strstr (out, "-S") == NULL)
- result = 0;
- else
- result = 1;
-
- return result;
-}
-
-char **
-do_lvs (void)
-{
- char *out;
- CLEANUP_FREE char *err = NULL;
- int r;
- const int has_S = test_lvs_has_S_opt ();
-
- if (has_S < 0)
- return NULL;
-
- if (has_S > 0) {
- r = command (&out, &err,
- str_lvm, "lvs",
- "-o", "vg_name,lv_name",
- "-S", "lv_role=public &&
lv_skip_activation!=yes",
- "--noheadings",
- "--separator", "/", NULL);
- if (r == -1) {
- reply_with_error ("%s", err);
- free (out);
- return NULL;
- }
-
- return convert_lvm_output (out, "/dev/");
- } else {
- r = command (&out, &err,
- str_lvm, "lvs",
- "-o", "lv_attr,vg_name,lv_name",
- "--noheadings",
- "--separator", ":", NULL);
- if (r == -1) {
- reply_with_error ("%s", err);
- free (out);
- return NULL;
- }
-
- return filter_convert_old_lvs_output (out);
- }
-}
-
/* These were so complex to implement that I ended up auto-generating
* the code. That code is in stubs.c, and it is generated as usual
* by generator.ml.
diff --git a/daemon/lvm.ml b/daemon/lvm.ml
new file mode 100644
index 000000000..55421b628
--- /dev/null
+++ b/daemon/lvm.ml
@@ -0,0 +1,92 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+
+let lvs_has_S_opt = lazy (
+ let out = command "lvm" ["lvs"; "--help"] in
+ String.find out "-S" >= 0
+)
+
+let rec lvs () + let has_S = Lazy.force lvs_has_S_opt in
+ if has_S then (
+ let out = command "lvm" ["lvs";
+ "-o"; "vg_name,lv_name";
+ "-S"; "lv_role=public &&
lv_skip_activation!=yes";
+ "--noheadings";
+ "--separator"; "/"] in
+ convert_lvm_output ~prefix:"/dev/" out
+ )
+ else (
+ let out = command "lvm" ["lvs";
+ "-o";
"lv_attr,vg_name,lv_name";
+ "--noheadings";
+ "--separator"; ":"] in
+ filter_convert_old_lvs_output out
+ )
+
+and convert_lvm_output ?(prefix = "") out + let lines =
String.nsplit "\n" out in
+
+ (* Skip leading and trailing ("pvs", I'm looking at you)
whitespace. *)
+ let lines = List.map String.trim lines in
+
+ (* Skip empty lines. *)
+ let lines = List.filter ((<>) "") lines in
+
+ (* Ignore "unknown device" message (RHBZ#1054761). *)
+ let lines = List.filter ((<>) "unknown device") lines in
+
+ (* Add a prefix? *)
+ let lines = List.map ((^) prefix) lines in
+
+ (* Sort and return. *)
+ List.sort compare lines
+
+(* Filter a colon-separated output of
+ * lvs -o lv_attr,vg_name,lv_name
+ * removing thin layouts, and building the device path as we expect it.
+ *
+ * This is used only when lvm has no -S.
+ *)
+and filter_convert_old_lvs_output out + let lines = String.nsplit
"\n" out in
+ let lines = List.map String.trim lines in
+ let lines = List.filter ((<>) "") lines in
+ let lines = List.filter ((<>) "unknown device") lines in
+
+ let lines = filter_map (
+ fun line ->
+ match String.nsplit ":" line with
+ | [ lv_attr; vg_name; lv_name ] ->
+ (* Ignore thin layouts (RHBZ#1278878). *)
+ if String.length lv_attr > 0 && lv_attr.[0] = 't'
then None
+ (* Ignore activationskip (RHBZ#1306666). *)
+ else if String.length lv_attr > 9 && lv_attr.[9] =
'k' then None
+ else
+ Some (sprintf "/dev/%s/%s" vg_name lv_name)
+ | _ ->
+ None
+ ) lines in
+
+ List.sort compare lines
diff --git a/daemon/lvm.mli b/daemon/lvm.mli
new file mode 100644
index 000000000..f254728cb
--- /dev/null
+++ b/daemon/lvm.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val lvs : unit -> string list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 331a5feb1..f6f006eee 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -1732,6 +1732,7 @@ See also C<guestfs_vgs_full>." };
{ defaults with
name = "lvs"; added = (0, 0, 4);
style = RStringList (RDevice, "logvols"), [], [];
+ impl = OCaml "Lvm.lvs";
optional = Some "lvm2";
tests = [
InitBasicFSonLVM, Always, TestResult (
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 15/29] daemon: Reimplement ‘list_md_devices’ API in OCaml.
---
daemon/Makefile.am | 2 +
daemon/md.c | 125 ++++++++++++----------------------------------
daemon/md.ml | 48 ++++++++++++++++++
daemon/md.mli | 19 +++++++
generator/actions_core.ml | 1 +
5 files changed, 101 insertions(+), 94 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index abd45b744..6ff71bb1f 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -265,6 +265,7 @@ SOURCES_MLI = \
ldm.mli \
link.mli \
lvm.mli \
+ md.mli \
mount.mli \
mountable.mli \
parted.mli \
@@ -285,6 +286,7 @@ SOURCES_ML = \
ldm.ml \
link.ml \
lvm.ml \
+ md.ml \
mount.ml \
parted.ml \
realpath.ml \
diff --git a/daemon/md.c b/daemon/md.c
index 64d98fae5..5c9ecd136 100644
--- a/daemon/md.c
+++ b/daemon/md.c
@@ -24,7 +24,6 @@
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
-#include <glob.h>
#ifdef HAVE_LINUX_RAID_MD_U_H
#include <sys/ioctl.h>
@@ -32,6 +31,8 @@
#include <linux/raid/md_u.h>
#endif /* HAVE_LINUX_RAID_MD_U_H */
+#include <caml/mlvalues.h>
+
#include "daemon.h"
#include "actions.h"
#include "optgroups.h"
@@ -45,6 +46,35 @@ optgroup_mdadm_available (void)
return prog_exists (str_mdadm);
}
+/* Check if 'dev' is a real RAID device, because in the case where md
+ * is linked directly into the kernel (not a module), /dev/md0 is
+ * sometimes created. This is called from OCaml function
+ * Md.list_md_devices.
+ */
+extern value guestfs_int_daemon_is_raid_device (value devicev);
+
+/* NB: This is a "noalloc" call. */
+value
+guestfs_int_daemon_is_raid_device (value devv)
+{
+ const char *dev = String_val (devv);
+ int ret = 1;
+
+#if defined(HAVE_LINUX_RAID_MD_U_H) && defined(GET_ARRAY_INFO)
+ int fd;
+ mdu_array_info_t array;
+
+ fd = open (dev, O_RDONLY);
+ if (fd >= 0) {
+ if (ioctl (fd, GET_ARRAY_INFO, &array) == -1 && errno ==
ENODEV)
+ ret = 0;
+ close (fd);
+ }
+#endif
+
+ return Val_bool (ret);
+}
+
static size_t
count_bits (uint64_t bitmap)
{
@@ -188,99 +218,6 @@ do_md_create (const char *name, char *const *devices,
#pragma GCC diagnostic pop
#endif
-static int
-glob_errfunc (const char *epath, int eerrno)
-{
- fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror
(eerrno));
- return 1;
-}
-
-/* Check if 'dev' is a real RAID device, because in the case where md
- * is linked directly into the kernel (not a module), /dev/md0 is
- * sometimes created.
- */
-static int
-is_raid_device (const char *dev)
-{
- int ret = 1;
-
-#if defined(HAVE_LINUX_RAID_MD_U_H) && defined(GET_ARRAY_INFO)
- int fd;
- mdu_array_info_t array;
-
- fd = open (dev, O_RDONLY);
- if (fd >= 0) {
- if (ioctl (fd, GET_ARRAY_INFO, &array) == -1 && errno ==
ENODEV)
- ret = 0;
- close (fd);
- }
-#endif
-
- return ret;
-}
-
-char **
-do_list_md_devices (void)
-{
- CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
- glob_t mds;
-
- memset (&mds, 0, sizeof mds);
-
-#define PREFIX "/sys/block/md"
-#define SUFFIX "/md"
-
- /* Look for directories under /sys/block matching md[0-9]*
- * As an additional check, we also make sure they have a md subdirectory.
- */
- const int err = glob (PREFIX "[0-9]*" SUFFIX, GLOB_ERR,
glob_errfunc, &mds);
- if (err == GLOB_NOSPACE) {
- reply_with_error ("glob: returned GLOB_NOSPACE: "
- "rerun with LIBGUESTFS_DEBUG=1");
- goto error;
- } else if (err == GLOB_ABORTED) {
- reply_with_error ("glob: returned GLOB_ABORTED: "
- "rerun with LIBGUESTFS_DEBUG=1");
- goto error;
- }
-
- for (size_t i = 0; i < mds.gl_pathc; i++) {
- size_t len;
- char *dev, *n;
-
- len = strlen (mds.gl_pathv[i]) - strlen (PREFIX) - strlen (SUFFIX);
-
-#define DEV "/dev/md"
- dev = malloc (strlen (DEV) + len + 1);
- if (NULL == dev) {
- reply_with_perror ("malloc");
- goto error;
- }
-
- n = dev;
- n = mempcpy (n, DEV, strlen (DEV));
- n = mempcpy (n, &mds.gl_pathv[i][strlen (PREFIX)], len);
- *n = '\0';
-
- if (!is_raid_device (dev)) {
- free (dev);
- continue;
- }
-
- if (add_string_nodup (&ret, dev) == -1) goto error;
- }
-
- if (end_stringsbuf (&ret) == -1) goto error;
- globfree (&mds);
-
- return take_stringsbuf (&ret);
-
- error:
- globfree (&mds);
-
- return NULL;
-}
-
char **
do_md_detail (const char *md)
{
diff --git a/daemon/md.ml b/daemon/md.ml
new file mode 100644
index 000000000..caf87cf8f
--- /dev/null
+++ b/daemon/md.ml
@@ -0,0 +1,48 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+
+external is_raid_device : string -> bool +
"guestfs_int_daemon_is_raid_device" "noalloc"
+
+let re_md = Str.regexp "^md[0-9]+$"
+
+let list_md_devices () + (* Look for directories under /sys/block matching
md[0-9]+
+ * As an additional check, we also make sure they have a md subdirectory.
+ *)
+ let devs = Sys.readdir "/sys/block" in
+ let devs = Array.to_list devs in
+ let devs = List.filter (fun d -> Str.string_match re_md d 0) devs in
+ let devs = List.filter (
+ fun d -> is_directory (sprintf "/sys/block/%s/md" d)
+ ) devs in
+
+ (* Construct the equivalent /dev/md[0-9]+ device names. *)
+ let devs = List.map ((^) "/dev/") devs in
+
+ (* Check they are really RAID devices. *)
+ let devs = List.filter is_raid_device devs in
+
+ (* Return the list sorted. *)
+ sort_device_names devs
diff --git a/daemon/md.mli b/daemon/md.mli
new file mode 100644
index 000000000..56b6ea65e
--- /dev/null
+++ b/daemon/md.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val list_md_devices : unit -> string list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index f6f006eee..140ba6c1b 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -6632,6 +6632,7 @@ If not set, this defaults to C<raid1>.
{ defaults with
name = "list_md_devices"; added = (1, 15, 4);
style = RStringList (RDevice, "devices"), [], [];
+ impl = OCaml "Md.list_md_devices";
shortdesc = "list Linux md (RAID) devices";
longdesc = "\
List all Linux md devices." };
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 16/29] daemon: Generate OCaml wrappers for optgroup_*_available functions.
It is sometimes useful to be able to call these from OCaml code.
---
generator/daemon.ml | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index fd01e5d8a..1d7461f8c 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -976,6 +976,10 @@ let generate_daemon_optgroups_c () generate_header
CStyle GPLv2plus;
pr "#include <config.h>\n";
+ pr "#include <stdio.h>\n";
+ pr "#include <stdlib.h>\n";
+ pr "\n";
+ pr "#include <caml/mlvalues.h>\n";
pr "\n";
pr "#include \"daemon.h\"\n";
pr "#include \"optgroups.h\"\n";
@@ -999,7 +1003,24 @@ let generate_daemon_optgroups_c () pr " {
\"%s\", optgroup_%s_available },\n" group group
) optgroups_names_all;
pr " { NULL, NULL }\n";
- pr "};\n"
+ pr "};\n";
+ pr "\n";
+ pr "/* Wrappers so these functions can be called from OCaml code.
*/\n";
+ List.iter (
+ fun group ->
+ if not (List.mem group optgroups_retired) then (
+ pr "extern value guestfs_int_daemon_optgroup_%s_available
(value);\n"
+ group;
+ pr "\n";
+ pr "/* NB: This is a \"noalloc\" call. */\n";
+ pr "value\n";
+ pr "guestfs_int_daemon_optgroup_%s_available (value unitv)\n"
group;
+ pr "{\n";
+ pr " return Val_bool (optgroup_%s_available ());\n" group;
+ pr "}\n";
+ pr "\n"
+ )
+ ) optgroups_names_all
let generate_daemon_optgroups_h () generate_header CStyle GPLv2plus;
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 17/29] daemon: Enable RStruct, RStructList for OCaml-implemented APIs.
---
.gitignore | 1 +
daemon/Makefile.am | 1 +
generator/OCaml.ml | 8 ++++
generator/OCaml.mli | 1 +
generator/daemon.ml | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++-
generator/main.ml | 2 +
6 files changed, 127 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index 29596594a..8aea2cdb4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,6 +180,7 @@ Makefile.in
/daemon/stamp-guestfsd.pod
/daemon/structs-cleanups.c
/daemon/structs-cleanups.h
+/daemon/structs.ml
/daemon/stubs-?.c
/daemon/stubs.h
/daemon/types.ml
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 6ff71bb1f..4a818a7a9 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -275,6 +275,7 @@ SOURCES_MLI = \
SOURCES_ML = \
types.ml \
utils.ml \
+ structs.ml \
sysroot.ml \
mountable.ml \
chroot.ml \
diff --git a/generator/OCaml.ml b/generator/OCaml.ml
index 53f105198..853b41bb3 100644
--- a/generator/OCaml.ml
+++ b/generator/OCaml.ml
@@ -888,3 +888,11 @@ and generate_ocaml_function_type ?(extra_unit = false)
(ret, args, optargs) | RStructList (_, typ) -> pr "%s array"
typ
| RHashtable _ -> pr "(string * string) list"
)
+
+(* Structure definitions (again). These are used in the daemon,
+ * but it's convenient to generate them here.
+ *)
+and generate_ocaml_daemon_structs () + generate_header OCamlStyle GPLv2plus;
+
+ generate_ocaml_structure_decls ()
diff --git a/generator/OCaml.mli b/generator/OCaml.mli
index 4e79a5b5a..a36fbe02f 100644
--- a/generator/OCaml.mli
+++ b/generator/OCaml.mli
@@ -20,3 +20,4 @@ val generate_ocaml_c : unit -> unit
val generate_ocaml_c_errnos : unit -> unit
val generate_ocaml_ml : unit -> unit
val generate_ocaml_mli : unit -> unit
+val generate_ocaml_daemon_structs : unit -> unit
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 1d7461f8c..8cac5ccb1 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -575,6 +575,110 @@ return_string_list (value retv)
";
+ (* Implement code for returning structs and struct lists. *)
+ let emit_return_struct typ + let struc = Structs.lookup_struct typ in
+ pr "/* Implement RStruct (%S, _). */\n" typ;
+ pr "static guestfs_int_%s *\n" typ;
+ pr "return_%s (value retv)\n" typ;
+ pr "{\n";
+ pr " guestfs_int_%s *ret;\n" typ;
+ pr " value v;\n";
+ pr "\n";
+ pr " ret = malloc (sizeof (*ret));\n";
+ pr " if (ret == NULL) {\n";
+ pr " reply_with_perror (\"malloc\");\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+ iteri (
+ fun i ->
+ pr " v = Field (retv, %d);\n" i;
+ function
+ | n, (FString|FUUID) ->
+ pr " ret->%s = strdup (String_val (v));\n" n;
+ pr " if (ret->%s == NULL) return NULL;\n" n
+ | n, FBuffer ->
+ pr " ret->%s_len = caml_string_length (v);\n" n;
+ pr " ret->%s = strdup (String_val (v));\n" n;
+ pr " if (ret->%s == NULL) return NULL;\n" n
+ | n, (FBytes|FInt64|FUInt64) ->
+ pr " ret->%s = Int64_val (v);\n" n
+ | n, (FInt32|FUInt32) ->
+ pr " ret->%s = Int32_val (v);\n" n
+ | n, FOptPercent ->
+ pr " if (v == Val_int (0)) /* None */\n";
+ pr " ret->%s = -1;\n" n;
+ pr " else {\n";
+ pr " v = Field (v, 0);\n";
+ pr " ret->%s = Double_val (v);\n" n;
+ pr " }\n"
+ | n, FChar ->
+ pr " ret->%s = Int_val (v);\n" n
+ ) struc.s_cols;
+ pr "\n";
+ pr " return ret;\n";
+ pr "}\n";
+ pr "\n"
+
+ and emit_return_struct_list typ + pr "/* Implement RStructList (%S,
_). */\n" typ;
+ pr "static guestfs_int_%s_list *\n" typ;
+ pr "return_%s_list (value retv)\n" typ;
+ pr "{\n";
+ pr " guestfs_int_%s_list *ret;\n" typ;
+ pr " guestfs_int_%s *r;\n" typ;
+ pr " size_t i, len;\n";
+ pr " value v, rv;\n";
+ pr "\n";
+ pr " /* Count the number of elements in the list. */\n";
+ pr " rv = retv;\n";
+ pr " len = 0;\n";
+ pr " while (rv != Val_int (0)) {\n";
+ pr " len++;\n";
+ pr " rv = Field (rv, 1);\n";
+ pr " }\n";
+ pr "\n";
+ pr " ret = malloc (sizeof *ret);\n";
+ pr " if (ret == NULL) {\n";
+ pr " reply_with_perror (\"malloc\");\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr " ret->guestfs_int_%s_list_len = len;\n" typ;
+ pr " ret->guestfs_int_%s_list_val =\n" typ;
+ pr " calloc (len, sizeof (guestfs_int_%s));\n" typ;
+ pr " if (ret->guestfs_int_%s_list_val == NULL) {\n" typ;
+ pr " reply_with_perror (\"calloc\");\n";
+ pr " free (ret);\n";
+ pr " return NULL;\n";
+ pr " }\n";
+ pr "\n";
+ pr " rv = retv;\n";
+ pr " for (i = 0; i < len; ++i) {\n";
+ pr " v = Field (rv, 0);\n";
+ pr " r = return_%s (v);\n" typ;
+ pr " if (r == NULL)\n";
+ pr " return NULL; /* XXX leaks memory along this error path
*/\n";
+ pr " memcpy (&ret->guestfs_int_%s_list_val[i], r, sizeof
(*r));\n" typ;
+ pr " free (r);\n";
+ pr " rv = Field (rv, 1);\n";
+ pr " }\n";
+ pr "\n";
+ pr " return ret;\n";
+ pr "}\n";
+ pr "\n";
+ in
+
+ List.iter (
+ function
+ | typ, RStructOnly ->
+ emit_return_struct typ
+ | typ, (RStructListOnly | RStructAndList) ->
+ emit_return_struct typ;
+ emit_return_struct_list typ
+ ) (rstructs_used_by (actions |> impl_ocaml_functions));
+
+ (* Implement the wrapper functions. *)
List.iter (
fun ({ name = name; style = ret, args, optargs } as f) ->
let uc_name = String.uppercase_ascii name in
@@ -709,8 +813,16 @@ return_string_list (value retv)
| RStringList _ ->
pr " char **ret = return_string_list (retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
- | RStruct _ -> assert false
- | RStructList _ -> assert false
+ | RStruct (_, typ) ->
+ pr " guestfs_int_%s *ret =\n" typ;
+ pr " return_%s (retv);\n" typ;
+ pr " /* caller frees */\n";
+ pr " CAMLreturnT (guestfs_int_%s *, ret);\n" typ
+ | RStructList (_, typ) ->
+ pr " guestfs_int_%s_list *ret =\n" typ;
+ pr " return_%s_list (retv);\n" typ;
+ pr " /* caller frees */\n";
+ pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ
| RHashtable _ -> assert false
| RBufferOut _ -> assert false
);
diff --git a/generator/main.ml b/generator/main.ml
index a6c805e2e..72f704b8e 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -191,6 +191,8 @@ Run it from the top source directory using the command
OCaml.generate_ocaml_c;
output_to "ocaml/guestfs-c-errnos.c"
OCaml.generate_ocaml_c_errnos;
+ output_to "daemon/structs.ml"
+ OCaml.generate_ocaml_daemon_structs;
output_to "ocaml/bindtests.ml"
Bindtests.generate_ocaml_bindtests;
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 18/29] daemon: Reimplement ‘btrfs_subvolume_list’ and ‘btrfs_subvolume_get_default’ in OCaml.
---
daemon/Makefile.am | 2 +
daemon/btrfs.c | 175 ----------------------------------------------
daemon/btrfs.ml | 127 +++++++++++++++++++++++++++++++++
daemon/btrfs.mli | 26 +++++++
generator/actions_core.ml | 2 +
generator/daemon.ml | 5 +-
6 files changed, 160 insertions(+), 177 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 4a818a7a9..459b5d7cc 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -256,6 +256,7 @@ guestfsd_CFLAGS = \
# https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
SOURCES_MLI = \
blkid.mli \
+ btrfs.mli \
chroot.mli \
sysroot.mli \
devsparts.mli \
@@ -280,6 +281,7 @@ SOURCES_ML = \
mountable.ml \
chroot.ml \
blkid.ml \
+ btrfs.ml \
devsparts.ml \
file.ml \
filearch.ml \
diff --git a/daemon/btrfs.c b/daemon/btrfs.c
index 4f52b71e8..d9043d53c 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -41,11 +41,6 @@ GUESTFSD_EXT_CMD(str_mount, mount);
GUESTFSD_EXT_CMD(str_umount, umount);
GUESTFSD_EXT_CMD(str_btrfsimage, btrfs-image);
-COMPILE_REGEXP (re_btrfs_subvolume_list,
- "ID\\s+(\\d+).*\\s"
- "top level\\s+(\\d+).*\\s"
- "path\\s(.*)",
- 0)
COMPILE_REGEXP (re_btrfs_balance_status, "Balance on '.*' is
(.*)", 0)
int
@@ -483,137 +478,6 @@ umount (char *fs_buf, const mountable_t *fs)
return 0;
}
-guestfs_int_btrfssubvolume_list *
-do_btrfs_subvolume_list (const mountable_t *fs)
-{
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- size_t i = 0;
- const size_t MAX_ARGS = 64;
- const char *argv[MAX_ARGS];
-
- /* Execute 'btrfs subvolume list <fs>', and split the output
into lines */
- {
- char *fs_buf = mount (fs);
-
- if (!fs_buf)
- return NULL;
-
- ADD_ARG (argv, i, str_btrfs);
- ADD_ARG (argv, i, "subvolume");
- ADD_ARG (argv, i, "list");
- ADD_ARG (argv, i, fs_buf);
- ADD_ARG (argv, i, NULL);
-
- CLEANUP_FREE char *out = NULL, *errout = NULL;
- int r = commandv (&out, &errout, argv);
-
- if (umount (fs_buf, fs) != 0)
- return NULL;
-
- if (r == -1) {
- CLEANUP_FREE char *fs_desc = mountable_to_string (fs);
- if (fs_desc == NULL) {
- fprintf (stderr, "malloc: %m");
- }
- reply_with_error ("%s: %s", fs_desc ? fs_desc :
"malloc", errout);
- return NULL;
- }
-
- lines = split_lines (out);
- if (!lines) return NULL;
- }
-
- /* Output is:
- *
- * ID 256 gen 30 top level 5 path test1
- * ID 257 gen 30 top level 5 path dir/test2
- * ID 258 gen 30 top level 5 path test3
- *
- * "ID <n>" is the subvolume ID.
- * "gen <n>" is the generation when the root was created or
last
- * updated.
- * "top level <n>" is the top level subvolume ID.
- * "path <str>" is the subvolume path, relative to the top of
the
- * filesystem.
- *
- * Note that the order that each of the above is fixed, but
- * different versions of btrfs may display different sets of data.
- * Specifically, older versions of btrfs do not display gen.
- */
-
- guestfs_int_btrfssubvolume_list *ret = NULL;
-
- const size_t nr_subvolumes = guestfs_int_count_strings (lines);
-
- ret = malloc (sizeof *ret);
- if (!ret) {
- reply_with_perror ("malloc");
- return NULL;
- }
-
- ret->guestfs_int_btrfssubvolume_list_len = nr_subvolumes;
- ret->guestfs_int_btrfssubvolume_list_val - calloc (nr_subvolumes,
sizeof (struct guestfs_int_btrfssubvolume));
- if (ret->guestfs_int_btrfssubvolume_list_val == NULL) {
- reply_with_perror ("calloc");
- goto error;
- }
-
- for (i = 0; i < nr_subvolumes; ++i) {
- /* To avoid allocations, reuse the 'line' buffer to store the
- * path. Thus we don't need to free 'line', since it will be
- * freed by the calling (XDR) code.
- */
- char *line = lines[i];
-#define N_MATCHES 4
- int ovector[N_MATCHES * 3];
-
- if (pcre_exec (re_btrfs_subvolume_list, NULL, line, strlen (line), 0, 0,
- ovector, N_MATCHES * 3) < 0)
-#undef N_MATCHES
- {
- unexpected_output:
- reply_with_error ("unexpected output from 'btrfs subvolume list'
command: %s", line);
- goto error;
- }
-
- struct guestfs_int_btrfssubvolume *this -
&ret->guestfs_int_btrfssubvolume_list_val[i];
-
-#if __WORDSIZE == 64
-#define XSTRTOU64 xstrtoul
-#else
-#define XSTRTOU64 xstrtoull
-#endif
-
- if (XSTRTOU64 (line + ovector[2], NULL, 10,
- &this->btrfssubvolume_id, NULL) != LONGINT_OK)
- goto unexpected_output;
- if (XSTRTOU64 (line + ovector[4], NULL, 10,
- &this->btrfssubvolume_top_level_id, NULL) !=
LONGINT_OK)
- goto unexpected_output;
-
-#undef XSTRTOU64
-
- this->btrfssubvolume_path - strndup (line + ovector[6], ovector[7]
- ovector[6]);
- if (this->btrfssubvolume_path == NULL)
- goto error;
- }
-
- return ret;
-
- error:
- if (ret->guestfs_int_btrfssubvolume_list_val) {
- for (i = 0; i < nr_subvolumes; ++i)
- free
(ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_path);
- free (ret->guestfs_int_btrfssubvolume_list_val);
- }
- free (ret);
-
- return NULL;
-}
-
int
do_btrfs_subvolume_set_default (int64_t id, const char *fs)
{
@@ -649,45 +513,6 @@ do_btrfs_subvolume_set_default (int64_t id, const char *fs)
return 0;
}
-int64_t
-do_btrfs_subvolume_get_default (const mountable_t *fs)
-{
- const size_t MAX_ARGS = 64;
- const char *argv[MAX_ARGS];
- size_t i = 0;
- char *fs_buf = NULL;
- CLEANUP_FREE char *err = NULL;
- CLEANUP_FREE char *out = NULL;
- int r;
- int64_t ret = -1;
-
- fs_buf = mount (fs);
- if (fs_buf == NULL)
- goto error;
-
- ADD_ARG (argv, i, str_btrfs);
- ADD_ARG (argv, i, "subvolume");
- ADD_ARG (argv, i, "get-default");
- ADD_ARG (argv, i, fs_buf);
- ADD_ARG (argv, i, NULL);
-
- r = commandv (&out, &err, argv);
- if (r == -1) {
- reply_with_error ("%s: %s", fs_buf, err);
- goto error;
- }
- if (sscanf (out, "ID %" SCNi64, &ret) != 1) {
- reply_with_error ("%s: could not parse subvolume id: %s",
argv[0], out);
- ret = -1;
- goto error;
- }
-
- error:
- if (fs_buf && umount (fs_buf, fs) != 0)
- return -1;
- return ret;
-}
-
int
do_btrfs_filesystem_sync (const char *fs)
{
diff --git a/daemon/btrfs.ml b/daemon/btrfs.ml
new file mode 100644
index 000000000..554212ccf
--- /dev/null
+++ b/daemon/btrfs.ml
@@ -0,0 +1,127 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Scanf
+open Unix
+
+open Std_utils
+
+open Mountable
+open Utils
+
+include Structs
+
+(* In order to examine subvolumes, quota and other things, the btrfs
+ * filesystem has to be mounted. However we're passed a mountable
+ * in these cases, so we must mount the filesystem. But we cannot
+ * mount it under the sysroot, as something else might be mounted
+ * there so this function mounts the filesystem on a temporary
+ * directory and ensures it is always unmounted afterwards.
+ *)
+let with_mounted mountable f + let tmpdir = sprintf "/tmp/%s"
(String.random8 ()) in
+ (* This is the cleanup function which is called to unmount and
+ * remove the temporary directory. This is called on error and
+ * ordinary exit paths.
+ *)
+ let finally () + ignore (Sys.command (sprintf "umount %s" (quote
tmpdir)));
+ rmdir tmpdir
+ in
+
+ match mountable.m_type with
+ | MountablePath ->
+ (* This corner-case happens for Mountable_or_Path parameters, where
+ * a path was supplied by the caller. The path (the m_device
+ * field) is relative to the sysroot.
+ *)
+ f (Sysroot.sysroot () // mountable.m_device)
+
+ | MountableDevice ->
+ protect ~finally ~f:(
+ fun () ->
+ mkdir tmpdir 0o700;
+ ignore (command "mount" [mountable.m_device; tmpdir]);
+ f tmpdir
+ )
+
+ | MountableBtrfsVol subvol ->
+ protect ~finally ~f:(
+ fun () ->
+ mkdir tmpdir 0o700;
+ ignore (command "mount" ["-o"; "subvol="
^ subvol (* XXX quoting? *);
+ mountable.m_device; tmpdir]);
+ f tmpdir
+ )
+
+let re_btrfs_subvolume_list + Str.regexp ("ID[ \t]+\\([0-9]+\\).*[
\t]" ^
+ "top level[ \t]+\\([0-9]+\\).*[ \t]" ^
+ "path[ \t]+\\(.*\\)")
+
+let btrfs_subvolume_list mountable + (* Execute 'btrfs subvolume list
<fs>', and split the output into lines *)
+ let lines + with_mounted mountable (
+ fun mp -> command "btrfs" ["subvolume";
"list"; mp]
+ ) in
+ let lines = String.nsplit "\n" lines in
+
+ (* Output is:
+ *
+ * ID 256 gen 30 top level 5 path test1
+ * ID 257 gen 30 top level 5 path dir/test2
+ * ID 258 gen 30 top level 5 path test3
+ *
+ * "ID <n>" is the subvolume ID.
+ * "gen <n>" is the generation when the root was created or
last
+ * updated.
+ * "top level <n>" is the top level subvolume ID.
+ * "path <str>" is the subvolume path, relative to the top of
the
+ * filesystem.
+ *
+ * Note that the order that each of the above is fixed, but
+ * different versions of btrfs may display different sets of data.
+ * Specifically, older versions of btrfs do not display gen.
+ *)
+ filter_map (
+ fun line ->
+ if line = "" then None
+ else if Str.string_match re_btrfs_subvolume_list line 0 then (
+ let id = Int64.of_string (Str.matched_group 1 line)
+ and top_level_id = Int64.of_string (Str.matched_group 2 line)
+ and path = Str.matched_group 3 line in
+
+ Some {
+ btrfssubvolume_id = id;
+ btrfssubvolume_top_level_id = top_level_id;
+ btrfssubvolume_path = path
+ }
+ )
+ else
+ failwithf "unexpected output from 'btrfs subvolume list'
command: %s"
+ line
+ ) lines
+
+let btrfs_subvolume_get_default mountable + let out + with_mounted
mountable (
+ fun mp -> command "btrfs" ["subvolume";
"get-default"; mp]
+ ) in
+ sscanf out "ID %Ld" identity
diff --git a/daemon/btrfs.mli b/daemon/btrfs.mli
new file mode 100644
index 000000000..55a38e42d
--- /dev/null
+++ b/daemon/btrfs.mli
@@ -0,0 +1,26 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+type btrfssubvolume = {
+ btrfssubvolume_id : int64;
+ btrfssubvolume_top_level_id : int64;
+ btrfssubvolume_path : string;
+}
+
+val btrfs_subvolume_list : Mountable.t -> btrfssubvolume list
+val btrfs_subvolume_get_default : Mountable.t -> int64
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 140ba6c1b..bd3c21d3b 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7304,6 +7304,7 @@ created subvolume will be added to." };
{ defaults with
name = "btrfs_subvolume_list"; added = (1, 17, 35);
style = RStructList ("subvolumes", "btrfssubvolume"),
[String (Mountable_or_Path, "fs")], [];
+ impl = OCaml "Btrfs.btrfs_subvolume_list";
optional = Some "btrfs"; camel_name =
"BTRFSSubvolumeList";
test_excuse = "tested in tests/btrfs";
shortdesc = "list btrfs snapshots and subvolumes";
@@ -8783,6 +8784,7 @@ This uses the L<blockdev(8)> command." };
{ defaults with
name = "btrfs_subvolume_get_default"; added = (1, 29, 17);
style = RInt64 "id", [String (Mountable_or_Path,
"fs")], [];
+ impl = OCaml "Btrfs.btrfs_subvolume_get_default";
optional = Some "btrfs"; camel_name =
"BTRFSSubvolumeGetDefault";
tests = [
InitPartition, Always, TestResult (
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 8cac5ccb1..83994e9d3 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -758,7 +758,7 @@ return_string_list (value retv)
| Int64 n -> pr "caml_copy_int64 (%s)" n
| String ((PlainString|Device|Pathname|Dev_or_Path), n) ->
pr "caml_copy_string (%s)" n
- | String (Mountable, n) ->
+ | String ((Mountable|Mountable_or_Path), n) ->
pr "copy_mountable (%s)" n
| String _ -> assert false
| OptString _ -> assert false
@@ -797,7 +797,8 @@ return_string_list (value retv)
pr " CAMLreturnT (int, 0);\n"
| RInt _ ->
pr " CAMLreturnT (int, Int_val (retv));\n"
- | RInt64 _ -> assert false
+ | RInt64 _ ->
+ pr " CAMLreturnT (int, Int64_val (retv));\n"
| RBool _ ->
pr " CAMLreturnT (int, Bool_val (retv));\n"
| RConstString _ -> assert false
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 19/29] daemon: Reimplement ‘list_filesystems’ API in the daemon, in OCaml.
Move the list_filesystems API into the daemon, reimplementing it in
OCaml. Since this API makes many other API calls, it runs a lot
faster in the daemon.
---
daemon/Makefile.am | 2 +
daemon/ldm.ml | 3 +
daemon/ldm.mli | 2 +
daemon/listfs.ml | 156 +++++++++++++++++++++++++++++
daemon/listfs.mli | 19 ++++
daemon/lvm.ml | 3 +
daemon/lvm.mli | 2 +
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 75 +++++++-------
generator/daemon.ml | 59 ++++++++++-
generator/proc_nr.ml | 1 +
lib/MAX_PROC_NR | 2 +-
lib/Makefile.am | 1 -
lib/listfs.c | 246 ----------------------------------------------
14 files changed, 285 insertions(+), 287 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 459b5d7cc..fbe4734cf 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -265,6 +265,7 @@ SOURCES_MLI = \
is.mli \
ldm.mli \
link.mli \
+ listfs.mli \
lvm.mli \
md.mli \
mount.mli \
@@ -292,6 +293,7 @@ SOURCES_ML = \
md.ml \
mount.ml \
parted.ml \
+ listfs.ml \
realpath.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/ldm.ml b/daemon/ldm.ml
index dc7b36f9c..19cd03e83 100644
--- a/daemon/ldm.ml
+++ b/daemon/ldm.ml
@@ -20,6 +20,9 @@ open Std_utils
open Utils
+external available : unit -> bool +
"guestfs_int_daemon_optgroup_lvm2_available" "noalloc"
+
(* All device mapper devices are called /dev/mapper/ldm_vol_*. XXX We
* could tighten this up in future if ldmtool had a way to read these
* names back after they have been created.
diff --git a/daemon/ldm.mli b/daemon/ldm.mli
index 789abb0b3..e6edfabd8 100644
--- a/daemon/ldm.mli
+++ b/daemon/ldm.mli
@@ -16,5 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
+val available : unit -> bool
+
val list_ldm_volumes : unit -> string list
val list_ldm_partitions : unit -> string list
diff --git a/daemon/listfs.ml b/daemon/listfs.ml
new file mode 100644
index 000000000..df5404f81
--- /dev/null
+++ b/daemon/listfs.ml
@@ -0,0 +1,156 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+let rec list_filesystems () + let has_lvm2 = Lvm.available () in
+ let has_ldm = Ldm.available () in
+
+ let devices = Devsparts.list_devices () in
+ let partitions = Devsparts.list_partitions () in
+ let mds = Md.list_md_devices () in
+
+ (* Look to see if any devices directly contain filesystems
+ * (RHBZ#590167). However vfs-type will fail to tell us anything
+ * useful about devices which just contain partitions, so we also
+ * get the list of partitions and exclude the corresponding devices
+ * by using part-to-dev.
+ *)
+ let devices = List.fold_left (
+ fun devices part ->
+ let d = Devsparts.part_to_dev part in
+ List.filter ((<>) d) devices
+ ) devices partitions in
+
+ (* Use vfs-type to check for filesystems on devices. *)
+ let ret = filter_map check_with_vfs_type devices in
+
+ (* Use vfs-type to check for filesystems on partitions, but
+ * ignore MBR partition type 42 used by LDM.
+ *)
+ let ret + ret @
+ filter_map (
+ fun part ->
+ if not has_ldm || not (is_mbr_partition_type_42 part) then
+ check_with_vfs_type part
+ else
+ None (* ignore type 42 *)
+ ) partitions in
+
+ (* Use vfs-type to check for filesystems on md devices. *)
+ let ret = ret @ filter_map check_with_vfs_type mds in
+
+ (* LVM. *)
+ let ret + if has_lvm2 then (
+ let lvs = Lvm.lvs () in
+ (* Use vfs-type to check for filesystems on LVs. *)
+ ret @ filter_map check_with_vfs_type lvs
+ )
+ else ret in
+
+ (* LDM. *)
+ let ret + if has_ldm then (
+ let ldmvols = Ldm.list_ldm_volumes () in
+ let ldmparts = Ldm.list_ldm_partitions () in
+ (* Use vfs-type to check for filesystems on Windows dynamic disks. *)
+ ret @
+ filter_map check_with_vfs_type ldmvols @
+ filter_map check_with_vfs_type ldmparts
+ )
+ else ret in
+
+ List.flatten ret
+
+(* Use vfs-type to check for a filesystem of some sort of [device].
+ * Returns [Some [device, vfs_type; ...]] if found (there may be
+ * multiple devices found in the case of btrfs), else [None] if nothing
+ * is found.
+ *)
+and check_with_vfs_type device + let mountable = Mountable.of_device device in
+ let vfs_type + try Blkid.vfs_type mountable
+ with exn ->
+ if verbose () then
+ eprintf "check_with_vfs_type: %s: %s\n"
+ device (Printexc.to_string exn);
+ "" in
+
+ if vfs_type = "" then
+ Some [mountable, "unknown"]
+
+ (* Ignore all "*_member" strings. In libblkid these are returned
+ * for things which are members of some RAID or LVM set, most
+ * importantly "LVM2_member" which is a PV.
+ *)
+ else if String.is_suffix vfs_type "_member" then
+ None
+
+ (* Ignore LUKS-encrypted partitions. These are also containers, as above. *)
+ else if vfs_type = "crypto_LUKS" then
+ None
+
+ (* A single btrfs device can turn into many volumes. *)
+ else if vfs_type = "btrfs" then (
+ let vols = Btrfs.btrfs_subvolume_list mountable in
+
+ (* Filter out the default subvolume. You can access that by
+ * simply mounting the whole device, so we will add the whole
+ * device at the beginning of the returned list instead.
+ *)
+ let default_volume = Btrfs.btrfs_subvolume_get_default mountable in
+ let vols + List.filter (
+ fun { Btrfs.btrfssubvolume_id = id } -> id <> default_volume
+ ) vols in
+
+ Some (
+ (mountable, vfs_type) (* whole device = default volume *)
+ :: List.map (
+ fun { Btrfs.btrfssubvolume_path = path } ->
+ let mountable = Mountable.of_btrfsvol device path in
+ (mountable, "btrfs")
+ ) vols
+ )
+ )
+
+ else
+ Some [mountable, vfs_type]
+
+(* We should ignore partitions that have MBR type byte 0x42, because
+ * these are members of a Windows dynamic disk group. Trying to read
+ * them will cause errors (RHBZ#887520). Assuming that libguestfs was
+ * compiled with ldm support, we'll get the filesystems on these later.
+ *)
+and is_mbr_partition_type_42 partition + try
+ let partnum = Devsparts.part_to_partnum partition in
+ let device = Devsparts.part_to_dev partition in
+ let mbr_id = Parted.part_get_mbr_id device partnum in
+ mbr_id = 0x42
+ with exn ->
+ if verbose () then
+ eprintf "is_mbr_partition_type_42: %s: %s\n"
+ partition (Printexc.to_string exn);
+ false
diff --git a/daemon/listfs.mli b/daemon/listfs.mli
new file mode 100644
index 000000000..69958da77
--- /dev/null
+++ b/daemon/listfs.mli
@@ -0,0 +1,19 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val list_filesystems : unit -> (Mountable.t * string) list
diff --git a/daemon/lvm.ml b/daemon/lvm.ml
index 55421b628..14f0a8578 100644
--- a/daemon/lvm.ml
+++ b/daemon/lvm.ml
@@ -22,6 +22,9 @@ open Std_utils
open Utils
+external available : unit -> bool +
"guestfs_int_daemon_optgroup_lvm2_available" "noalloc"
+
let lvs_has_S_opt = lazy (
let out = command "lvm" ["lvs"; "--help"] in
String.find out "-S" >= 0
diff --git a/daemon/lvm.mli b/daemon/lvm.mli
index f254728cb..1cf61ecfb 100644
--- a/daemon/lvm.mli
+++ b/daemon/lvm.mli
@@ -16,4 +16,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
+val available : unit -> bool
+
val lvs : unit -> string list
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 9562aed89..71e350764 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -321,7 +321,6 @@ lib/launch.c
lib/libvirt-auth.c
lib/libvirt-domain.c
lib/libvirt-is-version.c
-lib/listfs.c
lib/lpj.c
lib/match.c
lib/mountable.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index bd3c21d3b..d5946b3f5 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -209,43 +209,6 @@ If the mountable does not represent a btrfs subvolume, then
this function fails and the C<errno> is set to C<EINVAL>." };
{ defaults with
- name = "list_filesystems"; added = (1, 5, 15);
- style = RHashtable (RMountable, RPlainString, "fses"), [], [];
- shortdesc = "list filesystems";
- longdesc = "\
-This inspection command looks for filesystems on partitions,
-block devices and logical volumes, returning a list of C<mountables>
-containing filesystems and their type.
-
-The return value is a hash, where the keys are the devices
-containing filesystems, and the values are the filesystem types.
-For example:
-
- \"/dev/sda1\" => \"ntfs\"
- \"/dev/sda2\" => \"ext2\"
- \"/dev/vg_guest/lv_root\" => \"ext4\"
- \"/dev/vg_guest/lv_swap\" => \"swap\"
-
-The key is not necessarily a block device. It may also be an opaque
-‘mountable’ string which can be passed to C<guestfs_mount>.
-
-The value can have the special value \"unknown\", meaning the
-content of the device is undetermined or empty.
-\"swap\" means a Linux swap partition.
-
-This command runs other libguestfs commands, which might include
-C<guestfs_mount> and C<guestfs_umount>, and therefore you should
-use this soon after launch and only when nothing is mounted.
-
-Not all of the filesystems returned will be mountable. In
-particular, swap partitions are returned in the list. Also
-this command does not check that each filesystem
-found is valid and mountable, and some filesystems might
-be mountable but require special options. Filesystems may
-not all belong to a single logical operating system
-(use C<guestfs_inspect_os> to look for OSes)." };
-
- { defaults with
name = "add_drive"; added = (0, 0, 3);
style = RErr, [String (PlainString, "filename")], [OBool
"readonly"; OString "format"; OString "iface";
OString "name"; OString "label"; OString
"protocol"; OStringList "server"; OString
"username"; OString "secret"; OString "cachemode";
OString "discard"; OBool "copyonread"];
once_had_no_optargs = true;
@@ -9635,4 +9598,42 @@ initrd or kernel module(s) instead.
=back" };
+ { defaults with
+ name = "list_filesystems"; added = (1, 5, 15);
+ style = RHashtable (RMountable, RPlainString, "fses"), [], [];
+ impl = OCaml "Listfs.list_filesystems";
+ shortdesc = "list filesystems";
+ longdesc = "\
+This inspection command looks for filesystems on partitions,
+block devices and logical volumes, returning a list of C<mountables>
+containing filesystems and their type.
+
+The return value is a hash, where the keys are the devices
+containing filesystems, and the values are the filesystem types.
+For example:
+
+ \"/dev/sda1\" => \"ntfs\"
+ \"/dev/sda2\" => \"ext2\"
+ \"/dev/vg_guest/lv_root\" => \"ext4\"
+ \"/dev/vg_guest/lv_swap\" => \"swap\"
+
+The key is not necessarily a block device. It may also be an opaque
+‘mountable’ string which can be passed to C<guestfs_mount>.
+
+The value can have the special value \"unknown\", meaning the
+content of the device is undetermined or empty.
+\"swap\" means a Linux swap partition.
+
+This command runs other libguestfs commands, which might include
+C<guestfs_mount> and C<guestfs_umount>, and therefore you should
+use this soon after launch and only when nothing is mounted.
+
+Not all of the filesystems returned will be mountable. In
+particular, swap partitions are returned in the list. Also
+this command does not check that each filesystem
+found is valid and mountable, and some filesystems might
+be mountable but require special options. Filesystems may
+not all belong to a single logical operating system
+(use C<guestfs_inspect_os> to look for OSes)." };
+
]
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 83994e9d3..66b625388 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -573,6 +573,58 @@ return_string_list (value retv)
return take_stringsbuf (&ret); /* caller frees */
}
+/* Implement RString (RMountable, _). */
+static char *
+return_string_mountable (value retv)
+{
+ value typev = Field (retv, 0);
+ value devicev = Field (retv, 1);
+ value subvolv;
+ char *ret;
+
+ if (Is_long (typev)) { /* MountableDevice or MountablePath */
+ ret = strdup (String_val (devicev));
+ if (ret == NULL)
+ reply_with_perror (\"strdup\");
+ return ret;
+ }
+ else { /* MountableBtrfsVol of subvol */
+ subvolv = Field (typev, 0);
+ if (asprintf (&ret, \"btrfsvol:%%s/%%s\",
+ String_val (devicev), String_val (subvolv)) == -1)
+ reply_with_perror (\"asprintf\");
+ return ret;
+ }
+}
+
+/* Implement RHashtable (RMountable, RPlainString, _). */
+static char **
+return_hashtable_mountable_string (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value v, mv, sv;
+ char *m;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0); /* (Mountable.t, string) */
+ mv = Field (v, 0); /* Mountable.t */
+ m = return_string_mountable (mv);
+ if (m == NULL)
+ return NULL;
+ if (add_string_nodup (&ret, m) == -1)
+ return NULL;
+ sv = Field (v, 1); /* string */
+ if (add_string (&ret, String_val (sv)) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
";
(* Implement code for returning structs and struct lists. *)
@@ -810,7 +862,9 @@ return_string_list (value retv)
pr " CAMLreturnT (char *, NULL);\n";
pr " }\n";
pr " CAMLreturnT (char *, ret); /* caller frees */\n"
- | RString _ -> assert false
+ | RString (RMountable, _) ->
+ pr " char *ret = return_string_mountable (retv);\n";
+ pr " CAMLreturnT (char *, ret); /* caller frees */\n"
| RStringList _ ->
pr " char **ret = return_string_list (retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
@@ -824,6 +878,9 @@ return_string_list (value retv)
pr " return_%s_list (retv);\n" typ;
pr " /* caller frees */\n";
pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ
+ | RHashtable (RMountable, RPlainString, _) ->
+ pr " char **ret = return_hashtable_mountable_string
(retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RHashtable _ -> assert false
| RBufferOut _ -> assert false
);
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index 1b0feae87..dec02f5fa 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -483,6 +483,7 @@ let proc_nr = [
473, "yara_destroy";
474, "internal_yara_scan";
475, "file_architecture";
+476, "list_filesystems";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 7573eff88..b86395733 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-475
+476
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 71dc25b9b..7eaff88ee 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -107,7 +107,6 @@ libguestfs_la_SOURCES = \
launch-unix.c \
libvirt-auth.c \
libvirt-domain.c \
- listfs.c \
lpj.c \
match.c \
mountable.c \
diff --git a/lib/listfs.c b/lib/listfs.c
deleted file mode 100644
index 60aff3305..000000000
--- a/lib/listfs.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-#include "guestfs-internal-actions.h"
-#include "structs-cleanups.h"
-
-/* List filesystems.
- *
- * The current implementation just uses guestfs_vfs_type and doesn't
- * try mounting anything, but we reserve the right in future to try
- * mounting filesystems.
- */
-
-static void remove_from_list (char **list, const char *item);
-static int check_with_vfs_type (guestfs_h *g, const char *dev, struct
stringsbuf *sb);
-static int is_mbr_partition_type_42 (guestfs_h *g, const char *partition);
-
-char **
-guestfs_impl_list_filesystems (guestfs_h *g)
-{
- size_t i;
- DECLARE_STRINGSBUF (ret);
-
- const char *lvm2[] = { "lvm2", NULL };
- const int has_lvm2 = guestfs_feature_available (g, (char **) lvm2);
- const char *ldm[] = { "ldm", NULL };
- const int has_ldm = guestfs_feature_available (g, (char **) ldm);
-
- CLEANUP_FREE_STRING_LIST char **devices = NULL;
- CLEANUP_FREE_STRING_LIST char **partitions = NULL;
- CLEANUP_FREE_STRING_LIST char **mds = NULL;
- CLEANUP_FREE_STRING_LIST char **lvs = NULL;
- CLEANUP_FREE_STRING_LIST char **ldmvols = NULL;
- CLEANUP_FREE_STRING_LIST char **ldmparts = NULL;
-
- /* Look to see if any devices directly contain filesystems
- * (RHBZ#590167). However vfs-type will fail to tell us anything
- * useful about devices which just contain partitions, so we also
- * get the list of partitions and exclude the corresponding devices
- * by using part-to-dev.
- */
- devices = guestfs_list_devices (g);
- if (devices == NULL) goto error;
- partitions = guestfs_list_partitions (g);
- if (partitions == NULL) goto error;
- mds = guestfs_list_md_devices (g);
- if (mds == NULL) goto error;
-
- for (i = 0; partitions[i] != NULL; ++i) {
- CLEANUP_FREE char *dev = guestfs_part_to_dev (g, partitions[i]);
- if (dev)
- remove_from_list (devices, dev);
- }
-
- /* Use vfs-type to check for filesystems on devices. */
- for (i = 0; devices[i] != NULL; ++i)
- if (check_with_vfs_type (g, devices[i], &ret) == -1)
- goto error;
-
- /* Use vfs-type to check for filesystems on partitions. */
- for (i = 0; partitions[i] != NULL; ++i) {
- if (has_ldm == 0 || ! is_mbr_partition_type_42 (g, partitions[i])) {
- if (check_with_vfs_type (g, partitions[i], &ret) == -1)
- goto error;
- }
- }
-
- /* Use vfs-type to check for filesystems on md devices. */
- for (i = 0; mds[i] != NULL; ++i)
- if (check_with_vfs_type (g, mds[i], &ret) == -1)
- goto error;
-
- if (has_lvm2 > 0) {
- /* Use vfs-type to check for filesystems on LVs. */
- lvs = guestfs_lvs (g);
- if (lvs == NULL) goto error;
-
- for (i = 0; lvs[i] != NULL; ++i)
- if (check_with_vfs_type (g, lvs[i], &ret) == -1)
- goto error;
- }
-
- if (has_ldm > 0) {
- /* Use vfs-type to check for filesystems on Windows dynamic disks. */
- ldmvols = guestfs_list_ldm_volumes (g);
- if (ldmvols == NULL) goto error;
-
- for (i = 0; ldmvols[i] != NULL; ++i)
- if (check_with_vfs_type (g, ldmvols[i], &ret) == -1)
- goto error;
-
- ldmparts = guestfs_list_ldm_partitions (g);
- if (ldmparts == NULL) goto error;
-
- for (i = 0; ldmparts[i] != NULL; ++i)
- if (check_with_vfs_type (g, ldmparts[i], &ret) == -1)
- goto error;
- }
-
- /* Finish off the list and return it. */
- guestfs_int_end_stringsbuf (g, &ret);
- return ret.argv;
-
- error:
- guestfs_int_free_stringsbuf (&ret);
- return NULL;
-}
-
-/* If 'item' occurs in 'list', remove and free it. */
-static void
-remove_from_list (char **list, const char *item)
-{
- size_t i;
-
- for (i = 0; list[i] != NULL; ++i)
- if (STREQ (list[i], item)) {
- free (list[i]);
- for (; list[i+1] != NULL; ++i)
- list[i] = list[i+1];
- list[i] = NULL;
- return;
- }
-}
-
-/* Use vfs-type to look for a filesystem of some sort on 'dev'.
- * Apart from some types which we ignore, add the result to the
- * 'ret' string list.
- */
-static int
-check_with_vfs_type (guestfs_h *g, const char *device, struct stringsbuf *sb)
-{
- const char *v;
- CLEANUP_FREE char *vfs_type = NULL;
-
- guestfs_push_error_handler (g, NULL, NULL);
- vfs_type = guestfs_vfs_type (g, device);
- guestfs_pop_error_handler (g);
-
- if (!vfs_type)
- v = "unknown";
- else if (STREQ (vfs_type, ""))
- v = "unknown";
- else if (STREQ (vfs_type, "btrfs")) {
- CLEANUP_FREE_BTRFSSUBVOLUME_LIST struct guestfs_btrfssubvolume_list *vols -
guestfs_btrfs_subvolume_list (g, device);
-
- if (vols == NULL)
- return -1;
-
- const int64_t default_volume - guestfs_btrfs_subvolume_get_default (g,
device);
-
- for (size_t i = 0; i < vols->len; i++) {
- struct guestfs_btrfssubvolume *this = &vols->val[i];
-
- /* Ignore the default subvolume. We get it by simply mounting
- * the whole device of this btrfs filesystem.
- */
- if (this->btrfssubvolume_id == (uint64_t) default_volume)
- continue;
-
- guestfs_int_add_sprintf (g, sb,
- "btrfsvol:%s/%s",
- device, this->btrfssubvolume_path);
- guestfs_int_add_string (g, sb, "btrfs");
- }
-
- v = vfs_type;
- }
- else {
- /* Ignore all "*_member" strings. In libblkid these are returned
- * for things which are members of some RAID or LVM set, most
- * importantly "LVM2_member" which is a PV.
- */
- const size_t n = strlen (vfs_type);
- if (n >= 7 && STREQ (&vfs_type[n-7], "_member"))
- return 0;
-
- /* Ignore LUKS-encrypted partitions. These are also containers. */
- if (STREQ (vfs_type, "crypto_LUKS"))
- return 0;
-
- v = vfs_type;
- }
-
- guestfs_int_add_string (g, sb, device);
- guestfs_int_add_string (g, sb, v);
-
- return 0;
-}
-
-/* We should ignore partitions that have MBR type byte 0x42, because
- * these are members of a Windows dynamic disk group. Trying to read
- * them will cause errors (RHBZ#887520). Assuming that libguestfs was
- * compiled with ldm support, we'll get the filesystems on these later.
- */
-static int
-is_mbr_partition_type_42 (guestfs_h *g, const char *partition)
-{
- CLEANUP_FREE char *device = NULL;
- int partnum;
- int mbr_id;
- int ret = 0;
-
- guestfs_push_error_handler (g, NULL, NULL);
-
- partnum = guestfs_part_to_partnum (g, partition);
- if (partnum == -1)
- goto out;
-
- device = guestfs_part_to_dev (g, partition);
- if (device == NULL)
- goto out;
-
- mbr_id = guestfs_part_get_mbr_id (g, device, partnum);
-
- ret = mbr_id == 0x42;
-
- out:
- guestfs_pop_error_handler (g);
-
- return ret;
-}
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 20/29] daemon: Reimplement ‘part_list’ API in OCaml.
---
daemon/parted.c | 56 -----------------------------------------------
daemon/parted.ml | 51 ++++++++++++++++++++++++++++++++++++++++++
daemon/parted.mli | 8 +++++++
generator/actions_core.ml | 1 +
4 files changed, 60 insertions(+), 56 deletions(-)
diff --git a/daemon/parted.c b/daemon/parted.c
index a1e5c81cf..125aec60b 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -387,62 +387,6 @@ do_part_get_parttype (const char *device)
return r;
}
-guestfs_int_partition_list *
-do_part_list (const char *device)
-{
- CLEANUP_FREE char *out = print_partition_table (device, true);
- if (!out)
- return NULL;
-
- CLEANUP_FREE_STRING_LIST char **lines = split_lines (out);
-
- if (!lines)
- return NULL;
-
- guestfs_int_partition_list *r;
-
- /* lines[0] is "BYT;", lines[1] is the device line which we ignore,
- * lines[2..] are the partitions themselves. Count how many.
- */
- size_t nr_rows = 0, row;
- for (row = 2; lines[row] != NULL; ++row)
- ++nr_rows;
-
- r = malloc (sizeof *r);
- if (r == NULL) {
- reply_with_perror ("malloc");
- return NULL;
- }
- r->guestfs_int_partition_list_len = nr_rows;
- r->guestfs_int_partition_list_val - malloc (nr_rows * sizeof
(guestfs_int_partition));
- if (r->guestfs_int_partition_list_val == NULL) {
- reply_with_perror ("malloc");
- goto error2;
- }
-
- /* Now parse the lines. */
- size_t i;
- for (i = 0, row = 2; lines[row] != NULL; ++i, ++row) {
- if (sscanf (lines[row], "%d:%" SCNi64 "B:%" SCNi64
"B:%" SCNi64 "B",
- &r->guestfs_int_partition_list_val[i].part_num,
- &r->guestfs_int_partition_list_val[i].part_start,
- &r->guestfs_int_partition_list_val[i].part_end,
- &r->guestfs_int_partition_list_val[i].part_size) != 4) {
- reply_with_error ("could not parse row from output of parted print
command: %s", lines[row]);
- goto error3;
- }
- }
-
- return r;
-
- error3:
- free (r->guestfs_int_partition_list_val);
- error2:
- free (r);
- return NULL;
-}
-
int
do_part_get_bootable (const char *device, int partnum)
{
diff --git a/daemon/parted.ml b/daemon/parted.ml
index 6be41cf66..37e1b42be 100644
--- a/daemon/parted.ml
+++ b/daemon/parted.ml
@@ -22,6 +22,8 @@ open Std_utils
open Utils
+include Structs
+
(* Test if [sfdisk] is recent enough to have [--part-type], to be used
* instead of [--print-id] and [--change-id].
*)
@@ -53,3 +55,52 @@ let part_get_mbr_id device partnum
(* It's printed in hex, possibly with a leading space. *)
sscanf out " %x" identity
+
+let print_partition_table ~add_m_option device + udev_settle ();
+
+ let args = ref [] in
+ if add_m_option then push_back args "-m";
+ push_back args "-s";
+ push_back args "--";
+ push_back args device;
+ push_back args "unit";
+ push_back args "b";
+ push_back args "print";
+
+ let out + try command "parted" !args
+ with
+ (* Translate "unrecognised disk label" into an errno code. *)
+ Failure str when String.find str "unrecognised disk label"
>= 0 ->
+ raise (Unix.Unix_error (Unix.EINVAL, "parted", device ^
": " ^ str)) in
+
+ udev_settle ();
+
+ (* Split the output into lines. *)
+ let out = String.trim out in
+ let lines = String.nsplit "\n" out in
+
+ (* lines[0] is "BYT;", lines[1] is the device line which we ignore,
+ * lines[2..] are the partitions themselves.
+ *)
+ match lines with
+ | "BYT;" :: _ :: lines -> lines
+ | [] | [_] ->
+ failwith "too few rows of output from 'parted print'
command"
+ | _ ->
+ failwith "did not see 'BYT;' magic value in 'parted
print' command"
+
+let part_list device + let lines = print_partition_table ~add_m_option:true
device in
+
+ List.map (
+ fun line ->
+ try sscanf line "%d:%LdB:%LdB:%LdB"
+ (fun num start end_ size ->
+ { part_num = Int32.of_int num;
+ part_start = start; part_end = end_; part_size = size })
+ with Scan_failure err ->
+ failwithf "could not parse row from output of 'parted
print' command: %s: %s"
+ line err
+ ) lines
diff --git a/daemon/parted.mli b/daemon/parted.mli
index 33eb6d30d..057d7e8c7 100644
--- a/daemon/parted.mli
+++ b/daemon/parted.mli
@@ -16,4 +16,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
+type partition = {
+ part_num : int32;
+ part_start : int64;
+ part_end : int64;
+ part_size : int64;
+}
+
val part_get_mbr_id : string -> int -> int
+val part_list : string -> partition list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index d5946b3f5..b1e2559e0 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5039,6 +5039,7 @@ table. This works on C<gpt> but not on C<mbr>
partitions." };
{ defaults with
name = "part_list"; added = (1, 0, 78);
style = RStructList ("partitions", "partition"),
[String (Device, "device")], [];
+ impl = OCaml "Parted.part_list";
tests = [] (* XXX Add a regression test for this. *);
shortdesc = "list partitions on a device";
longdesc = "\
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 21/29] daemon: Reimplement ‘findfs_uuid’ and ‘findfs_label’ APIs in OCaml.
This also reimplements the lv_canonical function in OCaml. We cannot
call the original C function because it calls reply_with_perror which
would break the OCaml bindings.
---
daemon/Makefile.am | 3 +-
daemon/findfs.c | 94 -----------------------------------------------
daemon/findfs.ml | 56 ++++++++++++++++++++++++++++
daemon/findfs.mli | 20 ++++++++++
daemon/lvm.ml | 28 ++++++++++++++
daemon/lvm.mli | 10 +++++
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 2 +
8 files changed, 118 insertions(+), 96 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index fbe4734cf..087f67258 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -108,7 +108,6 @@ guestfsd_SOURCES = \
ext2.c \
fallocate.c \
file.c \
- findfs.c \
fill.c \
find.c \
format.c \
@@ -262,6 +261,7 @@ SOURCES_MLI = \
devsparts.mli \
file.mli \
filearch.mli \
+ findfs.mli \
is.mli \
ldm.mli \
link.mli \
@@ -290,6 +290,7 @@ SOURCES_ML = \
ldm.ml \
link.ml \
lvm.ml \
+ findfs.ml \
md.ml \
mount.ml \
parted.ml \
diff --git a/daemon/findfs.c b/daemon/findfs.c
deleted file mode 100644
index f44137038..000000000
--- a/daemon/findfs.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* libguestfs - the guestfsd daemon
- * Copyright (C) 2010 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.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "daemon.h"
-#include "actions.h"
-
-GUESTFSD_EXT_CMD(str_findfs, findfs);
-
-static char *
-findfs (const char *tag, const char *label_or_uuid)
-{
- char *out;
- CLEANUP_FREE char *err = NULL;
- CLEANUP_FREE char *arg = NULL;
- int r;
- size_t len;
-
- /* Kill the cache file, forcing blkid to reread values from the
- * original filesystems. In blkid there is a '-p' option which is
- * supposed to do this, but (a) it doesn't work and (b) that option
- * is not supported in RHEL 5.
- */
- unlink ("/etc/blkid/blkid.tab");
- unlink ("/run/blkid/blkid.tab");
-
- if (asprintf (&arg, "%s=%s", tag, label_or_uuid) == -1) {
- reply_with_perror ("asprintf");
- return NULL;
- }
-
- r = command (&out, &err, str_findfs, arg, NULL);
- if (r == -1) {
- reply_with_error ("%s", err);
- free (out);
- return NULL;
- }
-
- /* Trim trailing \n if present. */
- len = strlen (out);
- if (len > 0 && out[len-1] == '\n')
- out[len-1] = '\0';
-
- if (STRPREFIX (out, "/dev/mapper/") || STRPREFIX (out,
"/dev/dm-")) {
- char *canonical;
- r = lv_canonical (out, &canonical);
- if (r == -1) {
- free (out);
- return NULL;
- }
- if (r == 1) {
- free (out);
- out = canonical;
- }
- /* Ignore the case where r == 0. /dev/mapper does not correspond
- * to an LV, so the best we can do is just return it as-is.
- */
- }
-
- return out; /* caller frees */
-}
-
-char *
-do_findfs_uuid (const char *uuid)
-{
- return findfs ("UUID", uuid);
-}
-
-char *
-do_findfs_label (const char *label)
-{
- return findfs ("LABEL", label);
-}
diff --git a/daemon/findfs.ml b/daemon/findfs.ml
new file mode 100644
index 000000000..8acb72928
--- /dev/null
+++ b/daemon/findfs.ml
@@ -0,0 +1,56 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+open Utils
+
+let rec findfs_uuid uuid + findfs "UUID" uuid
+and findfs_label label + findfs "LABEL"label
+
+and findfs tag str + (* Kill the cache file, forcing blkid to reread values
from the
+ * original filesystems. In blkid there is a '-p' option which is
+ * supposed to do this, but (a) it doesn't work and (b) that option
+ * is not supported in RHEL 5.
+ *)
+ (try unlink "/etc/blkid/blkid.tab" with Unix_error _ -> ());
+ (try unlink "/run/blkid/blkid.tab" with Unix_error _ -> ());
+
+ let out = command "findfs" [ sprintf "%s=%s" tag str ] in
+
+ (* Trim trailing \n if present. *)
+ let out = String.trim out in
+
+ if String.is_prefix out "/dev/mapper/" ||
+ String.is_prefix out "/dev/dm-" then (
+ match Lvm.lv_canonical out with
+ | None ->
+ (* Ignore the case where 'out' doesn't appear to be an LV.
+ * The best we can do is return the original as-is.
+ *)
+ out
+ | Some out -> out
+ )
+ else
+ out
diff --git a/daemon/findfs.mli b/daemon/findfs.mli
new file mode 100644
index 000000000..acef0395c
--- /dev/null
+++ b/daemon/findfs.mli
@@ -0,0 +1,20 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val findfs_uuid : string -> string
+val findfs_label : string -> string
diff --git a/daemon/lvm.ml b/daemon/lvm.ml
index 14f0a8578..5dd01d6b2 100644
--- a/daemon/lvm.ml
+++ b/daemon/lvm.ml
@@ -16,6 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
+open Unix
open Printf
open Std_utils
@@ -93,3 +94,30 @@ and filter_convert_old_lvs_output out ) lines in
List.sort compare lines
+
+(* Convert a non-canonical LV path like /dev/mapper/vg-lv or /dev/dm-0
+ * to a canonical one.
+ *
+ * This is harder than it should be. A LV device like /dev/VG/LV is
+ * really a symlink to a device-mapper device like /dev/dm-0. However
+ * at the device-mapper (kernel) level, nothing is really known about
+ * LVM (a userspace concept). Therefore we use a convoluted method to
+ * determine this, by listing out known LVs and checking whether the
+ * rdev (major/minor) of the device we are passed matches any of them.
+ *
+ * Note use of 'stat' instead of 'lstat' so that symlinks are
fully
+ * resolved.
+ *)
+let lv_canonical device + let stat1 = stat device in
+ let lvs = lvs () in
+ try
+ Some (
+ List.find (
+ fun lv ->
+ let stat2 = stat lv in
+ stat1.st_rdev = stat2.st_rdev
+ ) lvs
+ )
+ with
+ | Not_found -> None
diff --git a/daemon/lvm.mli b/daemon/lvm.mli
index 1cf61ecfb..7cde16ebb 100644
--- a/daemon/lvm.mli
+++ b/daemon/lvm.mli
@@ -19,3 +19,13 @@
val available : unit -> bool
val lvs : unit -> string list
+
+val lv_canonical : string -> string option
+(** Convert a non-canonical LV path like /dev/mapper/vg-lv or /dev/dm-0
+ to a canonical one.
+
+ On error this raises an exception. There are two possible non-error
+ return cases:
+
+ Some lv = conversion was successful, returning the canonical LV
+ None = input path was not an LV, it could not be made canonical *)
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 71e350764..5ec6f77bb 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -102,7 +102,6 @@ daemon/fallocate.c
daemon/file.c
daemon/fill.c
daemon/find.c
-daemon/findfs.c
daemon/format.c
daemon/fs-min-size.c
daemon/fsck.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index b1e2559e0..0a967f76d 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5746,6 +5746,7 @@ returns true iff this is the case." };
{ defaults with
name = "findfs_uuid"; added = (1, 5, 3);
style = RString (RDevice, "device"), [String (PlainString,
"uuid")], [];
+ impl = OCaml "Findfs.findfs_uuid";
shortdesc = "find a filesystem by UUID";
longdesc = "\
This command searches the filesystems and returns the one
@@ -5757,6 +5758,7 @@ To find the UUID of a filesystem, use
C<guestfs_vfs_uuid>." };
{ defaults with
name = "findfs_label"; added = (1, 5, 3);
style = RString (RDevice, "device"), [String (PlainString,
"label")], [];
+ impl = OCaml "Findfs.findfs_label";
shortdesc = "find a filesystem by label";
longdesc = "\
This command searches the filesystems and returns the one
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 22/29] daemon: Reimplement ‘nr_devices’ API in OCaml.
---
daemon/devsparts.c | 15 ---------------
daemon/devsparts.ml | 2 ++
daemon/devsparts.mli | 2 ++
generator/actions_core.ml | 1 +
4 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/daemon/devsparts.c b/daemon/devsparts.c
index 1aacb8e16..12e779326 100644
--- a/daemon/devsparts.c
+++ b/daemon/devsparts.c
@@ -54,21 +54,6 @@ do_device_index (const char *device)
return ret;
}
-int
-do_nr_devices (void)
-{
- size_t i;
- CLEANUP_FREE_STRING_LIST char **devices = do_list_devices ();
-
- if (devices == NULL)
- return -1;
-
- for (i = 0; devices[i] != NULL; ++i)
- ;
-
- return (int) i;
-}
-
#define GUESTFSDIR "/dev/disk/guestfs"
char **
diff --git a/daemon/devsparts.ml b/daemon/devsparts.ml
index e97ff1267..273612516 100644
--- a/daemon/devsparts.ml
+++ b/daemon/devsparts.ml
@@ -85,6 +85,8 @@ and add_partitions dev let parts = List.filter (fun part
-> String.is_prefix part dev) parts in
List.map (fun part -> "/dev/" ^ part) parts
+let nr_devices () = List.length (list_devices ())
+
let part_to_dev part let dev, part = split_device_partition part in
if part = 0 then
diff --git a/daemon/devsparts.mli b/daemon/devsparts.mli
index 4dfaa86e6..8be47e752 100644
--- a/daemon/devsparts.mli
+++ b/daemon/devsparts.mli
@@ -19,6 +19,8 @@
val list_devices : unit -> string list
val list_partitions : unit -> string list
+val nr_devices : unit -> int
+
val part_to_dev : string -> string
val part_to_partnum : string -> int
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 0a967f76d..db1411ff8 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7432,6 +7432,7 @@ See also C<guestfs_list_devices>,
C<guestfs_part_to_dev>." };
{ defaults with
name = "nr_devices"; added = (1, 19, 15);
+ impl = OCaml "Devsparts.nr_devices";
style = RInt "nrdisks", [], [];
tests = [
InitEmpty, Always, TestResult (
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 23/29] daemon: Reimplement ‘md_detail’ API in OCaml.
---
daemon/md.c | 66 -----------------------------------------------
daemon/md.ml | 37 ++++++++++++++++++++++++++
daemon/md.mli | 1 +
generator/actions_core.ml | 1 +
generator/daemon.ml | 27 +++++++++++++++++++
5 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/daemon/md.c b/daemon/md.c
index 5c9ecd136..549dd89fa 100644
--- a/daemon/md.c
+++ b/daemon/md.c
@@ -218,72 +218,6 @@ do_md_create (const char *name, char *const *devices,
#pragma GCC diagnostic pop
#endif
-char **
-do_md_detail (const char *md)
-{
- size_t i;
- int r;
-
- CLEANUP_FREE char *out = NULL, *err = NULL;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
-
- CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
-
- const char *mdadm[] = { str_mdadm, "-D", "--export", md,
NULL };
- r = commandv (&out, &err, mdadm);
- if (r == -1) {
- reply_with_error ("%s", err);
- return NULL;
- }
-
- /* Split the command output into lines */
- lines = split_lines (out);
- if (lines == NULL)
- return NULL;
-
- /* Parse the output of mdadm -D --export:
- * MD_LEVEL=raid1
- * MD_DEVICES=2
- * MD_METADATA=1.0
- * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1
- * MD_NAME=localhost.localdomain:0
- */
- for (i = 0; lines[i] != NULL; ++i) {
- char *line = lines[i];
-
- /* Skip blank lines (shouldn't happen) */
- if (line[0] == '\0') continue;
-
- /* Split the line in 2 at the equals sign */
- char *eq = strchr (line, '=');
- if (eq) {
- *eq = '\0'; eq++;
-
- /* Remove the MD_ prefix from the key and translate the remainder to
lower
- * case */
- if (STRPREFIX (line, "MD_")) {
- line += 3;
- for (char *j = line; *j != '\0'; j++) {
- *j = c_tolower (*j);
- }
- }
-
- /* Add the key/value pair to the output */
- if (add_string (&ret, line) == -1 ||
- add_string (&ret, eq) == -1) return NULL;
- } else {
- /* Ignore lines with no equals sign (shouldn't happen). Log to stderr
so
- * it will show up in LIBGUESTFS_DEBUG. */
- fprintf (stderr, "md-detail: unexpected mdadm output ignored:
%s", line);
- }
- }
-
- if (end_stringsbuf (&ret) == -1)
- return NULL;
-
- return take_stringsbuf (&ret);
-}
-
int
do_md_stop (const char *md)
{
diff --git a/daemon/md.ml b/daemon/md.ml
index caf87cf8f..ba045b5f7 100644
--- a/daemon/md.ml
+++ b/daemon/md.ml
@@ -46,3 +46,40 @@ let list_md_devices ()
(* Return the list sorted. *)
sort_device_names devs
+
+let md_detail md + let out = command "mdadm" ["-D";
"--export"; md] in
+
+ (* Split the command output into lines. *)
+ let out = String.trim out in
+ let lines = String.nsplit "\n" out in
+
+ (* Parse the output of mdadm -D --export:
+ * MD_LEVEL=raid1
+ * MD_DEVICES=2
+ * MD_METADATA=1.0
+ * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1
+ * MD_NAME=localhost.localdomain:0
+ *)
+ filter_map (
+ fun line ->
+ (* Skip blank lines (shouldn't happen). *)
+ if line = "" then None
+ else (
+ (* Split the line at the equals sign. *)
+ let key, value = String.split "=" line in
+
+ (* Remove the MD_ prefix from the key and translate the
+ * remainder to lower case.
+ *)
+ let key + if String.is_prefix key "MD_" then
+ String.sub key 3 (String.length key - 3)
+ else
+ key in
+ let key = String.lowercase_ascii key in
+
+ (* Add the key/value pair to the output. *)
+ Some (key, value)
+ )
+ ) lines
diff --git a/daemon/md.mli b/daemon/md.mli
index 56b6ea65e..8f0c79a7f 100644
--- a/daemon/md.mli
+++ b/daemon/md.mli
@@ -17,3 +17,4 @@
*)
val list_md_devices : unit -> string list
+val md_detail : string -> (string * string) list
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index db1411ff8..070a1c641 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -6606,6 +6606,7 @@ List all Linux md devices." };
{ defaults with
name = "md_detail"; added = (1, 15, 6);
style = RHashtable (RPlainString, RPlainString, "info"), [String
(Device, "md")], [];
+ impl = OCaml "Md.md_detail";
optional = Some "mdadm";
shortdesc = "obtain metadata for an MD device";
longdesc = "\
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 66b625388..f20c87bea 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -597,6 +597,30 @@ return_string_mountable (value retv)
}
}
+/* Implement RHashtable (RPlainString, RPlainString, _). */
+static char **
+return_hashtable_string_string (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value v, sv;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0); /* (string, string) */
+ sv = Field (v, 0); /* string */
+ if (add_string (&ret, String_val (sv)) == -1)
+ return NULL;
+ sv = Field (v, 1); /* string */
+ if (add_string (&ret, String_val (sv)) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
/* Implement RHashtable (RMountable, RPlainString, _). */
static char **
return_hashtable_mountable_string (value retv)
@@ -878,6 +902,9 @@ return_hashtable_mountable_string (value retv)
pr " return_%s_list (retv);\n" typ;
pr " /* caller frees */\n";
pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ
+ | RHashtable (RPlainString, RPlainString, _) ->
+ pr " char **ret = return_hashtable_string_string
(retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RHashtable (RMountable, RPlainString, _) ->
pr " char **ret = return_hashtable_mountable_string
(retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 24/29] daemon: Reimplement ‘realpath’ API in OCaml.
---
daemon/Makefile.am | 1 -
daemon/realpath.c | 50 -----------------------------------------------
daemon/realpath.ml | 5 +++++
daemon/realpath.mli | 1 +
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 1 +
6 files changed, 7 insertions(+), 52 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 087f67258..5a5bc9855 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -153,7 +153,6 @@ guestfsd_SOURCES = \
pingdaemon.c \
proto.c \
readdir.c \
- realpath.c \
rename.c \
rsync.c \
scrub.c \
diff --git a/daemon/realpath.c b/daemon/realpath.c
deleted file mode 100644
index f9d22d28d..000000000
--- a/daemon/realpath.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* libguestfs - the guestfsd daemon
- * Copyright (C) 2009-2017 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.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-#include "cloexec.h"
-
-#include "daemon.h"
-#include "optgroups.h"
-#include "actions.h"
-
-char *
-do_realpath (const char *path)
-{
- char *ret;
-
- CHROOT_IN;
- ret = realpath (path, NULL);
- CHROOT_OUT;
- if (ret == NULL) {
- reply_with_perror ("%s", path);
- return NULL;
- }
-
- return ret; /* caller frees */
-}
diff --git a/daemon/realpath.ml b/daemon/realpath.ml
index cffe86322..4b4971dd7 100644
--- a/daemon/realpath.ml
+++ b/daemon/realpath.ml
@@ -20,6 +20,11 @@ open Printf
open Std_utils
+let realpath path + let chroot = Chroot.create ~name:(sprintf "realpath:
%s" path)
+ (Sysroot.sysroot ()) in
+ Chroot.f chroot Unix_utils.Realpath.realpath path
+
(* The infamous case_sensitive_path function, which works around
* the bug in ntfs-3g that all paths are case sensitive even though
* the underlying filesystem is case insensitive.
diff --git a/daemon/realpath.mli b/daemon/realpath.mli
index 371e619fc..3da53c461 100644
--- a/daemon/realpath.mli
+++ b/daemon/realpath.mli
@@ -16,4 +16,5 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
+val realpath : string -> string
val case_sensitive_path : string -> string
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 5ec6f77bb..0b55c122f 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -145,7 +145,6 @@ daemon/parted.c
daemon/pingdaemon.c
daemon/proto.c
daemon/readdir.c
-daemon/realpath.c
daemon/rename.c
daemon/rsync.c
daemon/scrub.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 070a1c641..4ec83d22d 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -4197,6 +4197,7 @@ compress- or gzip-compressed.
{ defaults with
name = "realpath"; added = (1, 0, 66);
style = RString (RPlainString, "rpath"), [String (Pathname,
"path")], [];
+ impl = OCaml "Realpath.realpath";
tests = [
InitISOFS, Always, TestResultString (
[["realpath"; "/../directory"]],
"/directory"), []
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 25/29] daemon: Implement command flag CommandFlagFoldStdoutOnStderr.
Used to handle broken commands like parted, sgdisk which print errors
on stdout.
---
daemon/utils.ml | 19 ++++++++++++++-----
daemon/utils.mli | 11 +++++++++--
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 48f6b9c5c..808e575fd 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -25,9 +25,15 @@ let prog_exists prog try ignore (which prog); true
with Executable_not_found _ -> false
-let commandr prog args +type command_flag + CommandFlagFoldStdoutOnStderr
+
+let commandr ?(flags = []) prog args + let fold_stdout_on_stderr = List.mem
CommandFlagFoldStdoutOnStderr flags in
+
if verbose () then
- eprintf "command: %s %s\n%!"
+ eprintf "command:%s %s %s\n%!"
+ (if fold_stdout_on_stderr then " fold-stdout-on-stderr"
else "")
prog (String.concat " " args);
let argv = Array.of_list (prog :: args) in
@@ -43,7 +49,10 @@ let commandr prog args (* Child process. *)
dup2 stdin_fd stdin;
close stdin_fd;
- dup2 stdout_fd stdout;
+ if not fold_stdout_on_stderr then
+ dup2 stdout_fd stdout
+ else
+ dup2 stderr_fd stdout;
close stdout_fd;
dup2 stderr_fd stderr;
close stderr_fd;
@@ -91,8 +100,8 @@ let commandr prog args
(r, stdout, stderr)
-let command prog args - let r, stdout, stderr = commandr prog args in
+let command ?flags prog args + let r, stdout, stderr = commandr ?flags prog
args in
if r <> 0 then
failwithf "%s exited with status %d: %s" prog r stderr;
stdout
diff --git a/daemon/utils.mli b/daemon/utils.mli
index a1f956be3..d3c8bdf4d 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -60,7 +60,14 @@ val proc_unmangle_path : string -> string
(** Reverse kernel path escaping done in fs/seq_file.c:mangle_path.
This is inconsistently used for /proc fields. *)
-val command : string -> string list -> string
+type command_flag + CommandFlagFoldStdoutOnStderr
+ (** For broken external commands that send error messages to stdout
+ (hello, parted) but that don't have any useful stdout information,
+ use this flag to capture the error messages in the [stderr]
+ buffer. Nothing will be captured on stdout if you use this flag. *)
+
+val command : ?flags:command_flag list -> string -> string list ->
string
(** Run an external command without using the shell, and collect
stdout and stderr separately. Returns stdout if the command
runs successfully.
@@ -68,7 +75,7 @@ val command : string -> string list -> string
On failure of the command, this throws an exception containing
the stderr from the command. *)
-val commandr : string -> string list -> (int * string * string)
+val commandr : ?flags:command_flag list -> string -> string list ->
(int * string * string)
(** Run an external command without using the shell, and collect
stdout and stderr separately.
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 26/29] daemon: Reimplement ‘part_get_parttype’, ‘part_get_gpt_type’, ‘part_get_gpt_guid’ APIs in OCaml.
---
daemon/parted.c | 176 +++++-----------------------------------------
daemon/parted.ml | 74 ++++++++++++++++++-
daemon/parted.mli | 5 ++
generator/actions_core.ml | 3 +
4 files changed, 96 insertions(+), 162 deletions(-)
diff --git a/daemon/parted.c b/daemon/parted.c
index 125aec60b..1c81cd968 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -348,45 +348,6 @@ print_partition_table (const char *device, bool
add_m_option)
return out;
}
-char *
-do_part_get_parttype (const char *device)
-{
- CLEANUP_FREE char *out = print_partition_table (device, true);
- if (!out)
- return NULL;
-
- CLEANUP_FREE_STRING_LIST char **lines = split_lines (out);
- if (!lines)
- return NULL;
-
- if (lines[0] == NULL || STRNEQ (lines[0], "BYT;")) {
- reply_with_error ("unknown signature, expected \"BYT;\" as
first line of the output: %s",
- lines[0] ? lines[0] : "(signature was null)");
- return NULL;
- }
-
- if (lines[1] == NULL) {
- reply_with_error ("parted didn't return a line describing the
device");
- return NULL;
- }
-
- /* lines[1] is something like:
- * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
- */
- char *r = get_table_field (lines[1], 5);
- if (r == NULL)
- return NULL;
-
- /* If "loop" return an error (RHBZ#634246). */
- if (STREQ (r, "loop")) {
- free (r);
- reply_with_error ("not a partitioned device");
- return NULL;
- }
-
- return r;
-}
-
int
do_part_get_bootable (const char *device, int partnum)
{
@@ -557,126 +518,6 @@ do_part_set_gpt_guid (const char *device, int partnum,
const char *guid)
return 0;
}
-static char *
-sgdisk_info_extract_field (const char *device, int partnum, const char *field,
- char *(*extract) (const char *path))
-{
- if (partnum <= 0) {
- reply_with_error ("partition number must be >= 1");
- return NULL;
- }
-
- CLEANUP_FREE char *partnum_str = NULL;
- if (asprintf (&partnum_str, "%i", partnum) == -1) {
- reply_with_perror ("asprintf");
- return NULL;
- }
-
- udev_settle ();
-
- CLEANUP_FREE char *err = NULL;
- int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
- str_sgdisk, device, "-i", partnum_str, NULL);
-
- if (r == -1) {
- reply_with_error ("%s %s -i %s: %s", str_sgdisk, device,
partnum_str, err);
- return NULL;
- }
-
- udev_settle ();
-
- CLEANUP_FREE_STRING_LIST char **lines = split_lines (err);
- if (lines == NULL) {
- reply_with_error ("'%s %s -i %i' returned no output",
- str_sgdisk, device, partnum);
- return NULL;
- }
-
- const int fieldlen = strlen (field);
-
- /* 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'
- */
- for (char **i = lines; *i != NULL; i++) {
- char *line = *i;
-
- /* Skip blank lines */
- if (line[0] == '\0') continue;
-
- /* Split the line in 2 at the colon */
- char *colon = strchr (line, ':');
- if (colon) {
- if (colon - line == fieldlen &&
- memcmp (line, field, fieldlen) == 0)
- {
- /* The value starts after the colon */
- char *value = colon + 1;
-
- /* Skip any leading whitespace */
- value += strspn (value, " \t");
-
- /* Extract the actual information from the field. */
- char *ret = extract (value);
- if (ret == NULL) {
- /* The extraction function already sends the error. */
- return NULL;
- }
-
- return ret;
- }
- } else {
- /* Ignore lines with no colon. Log to stderr so it will show up in
- * LIBGUESTFS_DEBUG. */
- if (verbose) {
- fprintf (stderr, "get-gpt-type: unexpected sgdisk output ignored:
%s\n",
- line);
- }
- }
- }
-
- /* If we got here it means we didn't find the field */
- reply_with_error ("sgdisk output did not contain '%s'. "
- "See LIBGUESTFS_DEBUG output for more details",
field);
- 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_gpt_type (const char *device, int partnum)
-{
- return sgdisk_info_extract_field (device, partnum,
- "Partition GUID code",
extract_uuid);
-}
-
-char *
-do_part_get_gpt_guid (const char *device, int partnum)
-{
- return sgdisk_info_extract_field (device, partnum,
- "Partition unique GUID",
extract_uuid);
-}
-
char *
do_part_get_name (const char *device, int partnum)
{
@@ -840,6 +681,23 @@ do_part_get_mbr_part_type (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)
{
diff --git a/daemon/parted.ml b/daemon/parted.ml
index 37e1b42be..7c1e577dd 100644
--- a/daemon/parted.ml
+++ b/daemon/parted.ml
@@ -81,18 +81,18 @@ let print_partition_table ~add_m_option device let out =
String.trim out in
let lines = String.nsplit "\n" out in
- (* lines[0] is "BYT;", lines[1] is the device line which we ignore,
+ (* lines[0] is "BYT;", lines[1] is the device line,
* lines[2..] are the partitions themselves.
*)
match lines with
- | "BYT;" :: _ :: lines -> lines
+ | "BYT;" :: device_line :: lines -> device_line, lines
| [] | [_] ->
failwith "too few rows of output from 'parted print'
command"
| _ ->
failwith "did not see 'BYT;' magic value in 'parted
print' command"
let part_list device - let lines = print_partition_table ~add_m_option:true
device in
+ let _, lines = print_partition_table ~add_m_option:true device in
List.map (
fun line ->
@@ -104,3 +104,71 @@ let part_list device failwithf "could not
parse row from output of 'parted print' command: %s: %s"
line err
) lines
+
+let part_get_parttype device + let device_line, _ = print_partition_table
~add_m_option:true device in
+
+ (* device_line is something like:
+ * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;"
+ *)
+ let fields = String.nsplit ":" device_line in
+ match fields with
+ | _::_::_::_::_::"loop"::_ -> (* If "loop" return an
error (RHBZ#634246). *)
+ failwithf "%s: not a partitioned device" device
+ | _::_::_::_::_::ret::_ -> ret
+ | _ ->
+ failwithf "%s: cannot parse the output of parted" device
+
+let rec part_get_gpt_type device partnum + sgdisk_info_extract_uuid_field
device partnum "Partition GUID code"
+and part_get_gpt_guid device partnum + sgdisk_info_extract_uuid_field device
partnum "Partition unique GUID"
+
+and sgdisk_info_extract_uuid_field device partnum field + if partnum <= 0
then failwith "partition number must be >= 1";
+
+ udev_settle ();
+
+ let r, _, err + commandr ~flags:[CommandFlagFoldStdoutOnStderr]
+ "sgdisk" [ device; "-i"; string_of_int partnum
] in
+ if r <> 0 then
+ failwithf "sgdisk: %s" 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 UUID. *)
+ extract_uuid value
+
+ | _ :: lines -> loop lines
+ in
+ loop lines
+
+and extract_uuid value + (* The value contains only valid GUID characters. *)
+ String.sub value 0 (String.span value "-0123456789ABCDEF")
diff --git a/daemon/parted.mli b/daemon/parted.mli
index 057d7e8c7..5a77a8779 100644
--- a/daemon/parted.mli
+++ b/daemon/parted.mli
@@ -25,3 +25,8 @@ type partition = {
val part_get_mbr_id : string -> int -> int
val part_list : string -> partition list
+
+val part_get_parttype : string -> string
+
+val part_get_gpt_type : string -> int -> string
+val part_get_gpt_guid : string -> int -> string
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 4ec83d22d..c3421133e 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5073,6 +5073,7 @@ Size of the partition in bytes.
{ defaults with
name = "part_get_parttype"; added = (1, 0, 78);
style = RString (RPlainString, "parttype"), [String (Device,
"device")], [];
+ impl = OCaml "Parted.part_get_parttype";
tests = [
InitEmpty, Always, TestResultString (
[["part_disk"; "/dev/sda"; "gpt"];
@@ -8247,6 +8248,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";
tests = [
InitGPT, Always, TestResultString (
@@ -9067,6 +9069,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";
tests = [
InitGPT, Always, TestResultString (
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 27/29] lib: Move implementation of ‘hivex_value_utf8’ to new file ‘lib/hivex.c’.
Just a code movement, no change.
---
docs/C_SOURCE_FILES | 1 +
lib/Makefile.am | 1 +
lib/hivex.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++
lib/inspect-fs-windows.c | 83 -----------------------------------
4 files changed, 113 insertions(+), 83 deletions(-)
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 0b55c122f..39dcf9035 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -303,6 +303,7 @@ lib/guestfs-internal.h
lib/guestfs.h
lib/guid.c
lib/handle.c
+lib/hivex.c
lib/info.c
lib/inspect-apps.c
lib/inspect-fs-unix.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7eaff88ee..31568f933 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -92,6 +92,7 @@ libguestfs_la_SOURCES = \
fuse.c \
guid.c \
handle.c \
+ hivex.c \
info.c \
inspect.c \
inspect-apps.c \
diff --git a/lib/hivex.c b/lib/hivex.c
new file mode 100644
index 000000000..2d782e192
--- /dev/null
+++ b/lib/hivex.c
@@ -0,0 +1,111 @@
+/* libguestfs
+ * Copyright (C) 2010-2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <iconv.h>
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+
+/* Read the data from 'valueh', assume it is UTF16LE and convert it to
+ * UTF8. This is copied from hivex_value_string which doesn't work in
+ * the appliance because it uses iconv_open which doesn't work because
+ * we delete all the i18n databases.
+ */
+static char *utf16_to_utf8 (/* const */ char *input, size_t len);
+
+char *
+guestfs_impl_hivex_value_utf8 (guestfs_h *g, int64_t valueh)
+{
+ char *ret;
+ size_t buflen;
+
+ CLEANUP_FREE char *buf = guestfs_hivex_value_value (g, valueh, &buflen);
+ if (buf == NULL)
+ return NULL;
+
+ ret = utf16_to_utf8 (buf, buflen);
+ if (ret == NULL) {
+ perrorf (g, "hivex: conversion of registry value to UTF8
failed");
+ return NULL;
+ }
+
+ return ret;
+}
+
+static char *
+utf16_to_utf8 (/* const */ char *input, size_t len)
+{
+ iconv_t ic = iconv_open ("UTF-8", "UTF-16LE");
+ if (ic == (iconv_t) -1)
+ return NULL;
+
+ /* iconv(3) has an insane interface ... */
+
+ /* Mostly UTF-8 will be smaller, so this is a good initial guess. */
+ size_t outalloc = len;
+
+ again:;
+ size_t inlen = len;
+ size_t outlen = outalloc;
+ char *out = malloc (outlen + 1);
+ if (out == NULL) {
+ int err = errno;
+ iconv_close (ic);
+ errno = err;
+ return NULL;
+ }
+ char *inp = input;
+ char *outp = out;
+
+ const size_t r + iconv (ic, (ICONV_CONST char **) &inp, &inlen,
&outp, &outlen);
+ if (r == (size_t) -1) {
+ if (errno == E2BIG) {
+ const int err = errno;
+ const size_t prev = outalloc;
+ /* Try again with a larger output buffer. */
+ free (out);
+ outalloc *= 2;
+ if (outalloc < prev) {
+ iconv_close (ic);
+ errno = err;
+ return NULL;
+ }
+ goto again;
+ }
+ else {
+ /* Else some conversion failure, eg. EILSEQ, EINVAL. */
+ const int err = errno;
+ iconv_close (ic);
+ free (out);
+ errno = err;
+ return NULL;
+ }
+ }
+
+ *outp = '\0';
+ iconv_close (ic);
+
+ return out;
+}
diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c
index b14dc2e14..34f33c908 100644
--- a/lib/inspect-fs-windows.c
+++ b/lib/inspect-fs-windows.c
@@ -737,86 +737,3 @@ guestfs_int_case_sensitive_path_silently (guestfs_h *g,
const char *path)
return ret;
}
-
-/* Read the data from 'valueh', assume it is UTF16LE and convert it to
- * UTF8. This is copied from hivex_value_string which doesn't work in
- * the appliance because it uses iconv_open which doesn't work because
- * we delete all the i18n databases.
- */
-static char *utf16_to_utf8 (/* const */ char *input, size_t len);
-
-char *
-guestfs_impl_hivex_value_utf8 (guestfs_h *g, int64_t valueh)
-{
- char *ret;
- size_t buflen;
-
- CLEANUP_FREE char *buf = guestfs_hivex_value_value (g, valueh, &buflen);
- if (buf == NULL)
- return NULL;
-
- ret = utf16_to_utf8 (buf, buflen);
- if (ret == NULL) {
- perrorf (g, "hivex: conversion of registry value to UTF8
failed");
- return NULL;
- }
-
- return ret;
-}
-
-static char *
-utf16_to_utf8 (/* const */ char *input, size_t len)
-{
- iconv_t ic = iconv_open ("UTF-8", "UTF-16LE");
- if (ic == (iconv_t) -1)
- return NULL;
-
- /* iconv(3) has an insane interface ... */
-
- /* Mostly UTF-8 will be smaller, so this is a good initial guess. */
- size_t outalloc = len;
-
- again:;
- size_t inlen = len;
- size_t outlen = outalloc;
- char *out = malloc (outlen + 1);
- if (out == NULL) {
- int err = errno;
- iconv_close (ic);
- errno = err;
- return NULL;
- }
- char *inp = input;
- char *outp = out;
-
- const size_t r - iconv (ic, (ICONV_CONST char **) &inp, &inlen,
&outp, &outlen);
- if (r == (size_t) -1) {
- if (errno == E2BIG) {
- const int err = errno;
- const size_t prev = outalloc;
- /* Try again with a larger output buffer. */
- free (out);
- outalloc *= 2;
- if (outalloc < prev) {
- iconv_close (ic);
- errno = err;
- return NULL;
- }
- goto again;
- }
- else {
- /* Else some conversion failure, eg. EILSEQ, EINVAL. */
- const int err = errno;
- iconv_close (ic);
- free (out);
- errno = err;
- return NULL;
- }
- }
-
- *outp = '\0';
- iconv_close (ic);
-
- return out;
-}
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 28/29] daemon: Reimplement ‘device_index’ API in OCaml.
---
daemon/devsparts.c | 21 ---------------------
daemon/devsparts.ml | 11 +++++++++++
daemon/devsparts.mli | 6 ++----
generator/actions_core.ml | 1 +
4 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/daemon/devsparts.c b/daemon/devsparts.c
index 12e779326..7c65be1dc 100644
--- a/daemon/devsparts.c
+++ b/daemon/devsparts.c
@@ -33,27 +33,6 @@
#include "daemon.h"
#include "actions.h"
-int
-do_device_index (const char *device)
-{
- size_t i;
- int ret = -1;
- CLEANUP_FREE_STRING_LIST char **devices = do_list_devices ();
-
- if (devices == NULL)
- return -1;
-
- for (i = 0; devices[i] != NULL; ++i) {
- if (STREQ (device, devices[i]))
- ret = (int) i;
- }
-
- if (ret == -1)
- reply_with_error ("device not found");
-
- return ret;
-}
-
#define GUESTFSDIR "/dev/disk/guestfs"
char **
diff --git a/daemon/devsparts.ml b/daemon/devsparts.ml
index 273612516..4d273f59e 100644
--- a/daemon/devsparts.ml
+++ b/daemon/devsparts.ml
@@ -109,3 +109,14 @@ let is_whole_device device
try ignore (stat devpath); true
with Unix_error ((ENOENT|ENOTDIR), _, _) -> false
+
+let device_index device + (* This is the algorithm which was used by the C
version. Why
+ * can't we use drive_index from C_utils? XXX
+ *)
+ let rec loop i = function
+ | [] -> failwithf "%s: device not found" device
+ | dev :: devices when dev = device -> i
+ | _ :: devices -> loop (i+1) devices
+ in
+ loop 0 (list_devices ())
diff --git a/daemon/devsparts.mli b/daemon/devsparts.mli
index 8be47e752..4afb36bec 100644
--- a/daemon/devsparts.mli
+++ b/daemon/devsparts.mli
@@ -18,10 +18,8 @@
val list_devices : unit -> string list
val list_partitions : unit -> string list
-
-val nr_devices : unit -> int
-
val part_to_dev : string -> string
val part_to_partnum : string -> int
-
val is_whole_device : string -> bool
+val nr_devices : unit -> int
+val device_index : string -> int
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index c3421133e..ea0735676 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7419,6 +7419,7 @@ instead of, or after calling
C<guestfs_zero_free_space>." };
{ defaults with
name = "device_index"; added = (1, 19, 7);
style = RInt "index", [String (Device, "device")], [];
+ impl = OCaml "Devsparts.device_index";
tests = [
InitEmpty, Always, TestResult (
[["device_index"; "/dev/sda"]], "ret ==
0"), []
--
2.13.0
Richard W.M. Jones
2017-Jun-19 15:39 UTC
[Libguestfs] [PATCH v7 29/29] daemon: Reimplement most inspection APIs in the daemon, in OCaml.
Move the following APIs into the daemon, reimplemented in OCaml:
* inspect_os
* inspect_get_roots
* inspect_get_mountpoints
* inspect_get_filesystems
* inspect_get_format [deprecated]
* inspect_get_type
* inspect_get_distro
* inspect_get_package_format
* inspect_get_package_management
* inspect_get_product_name
* inspect_get_product_variant
* inspect_get_major_version
* inspect_get_minor_version
* inspect_get_arch
* inspect_get_hostname
* inspect_get_windows_systemroot
* inspect_get_windows_software_hive
* inspect_get_windows_system_hive
* inspect_get_windows_current_control_set
* inspect_get_drive_mappings
* inspect_is_live [deprecated]
* inspect_is_netinst [deprecated]
* inspect_is_multipart [deprecated]
The following inspection APIs have NOT been reimplemented in this commit:
* inspect_list_applications [deprecated]
* inspect_list_applications2
* inspect_get_icon
This also embeds the ocaml-augeas library (upstream here:
http://git.annexia.org/?p=ocaml-augeas.git;a=summary), but it's
identical to the upstream version and should remain so.
---
daemon/Makefile.am | 17 +
daemon/augeas-c.c | 288 ++++
daemon/augeas.README | 8 +
daemon/augeas.ml | 59 +
daemon/augeas.mli | 95 ++
daemon/chroot.ml | 2 +-
daemon/daemon_utils_tests.ml | 15 +
daemon/inspect.ml | 397 +++++
daemon/inspect.mli | 41 +
daemon/inspect_fs.ml | 336 +++++
daemon/inspect_fs.mli | 23 +
daemon/inspect_fs_unix.ml | 797 ++++++++++
daemon/inspect_fs_unix.mli | 53 +
daemon/inspect_fs_unix_fstab.ml | 518 +++++++
daemon/inspect_fs_unix_fstab.mli | 34 +
daemon/inspect_fs_windows.ml | 498 +++++++
daemon/inspect_fs_windows.mli | 25 +
daemon/inspect_types.ml | 317 ++++
daemon/inspect_types.mli | 168 +++
daemon/inspect_utils.ml | 175 +++
daemon/inspect_utils.mli | 51 +
daemon/mount.ml | 61 +
daemon/mount.mli | 2 +
daemon/utils.ml | 100 ++
daemon/utils.mli | 12 +
docs/C_SOURCE_FILES | 4 +-
generator/actions.ml | 2 +
generator/actions_inspection.ml | 394 ++---
generator/actions_inspection.mli | 1 +
generator/actions_inspection_deprecated.ml | 7 +
generator/actions_inspection_deprecated.mli | 1 +
generator/daemon.ml | 63 +-
generator/proc_nr.ml | 23 +
lib/MAX_PROC_NR | 2 +-
lib/Makefile.am | 3 -
lib/guestfs-internal.h | 185 +--
lib/handle.c | 1 -
lib/inspect-apps.c | 122 +-
lib/inspect-fs-unix.c | 2158 ---------------------------
lib/inspect-fs-windows.c | 739 ---------
lib/inspect-fs.c | 758 ----------
lib/inspect-icon.c | 261 ++--
lib/inspect.c | 732 +--------
lib/version.c | 28 +
44 files changed, 4600 insertions(+), 4976 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 5a5bc9855..222e45073 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -75,6 +75,7 @@ guestfsd_SOURCES = \
actions.h \
available.c \
augeas.c \
+ augeas-c.c \
base64.c \
blkdiscard.c \
blkid.c \
@@ -253,6 +254,7 @@ guestfsd_CFLAGS = \
# library and then linked to the daemon. See
# https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
SOURCES_MLI = \
+ augeas.mli \
blkid.mli \
btrfs.mli \
chroot.mli \
@@ -261,6 +263,13 @@ SOURCES_MLI = \
file.mli \
filearch.mli \
findfs.mli \
+ inspect.mli \
+ inspect_fs.mli \
+ inspect_fs_unix.mli \
+ inspect_fs_unix_fstab.mli \
+ inspect_fs_windows.mli \
+ inspect_types.mli \
+ inspect_utils.mli \
is.mli \
ldm.mli \
link.mli \
@@ -274,6 +283,7 @@ SOURCES_MLI = \
utils.mli
SOURCES_ML = \
+ augeas.ml \
types.ml \
utils.ml \
structs.ml \
@@ -295,6 +305,13 @@ SOURCES_ML = \
parted.ml \
listfs.ml \
realpath.ml \
+ inspect_types.ml \
+ inspect_utils.ml \
+ inspect_fs_unix_fstab.ml \
+ inspect_fs_unix.ml \
+ inspect_fs_windows.ml \
+ inspect_fs.ml \
+ inspect.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/augeas-c.c b/daemon/augeas-c.c
new file mode 100644
index 000000000..c06bf92da
--- /dev/null
+++ b/daemon/augeas-c.c
@@ -0,0 +1,288 @@
+/* Augeas OCaml bindings
+ * Copyright (C) 2008-2012 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas_c.c,v 1.1 2008/05/06 10:48:20 rjones Exp $
+ */
+
+#include "config.h"
+
+#include <augeas.h>
+
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+#include <caml/fail.h>
+#include <caml/callback.h>
+#include <caml/custom.h>
+
+typedef augeas *augeas_t;
+
+/* Raise an Augeas.Error exception. */
+static void
+raise_error (const char *msg)
+{
+ caml_raise_with_string (*caml_named_value ("Augeas.Error"), msg);
+}
+
+/* Map OCaml flags to C flags. */
+static int flag_map[] = {
+ /* AugSaveBackup */ AUG_SAVE_BACKUP,
+ /* AugSaveNewFile */ AUG_SAVE_NEWFILE,
+ /* AugTypeCheck */ AUG_TYPE_CHECK,
+ /* AugNoStdinc */ AUG_NO_STDINC,
+ /* AugSaveNoop */ AUG_SAVE_NOOP,
+ /* AugNoLoad */ AUG_NO_LOAD,
+};
+
+/* Wrap and unwrap augeas_t handles, with a finalizer. */
+#define Augeas_t_val(rv) (*(augeas_t *)Data_custom_val(rv))
+
+static void
+augeas_t_finalize (value tv)
+{
+ augeas_t t = Augeas_t_val (tv);
+ if (t) aug_close (t);
+}
+
+static struct custom_operations custom_operations = {
+ (char *) "augeas_t_custom_operations",
+ augeas_t_finalize,
+ custom_compare_default,
+ custom_hash_default,
+ custom_serialize_default,
+ custom_deserialize_default
+};
+
+static value Val_augeas_t (augeas_t t)
+{
+ CAMLparam0 ();
+ CAMLlocal1 (rv);
+ /* We could choose these so that the GC can make better decisions.
+ * See 18.9.2 of the OCaml manual.
+ */
+ const int used = 0;
+ const int max = 1;
+
+ rv = caml_alloc_custom (&custom_operations,
+ sizeof (augeas_t), used, max);
+ Augeas_t_val(rv) = t;
+
+ CAMLreturn (rv);
+}
+
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+/* val create : string -> string option -> flag list -> t */
+CAMLprim value
+ocaml_augeas_create (value rootv, value loadpathv, value flagsv)
+{
+ CAMLparam1 (rootv);
+ char *root = String_val (rootv);
+ char *loadpath;
+ int flags = 0, i;
+ augeas_t t;
+
+ /* Optional loadpath. */
+ loadpath + loadpathv == Val_int (0)
+ ? NULL
+ : String_val (Field (loadpathv, 0));
+
+ /* Convert list of flags to C. */
+ for (; flagsv != Val_int (0); flagsv = Field (flagsv, 1)) {
+ i = Int_val (Field (flagsv, 0));
+ flags |= flag_map[i];
+ }
+
+ t = aug_init (root, loadpath, flags);
+
+ if (t == NULL)
+ raise_error ("Augeas.create");
+
+ CAMLreturn (Val_augeas_t (t));
+}
+
+/* val close : t -> unit */
+CAMLprim value
+ocaml_augeas_close (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (t) {
+ aug_close (t);
+ Augeas_t_val(tv) = NULL; /* So the finalizer doesn't double-free. */
+ }
+
+ CAMLreturn (Val_unit);
+}
+
+/* val get : t -> path -> value option */
+CAMLprim value
+ocaml_augeas_get (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal2 (optv, v);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ const char *val;
+ int r;
+
+ r = aug_get (t, path, &val);
+ if (r == 1) { /* Return Some val */
+ v = caml_copy_string (val);
+ optv = caml_alloc (1, 0);
+ Field (optv, 0) = v;
+ } else if (r == 0) /* Return None */
+ optv = Val_int (0);
+ else if (r == -1) /* Error or multiple matches */
+ raise_error ("Augeas.get");
+ else
+ failwith ("Augeas.get: bad return value");
+
+ CAMLreturn (optv);
+}
+
+/* val exists : t -> path -> bool */
+CAMLprim value
+ocaml_augeas_exists (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal1 (v);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_get (t, path, NULL);
+ if (r == 1) /* Return true. */
+ v = Val_int (1);
+ else if (r == 0) /* Return false */
+ v = Val_int (0);
+ else if (r == -1) /* Error or multiple matches */
+ raise_error ("Augeas.exists");
+ else
+ failwith ("Augeas.exists: bad return value");
+
+ CAMLreturn (v);
+}
+
+/* val insert : t -> ?before:bool -> path -> string -> unit */
+CAMLprim value
+ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv)
+{
+ CAMLparam4 (tv, beforev, pathv, labelv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ char *label = String_val (labelv);
+ int before;
+
+ before = beforev == Val_int (0) ? 0 : Int_val (Field (beforev, 0));
+
+ if (aug_insert (t, path, label, before) == -1)
+ raise_error ("Augeas.insert");
+
+ CAMLreturn (Val_unit);
+}
+
+/* val rm : t -> path -> int */
+CAMLprim value
+ocaml_augeas_rm (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_rm (t, path);
+ if (r == -1)
+ raise_error ("Augeas.rm");
+
+ CAMLreturn (Val_int (r));
+}
+
+/* val matches : t -> path -> path list */
+CAMLprim value
+ocaml_augeas_match (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal3 (rv, v, cons);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ char **matches;
+ int r, i;
+
+ r = aug_match (t, path, &matches);
+ if (r == -1)
+ raise_error ("Augeas.matches");
+
+ /* Copy the paths to a list. */
+ rv = Val_int (0);
+ for (i = 0; i < r; ++i) {
+ v = caml_copy_string (matches[i]);
+ free (matches[i]);
+ cons = caml_alloc (2, 0);
+ Field (cons, 1) = rv;
+ Field (cons, 0) = v;
+ rv = cons;
+ }
+
+ free (matches);
+
+ CAMLreturn (rv);
+}
+
+/* val count_matches : t -> path -> int */
+CAMLprim value
+ocaml_augeas_count_matches (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_match (t, path, NULL);
+ if (r == -1)
+ raise_error ("Augeas.count_matches");
+
+ CAMLreturn (Val_int (r));
+}
+
+/* val save : t -> unit */
+CAMLprim value
+ocaml_augeas_save (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (aug_save (t) == -1)
+ raise_error ("Augeas.save");
+
+ CAMLreturn (Val_unit);
+}
+
+/* val load : t -> unit */
+CAMLprim value
+ocaml_augeas_load (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (aug_load (t) == -1)
+ raise_error ("Augeas.load");
+
+ CAMLreturn (Val_unit);
+}
diff --git a/daemon/augeas.README b/daemon/augeas.README
new file mode 100644
index 000000000..938dfd255
--- /dev/null
+++ b/daemon/augeas.README
@@ -0,0 +1,8 @@
+The files augeas-c.c, augeas.ml and augeas.mli come from the
+ocaml-augeas library:
+
+ http://git.annexia.org/?p=ocaml-augeas.git
+
+which is released under a compatible license. We try to keep them
+identical, so if you make changes to these files then you must also
+submit the changes to ocaml-augeas, and vice versa.
\ No newline at end of file
diff --git a/daemon/augeas.ml b/daemon/augeas.ml
new file mode 100644
index 000000000..f556df0f1
--- /dev/null
+++ b/daemon/augeas.ml
@@ -0,0 +1,59 @@
+(* Augeas OCaml bindings
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas.ml,v 1.2 2008/05/06 10:48:20 rjones Exp $
+ *)
+
+type t
+
+exception Error of string
+
+type flag + | AugSaveBackup
+ | AugSaveNewFile
+ | AugTypeCheck
+ | AugNoStdinc
+ | AugSaveNoop
+ | AugNoLoad
+
+type path = string
+
+type value = string
+
+external create : string -> string option -> flag list -> t
+ = "ocaml_augeas_create"
+external close : t -> unit
+ = "ocaml_augeas_close"
+external get : t -> path -> value option
+ = "ocaml_augeas_get"
+external exists : t -> path -> bool
+ = "ocaml_augeas_exists"
+external insert : t -> ?before:bool -> path -> string -> unit
+ = "ocaml_augeas_insert"
+external rm : t -> path -> int
+ = "ocaml_augeas_rm"
+external matches : t -> path -> path list
+ = "ocaml_augeas_match"
+external count_matches : t -> path -> int
+ = "ocaml_augeas_count_matches"
+external save : t -> unit
+ = "ocaml_augeas_save"
+external load : t -> unit
+ = "ocaml_augeas_load"
+
+let () + Callback.register_exception "Augeas.Error" (Error
"")
diff --git a/daemon/augeas.mli b/daemon/augeas.mli
new file mode 100644
index 000000000..64e824014
--- /dev/null
+++ b/daemon/augeas.mli
@@ -0,0 +1,95 @@
+(** Augeas OCaml bindings *)
+(* Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas.mli,v 1.2 2008/05/06 10:48:20 rjones Exp $
+ *)
+
+type t
+ (** Augeas library handle. *)
+
+exception Error of string
+ (** This exception is thrown when the underlying Augeas library
+ returns an error. *)
+
+type flag + | AugSaveBackup (** Rename original with .augsave *)
+ | AugSaveNewFile (** Save changes to .augnew *)
+ | AugTypeCheck (** Type-check lenses *)
+ | AugNoStdinc
+ | AugSaveNoop
+ | AugNoLoad
+ (** Flags passed to the {!create} function. *)
+
+type path = string
+ (** A path expression.
+
+ Note in future we may replace this with a type-safe path constructor. *)
+
+type value = string
+ (** A value. *)
+
+val create : string -> string option -> flag list -> t
+ (** [create root loadpath flags] creates an Augeas handle.
+
+ [root] is a file system path describing the location
+ of the configuration files.
+
+ [loadpath] is an optional colon-separated list of directories
+ which are searched for schema definitions.
+
+ [flags] is a list of flags. *)
+
+val close : t -> unit
+ (** [close handle] closes the handle.
+
+ You don't need to close handles explicitly with this function:
+ they will be finalized eventually by the garbage collector.
+ However calling this function frees up any resources used by the
+ underlying Augeas library immediately.
+
+ Do not use the handle after closing it. *)
+
+val get : t -> path -> value option
+ (** [get t path] returns the value at [path], or [None] if there
+ is no value. *)
+
+val exists : t -> path -> bool
+ (** [exists t path] returns true iff there is a value at [path]. *)
+
+val insert : t -> ?before:bool -> path -> string -> unit
+ (** [insert t ?before path label] inserts [label] as a sibling
+ of [path]. By default it is inserted after [path], unless
+ [~before:true] is specified. *)
+
+val rm : t -> path -> int
+ (** [rm t path] removes all nodes matching [path].
+
+ Returns the number of nodes removed (which may be 0). *)
+
+val matches : t -> path -> path list
+ (** [matches t path] returns a list of path expressions
+ of all nodes matching [path]. *)
+
+val count_matches : t -> path -> int
+ (** [count_matches t path] counts the number of nodes matching
+ [path] but does not return them (see {!matches}). *)
+
+val save : t -> unit
+ (** [save t] saves all pending changes to disk. *)
+
+val load : t -> unit
+ (** [load t] loads files into the tree. *)
diff --git a/daemon/chroot.ml b/daemon/chroot.ml
index 40dfa1dde..0fddfcffa 100644
--- a/daemon/chroot.ml
+++ b/daemon/chroot.ml
@@ -32,7 +32,7 @@ let create ?(name = prog) chroot
let f t func arg if verbose () then
- eprintf "chroot: %s: running ‘%s’\n%!" t.chroot t.name;
+ eprintf "chroot: %s: running '%s'\n%!" t.chroot t.name;
let rfd, wfd = pipe () in
diff --git a/daemon/daemon_utils_tests.ml b/daemon/daemon_utils_tests.ml
index 892509d89..b1f02de30 100644
--- a/daemon/daemon_utils_tests.ml
+++ b/daemon/daemon_utils_tests.ml
@@ -46,3 +46,18 @@ let () let () assert (proc_unmangle_path
"\\040" = " ");
assert (proc_unmangle_path "\\040\\040" = " ")
+
+(* Test unix_canonical_path. *)
+let () + assert (unix_canonical_path "/" = "/");
+ assert (unix_canonical_path "/usr" = "/usr");
+ assert (unix_canonical_path "/usr/" = "/usr");
+ assert (unix_canonical_path "/usr/local" = "/usr/local");
+ assert (unix_canonical_path "///" = "/");
+ assert (unix_canonical_path "///usr//local//" =
"/usr/local");
+ assert (unix_canonical_path "/usr///" = "/usr")
+
+(* Test utf16le_to_utf8. *)
+let () + assert (utf16le_to_utf8
"\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00" =
"Windows");
+ assert (utf16le_to_utf8
"\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\xae\x00" =
"Windows\xc2\xae")
diff --git a/daemon/inspect.ml b/daemon/inspect.ml
new file mode 100644
index 000000000..eaad5119c
--- /dev/null
+++ b/daemon/inspect.ml
@@ -0,0 +1,397 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+open Mountable
+open Inspect_types
+
+let re_primary_partition = Str.regexp "^/dev/(h|s|v)d.[1234]$"
+
+let rec inspect_os () + Mount.umount_all ();
+
+ (* Iterate over all detected filesystems. Inspect each one in turn. *)
+ let fses = Listfs.list_filesystems () in
+
+ let fses + filter_map (
+ fun (mountable, vfs_type) ->
+ Inspect_fs.check_for_filesystem_on mountable vfs_type
+ ) fses in
+ if verbose () then (
+ eprintf "inspect_os: fses:\n";
+ List.iter (fun fs -> eprintf "\t%s\n" (string_of_fs fs)) fses;
+ flush stderr
+ );
+
+ (* The OS inspection information for CoreOS are gathered by inspecting
+ * multiple filesystems. Gather all the inspected information in the
+ * inspect_fs struct of the root filesystem.
+ *)
+ let fses = collect_coreos_inspection_info fses in
+
+ (* Check if the same filesystem was listed twice as root in fses.
+ * This may happen for the *BSD root partition where an MBR partition
+ * is a shadow of the real root partition probably /dev/sda5
+ *)
+ let fses = check_for_duplicated_bsd_root fses in
+
+ (* For Linux guests with a separate /usr filesystem, merge some of the
+ * inspected information in that partition to the inspect_fs struct
+ * of the root filesystem.
+ *)
+ let fses = collect_linux_inspection_info fses in
+
+ (* Save what we found in a global variable. *)
+ Inspect_types.inspect_fses := fses;
+
+ (* At this point we have, in the handle, a list of all filesystems
+ * found and data about each one. Now we assemble the list of
+ * filesystems which are root devices.
+ *
+ * Fall through to inspect_get_roots to do that.
+ *)
+ inspect_get_roots ()
+
+(* Traverse through the filesystem list and find out if it contains
+ * the [/] and [/usr] filesystems of a CoreOS image. If this is the
+ * case, sum up all the collected information on the root fs.
+ *)
+and collect_coreos_inspection_info fses + (* Split the list into CoreOS
root(s), CoreOS usr(s), and
+ * everything else.
+ *)
+ let rec loop roots usrs others = function
+ | [] -> roots, usrs, others
+ | ({ role = RoleRoot { distro = Some DISTRO_COREOS } } as r) :: rest ->
+ loop (r::roots) usrs others rest
+ | ({ role = RoleUsr { distro = Some DISTRO_COREOS } } as u) :: rest ->
+ loop roots (u::usrs) others rest
+ | o :: rest ->
+ loop roots usrs (o::others) rest
+ in
+ let roots, usrs, others = loop [] [] [] fses in
+
+ match roots with
+ (* If there are no CoreOS roots, then there's nothing to do. *)
+ | [] -> fses
+ (* If there are more than one CoreOS roots, we cannot inspect the guest. *)
+ | _::_::_ -> failwith "multiple CoreOS root filesystems found"
+ | [root] ->
+ match usrs with
+ (* If there are no CoreOS usr partitions, nothing to do. *)
+ | [] -> fses
+ | usrs ->
+ (* CoreOS is designed to contain 2 /usr partitions (USR-A, USR-B):
+ * https://coreos.com/docs/sdk-distributors/sdk/disk-partitions/
+ * One is active and one passive. During the initial boot, the
+ * passive partition is empty and it gets filled up when an
+ * update is performed. Then, when the system reboots, the
+ * boot loader is instructed to boot from the passive partition.
+ * If both partitions are valid, we cannot determine which the
+ * active and which the passive is, unless we peep into the
+ * boot loader. As a workaround, we check the OS versions and
+ * pick the one with the higher version as active.
+ *)
+ let compare_versions u1 u2 + let v1 + match u1 with
+ | { role = RoleRoot { version = Some v } } -> v
+ | { role = RoleUsr { version = Some v } } -> v
+ | _ -> (0, 0) in
+ let v2 + match u2 with
+ | { role = RoleRoot { version = Some v } } -> v
+ | { role = RoleUsr { version = Some v } } -> v
+ | _ -> (0, 0) in
+ compare v2 v1 (* reverse order *)
+ in
+ let usrs = List.sort compare_versions usrs in
+ let usr = List.hd usrs in
+
+ let root = merge usr root in
+ root :: others
+
+(* On *BSD systems, sometimes [/dev/sda[1234]] is a shadow of the
+ * real root filesystem that is probably [/dev/sda5] (see:
+ * [http://www.freebsd.org/doc/handbook/disk-organization.html])
+ *)
+and check_for_duplicated_bsd_root fses + try
+ let is_primary_partition = function
+ | { m_type = (MountablePath | MountableBtrfsVol _) } -> false
+ | { m_type = MountableDevice; m_device = d } ->
+ Str.string_match re_primary_partition d 0
+ in
+
+ (* Try to find a "BSD primary", if there is one. *)
+ let bsd_primary + List.find (
+ function
+ | { fs_location = { mountable = mountable };
+ role = RoleRoot { os_type = Some t } } ->
+ (t = OS_TYPE_FREEBSD || t = OS_TYPE_NETBSD || t = OS_TYPE_OPENBSD)
+ && is_primary_partition mountable
+ | _ -> false
+ ) fses in
+
+ let bsd_primary_os_type + match bsd_primary with
+ | { role = RoleRoot { os_type = Some t } } -> t
+ | _ -> assert false in
+
+ (* Try to find a shadow of the primary, and if it is found the
+ * primary is removed.
+ *)
+ let fses_without_bsd_primary = List.filter ((!=) bsd_primary) fses in
+ let shadow_exists + List.exists (
+ function
+ | { role = RoleRoot { os_type = Some t } } ->
+ t = bsd_primary_os_type
+ | _ -> false
+ ) fses_without_bsd_primary in
+ if shadow_exists then fses_without_bsd_primary else fses
+ with
+ Not_found -> fses
+
+(* Traverse through the filesystem list and find out if it contains
+ * the [/] and [/usr] filesystems of a Linux image (but not CoreOS,
+ * for which there is a separate [collect_coreos_inspection_info]).
+ *
+ * If this is the case, sum up all the collected information on each
+ * root fs from the respective [/usr] filesystems.
+ *)
+and collect_linux_inspection_info fses + List.map (
+ function
+ | { role = RoleRoot { distro = Some d } } as root ->
+ if d <> DISTRO_COREOS then
+ collect_linux_inspection_info_for fses root
+ else
+ root
+ | fs -> fs
+ ) fses
+
+(* Traverse through the filesystems and find the /usr filesystem for
+ * the specified C<root>: if found, merge its basic inspection details
+ * to the root when they were set (i.e. because the /usr had os-release
+ * or other ways to identify the OS).
+ *)
+and collect_linux_inspection_info_for fses root + let root_distro, root_fstab
+ match root with
+ | { role = RoleRoot { distro = Some d; fstab = f } } -> d, f
+ | _ -> assert false in
+
+ try
+ let usr + List.find (
+ function
+ | { role = RoleUsr { distro = d } }
+ when d = Some root_distro || d = None -> true
+ | _ -> false
+ ) fses in
+
+ let usr_mountable = usr.fs_location.mountable in
+
+ (* This checks that [usr] is found in the fstab of the root
+ * filesystem. If not, [Not_found] is thrown.
+ *)
+ ignore (
+ List.find (fun (mountable, _) -> usr_mountable = mountable) root_fstab
+ );
+
+ merge usr root
+ with
+ Not_found -> root
+
+and inspect_get_roots () + let fses = !Inspect_types.inspect_fses in
+
+ let roots + filter_map (
+ fun fs -> try Some (root_of_fs fs) with Invalid_argument _ -> None
+ ) fses in
+ if verbose () then (
+ eprintf "inspect_get_roots: roots:\n";
+ List.iter (fun root -> eprintf "%s" (string_of_root root))
roots;
+ flush stderr
+ );
+
+ (* Only return the list of mountables, since subsequent calls will
+ * be used to retrieve the other information.
+ *)
+ List.map (fun { root_location = { mountable = m } } -> m) roots
+
+and root_of_fs + function
+ | { fs_location = location; role = RoleRoot data } ->
+ { root_location = location; inspection_data = data }
+ | { role = (RoleUsr _ | RoleSwap | RoleOther) } ->
+ invalid_arg "root_of_fs"
+
+and inspect_get_mountpoints root_mountable + let root = search_for_root
root_mountable in
+ let fstab = root.inspection_data.fstab in
+
+ (* If no fstab information (Windows) return just the root. *)
+ if fstab = [] then
+ [ "/", root_mountable ]
+ else (
+ filter_map (
+ fun (mountable, mp) ->
+ if String.length mp > 0 && mp.[0] = '/' then
+ Some (mp, mountable)
+ else
+ None
+ ) fstab
+ )
+
+and inspect_get_filesystems root_mountable + let root = search_for_root
root_mountable in
+ let fstab = root.inspection_data.fstab in
+
+ (* If no fstab information (Windows) return just the root. *)
+ if fstab = [] then
+ [ root_mountable ]
+ else
+ List.map fst fstab
+
+and inspect_get_format root = "installed"
+
+and inspect_get_type root + let root = search_for_root root in
+ match root.inspection_data.os_type with
+ | Some v -> string_of_os_type v
+ | None -> "unknown"
+
+and inspect_get_distro root + let root = search_for_root root in
+ match root.inspection_data.distro with
+ | Some v -> string_of_distro v
+ | None -> "unknown"
+
+and inspect_get_package_format root + let root = search_for_root root in
+ match root.inspection_data.package_format with
+ | Some v -> string_of_package_format v
+ | None -> "unknown"
+
+and inspect_get_package_management root + let root = search_for_root root in
+ match root.inspection_data.package_management with
+ | Some v -> string_of_package_management v
+ | None -> "unknown"
+
+and inspect_get_product_name root + let root = search_for_root root in
+ match root.inspection_data.product_name with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_product_variant root + let root = search_for_root root in
+ match root.inspection_data.product_variant with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_major_version root + let root = search_for_root root in
+ match root.inspection_data.version with
+ | Some (major, _) -> major
+ | None -> 0
+
+and inspect_get_minor_version root + let root = search_for_root root in
+ match root.inspection_data.version with
+ | Some (_, minor) -> minor
+ | None -> 0
+
+and inspect_get_arch root + let root = search_for_root root in
+ match root.inspection_data.arch with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_hostname root + let root = search_for_root root in
+ match root.inspection_data.hostname with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_windows_systemroot root + let root = search_for_root root in
+ match root.inspection_data.windows_systemroot with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or systemroot could not be
determined"
+
+and inspect_get_windows_system_hive root + let root = search_for_root root in
+ match root.inspection_data.windows_system_hive with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or system hive not found"
+
+and inspect_get_windows_software_hive root + let root = search_for_root root
in
+ match root.inspection_data.windows_software_hive with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or software hive not found"
+
+and inspect_get_windows_current_control_set root + let root = search_for_root
root in
+ match root.inspection_data.windows_current_control_set with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or CurrentControlSet could not be
determined"
+
+and inspect_is_live root = false
+
+and inspect_is_netinst root = false
+
+and inspect_is_multipart root = false
+
+and inspect_get_drive_mappings root + let root = search_for_root root in
+ root.inspection_data.drive_mappings
+
+and search_for_root root + let fses = !Inspect_types.inspect_fses in
+ if fses = [] then
+ failwith "no inspection data: call guestfs_inspect_os first";
+
+ let root + try
+ List.find (
+ function
+ | { fs_location = { mountable = m }; role = RoleRoot _ } -> root = m
+ | _ -> false
+ ) fses
+ with
+ Not_found ->
+ failwithf "%s: root device not found: only call this function with
a root device previously returned by guestfs_inspect_os"
+ (Mountable.to_string root) in
+
+ root_of_fs root
diff --git a/daemon/inspect.mli b/daemon/inspect.mli
new file mode 100644
index 000000000..29a1c1759
--- /dev/null
+++ b/daemon/inspect.mli
@@ -0,0 +1,41 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val inspect_os : unit -> Mountable.t list
+val inspect_get_roots : unit -> Mountable.t list
+val inspect_get_mountpoints : Mountable.t -> (string * Mountable.t) list
+val inspect_get_filesystems : Mountable.t -> Mountable.t list
+val inspect_get_format : Mountable.t -> string
+val inspect_get_type : Mountable.t -> string
+val inspect_get_distro : Mountable.t -> string
+val inspect_get_package_format : Mountable.t -> string
+val inspect_get_package_management : Mountable.t -> string
+val inspect_get_product_name : Mountable.t -> string
+val inspect_get_product_variant : Mountable.t -> string
+val inspect_get_major_version : Mountable.t -> int
+val inspect_get_minor_version : Mountable.t -> int
+val inspect_get_arch : Mountable.t -> string
+val inspect_get_hostname : Mountable.t -> string
+val inspect_get_windows_systemroot : Mountable.t -> string
+val inspect_get_windows_software_hive : Mountable.t -> string
+val inspect_get_windows_system_hive : Mountable.t -> string
+val inspect_get_windows_current_control_set : Mountable.t -> string
+val inspect_get_drive_mappings : Mountable.t -> (string * string) list
+val inspect_is_live : Mountable.t -> bool
+val inspect_is_netinst : Mountable.t -> bool
+val inspect_is_multipart : Mountable.t -> bool
diff --git a/daemon/inspect_fs.ml b/daemon/inspect_fs.ml
new file mode 100644
index 000000000..ddf3cb350
--- /dev/null
+++ b/daemon/inspect_fs.ml
@@ -0,0 +1,336 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Mountable
+open Inspect_types
+open Inspect_utils
+
+let rec check_for_filesystem_on mountable vfs_type + if verbose () then
+ eprintf "check_for_filesystem_on: %s (%s)\n%!"
+ (Mountable.to_string mountable) vfs_type;
+
+ let role + let is_swap = vfs_type = "swap" in
+ if is_swap then
+ Some RoleSwap
+ else (
+ (* Try mounting the device. Ignore errors if we can't do this. *)
+ let mounted + if vfs_type = "ufs" then ( (* Hack for the
*BSDs. *)
+ (* FreeBSD fs is a variant of ufs called ufs2 ... *)
+ try
+ Mount.mount_vfs (Some "ro,ufstype=ufs2") (Some
"ufs")
+ mountable "/";
+ true
+ with _ ->
+ (* while NetBSD and OpenBSD use another variant labeled 44bsd *)
+ try
+ Mount.mount_vfs (Some "ro,ufstype=44bsd") (Some
"ufs")
+ mountable "/";
+ true
+ with _ -> false
+ ) else (
+ try Mount.mount_ro mountable "/";
+ true
+ with _ -> false
+ ) in
+ if not mounted then None
+ else (
+ let role = check_filesystem mountable in
+ Mount.umount_all ();
+ role
+ )
+ ) in
+
+ match role with
+ | None -> None
+ | Some role ->
+ Some { fs_location = { mountable = mountable; vfs_type = vfs_type };
+ role = role }
+
+(* When this function is called, the filesystem is mounted on sysroot (). *)
+and check_filesystem mountable + let role = ref `Other in
+ let data = ref null_inspection_data in
+
+ (* Grub /boot? *)
+ if Is.is_file "/grub/menu.lst" ||
+ Is.is_file "/grub/grub.conf" ||
+ Is.is_file "/grub2/grub.cfg" then
+ ()
+ (* FreeBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/etc/freebsd-update.conf" &&
+ Is.is_file "/etc/fstab" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_freebsd_root mountable !data
+ )
+ (* NetBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/netbsd" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/release" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_netbsd_root mountable !data;
+ )
+ (* OpenBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/bsd" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/motd" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_openbsd_root mountable !data;
+ )
+ (* Hurd root? *)
+ else if Is.is_file "/hurd/console" &&
+ Is.is_file "/hurd/hello" &&
+ Is.is_file "/hurd/null" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_hurd_root mountable !data;
+ )
+ (* Minix root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/service/vm" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/version" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_minix_root !data;
+ )
+ (* Linux root? *)
+ else if Is.is_dir "/etc" &&
+ (Is.is_dir "/bin" ||
+ is_symlink_to "/bin" "usr/bin") &&
+ (Is.is_file "/etc/fstab" ||
+ Is.is_file "/etc/hosts") then (
+ role := `Root;
+ data := Inspect_fs_unix.check_linux_root mountable !data;
+ )
+ (* CoreOS root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/root" &&
+ Is.is_dir "/home" &&
+ Is.is_dir "/usr" &&
+ Is.is_file "/etc/coreos/update.conf" then (
+ role := `Root;
+ data := Inspect_fs_unix.check_coreos_root mountable !data;
+ )
+ (* Linux /usr/local? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ not (Is.is_dir "/local") &&
+ not (Is.is_file "/etc/fstab") then
+ ()
+ (* Linux /usr? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ Is.is_dir "/local" &&
+ not (Is.is_file "/etc/fstab") then (
+ role := `Usr;
+ data := Inspect_fs_unix.check_linux_usr !data;
+ )
+ (* CoreOS /usr? *)
+ else if Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ Is.is_dir "/local" &&
+ Is.is_dir "/share/coreos" then (
+ role := `Usr;
+ data := Inspect_fs_unix.check_coreos_usr mountable !data;
+ )
+ (* Linux /var? *)
+ else if Is.is_dir "/log" &&
+ Is.is_dir "/run" &&
+ Is.is_dir "/spool" then
+ ()
+ (* Windows root? *)
+ else if Inspect_fs_windows.is_windows_systemroot () then (
+ role := `Root;
+ data := Inspect_fs_windows.check_windows_root !data;
+ )
+ (* Windows volume with installed applications (but not root)? *)
+ else if is_dir_nocase "/System Volume Information" &&
+ is_dir_nocase "/Program Files" then
+ ()
+ (* Windows volume (but not root)? *)
+ else if is_dir_nocase "/System Volume Information" then
+ ()
+ (* FreeDOS? *)
+ else if is_dir_nocase "/FDOS" &&
+ is_file_nocase "/FDOS/FREEDOS.BSS" then (
+ role := `Root;
+ data := { !data with
+ os_type = Some OS_TYPE_DOS;
+ distro = Some DISTRO_FREEDOS;
+ (* FreeDOS is a mix of 16 and 32 bit, but
+ * assume it requires a 32 bit i386 processor.
+ *)
+ arch = Some "i386" }
+ );
+
+ (* The above code should have set [data.os_type] and [data.distro]
+ * fields, so we can now guess the package management system.
+ *)
+ let data = !data in
+ let data = { data with
+ package_format = check_package_format data;
+ package_management = check_package_management data } in
+ match !role with
+ | `Root -> Some (RoleRoot data)
+ | `Usr -> Some (RoleUsr data)
+ | `Other -> Some RoleOther
+
+and is_symlink_to file wanted_target + if not (Is.is_symlink file) then false
+ else Link.readlink file = wanted_target
+
+(* At the moment, package format and package management are just a
+ * simple function of the [distro] and [version[0]] fields, so these
+ * can never return an error. We might be cleverer in future.
+ *)
+and check_package_format { distro = distro } + match distro with
+ | None -> None
+ | Some DISTRO_FEDORA
+ | Some DISTRO_MEEGO
+ | Some DISTRO_REDHAT_BASED
+ | Some DISTRO_RHEL
+ | Some DISTRO_MAGEIA
+ | Some DISTRO_MANDRIVA
+ | Some DISTRO_SUSE_BASED
+ | Some DISTRO_OPENSUSE
+ | Some DISTRO_SLES
+ | Some DISTRO_CENTOS
+ | Some DISTRO_SCIENTIFIC_LINUX
+ | Some DISTRO_ORACLE_LINUX
+ | Some DISTRO_ALTLINUX ->
+ Some PACKAGE_FORMAT_RPM
+ | Some DISTRO_DEBIAN
+ | Some DISTRO_UBUNTU
+ | Some DISTRO_LINUX_MINT ->
+ Some PACKAGE_FORMAT_DEB
+ | Some DISTRO_ARCHLINUX ->
+ Some PACKAGE_FORMAT_PACMAN
+ | Some DISTRO_GENTOO ->
+ Some PACKAGE_FORMAT_EBUILD
+ | Some DISTRO_PARDUS ->
+ Some PACKAGE_FORMAT_PISI
+ | Some DISTRO_ALPINE_LINUX ->
+ Some PACKAGE_FORMAT_APK
+ | Some DISTRO_VOID_LINUX ->
+ Some PACKAGE_FORMAT_XBPS
+ | Some DISTRO_SLACKWARE
+ | Some DISTRO_TTYLINUX
+ | Some DISTRO_COREOS
+ | Some DISTRO_WINDOWS
+ | Some DISTRO_BUILDROOT
+ | Some DISTRO_CIRROS
+ | Some DISTRO_FREEDOS
+ | Some DISTRO_FREEBSD
+ | Some DISTRO_NETBSD
+ | Some DISTRO_OPENBSD
+ | Some DISTRO_FRUGALWARE
+ | Some DISTRO_PLD_LINUX ->
+ None
+
+and check_package_management { distro = distro; version = version } + let
major = match version with None -> 0 | Some (major, _) -> major in
+ match distro with
+ | None -> None
+
+ | Some DISTRO_MEEGO ->
+ Some PACKAGE_MANAGEMENT_YUM
+
+ | Some DISTRO_FEDORA ->
+ (* If Fedora >= 22 and dnf is installed, say "dnf". *)
+ if major >= 22 && Is.is_file ~followsymlinks:true
"/usr/bin/dnf" then
+ Some PACKAGE_MANAGEMENT_DNF
+ else if major >= 1 then
+ Some PACKAGE_MANAGEMENT_YUM
+ else
+ (* Probably parsing the release file failed, see RHBZ#1332025. *)
+ None
+
+ | Some DISTRO_REDHAT_BASED
+ | Some DISTRO_RHEL
+ | Some DISTRO_CENTOS
+ | Some DISTRO_SCIENTIFIC_LINUX
+ | Some DISTRO_ORACLE_LINUX ->
+ if major >= 8 then
+ Some PACKAGE_MANAGEMENT_DNF
+ else if major >= 5 then
+ Some PACKAGE_MANAGEMENT_YUM
+ else if major >= 2 then
+ Some PACKAGE_MANAGEMENT_UP2DATE
+ else
+ (* Probably parsing the release file failed, see RHBZ#1332025. *)
+ None
+
+ | Some DISTRO_DEBIAN
+ | Some DISTRO_UBUNTU
+ | Some DISTRO_LINUX_MINT
+ | Some DISTRO_ALTLINUX ->
+ Some PACKAGE_MANAGEMENT_APT
+
+ | Some DISTRO_ARCHLINUX ->
+ Some PACKAGE_MANAGEMENT_PACMAN
+
+ | Some DISTRO_GENTOO ->
+ Some PACKAGE_MANAGEMENT_PORTAGE
+
+ | Some DISTRO_PARDUS ->
+ Some PACKAGE_MANAGEMENT_PISI
+
+ | Some DISTRO_MAGEIA
+ | Some DISTRO_MANDRIVA ->
+ Some PACKAGE_MANAGEMENT_URPMI
+
+ | Some DISTRO_SUSE_BASED
+ | Some DISTRO_OPENSUSE
+ | Some DISTRO_SLES ->
+ Some PACKAGE_MANAGEMENT_ZYPPER
+
+ | Some DISTRO_ALPINE_LINUX ->
+ Some PACKAGE_MANAGEMENT_APK
+
+ | Some DISTRO_VOID_LINUX ->
+ Some PACKAGE_MANAGEMENT_XBPS;
+
+ | Some DISTRO_SLACKWARE
+ | Some DISTRO_TTYLINUX
+ | Some DISTRO_COREOS
+ | Some DISTRO_WINDOWS
+ | Some DISTRO_BUILDROOT
+ | Some DISTRO_CIRROS
+ | Some DISTRO_FREEDOS
+ | Some DISTRO_FREEBSD
+ | Some DISTRO_NETBSD
+ | Some DISTRO_OPENBSD
+ | Some DISTRO_FRUGALWARE
+ | Some DISTRO_PLD_LINUX ->
+ None
+
diff --git a/daemon/inspect_fs.mli b/daemon/inspect_fs.mli
new file mode 100644
index 000000000..53ea01587
--- /dev/null
+++ b/daemon/inspect_fs.mli
@@ -0,0 +1,23 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val check_for_filesystem_on : Mountable.t -> string ->
+ Inspect_types.fs option
+(** [check_for_filesystem_on cmdline mountable vfs_type] inspects
+ [mountable] looking for a single mountpoint from an operating
+ system. *)
diff --git a/daemon/inspect_fs_unix.ml b/daemon/inspect_fs_unix.ml
new file mode 100644
index 000000000..3c4d03ca7
--- /dev/null
+++ b/daemon/inspect_fs_unix.ml
@@ -0,0 +1,797 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 C_utils
+open Std_utils
+
+open Utils
+open Inspect_types
+open Inspect_utils
+
+let re_fedora = Str.regexp "Fedora release \\([0-9]+\\)"
+let re_rhel_old = Str.regexp "Red Hat.*release \\([0-9]+\\).*Update
\\([0-9]+\\)"
+let re_rhel = Str.regexp "Red Hat.*release
\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_rhel_no_minor = Str.regexp "Red Hat.*release \\([0-9]+\\)"
+let re_centos_old = Str.regexp "CentOS.*release \\([0-9]+\\).*Update
\\([0-9]+\\)"
+let re_centos = Str.regexp "CentOS.*release
\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_centos_no_minor = Str.regexp "CentOS.*release \\([0-9]+\\)"
+let re_scientific_linux_old + Str.regexp "Scientific Linux.*release
\\([0-9]+\\).*Update \\([0-9]+\\)"
+let re_scientific_linux + Str.regexp "Scientific Linux.*release
\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_scientific_linux_no_minor + Str.regexp "Scientific Linux.*release
\\([0-9]+\\)"
+let re_oracle_linux_old + Str.regexp "Oracle Linux.*release
\\([0-9]+\\).*Update \\([0-9]+\\)"
+let re_oracle_linux + Str.regexp "Oracle Linux.*release
\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_oracle_linux_no_minor = Str.regexp "Oracle Linux.*release
\\([0-9]+\\)"
+let re_netbsd = Str.regexp "^NetBSD \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_opensuse = Str.regexp "^\\(openSUSE|SuSE Linux|SUSE LINUX\\) "
+let re_sles = Str.regexp "^SUSE \\(Linux|LINUX\\) Enterprise "
+let re_nld = Str.regexp "^Novell Linux Desktop "
+let re_sles_version = Str.regexp "^VERSION = \\([0-9]+\\)"
+let re_sles_patchlevel = Str.regexp "^PATCHLEVEL = \\([0-9]+\\)"
+let re_minix = Str.regexp
"^\\([0-9]+\\)\\.\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?"
+let re_openbsd = Str.regexp "^OpenBSD
\\([0-9]+|\\?\\)\\.\\([0-9]+|\\?\\)"
+let re_frugalware = Str.regexp "Frugalware
\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_pldlinux = Str.regexp "\\([0-9]+\\)\\.\\([0-9]+\\) PLD Linux"
+
+let arch_binaries + [ "/bin/bash"; "/bin/ls";
"/bin/echo"; "/bin/rm"; "/bin/sh" ]
+
+let rec check_linux_root mountable data + let os_type = OS_TYPE_LINUX in
+ let data = { data with os_type = Some os_type } in
+
+ let tests = [
+ (* systemd distros include /etc/os-release which is reasonably
+ * standardized. This entry should be first.
+ *)
+ "/etc/os-release", parse_os_release;
+ (* LSB is also a reasonable standard. This entry should be second. *)
+ "/etc/lsb-release", parse_lsb_release;
+
+ (* Now we enter the Wild West ... *)
+
+ (* RHEL-based distros include a [/etc/redhat-release] file, hence their
+ * checks need to be performed before the Red-Hat one.
+ *)
+ "/etc/oracle-release", parse_generic ~rex:re_oracle_linux_old
+ DISTRO_ORACLE_LINUX;
+ "/etc/oracle-release", parse_generic ~rex:re_oracle_linux
+ DISTRO_ORACLE_LINUX;
+ "/etc/oracle-release", parse_generic
~rex:re_oracle_linux_no_minor
+ DISTRO_ORACLE_LINUX;
+ "/etc/centos-release", parse_generic ~rex:re_centos_old
+ DISTRO_CENTOS;
+ "/etc/centos-release", parse_generic ~rex:re_centos
+ DISTRO_CENTOS;
+ "/etc/centos-release", parse_generic ~rex:re_centos_no_minor
+ DISTRO_CENTOS;
+ "/etc/altlinux-release", parse_generic DISTRO_ALTLINUX;
+ "/etc/redhat-release", parse_generic ~rex:re_fedora
+ DISTRO_FEDORA;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel_old
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel_no_minor
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_centos_old
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_centos
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_centos_no_minor
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_scientific_linux_old
+ DISTRO_SCIENTIFIC_LINUX;
+ "/etc/redhat-release", parse_generic ~rex:re_scientific_linux
+ DISTRO_SCIENTIFIC_LINUX;
+ "/etc/redhat-release", parse_generic
~rex:re_scientific_linux_no_minor
+ DISTRO_SCIENTIFIC_LINUX;
+
+ (* If there's an /etc/redhat-release file, but nothing above
+ * matches, then it is a generic Red Hat-based distro.
+ *)
+ "/etc/redhat-release", parse_generic DISTRO_REDHAT_BASED;
+ "/etc/redhat-release",
+ (fun _ data -> { data with distro = Some DISTRO_REDHAT_BASED });
+
+ "/etc/debian_version", parse_generic DISTRO_DEBIAN;
+ "/etc/pardus-release", parse_generic DISTRO_PARDUS;
+
+ (* /etc/arch-release file is empty and I can't see a way to
+ * determine the actual release or product string.
+ *)
+ "/etc/arch-release",
+ (fun _ data -> { data with distro = Some DISTRO_ARCHLINUX });
+
+ "/etc/gentoo-release", parse_generic DISTRO_GENTOO;
+ "/etc/meego-release", parse_generic DISTRO_MEEGO;
+ "/etc/slackware-version", parse_generic DISTRO_SLACKWARE;
+ "/etc/ttylinux-target", parse_generic DISTRO_TTYLINUX;
+
+ "/etc/SuSE-release", parse_suse_release;
+ "/etc/SuSE-release",
+ (fun _ data -> { data with distro = Some DISTRO_SUSE_BASED });
+
+ "/etc/cirros/version", parse_generic DISTRO_CIRROS;
+ "/etc/br-version",
+ (fun release_file data ->
+ let distro + if Is.is_file ~followsymlinks:true
"/usr/share/cirros/logo" then
+ DISTRO_CIRROS
+ else
+ DISTRO_BUILDROOT in
+ (* /etc/br-version has the format YYYY.MM[-git/hg/svn release] *)
+ parse_generic distro release_file data);
+
+ "/etc/alpine-release", parse_generic DISTRO_ALPINE_LINUX;
+ "/etc/frugalware-release", parse_generic ~rex:re_frugalware
+ DISTRO_FRUGALWARE;
+ "/etc/pld-release", parse_generic ~rex:re_pldlinux
+ DISTRO_PLD_LINUX;
+ ] in
+
+ let rec loop = function
+ | (release_file, parse_fun) :: tests ->
+ if verbose () then
+ eprintf "check_linux_root: checking %s\n%!" release_file;
+ (try
+ if not (Is.is_file ~followsymlinks:true release_file) then
+ raise Not_found;
+ parse_fun release_file data
+ with
+ Not_found -> loop tests)
+ | [] -> data
+ in
+ let data = loop tests in
+
+ let data = {
+ data with
+ arch = check_architecture ();
+ fstab + Inspect_fs_unix_fstab.check_fstab ~mdadm_conf:true
mountable os_type;
+ hostname = check_hostname_linux ();
+ } in
+
+ data
+
+(* Parse a os-release file.
+ *
+ * Only few fields are parsed, falling back to the usual detection if we
+ * cannot read all of them.
+ *
+ * For the format of os-release, see also:
+ * http://www.freedesktop.org/software/systemd/man/os-release.html
+ *)
+and parse_os_release release_file data + let chroot = Chroot.create
~name:"parse_os_release" (Sysroot.sysroot ()) in
+ let lines + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n"
release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ let data = List.fold_left (
+ fun data line ->
+ let line = String.trim line in
+ if line = "" || line.[0] = '#' then
+ data
+ else (
+ let key, value = String.split "=" line in
+ let value + let n = String.length value in
+ if n >= 2 && value.[0] = '"' &&
value.[n-1] = '"' then
+ String.sub value 1 (n-2)
+ else
+ value in
+ if key = "ID" then (
+ let distro = distro_of_os_release_id value in
+ match distro with
+ | Some _ as distro -> { data with distro = distro }
+ | None -> data
+ )
+ else if key = "PRETTY_NAME" then
+ { data with product_name = Some value }
+ else if key = "VERSION_ID" then
+ parse_version_from_major_minor value data
+ else
+ data
+ )
+ ) data lines in
+
+ (* os-release in Debian and CentOS does not provide the full
+ * version number (VERSION_ID), just the major part of it. If
+ * we detect that situation then bail out and use the release
+ * files instead.
+ *)
+ (match data with
+ | { distro = Some (DISTRO_DEBIAN|DISTRO_CENTOS); version = Some (_, 0) }
->
+ raise Not_found
+ | _ -> ()
+ );
+
+ data
+
+(* ID="fedora" => Some DISTRO_FEDORA *)
+and distro_of_os_release_id = function
+ | "alpine" -> Some DISTRO_ALPINE_LINUX
+ | "altlinux" -> Some DISTRO_ALTLINUX
+ | "arch" -> Some DISTRO_ARCHLINUX
+ | "centos" -> Some DISTRO_CENTOS
+ | "coreos" -> Some DISTRO_COREOS
+ | "debian" -> Some DISTRO_DEBIAN
+ | "fedora" -> Some DISTRO_FEDORA
+ | "frugalware" -> Some DISTRO_FRUGALWARE
+ | "mageia" -> Some DISTRO_MAGEIA
+ | "opensuse" -> Some DISTRO_OPENSUSE
+ | "pld" -> Some DISTRO_PLD_LINUX
+ | "rhel" -> Some DISTRO_RHEL
+ | "sles" | "sled" -> Some DISTRO_SLES
+ | "ubuntu" -> Some DISTRO_UBUNTU
+ | "void" -> Some DISTRO_VOID_LINUX
+ | value ->
+ eprintf "/etc/os-release: unknown ID=%s\n" value;
+ None
+
+(* Ubuntu has /etc/lsb-release containing:
+ * DISTRIB_ID=Ubuntu # Distro
+ * DISTRIB_RELEASE=10.04 # Version
+ * DISTRIB_CODENAME=lucid
+ * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name
+ *
+ * [Ubuntu-derived ...] Linux Mint was found to have this:
+ * DISTRIB_ID=LinuxMint
+ * DISTRIB_RELEASE=10
+ * DISTRIB_CODENAME=julia
+ * DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
+ * Linux Mint also has /etc/linuxmint/info with more information,
+ * but we can use the LSB file.
+ *
+ * Mandriva has:
+ * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
+ * DISTRIB_ID=MandrivaLinux
+ * DISTRIB_RELEASE=2010.1
+ * DISTRIB_CODENAME=Henry_Farman
+ * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
+ * Mandriva also has a normal release file called /etc/mandriva-release.
+ *
+ * CoreOS has a /etc/lsb-release link to /usr/share/coreos/lsb-release
containing:
+ * DISTRIB_ID=CoreOS
+ * DISTRIB_RELEASE=647.0.0
+ * DISTRIB_CODENAME="Red Dog"
+ * DISTRIB_DESCRIPTION="CoreOS 647.0.0"
+ *)
+and parse_lsb_release release_file data + let chroot = Chroot.create
~name:"parse_lsb_release" (Sysroot.sysroot ()) in
+ let lines + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n"
release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ let data = List.fold_left (
+ fun data line ->
+ if data.distro = None && line = "DISTRIB_ID=Ubuntu"
then
+ { data with distro = Some DISTRO_UBUNTU }
+ else if data.distro = None && line =
"DISTRIB_ID=LinuxMint" then
+ { data with distro = Some DISTRO_LINUX_MINT }
+ else if data.distro = None && line =
"DISTRIB_ID=\"Mageia\"" then
+ { data with distro = Some DISTRO_MAGEIA }
+ else if data.distro = None && line =
"DISTRIB_ID=CoreOS" then
+ { data with distro = Some DISTRO_COREOS }
+ else if String.is_prefix line "DISTRIB_RELEASE=" then (
+ let line = String.sub line 16 (String.length line - 16) in
+ parse_version_from_major_minor line data
+ )
+ else if String.is_prefix line "DISTRIB_DESCRIPTION=\"" ||
+ String.is_prefix line "DISTRIB_DESCRIPTION='" then
(
+ let n = String.length line in
+ let product_name = String.sub line 21 (n-22) in
+ { data with product_name = Some product_name }
+ )
+ else if String.is_prefix line "DISTRIB_DESCRIPTION=" then (
+ let n = String.length line in
+ let product_name = String.sub line 20 (n-20) in
+ { data with product_name = Some product_name }
+ )
+ else
+ data
+ ) data lines in
+
+ data
+
+and parse_suse_release release_file data + let chroot = Chroot.create
~name:"parse_suse_release" (Sysroot.sysroot ()) in
+ let lines + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n"
release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ if lines = [] then raise Not_found;
+
+ (* First line is dist release name. *)
+ let product_name = List.hd lines in
+ let data = {
+ data with
+ product_name = Some product_name
+ } in
+
+ (* Match SLES first because openSuSE regex overlaps some SLES
+ * release strings.
+ *)
+ if Str.string_match re_sles product_name 0 ||
+ Str.string_match re_nld product_name 0 then (
+ (* Second line contains version string. *)
+ let major + if List.length lines >= 2 then (
+ let line = List.nth lines 1 in
+ if Str.string_match re_sles_version line 0 then
+ Some (int_of_string (Str.matched_group 1 line))
+ else None
+ )
+ else None in
+
+ (* Third line contains service pack string. *)
+ let minor + if List.length lines >= 3 then (
+ let line = List.nth lines 2 in
+ if Str.string_match re_sles_patchlevel line 0 then
+ Some (int_of_string (Str.matched_group 1 line))
+ else None
+ )
+ else None in
+
+ let version + match major, minor with
+ | Some major, Some minor -> Some (major, minor)
+ | Some major, None -> Some (major, 0)
+ | None, Some _ | None, None -> None in
+
+ { data with
+ distro = Some DISTRO_SLES;
+ version = version }
+ )
+ else if Str.string_match re_opensuse product_name 0 then (
+ (* Second line contains version string. *)
+ let data + if List.length lines >= 2 then (
+ let line = List.nth lines 1 in
+ parse_version_from_major_minor line data
+ )
+ else data in
+
+ { data with distro = Some DISTRO_OPENSUSE }
+ )
+ else
+ data
+
+(* Parse any generic /etc/x-release file.
+ *
+ * The optional regular expression which may match 0, 1 or 2
+ * substrings, which are used as the major and minor numbers.
+ *
+ * The fixed distro is always set, and the product name is
+ * set to the first line of the release file.
+ *)
+and parse_generic ?rex distro release_file data + let chroot = Chroot.create
~name:"parse_generic" (Sysroot.sysroot ()) in
+ let product_name + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n"
release_file;
+ raise Not_found
+ );
+ read_first_line_from_file release_file
+ ) () in
+ if product_name = "" then
+ raise Not_found;
+
+ if verbose () then
+ eprintf "parse_generic: product_name = %s\n%!" product_name;
+
+ let data + { data with product_name = Some product_name;
+ distro = Some distro } in
+
+ match rex with
+ | Some rex ->
+ (* If ~rex was supplied, then it must match the release file,
+ * else the parsing fails.
+ *)
+ if not (Str.string_match rex product_name 0) then
+ raise Not_found;
+
+ (* Although it's not documented, matched_group raises
+ * Invalid_argument if called with an unknown group number.
+ *)
+ let major + try Some (int_of_string (Str.matched_group 1
product_name))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ let minor + try Some (int_of_string (Str.matched_group 2
product_name))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ (match major, minor with
+ | None, None -> data
+ | None, Some _ -> data
+ | Some major, None -> { data with version = Some (major, 0) }
+ | Some major, Some minor -> { data with version = Some (major, minor) }
+ )
+
+ | None ->
+ (* However if no ~rex was supplied, then we make a best
+ * effort attempt to parse a version number, but don't
+ * fail if one cannot be found.
+ *)
+ parse_version_from_major_minor product_name data
+
+and check_architecture () + let rec loop = function
+ | [] -> None
+ | bin :: bins ->
+ (* Allow symlinks when checking the binaries:,so in case they are
+ * relative ones (which can be resolved within the same partition),
+ * then we can check the architecture of their target.
+ *)
+ if Is.is_file ~followsymlinks:true bin then (
+ try
+ let resolved = Realpath.realpath bin in
+ let arch = Filearch.file_architecture resolved in
+ Some arch
+ with exn ->
+ if verbose () then
+ eprintf "check_architecture: %s: %s\n%!" bin
+ (Printexc.to_string exn);
+ loop bins
+ )
+ else
+ loop bins
+ in
+ loop arch_binaries
+
+and check_hostname_linux () + (* Red Hat-derived would be in
/etc/sysconfig/network or
+ * /etc/hostname (RHEL 7+, F18+). Debian-derived in the file
+ * /etc/hostname. Very old Debian and SUSE use /etc/HOSTNAME.
+ * It's best to just look for each of these files in turn, rather
+ * than try anything clever based on distro.
+ *)
+ let rec loop = function
+ | [] -> None
+ | filename :: rest ->
+ match check_hostname_from_file filename with
+ | Some hostname -> Some hostname
+ | None -> loop rest
+ in
+ let hostname = loop [ "/etc/HOSTNAME"; "/etc/hostname" ]
in
+ match hostname with
+ | Some hostname -> Some hostname
+ | None ->
+ if Is.is_file "/etc/sysconfig/network" then
+ with_augeas ["/etc/sysconfig/network"]
+ check_hostname_from_sysconfig_network
+ else
+ None
+
+(* Parse the hostname where it is stored directly in a file. *)
+and check_hostname_from_file filename + let chroot + let name = sprintf
"check_hostname_linux_from_file: %s" filename in
+ Chroot.create ~name (Sysroot.sysroot ()) in
+
+ Chroot.f chroot (
+ fun () ->
+ if not (is_small_file filename) then (
+ eprintf "%s: not a regular file or too large\n" filename;
+ None
+ )
+ else
+ Some (read_first_line_from_file filename)
+ ) ()
+
+(* Parse the hostname from /etc/sysconfig/network. This must be
+ * called from the 'with_augeas' wrapper. Note that F18+ and
+ * RHEL7+ use /etc/hostname just like Debian.
+ *)
+and check_hostname_from_sysconfig_network aug + (* Errors here are not fatal
(RHBZ#726739), since it could be
+ * just missing HOSTNAME field in the file.
+ *)
+ Augeas.get aug "/files/etc/sysconfig/network/HOSTNAME"
+
+(* The currently mounted device looks like a Linux /usr. *)
+let check_linux_usr data + let data = { data with os_type = Some OS_TYPE_LINUX
} in
+
+ let data + if Is.is_file "/lib/os-release" ~followsymlinks:true
then
+ parse_os_release "/lib/os-release" data
+ else
+ data in
+
+ let data + match check_architecture () with
+ | None -> data
+ | (Some _) as arch -> { data with arch = arch } in
+
+ data
+
+(* The currently mounted device is a CoreOS root. From this partition we can
+ * only determine the hostname. All immutable OS files are under a separate
+ * read-only /usr partition.
+ *)
+let check_coreos_root mountable data + {
+ data with
+ os_type = Some OS_TYPE_LINUX;
+ distro = Some DISTRO_COREOS;
+
+ (* Determine hostname. *)
+ hostname = check_hostname_linux ();
+
+ (* CoreOS does not contain /etc/fstab to determine the mount points.
+ * Associate this filesystem with the "/" mount point.
+ *)
+ fstab = [ mountable, "/" ]
+ }
+
+(* The currently mounted device looks like a CoreOS /usr. In CoreOS
+ * the read-only /usr contains the OS version. The /etc/os-release is a
+ * link to /usr/share/coreos/os-release.
+ *)
+let check_coreos_usr mountable data + let data = { data with os_type = Some
OS_TYPE_LINUX;
+ distro = Some DISTRO_COREOS } in
+
+ let data + if Is.is_file "/lib/os-release" ~followsymlinks:true
then
+ parse_os_release "/lib/os-release" data
+ else if Is.is_file "/share/coreos/lsb-release"
~followsymlinks:true then
+ parse_lsb_release "/share/coreos/lsb-release" data
+ else
+ data in
+
+ (* Determine the architecture. *)
+ let data + match check_architecture () with
+ | None -> data
+ | (Some _) as arch -> { data with arch = arch } in
+
+ (* CoreOS does not contain /etc/fstab to determine the mount points.
+ * Associate this filesystem with the "/usr" mount point.
+ *)
+ let data = { data with fstab = [ mountable, "/usr" ] } in
+
+ data
+
+let rec check_freebsd_root mountable data + let os_type = OS_TYPE_FREEBSD and
distro = DISTRO_FREEBSD in
+ let data = { data with os_type = Some os_type;
+ distro = Some distro } in
+
+ (* FreeBSD has no authoritative version file. The version number is
+ * in /etc/motd, which the system administrator might edit, but
+ * we'll use that anyway.
+ *)
+ let data + if Is.is_file "/etc/motd" ~followsymlinks:true then
+ parse_generic distro "/etc/motd" data
+ else
+ data in
+
+ let data = {
+ data with
+ (* Determine the architecture. *)
+ arch = check_architecture ();
+ (* We already know /etc/fstab exists because it's part of the test
+ * in the caller.
+ *)
+ fstab = Inspect_fs_unix_fstab.check_fstab mountable os_type;
+ hostname = check_hostname_freebsd ()
+ } in
+
+ data
+
+(* Parse the hostname from /etc/rc.conf. On FreeBSD and NetBSD
+ * this file contains comments, blank lines and:
+ * hostname="freebsd8.example.com"
+ * ifconfig_re0="DHCP"
+ * keymap="uk.iso"
+ * sshd_enable="YES"
+ *)
+and check_hostname_freebsd () + let chroot = Chroot.create
~name:"check_hostname_freebsd"
+ (Sysroot.sysroot ()) in
+ let filename = "/etc/rc.conf" in
+
+ try
+ let lines + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file filename) then (
+ eprintf "%s: not a regular file or too large\n" filename;
+ raise Not_found
+ )
+ else (
+ let lines = read_whole_file filename in
+ String.nsplit "\n" lines
+ )
+ ) () in
+ let rec loop = function
+ | [] ->
+ raise Not_found
+ | line :: _ when String.is_prefix line "hostname=\"" ||
+ String.is_prefix line "hostname='" ->
+ let len = String.length line - 10 - 1 in
+ String.sub line 10 len
+ | line :: _ when String.is_prefix line "hostname=" ->
+ let len = String.length line - 9 in
+ String.sub line 9 len
+ | _ :: lines ->
+ loop lines
+ in
+ let hostname = loop lines in
+ Some hostname
+ with
+ Not_found -> None
+
+let rec check_netbsd_root mountable data + let os_type = OS_TYPE_NETBSD and
distro = DISTRO_NETBSD in
+ let data = { data with os_type = Some os_type;
+ distro = Some distro } in
+
+ let data + if Is.is_file "/etc/release" ~followsymlinks:true
then
+ parse_generic ~rex:re_netbsd distro "/etc/release" data
+ else
+ data in
+
+ let data = {
+ data with
+ (* Determine the architecture. *)
+ arch = check_architecture ();
+ (* We already know /etc/fstab exists because it's part of the test
+ * in the caller.
+ *)
+ fstab = Inspect_fs_unix_fstab.check_fstab mountable os_type;
+ hostname = check_hostname_freebsd ()
+ } in
+
+ data
+
+and check_hostname_netbsd () = check_hostname_freebsd ()
+
+let rec check_openbsd_root mountable data + let os_type = OS_TYPE_FREEBSD and
distro = DISTRO_FREEBSD in
+ let data = { data with os_type = Some os_type;
+ distro = Some distro } in
+
+ (* The first line of /etc/motd gets automatically updated at boot. *)
+ let data + if Is.is_file "/etc/motd" ~followsymlinks:true then
+ parse_generic distro "/etc/motd" data
+ else
+ data in
+
+ (* Before the first boot, the first line will look like this:
+ *
+ * OpenBSD ?.? (UNKNOWN)
+ *
+ * The previous C code used to check for this case explicitly,
+ * but in this code, parse_generic should be unable to extract
+ * any version and so should return with [data.version = None].
+ *)
+
+ let data = {
+ data with
+ (* Determine the architecture. *)
+ arch = check_architecture ();
+ (* We already know /etc/fstab exists because it's part of the test
+ * in the caller.
+ *)
+ fstab = Inspect_fs_unix_fstab.check_fstab mountable os_type;
+ hostname = check_hostname_freebsd ()
+ } in
+
+ data
+
+and check_hostname_openbsd () + check_hostname_from_file
"/etc/myname"
+
+(* The currently mounted device may be a Hurd root. Hurd has distros
+ * just like Linux.
+ *)
+let rec check_hurd_root mountable data + let os_type = OS_TYPE_HURD in
+ let data = { data with os_type = Some os_type } in
+
+ let data + if Is.is_file "/etc/debian_version"
~followsymlinks:true then (
+ let distro = DISTRO_DEBIAN in
+ parse_generic distro "/etc/debian_version" data
+ )
+ (* Arch Hurd also exists, but inconveniently it doesn't have
+ * the normal /etc/arch-release file. XXX
+ *)
+ else data in
+
+ let data = {
+ data with
+ (* Determine the architecture. *)
+ arch = check_architecture ();
+ (* We already know /etc/fstab exists because it's part of the test
+ * in the caller.
+ *)
+ fstab = Inspect_fs_unix_fstab.check_fstab mountable os_type;
+ hostname = check_hostname_hurd ()
+ } in
+
+ data
+
+and check_hostname_hurd () = check_hostname_linux ()
+
+let rec check_minix_root data + let os_type = OS_TYPE_MINIX in
+ let data = { data with os_type = Some os_type } in
+
+ let data + if Is.is_file "/etc/version" ~followsymlinks:true
then (
+ let data + parse_generic ~rex:re_minix DISTRO_MEEGO (* XXX unset
below *)
+ "/etc/version" data in
+ { data with distro = None }
+ )
+ else
+ data in
+
+ let data = {
+ data with
+ (* Determine the architecture. *)
+ arch = check_architecture ();
+ (* TODO: enable fstab inspection once resolve_fstab_device
+ * implements the proper mapping from the Minix device names
+ * to the appliance names.
+ *)
+ hostname = check_hostname_minix ()
+ } in
+
+ data
+
+and check_hostname_minix () + check_hostname_from_file
"/etc/hostname.file"
diff --git a/daemon/inspect_fs_unix.mli b/daemon/inspect_fs_unix.mli
new file mode 100644
index 000000000..655c765b4
--- /dev/null
+++ b/daemon/inspect_fs_unix.mli
@@ -0,0 +1,53 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val check_coreos_usr : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the CoreOS [/usr] filesystem mounted on sysroot. *)
+
+val check_coreos_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the CoreOS filesystem mounted on sysroot. *)
+
+val check_freebsd_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the FreeBSD filesystem mounted on sysroot. *)
+
+val check_hurd_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Hurd filesystem mounted on sysroot. *)
+
+val check_linux_usr : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Linux [/usr] filesystem mounted on sysroot. *)
+
+val check_linux_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Linux filesystem mounted on sysroot. *)
+
+val check_minix_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Minix filesystem mounted on sysroot. *)
+
+val check_netbsd_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the NetBSD filesystem mounted on sysroot. *)
+
+val check_openbsd_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the OpenBSD filesystem mounted on sysroot. *)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
new file mode 100644
index 000000000..f9df08c3b
--- /dev/null
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -0,0 +1,518 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 C_utils
+open Std_utils
+
+open Utils
+open Inspect_types
+open Inspect_utils
+
+let re_cciss = Str.regexp
"^/dev/\\(cciss/c[0-9]+d[0-9]+\\)\\(p\\([0-9]+\\)\\)?$"
+let re_diskbyid = Str.regexp "^/dev/disk/by-id/.*-part\\([0-9]+\\)$"
+let re_freebsd_gpt = Str.regexp
"^/dev/\\(ada{0,1}|vtbd\\)\\([0-9]+\\)p\\([0-9]+\\)$"
+let re_freebsd_mbr = Str.regexp
"^/dev/\\(ada{0,1}|vtbd\\)\\([0-9]+\\)s\\([0-9]+\\)\\([a-z]\\)$"
+let re_hurd_dev = Str.regexp
"^/dev/\\(h\\)d\\([0-9]+\\)s\\([0-9]+\\)$"
+let re_mdN = Str.regexp "^/dev/md[0-9]+$"
+let re_netbsd_dev = Str.regexp
"^/dev/\\(l|s\\)d\\([0-9]\\)\\([a-z]\\)$"
+let re_openbsd_dev = Str.regexp
"^/dev/\\(s|w\\)d\\([0-9]\\)\\([a-z]\\)$"
+let re_openbsd_duid = Str.regexp
"^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\.\\([a-z]\\)"
+let re_xdev = Str.regexp
"^/dev/\\(h|s|v|xv\\)d\\([a-z]+\\)\\([0-9]*\\)$"
+
+let rec check_fstab ?(mdadm_conf = false) (root_mountable : Mountable.t)
+ os_type + let configfiles + "/etc/fstab" ::
if mdadm_conf then ["/etc/mdadm.conf"] else [] in
+
+ with_augeas configfiles (check_fstab_aug mdadm_conf root_mountable os_type)
+
+and check_fstab_aug mdadm_conf root_mountable os_type aug + (* Generate a map
of MD device paths listed in /etc/mdadm.conf
+ * to MD device paths in the guestfs appliance.
+ *)
+ let md_map = if mdadm_conf then map_md_devices aug else StringMap.empty in
+
+ let path = "/files/etc/fstab/*[label() != '#comment']" in
+ let entries = Augeas.matches aug path in
+ filter_map (check_fstab_entry md_map root_mountable os_type aug) entries
+
+and check_fstab_entry md_map root_mountable os_type aug entry + if verbose ()
then
+ eprintf "check_fstab_entry: augeas path: %s\n%!" entry;
+
+ let is_bsd + os_type = OS_TYPE_FREEBSD ||
+ os_type = OS_TYPE_NETBSD ||
+ os_type = OS_TYPE_OPENBSD in
+
+ let spec = Augeas.get aug (entry ^ "/spec") in
+ let mp = Augeas.get aug (entry ^ "/file") in
+ let vfstype = Augeas.get aug (entry ^ "/vfstype") in
+
+ match spec, mp, vfstype with
+ | None, _, _ | Some _, None, _ | Some _, Some _, None -> None
+ | Some spec, Some mp, Some vfstype ->
+ if verbose () then
+ eprintf "check_fstab_entry: spec=%s mp=%s vfstype=%s\n%!"
+ spec mp vfstype;
+
+ (* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives.
+ *
+ * /dev/iso9660/FREEBSD_INSTALL can be found in FreeBSD's
+ * installation discs.
+ *)
+ if (String.is_prefix spec "/dev/fd" &&
+ String.length spec >= 8 && Char.isdigit spec.[7]) ||
+ (String.is_prefix spec "/dev/cd" &&
+ String.length spec >= 8 && Char.isdigit spec.[7]) ||
+ spec = "/dev/floppy" ||
+ spec = "/dev/cdrom" ||
+ String.is_prefix spec "/dev/iso9660/" then
+ None
+ else (
+ (* Canonicalize the path, so "///usr//local//" ->
"/usr/local" *)
+ let mp = unix_canonical_path mp in
+
+ (* Ignore certain mountpoints. *)
+ if String.is_prefix mp "/dev/" ||
+ mp = "/dev" ||
+ String.is_prefix mp "/media/" ||
+ String.is_prefix mp "/proc/" ||
+ mp = "/proc" ||
+ String.is_prefix mp "/selinux/" ||
+ mp = "/selinux" ||
+ String.is_prefix mp "/sys/" ||
+ mp = "/sys" then
+ None
+ else (
+ let mountable + (* Resolve UUID= and LABEL= to the actual
device. *)
+ if String.is_prefix spec "UUID=" then (
+ let uuid = String.sub spec 5 (String.length spec - 5) in
+ let uuid = shell_unquote uuid in
+ Some (Mountable.of_device (Findfs.findfs_uuid uuid))
+ )
+ else if String.is_prefix spec "LABEL=" then (
+ let label = String.sub spec 6 (String.length spec - 6) in
+ let label = shell_unquote label in
+ Some (Mountable.of_device (Findfs.findfs_label label))
+ )
+ (* Resolve /dev/root to the current device.
+ * Do the same for the / partition of the *BSD
+ * systems, since the BSD -> Linux device
+ * translation is not straight forward.
+ *)
+ else if spec = "/dev/root" || (is_bsd && mp =
"/") then
+ Some root_mountable
+ (* Resolve guest block device names. *)
+ else if String.is_prefix spec "/dev/" then
+ Some (resolve_fstab_device spec md_map os_type)
+ (* In OpenBSD's fstab you can specify partitions
+ * on a disk by appending a period and a partition
+ * letter to a Disklable Unique Identifier. The
+ * DUID is a 16 hex digit field found in the
+ * OpenBSD's altered BSD disklabel. For more info
+ * see here:
+ * http://www.openbsd.org/faq/faq14.html#intro
+ *)
+ else if Str.string_match re_openbsd_duid spec 0 then (
+ let part = Str.matched_group 1 spec in
+ (* We cannot peep into disklabels, we can only
+ * assume that this is the first disk.
+ *)
+ let device = sprintf "/dev/sd0%s" part in
+ Some (resolve_fstab_device device md_map os_type)
+ )
+ (* Ignore "/.swap" (Pardus) and pseudo-devices
+ * like "tmpfs". If we haven't resolved the device
+ * successfully by this point, just ignore it.
+ *)
+ else
+ None in
+
+ match mountable with
+ | None -> None
+ | Some mountable ->
+ let mountable + if vfstype = "btrfs" then
+ get_btrfs_mountable aug entry mountable
+ else mountable in
+
+ Some (mountable, mp)
+ )
+ )
+
+(* If an fstab entry corresponds to a btrfs filesystem, look for
+ * the 'subvol' option and if it is present then return a btrfs
+ * subvolume (else return the whole device).
+ *)
+and get_btrfs_mountable aug entry mountable + let device + match mountable
with
+ | { Mountable.m_type = Mountable.MountableDevice; m_device = device } ->
+ Some device
+ | { Mountable.m_type +
(Mountable.MountablePath|Mountable.MountableBtrfsVol _) } ->
+ None in
+
+ match device with
+ | None -> mountable
+ | Some device ->
+ let opts = Augeas.matches aug (entry ^ "/opt") in
+ let rec loop = function
+ | [] -> mountable (* no subvol, return whole device *)
+ | opt :: opts ->
+ let optname = Augeas.get aug opt in
+ match optname with
+ | None -> loop opts
+ | Some "subvol" ->
+ let subvol = Augeas.get aug (opt ^ "/value") in
+ (match subvol with
+ | None -> loop opts
+ | Some subvol ->
+ Mountable.of_btrfsvol device subvol
+ )
+ | Some _ ->
+ loop opts
+ in
+ loop opts
+
+(* Get a map of md device names in mdadm.conf to their device names
+ * in the appliance.
+ *)
+and map_md_devices aug + (* Get a map of md device uuids to their device names
in the appliance. *)
+ let uuid_map = map_app_md_devices () in
+
+ (* Nothing to do if there are no md devices. *)
+ if StringMap.is_empty uuid_map then StringMap.empty
+ else (
+ (* Get all arrays listed in mdadm.conf. *)
+ let entries = Augeas.matches aug "/files/etc/mdadm.conf/array" in
+
+ (* Log a debug entry if we've got md devices but nothing in mdadm.conf.
*)
+ if verbose () && entries = [] then
+ eprintf "warning: appliance has MD devices, but augeas returned no
array matches in /etc/mdadm.conf\n%!";
+
+ List.fold_left (
+ fun md_map entry ->
+ try
+ (* Get device name and uuid for each array. *)
+ let dev = Augeas.get aug (entry ^ "/devicename") in
+ let uuid = Augeas.get aug (entry ^ "/uuid") in
+ let dev + match dev with None -> raise Not_found | Some
dev -> dev in
+ let uuid + match uuid with None -> raise Not_found |
Some uuid -> uuid in
+
+ (* Parse the uuid into an md_uuid structure so we can look
+ * it up in the uuid_map.
+ *)
+ let uuid = parse_md_uuid uuid in
+
+ let md = StringMap.find uuid uuid_map in
+
+ (* If there's a corresponding uuid in the appliance, create
+ * a new entry in the transitive map.
+ *)
+ StringMap.add dev md md_map
+ with
+ (* No Augeas devicename or uuid node found, or could not parse
+ * uuid, or uuid not present in the uuid_map.
+ *
+ * This is not fatal, just ignore the entry.
+ *)
+ Not_found | Invalid_argument _ -> md_map
+ ) StringMap.empty entries
+ )
+
+(* Create a mapping of uuids to appliance md device names. *)
+and map_app_md_devices () + let mds = Md.list_md_devices () in
+ List.fold_left (
+ fun map md ->
+ let detail = Md.md_detail md in
+
+ try
+ (* Find the value of the "uuid" key. *)
+ let uuid = List.assoc "uuid" detail in
+ let uuid = parse_md_uuid uuid in
+ StringMap.add uuid md map
+ with
+ (* uuid not found, or could not be parsed - just ignore the entry *)
+ Not_found | Invalid_argument _ -> map
+ ) StringMap.empty mds
+
+(* Taken from parse_uuid in mdadm.
+ *
+ * Raises Invalid_argument if the input is not an MD UUID.
+ *)
+and parse_md_uuid uuid + let len = String.length uuid in
+ let out = Bytes.create len in
+ let j = ref 0 in
+
+ for i = 0 to len-1 do
+ let c = uuid.[i] in
+ if Char.isxdigit c then (
+ Bytes.set out !j c;
+ incr j
+ )
+ else if c = ':' || c = '.' || c = ' ' || c =
'-' then
+ ()
+ else
+ invalid_arg "parse_md_uuid: invalid character"
+ done;
+
+ if !j <> 32 then
+ invalid_arg "parse_md_uuid: invalid length";
+
+ Bytes.sub_string out 0 !j
+
+(* Resolve block device name to the libguestfs device name, eg.
+ * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV. This
+ * assumes that disks were added in the same order as they appear to
+ * the real VM, which is a reasonable assumption to make. Return
+ * anything we don't recognize unchanged.
+ *)
+and resolve_fstab_device spec md_map os_type + (* In any case where we
didn't match a device pattern or there was
+ * another problem, return this default mountable derived from [spec].
+ *)
+ let default = Mountable.of_device spec in
+
+ if String.is_prefix spec "/dev/mapper" then (
+ (* LVM2 does some strange munging on /dev/mapper paths for VGs and
+ * LVs which contain '-' character:
+ *
+ * ><fs> lvcreate LV--test VG--test 32
+ * ><fs> debug ls /dev/mapper
+ * VG----test-LV----test
+ *
+ * This makes it impossible to reverse those paths directly, so
+ * we have implemented lvm_canonical_lv_name in the daemon.
+ *)
+ try
+ match Lvm.lv_canonical spec with
+ | None -> Mountable.of_device spec
+ | Some device -> Mountable.of_device device
+ with
+ (* Ignore devices that don't exist. (RHBZ#811872) *)
+ | Unix.Unix_error (Unix.ENOENT, _, _) -> default
+ )
+
+ else if Str.string_match re_xdev spec 0 then (
+ let typ = Str.matched_group 1 spec
+ and disk = Str.matched_group 2 spec
+ and part = int_of_string (Str.matched_group 3 spec) in
+ resolve_xdev typ disk part default
+ )
+
+ else if Str.string_match re_cciss spec 0 then (
+ let disk = Str.matched_group 1 spec
+ (* group 2 = optional p<NN>, group 3 = <NN> *)
+ and part + try Some (int_of_string (Str.matched_group 3 spec))
+ with Not_found | Invalid_argument _ -> None in
+ resolve_cciss disk part default
+ )
+
+ else if Str.string_match re_mdN spec 0 then (
+ try
+ Mountable.of_device (StringMap.find spec md_map)
+ with
+ | Not_found -> default
+ )
+
+ else if Str.string_match re_diskbyid spec 0 then (
+ let part = int_of_string (Str.matched_group 1 spec) in
+ resolve_diskbyid part default
+ )
+
+ else if Str.string_match re_freebsd_gpt spec 0 then (
+ (* group 1 (type) is not used *)
+ let disk = int_of_string (Str.matched_group 2 spec)
+ and part = int_of_string (Str.matched_group 3 spec) in
+
+ (* If the FreeBSD disk contains GPT partitions, the translation to Linux
+ * device names is straight forward. Partitions on a virtio disk are
+ * prefixed with [vtbd]. IDE hard drives used to be prefixed with [ad]
+ * and now prefixed with [ada].
+ *)
+ if disk >= 0 && disk <= 26 && part >= 0 &&
part <= 128 then (
+ let dev = sprintf "/dev/sd%c%d"
+ (Char.chr (disk + Char.code 'a')) part in
+ Mountable.of_device dev
+ )
+ else default
+ )
+
+ else if Str.string_match re_freebsd_mbr spec 0 then (
+ (* group 1 (type) is not used *)
+ let disk = int_of_string (Str.matched_group 2 spec)
+ and slice = int_of_string (Str.matched_group 3 spec)
+ (* partition number counting from 0: *)
+ and part = Char.code (Str.matched_group 4 spec).[0] - Char.code 'a'
in
+
+ (* FreeBSD MBR disks are organized quite differently. See:
+ * http://www.freebsd.org/doc/handbook/disk-organization.html
+ * FreeBSD "partitions" are exposed as quasi-extended partitions
+ * numbered from 5 in Linux. I have no idea what happens when you
+ * have multiple "slices" (the FreeBSD term for MBR partitions).
+ *)
+
+ (* Partition 'c' has the size of the enclosing slice.
+ * Not mapped under Linux.
+ *)
+ let part = if part > 2 then part - 1 else part in
+
+ if disk >= 0 && disk <= 26 &&
+ slice > 0 && slice <= 1 (* > 4 .. see comment above *)
&&
+ part >= 0 && part < 25 then (
+ let dev = sprintf "/dev/sd%c%d"
+ (Char.chr (disk + Char.code 'a')) (part + 5) in
+ Mountable.of_device dev
+ )
+ else default
+ )
+
+ else if os_type = OS_TYPE_NETBSD &&
+ Str.string_match re_netbsd_dev spec 0 then (
+ (* group 1 (type) is not used *)
+ let disk = int_of_string (Str.matched_group 2 spec)
+ (* partition number counting from 0: *)
+ and part = Char.code (Str.matched_group 3 spec).[0] - Char.code 'a'
in
+
+ (* Partition 'c' is the disklabel partition and 'd' the
hard disk itself.
+ * Not mapped under Linux.
+ *)
+ let part = if part > 3 then part - 2 else part in
+
+ if disk >= 0 && part >= 0 && part < 24 then (
+ let dev = sprintf "/dev/sd%c%d"
+ (Char.chr (disk + Char.code 'a')) (part + 5) in
+ Mountable.of_device dev
+ )
+ else default
+ )
+
+ else if os_type = OS_TYPE_OPENBSD &&
+ Str.string_match re_openbsd_dev spec 0 then (
+ (* group 1 (type) is not used *)
+ let disk = int_of_string (Str.matched_group 2 spec)
+ (* partition number counting from 0: *)
+ and part = Char.code (Str.matched_group 3 spec).[0] - Char.code 'a'
in
+
+ (* Partition 'c' is the hard disk itself. Not mapped under Linux.
*)
+ let part = if part > 2 then part - 1 else part in
+
+ (* In OpenBSD MAXPARTITIONS is defined to 16 for all architectures. *)
+ if disk >= 0 && part >= 0 && part < 15 then (
+ let dev = sprintf "/dev/sd%c%d"
+ (Char.chr (disk + Char.code 'a')) (part + 5) in
+ Mountable.of_device dev
+ )
+ else default
+ )
+
+ else if Str.string_match re_hurd_dev spec 0 then (
+ let typ = Str.matched_group 1 spec
+ and disk = int_of_string (Str.matched_group 2 spec)
+ and part = int_of_string (Str.matched_group 3 spec) in
+
+ (* Hurd disk devices are like /dev/hdNsM, where hdN is the
+ * N-th disk and M is the M-th partition on that disk.
+ * Turn the disk number into a letter-based identifier, so
+ * we can resolve it easily.
+ *)
+ let disk = sprintf "%c" (Char.chr (disk + Char.code 'a'))
in
+
+ resolve_xdev typ disk part default
+ )
+
+ else default
+
+(* type: (h|s|v|xv)
+ * disk: [a-z]+
+ * part: \d*
+ *)
+and resolve_xdev typ disk part default + let devices = Devsparts.list_devices
() in
+ let devices = Array.of_list devices in
+
+ (* XXX Check any hints we were passed for a non-heuristic mapping.
+ * The C code used hints here to map device names as known by
+ * the library user (eg. from metadata) to libguestfs devices here.
+ * However none of the libguestfs tools ever used this feature.
+ * Nevertheless we should reimplement it at some point because
+ * outside callers might require it, and it's a good idea in general.
+ *)
+
+ (* Guess the appliance device name if we didn't find a matching hint. *)
+ let i = drive_index disk in
+ if i >= 0 && i < Array.length devices then (
+ let dev = Array.get devices i in
+ let dev = dev ^ string_of_int part in
+ if is_partition dev then
+ Mountable.of_device dev
+ else
+ default
+ )
+ else
+ default
+
+(* disk: (cciss/c\d+d\d+)
+ * part: (\d+)?
+ *)
+and resolve_cciss disk part default + (* XXX Check any hints we were passed
for a non-heuristic mapping.
+ * The C code used hints here to map device names as known by
+ * the library user (eg. from metadata) to libguestfs devices here.
+ * However none of the libguestfs tools ever used this feature.
+ * Nevertheless we should reimplement it at some point because
+ * outside callers might require it, and it's a good idea in general.
+ *)
+
+ (* We don't try to guess mappings for cciss devices. *)
+ default
+
+(* For /dev/disk/by-id there is a limit to what we can do because
+ * original SCSI ID information has likely been lost. This
+ * heuristic will only work for guests that have a single block
+ * device.
+ *
+ * So the main task here is to make sure the assumptions above are
+ * true.
+ *
+ * XXX Use hints from virt-p2v if available.
+ * See also: https://bugzilla.redhat.com/show_bug.cgi?id=836573#c3
+ *)
+and resolve_diskbyid part default + let nr_devices = Devsparts.nr_devices ()
in
+
+ (* If #devices isn't 1, give up trying to translate this fstab entry. *)
+ if nr_devices <> 1 then
+ default
+ else (
+ (* Make the partition name and check it exists. *)
+ let dev = sprintf "/dev/sda%d" part in
+ if is_partition dev then Mountable.of_device dev
+ else default
+ )
diff --git a/daemon/inspect_fs_unix_fstab.mli b/daemon/inspect_fs_unix_fstab.mli
new file mode 100644
index 000000000..3ce3aef05
--- /dev/null
+++ b/daemon/inspect_fs_unix_fstab.mli
@@ -0,0 +1,34 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val check_fstab : ?mdadm_conf:bool -> Mountable.t ->
Inspect_types.os_type ->
+ (Mountable.t * string) list
+(** [check_fstab] examines the [/etc/fstab] file of a mounted root
+ filesystem, returning the list of devices and their mount points.
+ Various devices (like CD-ROMs) are ignored in the process, and
+ this function also knows how to map (eg) BSD device names into
+ Linux/libguestfs device names.
+
+ [mdadm_conf] is true if you want to check [/etc/mdadm.conf] as well.
+
+ [root_mountable] is the [Mountable.t] of the root filesystem. (Note
+ that the root filesystem must be mounted on sysroot before this
+ function is called.)
+
+ [os_type] is the presumed operating system type of this root, and
+ is used to make some adjustments to fstab parsing. *)
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
new file mode 100644
index 000000000..bb949be47
--- /dev/null
+++ b/daemon/inspect_fs_windows.ml
@@ -0,0 +1,498 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+open Utils
+open Inspect_types
+open Inspect_utils
+
+(* Check a predefined list of common windows system root locations. *)
+let systemroot_paths + [ "/windows"; "/winnt";
"/win32"; "/win"; "/reactos" ]
+
+let re_boot_ini_os + Str.regexp
"^\\(multi|scsi\\)(\\([0-9]+\\))disk(\\([0-9]+\\))rdisk(\\([0-9]+\\))partition(\\([0-9]+\\))\\([^=]+\\)="
+
+let rec check_windows_root data + let systemroot + match
get_windows_systemroot () with
+ | None -> assert false (* Should never happen - see caller. *)
+ | Some systemroot -> systemroot in
+
+ let data = {
+ data with os_type = Some OS_TYPE_WINDOWS;
+ distro = Some DISTRO_WINDOWS;
+ windows_systemroot = Some systemroot;
+ arch = Some (check_windows_arch systemroot)
+ } in
+
+ (* Load further fields from the Windows registry. *)
+ check_windows_registry systemroot data
+
+and is_windows_systemroot () + get_windows_systemroot () <> None
+
+and get_windows_systemroot () + let rec loop = function
+ | [] -> None
+ | path :: paths ->
+ let path = case_sensitive_path_silently path in
+ match path with
+ | None -> loop paths
+ | Some path ->
+ if is_systemroot path then Some path
+ else loop paths
+ in
+ let systemroot = loop systemroot_paths in
+
+ let systemroot + match systemroot with
+ | Some systemroot -> Some systemroot
+ | None ->
+ (* If the fs contains boot.ini, check it for non-standard
+ * systemroot locations.
+ *)
+ let boot_ini_path = case_sensitive_path_silently "/boot.ini"
in
+ match boot_ini_path with
+ | None -> None
+ | Some boot_ini_path ->
+ get_windows_systemroot_from_boot_ini boot_ini_path in
+
+ match systemroot with
+ | None -> None
+ | Some systemroot ->
+ if verbose () then
+ eprintf "get_windows_systemroot: windows %%SYSTEMROOT%% =
%s\n%!"
+ systemroot;
+ Some systemroot
+
+and get_windows_systemroot_from_boot_ini boot_ini_path + let chroot +
Chroot.create ~name:"get_windows_systemroot_from_boot_ini"
+ (Sysroot.sysroot ()) in
+ let lines + Chroot.f chroot (
+ fun () ->
+ if not (is_small_file boot_ini_path) then (
+ eprintf "%s: not a regular file or too large\n"
boot_ini_path;
+ None
+ )
+ else
+ Some (read_whole_file boot_ini_path)
+ ) () in
+ match lines with
+ | None -> None
+ | Some lines ->
+ let lines = String.nsplit "\n" lines in
+
+ (* Find:
+ * [operating systems]
+ * followed by multiple lines starting with "multi" or
"scsi".
+ *)
+ let rec loop = function
+ | [] -> None
+ | str :: rest when String.is_prefix str "[operating systems]"
->
+ let rec loop2 = function
+ | [] -> []
+ | str :: rest when String.is_prefix str "multi(" ||
+ String.is_prefix str "scsi(" ->
+ str :: loop2 rest
+ | _ -> []
+ in
+ Some (loop2 rest)
+ | _ :: rest -> loop rest
+ in
+ match loop lines with
+ | None -> None
+ | Some oses ->
+ (* Rewrite multi|scsi lines, removing any which we cannot parse. *)
+ let oses + filter_map (
+ fun line ->
+ if Str.string_match re_boot_ini_os line 0 then (
+ let ctrlr_type = Str.matched_group 1 line
+ and ctrlr = int_of_string (Str.matched_group 2 line)
+ and disk = int_of_string (Str.matched_group 3 line)
+ and rdisk = int_of_string (Str.matched_group 4 line)
+ and part = int_of_string (Str.matched_group 5 line)
+ and path = Str.matched_group 6 line in
+
+ (* Swap backslashes for forward slashes in the
+ * system root path.
+ *)
+ let path = String.replace_char path '\\' '/' in
+
+ Some (ctrlr_type, ctrlr, disk, rdisk, part, path)
+ )
+ else None
+ ) oses in
+
+ (* The Windows system root may be on any disk. However, there
+ * are currently (at least) 2 practical problems preventing us
+ * from locating it on another disk:
+ *
+ * 1. We don't have enough metadata about the disks we were
+ * given to know if what controller they were on and what
+ * index they had.
+ *
+ * 2. The way inspection of filesystems currently works, we
+ * can't mark another filesystem, which we may have already
+ * inspected, to be inspected for a specific Windows system
+ * root.
+ *
+ * Solving 1 properly would require a new API at a minimum. We
+ * might be able to fudge something practical without this,
+ * though, e.g. by looking at the <partition>th partition of
+ * every disk for the specific windows root.
+ *
+ * Solving 2 would probably require a significant refactoring
+ * of the way filesystems are inspected. We should probably do
+ * this some time.
+ *
+ * For the moment, we ignore all partition information and
+ * assume the system root is on the current partition. In
+ * practice, this will normally be correct.
+ *)
+
+ let rec loop = function
+ | [] -> None
+ | (_, _, _, _, _, path) :: rest ->
+ if is_systemroot path then Some path
+ else loop rest
+ in
+ loop oses
+
+(* Try to find Windows systemroot using some common locations.
+ *
+ * Notes:
+ *
+ * (1) We check for some directories inside to see if it is a real
+ * systemroot, and not just a directory that happens to have the same
+ * name.
+ *
+ * (2) If a Windows guest has multiple disks and applications are
+ * installed on those other disks, then those other disks will contain
+ * "/Program Files" and "/System Volume Information".
Those would
+ * *not* be Windows root disks. (RHBZ#674130)
+ *)
+and is_systemroot systemroot + is_dir_nocase (systemroot ^
"/system32") &&
+ is_dir_nocase (systemroot ^ "/system32/config") &&
+ is_file_nocase (systemroot ^ "/system32/cmd.exe")
+
+(* Return the architecture of the guest from cmd.exe. *)
+and check_windows_arch systemroot + let cmd_exe = sprintf
"%s/system32/cmd.exe" systemroot in
+
+ (* Should exist because of previous check above in is_systemroot. *)
+ let cmd_exe = Realpath.case_sensitive_path cmd_exe in
+
+ Filearch.file_architecture cmd_exe
+
+(* Read further fields from the Windows registry. *)
+and check_windows_registry systemroot data + (* We know (from is_systemroot)
that the config directory exists. *)
+ let software_hive = sprintf "%s/system32/config/software"
systemroot in
+ let software_hive = Realpath.case_sensitive_path software_hive in
+ let software_hive + if Is.is_file software_hive then Some software_hive
else None in
+ let data = { data with windows_software_hive = software_hive } in
+
+ let system_hive = sprintf "%s/system32/config/system" systemroot in
+ let system_hive = Realpath.case_sensitive_path system_hive in
+ let system_hive + if Is.is_file system_hive then Some system_hive else
None in
+ let data = { data with windows_system_hive = system_hive } in
+
+ match software_hive, system_hive with
+ | None, _ | Some _, None -> data
+ | Some software_hive, Some system_hive ->
+ (* Check software hive. *)
+ let data = check_windows_software_registry software_hive data in
+
+ (* Check system hive. *)
+ let data = check_windows_system_registry system_hive data in
+
+ data
+
+(* At the moment, pull just the ProductName and version numbers from
+ * the registry. In future there is a case for making many more
+ * registry fields available to callers.
+ *)
+and check_windows_software_registry software_hive data + with_hive
(Sysroot.sysroot () // software_hive) (
+ fun h root ->
+ try
+ let path = [ "Microsoft"; "Windows NT";
"CurrentVersion" ] in
+ let node = get_node h root path in
+ let values = Hivex.node_values h node in
+ let values = Array.to_list values in
+ (* Convert to a list of (key, value) to make the following easier. *)
+ let values = List.map (fun v -> Hivex.value_key h v, v) values in
+
+ (* Look for ProductName key. *)
+ let data + try
+ let v = List.assoc "ProductName" values in
+ { data with product_name = Some (hivex_value_as_utf8 h v) }
+ with
+ Not_found -> data in
+
+ (* Version is complicated. Use CurrentMajorVersionNumber and
+ * CurrentMinorVersionNumber if present. If they are not
+ * found, fall back on CurrentVersion.
+ *)
+ let data + try
+ let major_v = List.assoc "CurrentMajorVersionNumber"
values
+ and minor_v = List.assoc "CurrentMinorVersionNumber"
values in
+ let major = Int32.to_int (Hivex.value_dword h major_v)
+ and minor = Int32.to_int (Hivex.value_dword h minor_v) in
+ { data with version = Some (major, minor) }
+ with
+ Not_found ->
+ let v = List.assoc "CurrentVersion" values in
+ let v = hivex_value_as_utf8 h v in
+ parse_version_from_major_minor v data in
+
+ (* InstallationType (product_variant). *)
+ let data + try
+ let v = List.assoc "InstallationType" values in
+ { data with product_variant = Some (hivex_value_as_utf8 h v) }
+ with
+ Not_found -> data in
+
+ data
+ with
+ | Not_found ->
+ if verbose () then
+ eprintf "check_windows_software_registry: cannot locate
HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\n%!";
+ data
+ ) (* with_hive *)
+
+and check_windows_system_registry system_hive data + with_hive
(Sysroot.sysroot () // system_hive) (
+ fun h root ->
+ let data = get_drive_mappings h root data in
+
+ let current_control_set = get_current_control_set h root in
+ let data + { data with windows_current_control_set =
current_control_set } in
+
+ match current_control_set with
+ | None -> data
+ | Some current_control_set ->
+ let hostname = get_hostname h root current_control_set in
+ let data = { data with hostname = hostname } in
+ data
+ ) (* with_hive *)
+
+(* Get the CurrentControlSet. *)
+and get_current_control_set h root + try
+ let path = [ "Select" ] in
+ let node = get_node h root path in
+ let current_v = Hivex.node_get_value h node "Current" in
+ let current_control_set + sprintf "ControlSet%03ld"
(Hivex.value_dword h current_v) in
+ Some current_control_set
+ with
+ | Not_found ->
+ if verbose () then
+ eprintf "check_windows_system_registry: cannot locate
HKLM\\SYSTEM\\Select\n%!";
+ None
+
+(* Get the drive mappings.
+ * This page explains the contents of HKLM\System\MountedDevices:
+ * http://www.goodells.net/multiboot/partsigs.shtml
+ *)
+and get_drive_mappings h root data + let devices = lazy
(Devsparts.list_devices ()) in
+ let partitions = lazy (Devsparts.list_partitions ()) in
+ try
+ let path = [ "MountedDevices" ] in
+ let node = get_node h root path in
+ let values = Hivex.node_values h node in
+ let values = Array.to_list values in
+ let values + filter_map (
+ fun value ->
+ let key = Hivex.value_key h value in
+ let keylen = String.length key in
+ if keylen >= 14 &&
+ String.lowercase_ascii (String.sub key 0 12) =
"\\dosdevices\\" &&
+ Char.isalpha key.[12] && key.[13] = ':' then (
+ let drive_letter = String.sub key 12 1 in
+
+ (* Get the binary value. Is it a fixed disk? *)
+ let (typ, blob) = Hivex.value_value h value in
+ let device + if typ = Hivex.REG_BINARY then (
+ if String.length blob >= 24 &&
+ String.is_prefix blob "DMIO:ID:" (* GPT *) then
+ map_registry_disk_blob_gpt (Lazy.force partitions) blob
+ else if String.length blob = 12 then
+ map_registry_disk_blob (Lazy.force devices) blob
+ else
+ None
+ )
+ else None in
+
+ match device with
+ | None -> None
+ | Some device -> Some (drive_letter, device)
+ )
+ else
+ None
+ ) values in
+
+ { data with drive_mappings = values }
+
+ with
+ | Not_found ->
+ if verbose () then
+ eprintf "check_windows_system_registry: cannot find drive
mappings\n%!";
+ data
+
+(* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data
+ * to store partitions. This blob is described here:
+ * http://www.goodells.net/multiboot/partsigs.shtml
+ * The following function maps this blob to a libguestfs partition
+ * name, if possible.
+ *)
+and map_registry_disk_blob devices blob + try
+ (* First 4 bytes are the disk ID. Search all devices to find the
+ * disk with this disk ID.
+ *)
+ let diskid = String.sub blob 0 4 in
+ let device = List.find (fun dev -> pread dev 4 0x01b8 = diskid) devices
in
+
+ (* Next 8 bytes are the offset of the partition in bytes(!) given as
+ * a 64 bit little endian number. Luckily it's easy to get the
+ * partition byte offset from Parted.part_list.
+ *)
+ let offset = String.sub blob 4 8 in
+ let offset = int_of_le64 offset in
+ let partitions = Parted.part_list device in
+ let partition + List.find (fun { Parted.part_start = s } -> s =
offset) partitions in
+
+ (* Construct the full device name. *)
+ Some (sprintf "%s%ld" device partition.Parted.part_num)
+ with
+ | Not_found -> None
+
+(* Matches Windows registry HKLM\SYSYTEM\MountedDevices\DosDevices blob to
+ * to libguestfs GPT partition device. For GPT disks, the blob is made of
+ * "DMIO:ID:" prefix followed by the GPT partition GUID.
+ *)
+and map_registry_disk_blob_gpt partitions blob + let blob_guid +
String.lowercase_ascii (extract_guid_from_registry_blob blob) in
+
+ try
+ let partition + List.find (
+ fun part ->
+ let partnum = Devsparts.part_to_partnum part in
+ let device = Devsparts.part_to_dev part in
+ let typ = Parted.part_get_parttype device in
+ if typ <> "gpt" then false
+ else (
+ let guid = Parted.part_get_gpt_guid device partnum in
+ String.lowercase_ascii guid = blob_guid
+ )
+ ) partitions in
+ Some partition
+ with
+ | Not_found -> None
+
+(* Extracts the binary GUID stored in blob from Windows registry
+ * HKLM\SYSTYEM\MountedDevices\DosDevices value and converts it to a
+ * GUID string so that it can be matched against libguestfs partition
+ * device GPT GUID.
+ *)
+and extract_guid_from_registry_blob blob + (* Copy relevant sections from blob
to respective ints.
+ * Note we have to skip 8 byte "DMIO:ID:" prefix.
+ *)
+ let data1 = int_of_le32 (String.sub blob 8 4)
+ and data2 = int_of_le16 (String.sub blob 12 2)
+ and data3 = int_of_le16 (String.sub blob 14 2)
+ and data4 = int_of_be64 (String.sub blob 16 8) (* really big endian! *) in
+
+ sprintf "%08Lx-%04Lx-%04Lx-%04Lx-%012Lx"
+ data1 data2 data3
+ (Int64.shift_right data4 48)
+ (data4 &^ 0xffffffffffff_L)
+
+and pread device size offset + let fd = Unix.openfile device [Unix.O_RDONLY;
Unix.O_CLOEXEC] 0 in
+ let ret + protect ~f:(
+ fun () ->
+ ignore (Unix.lseek fd offset Unix.SEEK_SET);
+ let ret = Bytes.create size in
+ if Unix.read fd ret 0 size < size then
+ failwithf "pread: %s: short read" device;
+ ret
+ ) ~finally:(fun () -> Unix.close fd) in
+ Bytes.to_string ret
+
+(* Get the hostname. *)
+and get_hostname h root current_control_set + try
+ let path = [ current_control_set; "Services"; "Tcpip";
"Parameters" ] in
+ let node = get_node h root path in
+ let values = Hivex.node_values h node in
+ let values = Array.to_list values in
+ (* Convert to a list of (key, value) to make the following easier. *)
+ let values = List.map (fun v -> Hivex.value_key h v, v) values in
+ let hostname_v = List.assoc "Hostname" values in
+ Some (hivex_value_as_utf8 h hostname_v)
+ with
+ | Not_found ->
+ if verbose () then
+ eprintf "check_windows_system_registry: cannot locate
HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters and/or Hostname key\n%!"
current_control_set;
+ None
+
+(* Raises [Not_found] if the node is not found. *)
+and get_node h node = function
+ | [] -> node
+ | x :: xs ->
+ let node = Hivex.node_get_child h node x in
+ get_node h node xs
+
+(* NB: This function DOES NOT test for the existence of the file. It
+ * will return non-NULL even if the file/directory does not exist.
+ * You have to call guestfs_is_file{,_opts} etc.
+ *)
+and case_sensitive_path_silently path + try
+ Some (Realpath.case_sensitive_path path)
+ with
+ | exn ->
+ if verbose () then
+ eprintf "case_sensitive_path_silently: %s: %s\n%!" path
+ (Printexc.to_string exn);
+ None
diff --git a/daemon/inspect_fs_windows.mli b/daemon/inspect_fs_windows.mli
new file mode 100644
index 000000000..936d695c6
--- /dev/null
+++ b/daemon/inspect_fs_windows.mli
@@ -0,0 +1,25 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val check_windows_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Windows [C:] filesystem mounted on sysroot. *)
+
+val is_windows_systemroot : unit -> bool
+(** Decide if the filesystem mounted on sysroot looks like a
+ Windows [C:] filesystem. *)
diff --git a/daemon/inspect_types.ml b/daemon/inspect_types.ml
new file mode 100644
index 000000000..7695fa15b
--- /dev/null
+++ b/daemon/inspect_types.ml
@@ -0,0 +1,317 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Std_utils
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role + | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+and inspection_data = {
+ os_type : os_type option;
+ distro : distro option;
+ package_format : package_format option;
+ package_management : package_management option;
+ product_name : string option;
+ product_variant : string option;
+ version : version option;
+ arch : string option;
+ hostname : string option;
+ fstab : fstab_entry list;
+ windows_systemroot : string option;
+ windows_software_hive : string option;
+ windows_system_hive : string option;
+ windows_current_control_set : string option;
+ drive_mappings : drive_mapping list;
+}
+and os_type + | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro + | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format + | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management + | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, device *)
+
+let rec string_of_fs { fs_location = location; role = role } + sprintf
"fs: %s role: %s"
+ (string_of_location location)
+ (match role with
+ | RoleRoot _ -> "root"
+ | RoleUsr _ -> "usr"
+ | RoleSwap -> "swap"
+ | RoleOther -> "other")
+
+and string_of_location { mountable = mountable; vfs_type = vfs_type } +
sprintf "%s (%s)" (Mountable.to_string mountable) vfs_type
+
+and string_of_root { root_location = location;
+ inspection_data = inspection_data } + sprintf
"%s:\n%s"
+ (string_of_location location)
+ (string_of_inspection_data inspection_data)
+
+and string_of_inspection_data data + let b = Buffer.create 1024 in
+ let bpf fs = bprintf b fs in
+ may (fun v -> bpf "\ttype: %s\n" (string_of_os_type v))
+ data.os_type;
+ may (fun v -> bpf "\tdistro: %s\n" (string_of_distro v))
+ data.distro;
+ may (fun v -> bpf "\tpackage_format: %s\n"
(string_of_package_format v))
+ data.package_format;
+ may (fun v -> bpf "\tpackage_management: %s\n"
(string_of_package_management v))
+ data.package_management;
+ may (fun v -> bpf "\tproduct_name: %s\n" v)
+ data.product_name;
+ may (fun v -> bpf "\tproduct_variant: %s\n" v)
+ data.product_variant;
+ may (fun (major, minor) -> bpf "\tversion: %d.%d\n" major minor)
+ data.version;
+ may (fun v -> bpf "\tarch: %s\n" v)
+ data.arch;
+ may (fun v -> bpf "\thostname: %s\n" v)
+ data.hostname;
+ if data.fstab <> [] then (
+ let v = List.map (
+ fun (a, b) -> sprintf "(%s, %s)" (Mountable.to_string a) b
+ ) data.fstab in
+ bpf "\tfstab: [%s]\n" (String.concat ", " v)
+ );
+ may (fun v -> bpf "\twindows_systemroot: %s\n" v)
+ data.windows_systemroot;
+ may (fun v -> bpf "\twindows_software_hive: %s\n" v)
+ data.windows_software_hive;
+ may (fun v -> bpf "\twindows_system_hive: %s\n" v)
+ data.windows_system_hive;
+ may (fun v -> bpf "\twindows_current_control_set: %s\n" v)
+ data.windows_current_control_set;
+ if data.drive_mappings <> [] then (
+ let v + List.map (fun (a, b) -> sprintf "(%s, %s)" a b)
data.drive_mappings in
+ bpf "\tdrive_mappings: [%s]\n" (String.concat ", " v)
+ );
+ Buffer.contents b
+
+and string_of_os_type = function
+ | OS_TYPE_DOS -> "dos"
+ | OS_TYPE_FREEBSD -> "freebsd"
+ | OS_TYPE_HURD -> "hurd"
+ | OS_TYPE_LINUX -> "linux"
+ | OS_TYPE_MINIX -> "minix"
+ | OS_TYPE_NETBSD -> "netbsd"
+ | OS_TYPE_OPENBSD -> "openbsd"
+ | OS_TYPE_WINDOWS -> "windows"
+
+and string_of_distro = function
+ | DISTRO_ALPINE_LINUX -> "alpinelinux"
+ | DISTRO_ALTLINUX -> "altlinux"
+ | DISTRO_ARCHLINUX -> "archlinux"
+ | DISTRO_BUILDROOT -> "buildroot"
+ | DISTRO_CENTOS -> "centos"
+ | DISTRO_CIRROS -> "cirros"
+ | DISTRO_COREOS -> "coreos"
+ | DISTRO_DEBIAN -> "debian"
+ | DISTRO_FEDORA -> "fedora"
+ | DISTRO_FREEBSD -> "freebsd"
+ | DISTRO_FREEDOS -> "freedos"
+ | DISTRO_FRUGALWARE -> "frugalware"
+ | DISTRO_GENTOO -> "gentoo"
+ | DISTRO_LINUX_MINT -> "linuxmint"
+ | DISTRO_MAGEIA -> "mageia"
+ | DISTRO_MANDRIVA -> "mandriva"
+ | DISTRO_MEEGO -> "meego"
+ | DISTRO_NETBSD -> "netbsd"
+ | DISTRO_OPENBSD -> "openbsd"
+ | DISTRO_OPENSUSE -> "opensuse"
+ | DISTRO_ORACLE_LINUX -> "oraclelinux"
+ | DISTRO_PARDUS -> "pardus"
+ | DISTRO_PLD_LINUX -> "pldlinux"
+ | DISTRO_REDHAT_BASED -> "redhat-based"
+ | DISTRO_RHEL -> "rhel"
+ | DISTRO_SCIENTIFIC_LINUX -> "scientificlinux"
+ | DISTRO_SLACKWARE -> "slackware"
+ | DISTRO_SLES -> "sles"
+ | DISTRO_SUSE_BASED -> "suse-based"
+ | DISTRO_TTYLINUX -> "ttylinux"
+ | DISTRO_UBUNTU -> "ubuntu"
+ | DISTRO_VOID_LINUX -> "voidlinux"
+ | DISTRO_WINDOWS -> "windows"
+
+and string_of_package_format = function
+ | PACKAGE_FORMAT_APK -> "apk"
+ | PACKAGE_FORMAT_DEB -> "deb"
+ | PACKAGE_FORMAT_EBUILD -> "ebuild"
+ | PACKAGE_FORMAT_PACMAN -> "pacman"
+ | PACKAGE_FORMAT_PISI -> "pisi"
+ | PACKAGE_FORMAT_PKGSRC -> "pkgsrc"
+ | PACKAGE_FORMAT_RPM -> "rpm"
+ | PACKAGE_FORMAT_XBPS -> "xbps"
+
+and string_of_package_management = function
+ | PACKAGE_MANAGEMENT_APK -> "apk"
+ | PACKAGE_MANAGEMENT_APT -> "apt"
+ | PACKAGE_MANAGEMENT_DNF -> "dnf"
+ | PACKAGE_MANAGEMENT_PACMAN -> "pacman"
+ | PACKAGE_MANAGEMENT_PISI -> "pisi"
+ | PACKAGE_MANAGEMENT_PORTAGE -> "portage"
+ | PACKAGE_MANAGEMENT_UP2DATE -> "up2date"
+ | PACKAGE_MANAGEMENT_URPMI -> "urpmi"
+ | PACKAGE_MANAGEMENT_XBPS -> "xbps"
+ | PACKAGE_MANAGEMENT_YUM -> "yum"
+ | PACKAGE_MANAGEMENT_ZYPPER -> "zypper"
+
+let null_inspection_data = {
+ os_type = None;
+ distro = None;
+ package_format = None;
+ package_management = None;
+ product_name = None;
+ product_variant = None;
+ version = None;
+ arch = None;
+ hostname = None;
+ fstab = [];
+ windows_systemroot = None;
+ windows_software_hive = None;
+ windows_system_hive = None;
+ windows_current_control_set = None;
+ drive_mappings = [];
+}
+
+let merge_inspection_data child parent + let merge child parent = if parent =
None then child else parent in
+
+ { os_type = merge child.os_type parent.os_type;
+ distro = merge child.distro parent.distro;
+ package_format = merge child.package_format parent.package_format;
+ package_management + merge child.package_management
parent.package_management;
+ product_name = merge child.product_name parent.product_name;
+ product_variant = merge child.product_variant parent.product_variant;
+ version = merge child.version parent.version;
+ arch = merge child.arch parent.arch;
+ hostname = merge child.hostname parent.hostname;
+ fstab = child.fstab @ parent.fstab;
+ windows_systemroot + merge child.windows_systemroot
parent.windows_systemroot;
+ windows_software_hive + merge child.windows_software_hive
parent.windows_software_hive;
+ windows_system_hive + merge child.windows_system_hive
parent.windows_system_hive;
+ windows_current_control_set + merge child.windows_current_control_set
parent.windows_current_control_set;
+
+ (* This is what the old C code did, but I doubt that it's correct. *)
+ drive_mappings = child.drive_mappings @ parent.drive_mappings;
+ }
+
+let merge child_fs parent_fs + let inspection_data_of_fs = function
+ | { role = RoleRoot data }
+ | { role = RoleUsr data } -> data
+ | { role = (RoleSwap|RoleOther) } -> assert false
+ in
+
+ match parent_fs with
+ | { role = RoleRoot parent_data } ->
+ { parent_fs with
+ role = RoleRoot (merge_inspection_data (inspection_data_of_fs child_fs)
+ parent_data) }
+ | { role = RoleUsr parent_data } ->
+ { parent_fs with
+ role = RoleUsr (merge_inspection_data (inspection_data_of_fs child_fs)
+ parent_data) }
+ | { role = (RoleSwap|RoleOther) } -> parent_fs
+
+let inspect_fses = ref []
diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli
new file mode 100644
index 000000000..a9564b16b
--- /dev/null
+++ b/daemon/inspect_types.mli
@@ -0,0 +1,168 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role + | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+and inspection_data = {
+ os_type : os_type option;
+ distro : distro option;
+ package_format : package_format option;
+ package_management : package_management option;
+ product_name : string option;
+ product_variant : string option;
+ version : version option;
+ arch : string option;
+ hostname : string option;
+ fstab : fstab_entry list;
+ windows_systemroot : string option;
+ windows_software_hive : string option;
+ windows_system_hive : string option;
+ windows_current_control_set : string option;
+ drive_mappings : drive_mapping list;
+}
+and os_type + | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro + | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format + | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management + | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, device *)
+
+val merge_inspection_data : inspection_data -> inspection_data ->
inspection_data
+(** [merge_inspection_data child parent] merges two sets of inspection
+ data into a single set. The parent inspection data fields, if
+ present, take precedence over the child inspection data fields.
+
+ It's intended that you merge upwards, ie.
+ [merge_inspection_data usr root] *)
+
+val merge : fs -> fs -> fs
+(** [merge child_fs parent_fs] merges two filesystems,
+ using [merge_inspection_data] to merge the inspection data of
+ the child into the parent. *)
+
+val string_of_fs : fs -> string
+(** Convert [fs] into a single line string, for debugging only. *)
+
+val string_of_root : root -> string
+(** Convert [root] into a multi-line string, for debugging only. *)
+
+val string_of_location : location -> string
+(** Convert [location] into a string, for debugging only. *)
+
+val string_of_inspection_data : inspection_data -> string
+(** Convert [inspection_data] into a multi-line string, for debugging only. *)
+
+val string_of_os_type : os_type -> string
+(** Convert [os_type] to a string.
+ The string is part of the public API. *)
+
+val string_of_distro : distro -> string
+(** Convert [distro] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_format : package_format -> string
+(** Convert [package_format] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_management : package_management -> string
+(** Convert [package_management] to a string.
+ The string is part of the public API. *)
+
+val null_inspection_data : inspection_data
+(** {!inspection_data} structure with all fields set to [None]. *)
+
+val inspect_fses : fs list ref
+(** The global list of filesystems found by the previous call to
+ inspect_os. *)
diff --git a/daemon/inspect_utils.ml b/daemon/inspect_utils.ml
new file mode 100644
index 000000000..b2388af53
--- /dev/null
+++ b/daemon/inspect_utils.ml
@@ -0,0 +1,175 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Std_utils
+
+open Utils
+open Inspect_types
+
+let max_augeas_file_size = 100 * 1000
+
+let rec with_augeas configfiles f + let sysroot = Sysroot.sysroot () in
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+
+ (* Security:
+ *
+ * The old C code had a few problems: It ignored non-regular-file
+ * objects (eg. devices), passing them to Augeas, so relying on
+ * Augeas to do the right thing. Also too-large regular files
+ * caused the whole inspection operation to fail.
+ *
+ * I have tried to improve this so that non-regular files and
+ * too large files are ignored (dropped from the configfiles list),
+ * so that Augeas won't touch them, but they also won't stop
+ * inspection.
+ *)
+ let safe_file file + Is.is_file ~followsymlinks:true file && (
+ let size = (Chroot.f chroot Unix.stat file).Unix.st_size in
+ size <= max_augeas_file_size
+ )
+ in
+ let configfiles = List.filter safe_file configfiles in
+
+ let aug + Augeas.create sysroot None [Augeas.AugSaveNoop;
Augeas.AugNoLoad] in
+
+ protect
+ ~f:(fun () ->
+ (* Tell Augeas to only load configfiles and no other files. This
+ * prevents a rogue guest from performing a denial of service attack
+ * by having large, over-complicated configuration files which are
+ * unrelated to the task at hand. (Thanks Dominic Cleal).
+ * Note this requires Augeas >= 1.0.0 because of RHBZ#975412.
+ *)
+ let pathexpr = make_augeas_path_expression configfiles in
+ ignore (Augeas.rm aug pathexpr);
+ Augeas.load aug;
+
+ (* Check that augeas did not get a parse error for any of the
+ * configfiles, otherwise we are silently missing information.
+ *)
+ let matches = Augeas.matches aug "/augeas/files//error" in
+ List.iter (
+ fun match_ ->
+ List.iter (
+ fun file ->
+ let errorpath = sprintf "/augeas/files%s/error" file in
+ if match_ = errorpath then (
+ (* There's been an error - get the error details. *)
+ let get path + match Augeas.get aug (errorpath
^ path) with
+ | None -> "<missing>"
+ | Some v -> v
+ in
+ let message = get "message" in
+ let line = get "line" in
+ let charp = get "char" in
+ failwithf "%s:%s:%s: augeas parse failure: %s"
+ file line charp message
+ )
+ ) configfiles
+ ) matches;
+
+ f aug
+ )
+ ~finally:(
+ fun () -> Augeas.close aug
+ )
+
+(* Explained here: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *)
+and make_augeas_path_expression files + let subexprs + List.map (
+ fun file ->
+ (* v NB trailing '/' after filename *)
+ sprintf "\"%s/\" !~ regexp('^') + glob(incl) +
regexp('/.*')" file
+ ) files in
+ let subexprs = String.concat " and " subexprs in
+
+ let ret = sprintf "/augeas/load/*[ %s ]" subexprs in
+ if verbose () then
+ eprintf "augeas pathexpr = %s\n%!" ret;
+
+ ret
+
+let is_file_nocase path + let path + try Some (Realpath.case_sensitive_path
path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_file path
+
+and is_dir_nocase path + let path + try Some (Realpath.case_sensitive_path
path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_dir path
+
+(* Rather hairy test for "is a partition", taken directly from
+ * the old C inspection code.
+ *)
+let is_partition partition + try
+ let device = Devsparts.part_to_dev partition in
+ let i = Devsparts.device_index device in
+ true
+ with _ -> false
+
+(* Without non-greedy matching, it's difficult to write these regular
+ * expressions properly. We should really switch to using PCRE. XXX
+ *)
+let re_major_minor = Str.regexp "[^0-9]*\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_major_no_minor = Str.regexp "[^0-9]*\\([0-9]+\\)"
+
+let parse_version_from_major_minor str data + if Str.string_match
re_major_minor str 0 ||
+ Str.string_match re_major_no_minor str 0 then (
+ let major + try Some (int_of_string (Str.matched_group 1 str))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ let minor + try Some (int_of_string (Str.matched_group 2 str))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ match major, minor with
+ | None, None -> data
+ | None, Some _ -> data
+ | Some major, None -> { data with version = Some (major, 0) }
+ | Some major, Some minor -> { data with version = Some (major, minor) }
+ )
+ else (
+ eprintf "parse_version_from_major_minor: cannot parse version from
'%s'\n"
+ str;
+ data
+ )
+
+let with_hive hive_filename f + let flags = [ Hivex.OPEN_UNSAFE ] in
+ let flags = if verbose () then Hivex.OPEN_VERBOSE :: flags else flags in
+ let h = Hivex.open_file hive_filename flags in
+ protect ~f:(fun () -> f h (Hivex.root h)) ~finally:(fun () ->
Hivex.close h)
+
+let hivex_value_as_utf8 h value + utf16le_to_utf8 (snd (Hivex.value_value h
value))
diff --git a/daemon/inspect_utils.mli b/daemon/inspect_utils.mli
new file mode 100644
index 000000000..96363c011
--- /dev/null
+++ b/daemon/inspect_utils.mli
@@ -0,0 +1,51 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val with_augeas : string list -> (Augeas.t -> 'a) -> 'a
+(** Open an Augeas handle, parse only 'configfiles' (these
+ files must exist), and then call 'f' with the Augeas handle.
+
+ As a security measure, this bails if any file is too large for
+ a reasonable configuration file. After the call to 'f' the
+ Augeas handle is closed. *)
+
+val is_file_nocase : string -> bool
+val is_dir_nocase : string -> bool
+(** With a filesystem mounted under sysroot, check if [path] is
+ a file or directory under that sysroot. The [path] is
+ checked case-insensitively. *)
+
+val is_partition : string -> bool
+(** Return true if the device is a partition. *)
+
+val parse_version_from_major_minor : string -> Inspect_types.inspection_data
->
+ Inspect_types.inspection_data
+(** Make a best effort attempt to parse either X or X.Y from a string,
+ usually the product_name string. *)
+
+val with_hive : string -> (Hivex.t -> Hivex.node -> 'a) ->
'a
+(** Open a Windows registry "hive", and call the function on the
+ handle and root node.
+
+ After the call to the function, the hive is always closed.
+
+ The hive is opened readonly. *)
+
+val hivex_value_as_utf8 : Hivex.t -> Hivex.value -> string
+(** Convert a Hivex value which we interpret as UTF-16LE to UTF-8.
+ The type field stored in the registry is ignored. *)
diff --git a/daemon/mount.ml b/daemon/mount.ml
index 4bb74fb82..40c81be0e 100644
--- a/daemon/mount.ml
+++ b/daemon/mount.ml
@@ -60,3 +60,64 @@ let mount_vfs options vfs mountable mountpoint let mount =
mount_vfs None None
let mount_ro = mount_vfs (Some "ro") None
let mount_options options = mount_vfs (Some options) None
+
+(* Unmount everything mounted under /sysroot.
+ *
+ * We have to unmount in the correct order, so we sort the paths by
+ * longest first to ensure that child paths are unmounted by parent
+ * paths.
+ *
+ * This call is more important than it appears at first, because it
+ * is widely used by both test and production code in order to
+ * get back to a known state (nothing mounted, everything synchronized).
+ *)
+let rec umount_all () + (* This is called from internal_autosync and generally
as a cleanup
+ * function, and since the umount will definitely fail if any
+ * handles are open, we may as well close them.
+ *)
+ (* XXX
+ aug_finalize ();
+ hivex_finalize ();
+ journal_finalize ();
+ *)
+
+ let sysroot = Sysroot.sysroot () in
+ let sysroot_len = String.length sysroot in
+
+ let info = read_whole_file "/proc/self/mountinfo" in
+ let info = String.nsplit "\n" info in
+
+ let mps = ref [] in
+ List.iter (
+ fun line ->
+ let line = String.nsplit " " line in
+ (* The field of interest is the 5th field. Whitespace is escaped
+ * with octal sequences like \040 (for space).
+ * See fs/seq_file.c:mangle_path.
+ *)
+ if List.length line >= 5 then (
+ let mp = List.nth line 4 in
+ let mp = proc_unmangle_path mp in
+
+ (* Allow a mount directory like "/sysroot" or
"/sysroot/..." *)
+ if (sysroot_len > 0 && String.is_prefix mp sysroot) ||
+ (String.is_prefix mp sysroot &&
+ String.length mp > sysroot_len &&
+ mp.[sysroot_len] = '/') then
+ push_front mp mps
+ )
+ ) info;
+
+ let mps = !mps in
+ let mps = List.sort compare_longest_first mps in
+
+ (* Unmount them. *)
+ List.iter (
+ fun mp -> ignore (command "umount" [mp])
+ ) mps
+
+and compare_longest_first s1 s2 + let n1 = String.length s1 in
+ let n2 = String.length s2 in
+ n2 - n1
diff --git a/daemon/mount.mli b/daemon/mount.mli
index e43d97c42..abf538521 100644
--- a/daemon/mount.mli
+++ b/daemon/mount.mli
@@ -20,3 +20,5 @@ val mount : Mountable.t -> string -> unit
val mount_ro : Mountable.t -> string -> unit
val mount_options : string -> Mountable.t -> string -> unit
val mount_vfs : string option -> string option -> Mountable.t ->
string -> unit
+
+val umount_all : unit -> unit
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 808e575fd..e53b4bf02 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -247,3 +247,103 @@ let proc_unmangle_path path let is_small_file path
is_regular_file path &&
(stat path).st_size <= 2 * 1048 * 1024
+
+let unix_canonical_path path + let is_absolute = String.length path > 0
&& path.[0] = '/' in
+ let path = String.nsplit "/" path in
+ let path = List.filter ((<>) "") path in
+ (if is_absolute then "/" else "") ^ String.concat
"/" path
+
+(* Note that we cannot use iconv here because inside the appliance
+ * all i18n databases are deleted. For the same reason we cannot
+ * use functions like hivex_value_string, as they also use iconv
+ * internally.
+ *
+ * https://en.wikipedia.org/wiki/UTF-16
+ * Also inspired by functions in glib's glib/gutf8.c
+ *)
+let rec utf16le_to_utf8 instr + (* If the length is odd and the last character
is ASCII NUL, just
+ * drop that. (If it's not ASCII NUL, then there's an error)
+ *)
+ let len = String.length instr in
+ let instr + if len mod 1 = 1 then (
+ if instr.[len-1] = '\000' then String.sub instr 0 (len-1)
+ else invalid_arg "input is not a valid UTF16-LE string: length is
odd"
+ ) else instr in
+
+ (* The length should now be even. If the last two bytes are
+ * '\0\0' then assume it's a NUL-terminated string from the
+ * Windows registry and drop both characters.
+ *)
+ let len = String.length instr in
+ let instr + if len >= 2 && instr.[len-2] = '\000'
&& instr.[len-1] = '\000' then
+ String.sub instr 0 (len-2)
+ else instr in
+
+ let outbuf = Buffer.create len in
+
+ (* Encode a wide character as UTF-8 and write to outbuf.
+ * Basically this is g_unichar_to_utf8 implemented in OCaml.
+ *)
+ let encode_utf8 c + let first, len + if c < 0x80 then
+ (0, 1)
+ else if c < 0x800 then
+ (0xc0, 2)
+ else if c < 0x10000 then
+ (0xe0, 3)
+ else if c < 0x200000 then
+ (0xf0, 4)
+ else if c < 0x4000000 then
+ (0xf8, 5)
+ else
+ (0xfc, 6) in
+ let rec loop i c + if i = 0 then Buffer.add_char outbuf (Char.chr (c
lor first))
+ else if i > 0 then (
+ loop (i-1) (c lsr 6);
+ Buffer.add_char outbuf (Char.chr ((c land 0x3f) lor 0x80))
+ )
+ in
+ loop (len-1) c
+ in
+
+ (* Loop over the input UTF16-LE characters. *)
+ let is_high_surrogate c = c >= 0xd800 && c < 0xdc00
+ and is_low_surrogate c = c >= 0xdc00 && c < 0xe000
+ and surrogate_value highc lowc + 0x1_0000 + (highc - 0xd800) * 0x400 +
lowc - 0xdc00
+ in
+
+ let len = String.length instr in
+ let rec loop i + if i+1 >= len then ()
+ else (
+ let c = Char.code instr.[i] + (Char.code instr.[i+1] lsl 8) in
+
+ let wc, skip + (* High surrogate - must come first. *)
+ if is_high_surrogate c then (
+ if i+3 >= len then
+ invalid_arg "input is not a valid UTF16-LE string: high
surrogate at end of string";
+ let lowc = Char.code instr.[i+2] + (Char.code instr.[i+3] lsl 8) in
+ if not (is_low_surrogate lowc) then
+ invalid_arg "input is not a valid UTF16-LE string: high
surrogate not followed by low surrogate";
+ (surrogate_value c lowc, 4)
+ )
+ else if is_low_surrogate c then
+ invalid_arg "input is not a valid UTF16-LE string: unexpected
low surrogate"
+ else
+ (c, 2) in
+
+ encode_utf8 wc;
+ loop (i+skip)
+ )
+ in
+ loop 0;
+
+ Buffer.contents outbuf
diff --git a/daemon/utils.mli b/daemon/utils.mli
index d3c8bdf4d..94a77de01 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -85,3 +85,15 @@ val commandr : ?flags:command_flag list -> string ->
string list -> (int * strin
val is_small_file : string -> bool
(** Return true if the path is a small regular file. *)
+
+val unix_canonical_path : string -> string
+(** Canonicalize a Unix path, so "///usr//local//" ->
"/usr/local"
+
+ The path is modified in place because the result is always
+ the same length or shorter than the argument passed. *)
+
+val utf16le_to_utf8 : string -> string
+(** Convert a UTF16-LE string to UTF-8.
+
+ This uses a simple internal implementation since we cannot use
+ iconv inside the daemon. *)
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 39dcf9035..e90aac0f0 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -64,6 +64,7 @@ customize/perl_edit-c.c
daemon/9p.c
daemon/acl.c
daemon/actions.h
+daemon/augeas-c.c
daemon/augeas.c
daemon/available.c
daemon/base64.c
@@ -306,9 +307,6 @@ lib/handle.c
lib/hivex.c
lib/info.c
lib/inspect-apps.c
-lib/inspect-fs-unix.c
-lib/inspect-fs-windows.c
-lib/inspect-fs.c
lib/inspect-icon.c
lib/inspect.c
lib/journal.c
diff --git a/generator/actions.ml b/generator/actions.ml
index 75742397a..515096881 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -51,6 +51,8 @@ let daemon_functions
Actions_core_deprecated.daemon_functions @
Actions_debug.daemon_functions @
Actions_hivex.daemon_functions @
+ Actions_inspection.daemon_functions @
+ Actions_inspection_deprecated.daemon_functions @
Actions_tsk.daemon_functions @
Actions_yara.daemon_functions
diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml
index cd8b9da18..d67d00833 100644
--- a/generator/actions_inspection.ml
+++ b/generator/actions_inspection.ml
@@ -22,10 +22,11 @@ open Types
(* Inspection APIs. *)
-let non_daemon_functions = [
+let daemon_functions = [
{ defaults with
name = "inspect_os"; added = (1, 5, 3);
style = RStringList (RMountable, "roots"), [], [];
+ impl = OCaml "Inspect.inspect_os";
shortdesc = "inspect disk and return list of operating systems
found";
longdesc = "\
This function uses other libguestfs functions and certain
@@ -61,8 +62,24 @@ Please read L<guestfs(3)/INSPECTION> for more details.
See also C<guestfs_list_filesystems>." };
{ defaults with
+ name = "inspect_get_roots"; added = (1, 7, 3);
+ style = RStringList (RMountable, "roots"), [], [];
+ impl = OCaml "Inspect.inspect_get_roots";
+ shortdesc = "return list of operating systems found by last
inspection";
+ longdesc = "\
+This function is a convenient way to get the list of root
+devices, as returned from a previous call to C<guestfs_inspect_os>,
+but without redoing the whole inspection process.
+
+This returns an empty list if either no root devices were
+found or the caller has not called C<guestfs_inspect_os>.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
name = "inspect_get_type"; added = (1, 5, 3);
style = RString (RPlainString, "name"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_type";
shortdesc = "get type of inspected operating system";
longdesc = "\
This returns the type of the inspected operating system.
@@ -116,6 +133,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_get_arch"; added = (1, 5, 3);
style = RString (RPlainString, "arch"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_arch";
shortdesc = "get architecture of inspected operating system";
longdesc = "\
This returns the architecture of the inspected operating system.
@@ -130,6 +148,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_get_distro"; added = (1, 5, 3);
style = RString (RPlainString, "distro"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_distro";
shortdesc = "get distro of inspected operating system";
longdesc = "\
This returns the distro (distribution) of the inspected operating
@@ -286,6 +305,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_get_major_version"; added = (1, 5, 3);
style = RInt "major", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_major_version";
shortdesc = "get major version of inspected operating system";
longdesc = "\
This returns the major version number of the inspected operating
@@ -305,6 +325,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_get_minor_version"; added = (1, 5, 3);
style = RInt "minor", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_minor_version";
shortdesc = "get minor version of inspected operating system";
longdesc = "\
This returns the minor version number of the inspected operating
@@ -318,6 +339,7 @@ See also C<guestfs_inspect_get_major_version>."
};
{ defaults with
name = "inspect_get_product_name"; added = (1, 5, 3);
style = RString (RPlainString, "product"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_product_name";
shortdesc = "get product name of inspected operating system";
longdesc = "\
This returns the product name of the inspected operating
@@ -331,8 +353,164 @@ string C<unknown> is returned.
Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
+ name = "inspect_get_windows_systemroot"; added = (1, 5, 25);
+ style = RString (RPlainString, "systemroot"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_systemroot";
+ shortdesc = "get Windows systemroot of inspected operating
system";
+ longdesc = "\
+This returns the Windows systemroot of the inspected guest.
+The systemroot is a directory path such as F</WINDOWS>.
+
+This call assumes that the guest is Windows and that the
+systemroot could be determined by inspection. If this is not
+the case then an error is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_package_format"; added = (1, 7, 5);
+ style = RString (RPlainString, "packageformat"), [String
(Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_package_format";
+ shortdesc = "get package format used by the operating system";
+ longdesc = "\
+This function and C<guestfs_inspect_get_package_management> return
+the package format and package management tool used by the
+inspected operating system. For example for Fedora these
+functions would return C<rpm> (package format), and
+C<yum> or C<dnf> (package management).
+
+This returns the string C<unknown> if we could not determine the
+package format I<or> if the operating system does not have
+a real packaging system (eg. Windows).
+
+Possible strings include:
+C<rpm>, C<deb>, C<ebuild>, C<pisi>, C<pacman>,
C<pkgsrc>, C<apk>,
+C<xbps>.
+Future versions of libguestfs may return other strings.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_package_management"; added = (1, 7, 5);
+ style = RString (RPlainString, "packagemanagement"), [String
(Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_package_management";
+ shortdesc = "get package management tool used by the operating
system";
+ longdesc = "\
+C<guestfs_inspect_get_package_format> and this function return
+the package format and package management tool used by the
+inspected operating system. For example for Fedora these
+functions would return C<rpm> (package format), and
+C<yum> or C<dnf> (package management).
+
+This returns the string C<unknown> if we could not determine the
+package management tool I<or> if the operating system does not have
+a real packaging system (eg. Windows).
+
+Possible strings include: C<yum>, C<dnf>, C<up2date>,
+C<apt> (for all Debian derivatives),
+C<portage>, C<pisi>, C<pacman>, C<urpmi>,
C<zypper>, C<apk>, C<xbps>.
+Future versions of libguestfs may return other strings.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_hostname"; added = (1, 7, 9);
+ style = RString (RPlainString, "hostname"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_hostname";
+ shortdesc = "get hostname of the operating system";
+ longdesc = "\
+This function returns the hostname of the operating system
+as found by inspection of the guest’s configuration files.
+
+If the hostname could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_product_variant"; added = (1, 9, 13);
+ style = RString (RPlainString, "variant"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_product_variant";
+ shortdesc = "get product variant of inspected operating system";
+ longdesc = "\
+This returns the product variant of the inspected operating
+system.
+
+For Windows guests, this returns the contents of the Registry key
+C<HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion>
+C<InstallationType> which is usually a string such as
+C<Client> or C<Server> (other values are possible). This
+can be used to distinguish consumer and enterprise versions
+of Windows that have the same version number (for example,
+Windows 7 and Windows 2008 Server are both version 6.1,
+but the former is C<Client> and the latter is C<Server>).
+
+For enterprise Linux guests, in future we intend this to return
+the product variant such as C<Desktop>, C<Server> and so on. But
+this is not implemented at present.
+
+If the product variant could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.
+See also C<guestfs_inspect_get_product_name>,
+C<guestfs_inspect_get_major_version>." };
+
+ { defaults with
+ name = "inspect_get_windows_current_control_set"; added = (1, 9,
17);
+ style = RString (RPlainString, "controlset"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_current_control_set";
+ shortdesc = "get Windows CurrentControlSet of inspected operating
system";
+ longdesc = "\
+This returns the Windows CurrentControlSet of the inspected guest.
+The CurrentControlSet is a registry key name such as C<ControlSet001>.
+
+This call assumes that the guest is Windows and that the
+Registry could be examined by inspection. If this is not
+the case then an error is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_windows_software_hive"; added = (1, 35, 26);
+ style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_software_hive";
+ shortdesc = "get the path of the Windows software hive";
+ longdesc = "\
+This returns the path to the hive (binary Windows Registry file)
+corresponding to HKLM\\SOFTWARE.
+
+This call assumes that the guest is Windows and that the guest
+has a software hive file with the right name. If this is not the
+case then an error is returned. This call does not check that the
+hive is a valid Windows Registry hive.
+
+You can use C<guestfs_hivex_open> to read or write to the hive.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_windows_system_hive"; added = (1, 35, 26);
+ style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_system_hive";
+ shortdesc = "get the path of the Windows system hive";
+ longdesc = "\
+This returns the path to the hive (binary Windows Registry file)
+corresponding to HKLM\\SYSTEM.
+
+This call assumes that the guest is Windows and that the guest
+has a system hive file with the right name. If this is not the
+case then an error is returned. This call does not check that the
+hive is a valid Windows Registry hive.
+
+You can use C<guestfs_hivex_open> to read or write to the hive.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
name = "inspect_get_mountpoints"; added = (1, 5, 3);
style = RHashtable (RPlainString, RMountable, "mountpoints"),
[String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_mountpoints";
shortdesc = "get mountpoints of inspected operating system";
longdesc = "\
This returns a hash of where we think the filesystems
@@ -364,6 +542,7 @@ See also C<guestfs_inspect_get_filesystems>." };
{ defaults with
name = "inspect_get_filesystems"; added = (1, 5, 3);
style = RStringList (RMountable, "filesystems"), [String
(Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_filesystems";
shortdesc = "get filesystems associated with inspected operating
system";
longdesc = "\
This returns a list of all the filesystems that we think
@@ -378,77 +557,43 @@ Please read L<guestfs(3)/INSPECTION> for more
details.
See also C<guestfs_inspect_get_mountpoints>." };
{ defaults with
- name = "inspect_get_windows_systemroot"; added = (1, 5, 25);
- style = RString (RPlainString, "systemroot"), [String (Mountable,
"root")], [];
- shortdesc = "get Windows systemroot of inspected operating
system";
+ name = "inspect_get_drive_mappings"; added = (1, 9, 17);
+ style = RHashtable (RPlainString, RDevice, "drives"), [String
(Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_drive_mappings";
+ shortdesc = "get drive letter mappings";
longdesc = "\
-This returns the Windows systemroot of the inspected guest.
-The systemroot is a directory path such as F</WINDOWS>.
+This call is useful for Windows which uses a primitive system
+of assigning drive letters (like F<C:\\>) to partitions.
+This inspection API examines the Windows Registry to find out
+how disks/partitions are mapped to drive letters, and returns
+a hash table as in the example below:
-This call assumes that the guest is Windows and that the
-systemroot could be determined by inspection. If this is not
-the case then an error is returned.
+ C => /dev/vda2
+ E => /dev/vdb1
+ F => /dev/vdc1
-Please read L<guestfs(3)/INSPECTION> for more details." };
+Note that keys are drive letters. For Windows, the key is
+case insensitive and just contains the drive letter, without
+the customary colon separator character.
- { defaults with
- name = "inspect_get_roots"; added = (1, 7, 3);
- style = RStringList (RMountable, "roots"), [], [];
- shortdesc = "return list of operating systems found by last
inspection";
- longdesc = "\
-This function is a convenient way to get the list of root
-devices, as returned from a previous call to C<guestfs_inspect_os>,
-but without redoing the whole inspection process.
-
-This returns an empty list if either no root devices were
-found or the caller has not called C<guestfs_inspect_os>.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_package_format"; added = (1, 7, 5);
- style = RString (RPlainString, "packageformat"), [String
(Mountable, "root")], [];
- shortdesc = "get package format used by the operating system";
- longdesc = "\
-This function and C<guestfs_inspect_get_package_management> return
-the package format and package management tool used by the
-inspected operating system. For example for Fedora these
-functions would return C<rpm> (package format), and
-C<yum> or C<dnf> (package management).
+In future we may support other operating systems that also used drive
+letters, but the keys for those might not be case insensitive
+and might be longer than 1 character. For example in OS-9,
+hard drives were named C<h0>, C<h1> etc.
-This returns the string C<unknown> if we could not determine the
-package format I<or> if the operating system does not have
-a real packaging system (eg. Windows).
-
-Possible strings include:
-C<rpm>, C<deb>, C<ebuild>, C<pisi>, C<pacman>,
C<pkgsrc>, C<apk>,
-C<xbps>.
-Future versions of libguestfs may return other strings.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_package_management"; added = (1, 7, 5);
- style = RString (RPlainString, "packagemanagement"), [String
(Mountable, "root")], [];
- shortdesc = "get package management tool used by the operating
system";
- longdesc = "\
-C<guestfs_inspect_get_package_format> and this function return
-the package format and package management tool used by the
-inspected operating system. For example for Fedora these
-functions would return C<rpm> (package format), and
-C<yum> or C<dnf> (package management).
+For Windows guests, currently only hard drive mappings are
+returned. Removable disks (eg. DVD-ROMs) are ignored.
-This returns the string C<unknown> if we could not determine the
-package management tool I<or> if the operating system does not have
-a real packaging system (eg. Windows).
+For guests that do not use drive mappings, or if the drive mappings
+could not be determined, this returns an empty hash table.
-Possible strings include: C<yum>, C<dnf>, C<up2date>,
-C<apt> (for all Debian derivatives),
-C<portage>, C<pisi>, C<pacman>, C<urpmi>,
C<zypper>, C<apk>, C<xbps>.
-Future versions of libguestfs may return other strings.
+Please read L<guestfs(3)/INSPECTION> for more details.
+See also C<guestfs_inspect_get_mountpoints>,
+C<guestfs_inspect_get_filesystems>." };
-Please read L<guestfs(3)/INSPECTION> for more details." };
+]
+let non_daemon_functions = [
{ defaults with
name = "inspect_list_applications2"; added = (1, 19, 56);
style = RStructList ("applications2", "application2"),
[String (Mountable, "root")], [];
@@ -553,95 +698,6 @@ If unavailable this is returned as an empty string
C<\"\">.
Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
- name = "inspect_get_hostname"; added = (1, 7, 9);
- style = RString (RPlainString, "hostname"), [String (Mountable,
"root")], [];
- shortdesc = "get hostname of the operating system";
- longdesc = "\
-This function returns the hostname of the operating system
-as found by inspection of the guest’s configuration files.
-
-If the hostname could not be determined, then the
-string C<unknown> is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_product_variant"; added = (1, 9, 13);
- style = RString (RPlainString, "variant"), [String (Mountable,
"root")], [];
- shortdesc = "get product variant of inspected operating system";
- longdesc = "\
-This returns the product variant of the inspected operating
-system.
-
-For Windows guests, this returns the contents of the Registry key
-C<HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion>
-C<InstallationType> which is usually a string such as
-C<Client> or C<Server> (other values are possible). This
-can be used to distinguish consumer and enterprise versions
-of Windows that have the same version number (for example,
-Windows 7 and Windows 2008 Server are both version 6.1,
-but the former is C<Client> and the latter is C<Server>).
-
-For enterprise Linux guests, in future we intend this to return
-the product variant such as C<Desktop>, C<Server> and so on. But
-this is not implemented at present.
-
-If the product variant could not be determined, then the
-string C<unknown> is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details.
-See also C<guestfs_inspect_get_product_name>,
-C<guestfs_inspect_get_major_version>." };
-
- { defaults with
- name = "inspect_get_windows_current_control_set"; added = (1, 9,
17);
- style = RString (RPlainString, "controlset"), [String (Mountable,
"root")], [];
- shortdesc = "get Windows CurrentControlSet of inspected operating
system";
- longdesc = "\
-This returns the Windows CurrentControlSet of the inspected guest.
-The CurrentControlSet is a registry key name such as C<ControlSet001>.
-
-This call assumes that the guest is Windows and that the
-Registry could be examined by inspection. If this is not
-the case then an error is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_drive_mappings"; added = (1, 9, 17);
- style = RHashtable (RPlainString, RDevice, "drives"), [String
(Mountable, "root")], [];
- shortdesc = "get drive letter mappings";
- longdesc = "\
-This call is useful for Windows which uses a primitive system
-of assigning drive letters (like F<C:\\>) to partitions.
-This inspection API examines the Windows Registry to find out
-how disks/partitions are mapped to drive letters, and returns
-a hash table as in the example below:
-
- C => /dev/vda2
- E => /dev/vdb1
- F => /dev/vdc1
-
-Note that keys are drive letters. For Windows, the key is
-case insensitive and just contains the drive letter, without
-the customary colon separator character.
-
-In future we may support other operating systems that also used drive
-letters, but the keys for those might not be case insensitive
-and might be longer than 1 character. For example in OS-9,
-hard drives were named C<h0>, C<h1> etc.
-
-For Windows guests, currently only hard drive mappings are
-returned. Removable disks (eg. DVD-ROMs) are ignored.
-
-For guests that do not use drive mappings, or if the drive mappings
-could not be determined, this returns an empty hash table.
-
-Please read L<guestfs(3)/INSPECTION> for more details.
-See also C<guestfs_inspect_get_mountpoints>,
-C<guestfs_inspect_get_filesystems>." };
-
- { defaults with
name = "inspect_get_icon"; added = (1, 11, 12);
style = RBufferOut "icon", [String (Mountable,
"root")], [OBool "favicon"; OBool "highquality"];
shortdesc = "get the icon corresponding to this operating
system";
@@ -706,38 +762,4 @@ advice before using trademarks in applications.
=back" };
- { defaults with
- name = "inspect_get_windows_software_hive"; added = (1, 35, 26);
- style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
- shortdesc = "get the path of the Windows software hive";
- longdesc = "\
-This returns the path to the hive (binary Windows Registry file)
-corresponding to HKLM\\SOFTWARE.
-
-This call assumes that the guest is Windows and that the guest
-has a software hive file with the right name. If this is not the
-case then an error is returned. This call does not check that the
-hive is a valid Windows Registry hive.
-
-You can use C<guestfs_hivex_open> to read or write to the hive.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_windows_system_hive"; added = (1, 35, 26);
- style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
- shortdesc = "get the path of the Windows system hive";
- longdesc = "\
-This returns the path to the hive (binary Windows Registry file)
-corresponding to HKLM\\SYSTEM.
-
-This call assumes that the guest is Windows and that the guest
-has a system hive file with the right name. If this is not the
-case then an error is returned. This call does not check that the
-hive is a valid Windows Registry hive.
-
-You can use C<guestfs_hivex_open> to read or write to the hive.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
]
diff --git a/generator/actions_inspection.mli b/generator/actions_inspection.mli
index 327f7aa4f..06b8116c4 100644
--- a/generator/actions_inspection.mli
+++ b/generator/actions_inspection.mli
@@ -19,3 +19,4 @@
(* Please read generator/README first. *)
val non_daemon_functions : Types.action list
+val daemon_functions : Types.action list
diff --git a/generator/actions_inspection_deprecated.ml
b/generator/actions_inspection_deprecated.ml
index 7c34cb1a8..c3e76ff7b 100644
--- a/generator/actions_inspection_deprecated.ml
+++ b/generator/actions_inspection_deprecated.ml
@@ -121,9 +121,13 @@ If unavailable this is returned as an empty string
C<\"\">.
Please read L<guestfs(3)/INSPECTION> for more details." };
+]
+
+let daemon_functions = [
{ defaults with
name = "inspect_get_format"; added = (1, 9, 4);
style = RString (RPlainString, "format"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_format";
deprecated_by = Deprecated_no_replacement;
shortdesc = "get format of inspected operating system";
longdesc = "\
@@ -155,6 +159,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_is_live"; added = (1, 9, 4);
style = RBool "live", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_is_live";
deprecated_by = Deprecated_no_replacement;
shortdesc = "get live flag for install disk";
longdesc = "\
@@ -165,6 +170,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_is_netinst"; added = (1, 9, 4);
style = RBool "netinst", [String (Mountable, "root")],
[];
+ impl = OCaml "Inspect.inspect_is_netinst";
deprecated_by = Deprecated_no_replacement;
shortdesc = "get netinst (network installer) flag for install
disk";
longdesc = "\
@@ -175,6 +181,7 @@ Please read L<guestfs(3)/INSPECTION> for more
details." };
{ defaults with
name = "inspect_is_multipart"; added = (1, 9, 4);
style = RBool "multipart", [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_is_multipart";
deprecated_by = Deprecated_no_replacement;
shortdesc = "get multipart flag for install disk";
longdesc = "\
diff --git a/generator/actions_inspection_deprecated.mli
b/generator/actions_inspection_deprecated.mli
index 327f7aa4f..06b8116c4 100644
--- a/generator/actions_inspection_deprecated.mli
+++ b/generator/actions_inspection_deprecated.mli
@@ -19,3 +19,4 @@
(* Please read generator/README first. *)
val non_daemon_functions : Types.action list
+val daemon_functions : Types.action list
diff --git a/generator/daemon.ml b/generator/daemon.ml
index f20c87bea..b8d0a3a88 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -597,6 +597,30 @@ return_string_mountable (value retv)
}
}
+/* Implement RStringList (RMountable, _). */
+static char **
+return_string_mountable_list (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value v;
+ char *m;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0);
+ m = return_string_mountable (v);
+ if (m == NULL)
+ return NULL;
+ if (add_string_nodup (&ret, m) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
/* Implement RHashtable (RPlainString, RPlainString, _). */
static char **
return_hashtable_string_string (value retv)
@@ -649,6 +673,34 @@ return_hashtable_mountable_string (value retv)
return take_stringsbuf (&ret); /* caller frees */
}
+/* Implement RHashtable (RPlainString, RMountable, _). */
+static char **
+return_hashtable_string_mountable (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value sv, v, mv;
+ char *m;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0); /* (string, Mountable.t) */
+ sv = Field (v, 0); /* string */
+ if (add_string (&ret, String_val (sv)) == -1)
+ return NULL;
+ mv = Field (v, 1); /* Mountable.t */
+ m = return_string_mountable (mv);
+ if (m == NULL)
+ return NULL;
+ if (add_string_nodup (&ret, m) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
";
(* Implement code for returning structs and struct lists. *)
@@ -889,9 +941,12 @@ return_hashtable_mountable_string (value retv)
| RString (RMountable, _) ->
pr " char *ret = return_string_mountable (retv);\n";
pr " CAMLreturnT (char *, ret); /* caller frees */\n"
- | RStringList _ ->
+ | RStringList ((RPlainString|RDevice), _) ->
pr " char **ret = return_string_list (retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
+ | RStringList (RMountable, _) ->
+ pr " char **ret = return_string_mountable_list (retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RStruct (_, typ) ->
pr " guestfs_int_%s *ret =\n" typ;
pr " return_%s (retv);\n" typ;
@@ -902,12 +957,16 @@ return_hashtable_mountable_string (value retv)
pr " return_%s_list (retv);\n" typ;
pr " /* caller frees */\n";
pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ
- | RHashtable (RPlainString, RPlainString, _) ->
+ | RHashtable (RPlainString, RPlainString, _)
+ | RHashtable (RPlainString, RDevice, _) ->
pr " char **ret = return_hashtable_string_string
(retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RHashtable (RMountable, RPlainString, _) ->
pr " char **ret = return_hashtable_mountable_string
(retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
+ | RHashtable (RPlainString, RMountable, _) ->
+ pr " char **ret = return_hashtable_string_mountable
(retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RHashtable _ -> assert false
| RBufferOut _ -> assert false
);
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index dec02f5fa..0a41f9c24 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -484,6 +484,29 @@ let proc_nr = [
474, "internal_yara_scan";
475, "file_architecture";
476, "list_filesystems";
+477, "inspect_os";
+478, "inspect_get_roots";
+479, "inspect_get_format";
+480, "inspect_get_type";
+481, "inspect_get_distro";
+482, "inspect_get_package_format";
+483, "inspect_get_package_management";
+484, "inspect_get_product_name";
+485, "inspect_get_product_variant";
+486, "inspect_get_major_version";
+487, "inspect_get_minor_version";
+488, "inspect_get_arch";
+489, "inspect_get_hostname";
+490, "inspect_get_windows_systemroot";
+491, "inspect_get_windows_software_hive";
+492, "inspect_get_windows_system_hive";
+493, "inspect_get_windows_current_control_set";
+494, "inspect_is_live";
+495, "inspect_is_netinst";
+496, "inspect_is_multipart";
+497, "inspect_get_mountpoints";
+498, "inspect_get_filesystems";
+499, "inspect_get_drive_mappings";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index b86395733..761fcd3ac 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-476
+499
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 31568f933..80adebe69 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -96,9 +96,6 @@ libguestfs_la_SOURCES = \
info.c \
inspect.c \
inspect-apps.c \
- inspect-fs.c \
- inspect-fs-unix.c \
- inspect-fs-windows.c \
inspect-icon.c \
journal.c \
launch.c \
diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h
index 81755177d..1fa621cf0 100644
--- a/lib/guestfs-internal.h
+++ b/lib/guestfs-internal.h
@@ -116,21 +116,6 @@
/* Some limits on what the inspection code will read, for safety. */
-/* Small text configuration files.
- *
- * The upper limit is for general files that we grep or download. The
- * largest such file is probably "txtsetup.sif" from Windows CDs
- * (~500K). This number has to be larger than any legitimate file and
- * smaller than the protocol message size.
- *
- * The lower limit is for files parsed by Augeas on the daemon side,
- * where Augeas is running in reduced memory and can potentially
- * create a lot of metadata so we really need to be careful about
- * those.
- */
-#define MAX_SMALL_FILE_SIZE (2 * 1000 * 1000)
-#define MAX_AUGEAS_FILE_SIZE (100 * 1000)
-
/* Maximum RPM or dpkg database we will download to /tmp. RPM
* 'Packages' database can get very large: 70 MB is roughly the
* standard size for a new Fedora install, and after lots of package
@@ -470,12 +455,6 @@ struct guestfs_h {
struct event *events;
size_t nr_events;
- /* Information gathered by inspect_os. Must be freed by calling
- * guestfs_int_free_inspect_info.
- */
- struct inspect_fs *fses;
- size_t nr_fses;
-
/* Private data area. */
struct hash_table *pda;
struct pda_entry *pda_next;
@@ -536,133 +515,6 @@ struct version {
int v_micro;
};
-/* Per-filesystem data stored for inspect_os. */
-enum inspect_os_format {
- OS_FORMAT_UNKNOWN = 0,
- OS_FORMAT_INSTALLED,
- OS_FORMAT_INSTALLER,
- /* in future: supplemental disks */
-};
-
-enum inspect_os_type {
- OS_TYPE_UNKNOWN = 0,
- OS_TYPE_LINUX,
- OS_TYPE_WINDOWS,
- OS_TYPE_FREEBSD,
- OS_TYPE_NETBSD,
- OS_TYPE_HURD,
- OS_TYPE_DOS,
- OS_TYPE_OPENBSD,
- OS_TYPE_MINIX,
-};
-
-enum inspect_os_distro {
- OS_DISTRO_UNKNOWN = 0,
- OS_DISTRO_DEBIAN,
- OS_DISTRO_FEDORA,
- OS_DISTRO_REDHAT_BASED,
- OS_DISTRO_RHEL,
- OS_DISTRO_WINDOWS,
- OS_DISTRO_PARDUS,
- OS_DISTRO_ARCHLINUX,
- OS_DISTRO_GENTOO,
- OS_DISTRO_UBUNTU,
- OS_DISTRO_MEEGO,
- OS_DISTRO_LINUX_MINT,
- OS_DISTRO_MANDRIVA,
- OS_DISTRO_SLACKWARE,
- OS_DISTRO_CENTOS,
- OS_DISTRO_SCIENTIFIC_LINUX,
- OS_DISTRO_TTYLINUX,
- OS_DISTRO_MAGEIA,
- OS_DISTRO_OPENSUSE,
- OS_DISTRO_BUILDROOT,
- OS_DISTRO_CIRROS,
- OS_DISTRO_FREEDOS,
- OS_DISTRO_SUSE_BASED,
- OS_DISTRO_SLES,
- OS_DISTRO_OPENBSD,
- OS_DISTRO_ORACLE_LINUX,
- OS_DISTRO_FREEBSD,
- OS_DISTRO_NETBSD,
- OS_DISTRO_COREOS,
- OS_DISTRO_ALPINE_LINUX,
- OS_DISTRO_ALTLINUX,
- OS_DISTRO_FRUGALWARE,
- OS_DISTRO_PLD_LINUX,
- OS_DISTRO_VOID_LINUX,
-};
-
-enum inspect_os_package_format {
- OS_PACKAGE_FORMAT_UNKNOWN = 0,
- OS_PACKAGE_FORMAT_RPM,
- OS_PACKAGE_FORMAT_DEB,
- OS_PACKAGE_FORMAT_PACMAN,
- OS_PACKAGE_FORMAT_EBUILD,
- OS_PACKAGE_FORMAT_PISI,
- OS_PACKAGE_FORMAT_PKGSRC,
- OS_PACKAGE_FORMAT_APK,
- OS_PACKAGE_FORMAT_XBPS,
-};
-
-enum inspect_os_package_management {
- OS_PACKAGE_MANAGEMENT_UNKNOWN = 0,
- OS_PACKAGE_MANAGEMENT_YUM,
- OS_PACKAGE_MANAGEMENT_UP2DATE,
- OS_PACKAGE_MANAGEMENT_APT,
- OS_PACKAGE_MANAGEMENT_PACMAN,
- OS_PACKAGE_MANAGEMENT_PORTAGE,
- OS_PACKAGE_MANAGEMENT_PISI,
- OS_PACKAGE_MANAGEMENT_URPMI,
- OS_PACKAGE_MANAGEMENT_ZYPPER,
- OS_PACKAGE_MANAGEMENT_DNF,
- OS_PACKAGE_MANAGEMENT_APK,
- OS_PACKAGE_MANAGEMENT_XBPS,
-};
-
-enum inspect_os_role {
- OS_ROLE_UNKNOWN = 0,
- OS_ROLE_ROOT,
- OS_ROLE_USR,
-};
-
-/**
- * The inspection code maintains one of these structures per mountable
- * filesystem found in the disk image. The struct (or structs) which
- * have the C<role> attribute set to C<OS_ROLE_ROOT> are inspection
roots,
- * each corresponding to a single guest. Note that a filesystem can be
- * shared between multiple guests.
- */
-struct inspect_fs {
- enum inspect_os_role role;
- char *mountable;
- enum inspect_os_type type;
- enum inspect_os_distro distro;
- enum inspect_os_package_format package_format;
- enum inspect_os_package_management package_management;
- char *product_name;
- char *product_variant;
- struct version version;
- char *arch;
- char *hostname;
- char *windows_systemroot;
- char *windows_software_hive;
- char *windows_system_hive;
- char *windows_current_control_set;
- char **drive_mappings;
- enum inspect_os_format format;
- int is_live_disk;
- int is_netinst_disk;
- int is_multipart_disk;
- struct inspect_fstab_entry *fstab;
- size_t nr_fstab;
-};
-
-struct inspect_fstab_entry {
- char *mountable;
- char *mountpoint;
-};
-
struct guestfs_message_header;
struct guestfs_message_error;
struct guestfs_progress;
@@ -854,40 +706,7 @@ extern int guestfs_int_set_backend (guestfs_h *g, const
char *method);
} while (0)
/* inspect.c */
-extern void guestfs_int_free_inspect_info (guestfs_h *g);
-extern char *guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs,
const char *filename, const char *basename, uint64_t max_size);
-extern struct inspect_fs *guestfs_int_search_for_root (guestfs_h *g, const char
*root);
-extern int guestfs_int_is_partition (guestfs_h *g, const char *partition);
-
-/* inspect-fs.c */
-extern int guestfs_int_is_file_nocase (guestfs_h *g, const char *);
-extern int guestfs_int_is_dir_nocase (guestfs_h *g, const char *);
-extern int guestfs_int_check_for_filesystem_on (guestfs_h *g,
- const char *mountable);
-extern int guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str);
-extern int guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const
char *str);
-extern int guestfs_int_parse_major_minor (guestfs_h *g, struct inspect_fs *fs);
-extern char *guestfs_int_first_line_of_file (guestfs_h *g, const char
*filename);
-extern int guestfs_int_first_egrep_of_file (guestfs_h *g, const char *filename,
const char *eregex, int iflag, char **ret);
-extern void guestfs_int_check_package_format (guestfs_h *g, struct inspect_fs
*fs);
-extern void guestfs_int_check_package_management (guestfs_h *g, struct
inspect_fs *fs);
-extern void guestfs_int_merge_fs_inspections (guestfs_h *g, struct inspect_fs
*dst, struct inspect_fs *src);
-
-/* inspect-fs-unix.c */
-extern int guestfs_int_check_linux_root (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_linux_usr (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_freebsd_root (guestfs_h *g, struct inspect_fs
*fs);
-extern int guestfs_int_check_netbsd_root (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_openbsd_root (guestfs_h *g, struct inspect_fs
*fs);
-extern int guestfs_int_check_hurd_root (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_minix_root (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_coreos_root (guestfs_h *g, struct inspect_fs *fs);
-extern int guestfs_int_check_coreos_usr (guestfs_h *g, struct inspect_fs *fs);
-
-/* inspect-fs-windows.c */
-extern char *guestfs_int_case_sensitive_path_silently (guestfs_h *g, const char
*);
-extern char * guestfs_int_get_windows_systemroot (guestfs_h *g);
-extern int guestfs_int_check_windows_root (guestfs_h *g, struct inspect_fs *fs,
char *windows_systemroot);
+extern char *guestfs_int_download_to_tmp (guestfs_h *g, const char *filename,
const char *basename, uint64_t max_size);
/* dbdump.c */
typedef int (*guestfs_int_db_dump_callback) (guestfs_h *g, const unsigned char
*key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque);
@@ -969,6 +788,8 @@ struct rusage;
extern int guestfs_int_wait4 (guestfs_h *g, pid_t pid, int *status, struct
rusage *rusage, const char *errmsg);
/* version.c */
+extern int guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str);
+extern int guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const
char *str);
extern void guestfs_int_version_from_libvirt (struct version *v, int vernum);
extern void guestfs_int_version_from_values (struct version *v, int maj, int
min, int mic);
extern int guestfs_int_version_from_x_y (guestfs_h *g, struct version *v, const
char *str);
diff --git a/lib/handle.c b/lib/handle.c
index 91f5f755d..57fda24b1 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -369,7 +369,6 @@ guestfs_close (guestfs_h *g)
guestfs_int_free_fuse (g);
#endif
- guestfs_int_free_inspect_info (g);
guestfs_int_free_drives (g);
for (hp = g->hv_params; hp; hp = hp_next) {
diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c
index 25192340c..2a0e2beba 100644
--- a/lib/inspect-apps.c
+++ b/lib/inspect-apps.c
@@ -46,12 +46,12 @@
#include "structs-cleanups.h"
#ifdef DB_DUMP
-static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g,
struct inspect_fs *fs);
+static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g,
const char *root);
#endif
-static struct guestfs_application2_list *list_applications_deb (guestfs_h *g,
struct inspect_fs *fs);
-static struct guestfs_application2_list *list_applications_pacman (guestfs_h
*g, struct inspect_fs *fs);
-static struct guestfs_application2_list *list_applications_apk (guestfs_h *g,
struct inspect_fs *fs);
-static struct guestfs_application2_list *list_applications_windows (guestfs_h
*g, struct inspect_fs *fs);
+static struct guestfs_application2_list *list_applications_deb (guestfs_h *g,
const char *root);
+static struct guestfs_application2_list *list_applications_pacman (guestfs_h
*g, const char *root);
+static struct guestfs_application2_list *list_applications_apk (guestfs_h *g,
const char *root);
+static struct guestfs_application2_list *list_applications_windows (guestfs_h
*g, const char *root);
static void add_application (guestfs_h *g, struct guestfs_application2_list *,
const char *name, const char *display_name, int32_t epoch, const char *version,
const char *release, const char *arch, const char *install_path, const char
*publisher, const char *url, const char *source, const char *summary, const char
*description);
static void sort_applications (struct guestfs_application2_list *);
@@ -109,68 +109,45 @@ struct guestfs_application2_list *
guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root)
{
struct guestfs_application2_list *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
+ CLEANUP_FREE char *type = NULL;
+ CLEANUP_FREE char *package_format = NULL;
+
+ type = guestfs_inspect_get_type (g, root);
+ if (!type)
+ return NULL;
+ package_format = guestfs_inspect_get_package_format (g, root);
+ if (!package_format)
return NULL;
- /* Presently we can only list applications for installed disks. It
- * is possible in future to get lists of packages from installers.
- */
- if (fs->format == OS_FORMAT_INSTALLED) {
- switch (fs->type) {
- case OS_TYPE_LINUX:
- case OS_TYPE_HURD:
- switch (fs->package_format) {
- case OS_PACKAGE_FORMAT_RPM:
+ if (STREQ (type, "linux") || STREQ (type, "hurd")) {
+ if (STREQ (package_format, "rpm")) {
#ifdef DB_DUMP
- ret = list_applications_rpm (g, fs);
- if (ret == NULL)
- return NULL;
+ ret = list_applications_rpm (g, root);
+ if (ret == NULL)
+ return NULL;
#endif
- break;
-
- case OS_PACKAGE_FORMAT_DEB:
- ret = list_applications_deb (g, fs);
- if (ret == NULL)
- return NULL;
- break;
-
- case OS_PACKAGE_FORMAT_PACMAN:
- ret = list_applications_pacman (g, fs);
- if (ret == NULL)
- return NULL;
- break;
-
- case OS_PACKAGE_FORMAT_APK:
- ret = list_applications_apk (g, fs);
- if (ret == NULL)
- return NULL;
- break;
-
- case OS_PACKAGE_FORMAT_EBUILD:
- case OS_PACKAGE_FORMAT_PISI:
- case OS_PACKAGE_FORMAT_PKGSRC:
- case OS_PACKAGE_FORMAT_XBPS:
- case OS_PACKAGE_FORMAT_UNKNOWN:
- ; /* nothing */
- }
- break;
-
- case OS_TYPE_WINDOWS:
- ret = list_applications_windows (g, fs);
+ }
+ else if (STREQ (package_format, "deb")) {
+ ret = list_applications_deb (g, root);
+ if (ret == NULL)
+ return NULL;
+ }
+ else if (STREQ (package_format, "pacman")) {
+ ret = list_applications_pacman (g, root);
+ if (ret == NULL)
+ return NULL;
+ }
+ else if (STREQ (package_format, "apk")) {
+ ret = list_applications_apk (g, root);
if (ret == NULL)
return NULL;
- break;
-
- case OS_TYPE_FREEBSD:
- case OS_TYPE_MINIX:
- case OS_TYPE_NETBSD:
- case OS_TYPE_DOS:
- case OS_TYPE_OPENBSD:
- case OS_TYPE_UNKNOWN:
- ; /* nothing */
}
}
+ else if (STREQ (type, "windows")) {
+ ret = list_applications_windows (g, root);
+ if (ret == NULL)
+ return NULL;
+ }
if (ret == NULL) {
/* Don't know how to do inspection. Not an error, return an
@@ -386,20 +363,20 @@ read_package (guestfs_h *g,
#pragma GCC diagnostic pop
static struct guestfs_application2_list *
-list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
+list_applications_rpm (guestfs_h *g, const char *root)
{
CLEANUP_FREE char *Name = NULL, *Packages = NULL;
struct rpm_names_list list = { .names = NULL, .len = 0 };
struct guestfs_application2_list *apps = NULL;
struct read_package_data data;
- Name = guestfs_int_download_to_tmp (g, fs,
+ Name = guestfs_int_download_to_tmp (g,
"/var/lib/rpm/Name", "rpm_Name",
MAX_PKG_DB_SIZE);
if (Name == NULL)
goto error;
- Packages = guestfs_int_download_to_tmp (g, fs,
+ Packages = guestfs_int_download_to_tmp (g,
"/var/lib/rpm/Packages", "rpm_Packages",
MAX_PKG_DB_SIZE);
if (Packages == NULL)
@@ -437,7 +414,7 @@ list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
#endif /* defined DB_DUMP */
static struct guestfs_application2_list *
-list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
+list_applications_deb (guestfs_h *g, const char *root)
{
CLEANUP_FREE char *status = NULL;
struct guestfs_application2_list *apps = NULL, *ret = NULL;
@@ -452,7 +429,7 @@ list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
char **continuation_field = NULL;
size_t continuation_field_len = 0;
- status = guestfs_int_download_to_tmp (g, fs,
"/var/lib/dpkg/status", "status",
+ status = guestfs_int_download_to_tmp (g, "/var/lib/dpkg/status",
"status",
MAX_PKG_DB_SIZE);
if (status == NULL)
return NULL;
@@ -594,7 +571,7 @@ list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
}
static struct guestfs_application2_list *
-list_applications_pacman (guestfs_h *g, struct inspect_fs *fs)
+list_applications_pacman (guestfs_h *g, const char *root)
{
CLEANUP_FREE char *desc_file = NULL, *fname = NULL, *line = NULL;
CLEANUP_FREE_DIRENT_LIST struct guestfs_dirent_list *local_db = NULL;
@@ -628,7 +605,7 @@ list_applications_pacman (guestfs_h *g, struct inspect_fs
*fs)
fname = safe_malloc (g, strlen (curr->name) + path_len + 1);
sprintf (fname, "/var/lib/pacman/local/%s/desc", curr->name);
free (desc_file);
- desc_file = guestfs_int_download_to_tmp (g, fs, fname, curr->name,
8192);
+ desc_file = guestfs_int_download_to_tmp (g, fname, curr->name, 8192);
/* The desc files are small (4K). If the desc file does not exist or is
* larger than the 8K limit we've used, the database is probably
corrupted,
@@ -725,7 +702,7 @@ list_applications_pacman (guestfs_h *g, struct inspect_fs
*fs)
}
static struct guestfs_application2_list *
-list_applications_apk (guestfs_h *g, struct inspect_fs *fs)
+list_applications_apk (guestfs_h *g, const char *root)
{
CLEANUP_FREE char *installed = NULL, *line = NULL;
struct guestfs_application2_list *apps = NULL, *ret = NULL;
@@ -736,7 +713,7 @@ list_applications_apk (guestfs_h *g, struct inspect_fs *fs)
CLEANUP_FREE char *name = NULL, *version = NULL, *release = NULL, *arch =
NULL,
*url = NULL, *description = NULL;
- installed = guestfs_int_download_to_tmp (g, fs,
"/lib/apk/db/installed",
+ installed = guestfs_int_download_to_tmp (g,
"/lib/apk/db/installed",
"installed",
MAX_PKG_DB_SIZE);
if (installed == NULL)
return NULL;
@@ -843,14 +820,15 @@ list_applications_apk (guestfs_h *g, struct inspect_fs
*fs)
static void list_applications_windows_from_path (guestfs_h *g, struct
guestfs_application2_list *apps, const char **path, size_t path_len);
static struct guestfs_application2_list *
-list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
+list_applications_windows (guestfs_h *g, const char *root)
{
struct guestfs_application2_list *ret = NULL;
-
- if (!fs->windows_software_hive)
+ CLEANUP_FREE char *software_hive +
guestfs_inspect_get_windows_software_hive (g, root);
+ if (!software_hive)
return NULL;
- if (guestfs_hivex_open (g, fs->windows_software_hive,
+ if (guestfs_hivex_open (g, software_hive,
GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose,
GUESTFS_HIVEX_OPEN_UNSAFE, 1,
-1) == -1)
diff --git a/lib/inspect-fs-unix.c b/lib/inspect-fs-unix.c
deleted file mode 100644
index 9b6bfbf38..000000000
--- a/lib/inspect-fs-unix.c
+++ /dev/null
@@ -1,2158 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010-2012 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <string.h>
-#include <libintl.h>
-#include <errno.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#include "c-ctype.h"
-#include "ignore-value.h"
-#include "hash-pjw.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-
-COMPILE_REGEXP (re_fedora, "Fedora release (\\d+)", 0)
-COMPILE_REGEXP (re_rhel_old, "Red Hat.*release (\\d+).*Update
(\\d+)", 0)
-COMPILE_REGEXP (re_rhel, "Red Hat.*release (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_rhel_no_minor, "Red Hat.*release (\\d+)", 0)
-COMPILE_REGEXP (re_centos_old, "CentOS.*release (\\d+).*Update
(\\d+)", 0)
-COMPILE_REGEXP (re_centos, "CentOS.*release (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_centos_no_minor, "CentOS.*release (\\d+)", 0)
-COMPILE_REGEXP (re_scientific_linux_old,
- "Scientific Linux.*release (\\d+).*Update (\\d+)", 0)
-COMPILE_REGEXP (re_scientific_linux,
- "Scientific Linux.*release (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_scientific_linux_no_minor,
- "Scientific Linux.*release (\\d+)", 0)
-COMPILE_REGEXP (re_oracle_linux_old,
- "Oracle Linux.*release (\\d+).*Update (\\d+)", 0)
-COMPILE_REGEXP (re_oracle_linux,
- "Oracle Linux.*release (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_oracle_linux_no_minor, "Oracle Linux.*release
(\\d+)", 0)
-COMPILE_REGEXP (re_xdev, "^/dev/(h|s|v|xv)d([a-z]+)(\\d*)$", 0)
-COMPILE_REGEXP (re_cciss, "^/dev/(cciss/c\\d+d\\d+)(?:p(\\d+))?$", 0)
-COMPILE_REGEXP (re_mdN, "^(/dev/md\\d+)$", 0)
-COMPILE_REGEXP (re_freebsd_mbr,
- "^/dev/(ada{0,1}|vtbd)(\\d+)s(\\d+)([a-z])$", 0)
-COMPILE_REGEXP (re_freebsd_gpt,
"^/dev/(ada{0,1}|vtbd)(\\d+)p(\\d+)$", 0)
-COMPILE_REGEXP (re_diskbyid, "^/dev/disk/by-id/.*-part(\\d+)$", 0)
-COMPILE_REGEXP (re_netbsd, "^NetBSD (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_opensuse, "^(openSUSE|SuSE Linux|SUSE LINUX) ", 0)
-COMPILE_REGEXP (re_sles, "^SUSE (Linux|LINUX) Enterprise ", 0)
-COMPILE_REGEXP (re_nld, "^Novell Linux Desktop ", 0)
-COMPILE_REGEXP (re_opensuse_version, "^VERSION = (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_sles_version, "^VERSION = (\\d+)", 0)
-COMPILE_REGEXP (re_sles_patchlevel, "^PATCHLEVEL = (\\d+)", 0)
-COMPILE_REGEXP (re_minix, "^(\\d+)\\.(\\d+)(\\.(\\d+))?", 0)
-COMPILE_REGEXP (re_hurd_dev, "^/dev/(h)d(\\d+)s(\\d+)$", 0)
-COMPILE_REGEXP (re_openbsd, "^OpenBSD (\\d+|\\?)\\.(\\d+|\\?)", 0)
-COMPILE_REGEXP (re_openbsd_duid, "^[0-9a-f]{16}\\.[a-z]", 0)
-COMPILE_REGEXP (re_openbsd_dev, "^/dev/(s|w)d([0-9])([a-z])$", 0)
-COMPILE_REGEXP (re_netbsd_dev, "^/dev/(l|s)d([0-9])([a-z])$", 0)
-COMPILE_REGEXP (re_altlinux, "
(?:(\\d+)(?:\\.(\\d+)(?:[\\.\\d]+)?)?)\\s+\\((?:[^)]+)\\)$", 0)
-COMPILE_REGEXP (re_frugalware, "Frugalware (\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_pldlinux, "(\\d+)\\.(\\d+) PLD Linux", 0)
-
-static void check_architecture (guestfs_h *g, struct inspect_fs *fs);
-static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs);
-static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs);
-static int check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs);
-static int check_fstab (guestfs_h *g, struct inspect_fs *fs);
-static void add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
- const char *mountable, const char *mp);
-static char *resolve_fstab_device (guestfs_h *g, const char *spec,
- Hash_table *md_map,
- enum inspect_os_type os_type);
-static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char
**configfiles, int (*f) (guestfs_h *, struct inspect_fs *));
-static void canonical_mountpoint (char *mp);
-
-/* Hash structure for uuid->path lookups */
-typedef struct md_uuid {
- uint32_t uuid[4];
- char *path;
-} md_uuid;
-
-static size_t uuid_hash(const void *x, size_t table_size);
-static bool uuid_cmp(const void *x, const void *y);
-static void md_uuid_free(void *x);
-
-static int parse_uuid(const char *str, uint32_t *uuid);
-
-/* Hash structure for path(mdadm)->path(appliance) lookup */
-typedef struct {
- char *mdadm;
- char *app;
-} mdadm_app;
-
-static size_t mdadm_app_hash(const void *x, size_t table_size);
-static bool mdadm_app_cmp(const void *x, const void *y);
-static void mdadm_app_free(void *x);
-
-static ssize_t map_app_md_devices (guestfs_h *g, Hash_table **map);
-static int map_md_devices(guestfs_h *g, Hash_table **map);
-
-/* Set fs->product_name to the first line of the release file. */
-static int
-parse_release_file (guestfs_h *g, struct inspect_fs *fs,
- const char *release_filename)
-{
- fs->product_name = guestfs_int_first_line_of_file (g, release_filename);
- if (fs->product_name == NULL)
- return -1;
- if (STREQ (fs->product_name, "")) {
- free (fs->product_name);
- fs->product_name = NULL;
- error (g, _("release file %s is empty or malformed"),
release_filename);
- return -1;
- }
- return 0;
-}
-
-/* Parse a os-release file.
- *
- * Only few fields are parsed, falling back to the usual detection if we
- * cannot read all of them.
- *
- * For the format of os-release, see also:
- * http://www.freedesktop.org/software/systemd/man/os-release.html
- */
-static int
-parse_os_release (guestfs_h *g, struct inspect_fs *fs, const char *filename)
-{
- int64_t size;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- size_t i;
- enum inspect_os_distro distro = OS_DISTRO_UNKNOWN;
- CLEANUP_FREE char *product_name = NULL;
- struct version version;
- guestfs_int_version_from_values (&version, -1, -1, 0);
-
- /* Don't trust guestfs_read_lines not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return -1;
- }
-
- lines = guestfs_read_lines (g, filename);
- if (lines == NULL)
- return -1;
-
- for (i = 0; lines[i] != NULL; ++i) {
- const char *line = lines[i];
- const char *value;
- size_t value_len;
-
- if (line[0] == '#')
- continue;
-
- value = strchr (line, '=');
- if (value == NULL)
- continue;
-
- ++value;
- value_len = strlen (line) - (value - line);
- if (value_len > 1 && value[0] == '"' &&
value[value_len-1] == '"') {
- ++value;
- value_len -= 2;
- }
-
-#define VALUE_IS(a) STREQLEN(value, a, value_len)
- if (STRPREFIX (line, "ID=")) {
- if (VALUE_IS ("alpine"))
- distro = OS_DISTRO_ALPINE_LINUX;
- else if (VALUE_IS ("altlinux"))
- distro = OS_DISTRO_ALTLINUX;
- else if (VALUE_IS ("arch"))
- distro = OS_DISTRO_ARCHLINUX;
- else if (VALUE_IS ("centos"))
- distro = OS_DISTRO_CENTOS;
- else if (VALUE_IS ("coreos"))
- distro = OS_DISTRO_COREOS;
- else if (VALUE_IS ("debian"))
- distro = OS_DISTRO_DEBIAN;
- else if (VALUE_IS ("fedora"))
- distro = OS_DISTRO_FEDORA;
- else if (VALUE_IS ("frugalware"))
- distro = OS_DISTRO_FRUGALWARE;
- else if (VALUE_IS ("mageia"))
- distro = OS_DISTRO_MAGEIA;
- else if (VALUE_IS ("opensuse"))
- distro = OS_DISTRO_OPENSUSE;
- else if (VALUE_IS ("pld"))
- distro = OS_DISTRO_PLD_LINUX;
- else if (VALUE_IS ("rhel"))
- distro = OS_DISTRO_RHEL;
- else if (VALUE_IS ("sles") || VALUE_IS ("sled"))
- distro = OS_DISTRO_SLES;
- else if (VALUE_IS ("ubuntu"))
- distro = OS_DISTRO_UBUNTU;
- else if (VALUE_IS ("void"))
- distro = OS_DISTRO_VOID_LINUX;
- } else if (STRPREFIX (line, "PRETTY_NAME=")) {
- free (product_name);
- product_name = safe_strndup (g, value, value_len);
- } else if (STRPREFIX (line, "VERSION_ID=")) {
- CLEANUP_FREE char *buf - safe_asprintf (g, "%.*s", (int)
value_len, value);
- if (guestfs_int_version_from_x_y_or_x (g, &version, buf) == -1)
- return -1;
- }
-#undef VALUE_IS
- }
-
- /* If we haven't got all the fields, exit right away. */
- if (distro == OS_DISTRO_UNKNOWN || product_name == NULL)
- return 0;
-
- if (version.v_major == -1 || version.v_minor == -1) {
- /* Void Linux has no VERSION_ID (yet), but since it's a rolling
- * distro and has no other version/release-like file. */
- if (distro == OS_DISTRO_VOID_LINUX)
- version_init_null (&version);
- else
- return 0;
- }
-
- /* Apparently, os-release in Debian and CentOS does not provide the full
- * version number in VERSION_ID, but just the "major" part of it.
- * Hence, if version.v_minor is 0, act as there was no information in
- * os-release, which will continue the inspection using the release files
- * as done previously.
- */
- if ((distro == OS_DISTRO_DEBIAN || distro == OS_DISTRO_CENTOS) &&
- version.v_minor == 0)
- return 0;
-
- /* We got everything, so set the fields and report the inspection
- * was successful.
- */
- fs->distro = distro;
- fs->product_name = product_name;
- product_name = NULL;
- fs->version = version;
-
- return 1;
-}
-
-/* Ubuntu has /etc/lsb-release containing:
- * DISTRIB_ID=Ubuntu # Distro
- * DISTRIB_RELEASE=10.04 # Version
- * DISTRIB_CODENAME=lucid
- * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name
- *
- * [Ubuntu-derived ...] Linux Mint was found to have this:
- * DISTRIB_ID=LinuxMint
- * DISTRIB_RELEASE=10
- * DISTRIB_CODENAME=julia
- * DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
- * Linux Mint also has /etc/linuxmint/info with more information,
- * but we can use the LSB file.
- *
- * Mandriva has:
- * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
- * DISTRIB_ID=MandrivaLinux
- * DISTRIB_RELEASE=2010.1
- * DISTRIB_CODENAME=Henry_Farman
- * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
- * Mandriva also has a normal release file called /etc/mandriva-release.
- *
- * CoreOS has a /etc/lsb-release link to /usr/share/coreos/lsb-release
containing:
- * DISTRIB_ID=CoreOS
- * DISTRIB_RELEASE=647.0.0
- * DISTRIB_CODENAME="Red Dog"
- * DISTRIB_DESCRIPTION="CoreOS 647.0.0"
- */
-static int
-parse_lsb_release (guestfs_h *g, struct inspect_fs *fs, const char *filename)
-{
- int64_t size;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- size_t i;
- int r = 0;
-
- /* Don't trust guestfs_head_n not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return -1;
- }
-
- lines = guestfs_head_n (g, 10, filename);
- if (lines == NULL)
- return -1;
-
- for (i = 0; lines[i] != NULL; ++i) {
- if (fs->distro == 0 &&
- STREQ (lines[i], "DISTRIB_ID=Ubuntu")) {
- fs->distro = OS_DISTRO_UBUNTU;
- r = 1;
- }
- else if (fs->distro == 0 &&
- STREQ (lines[i], "DISTRIB_ID=LinuxMint")) {
- fs->distro = OS_DISTRO_LINUX_MINT;
- r = 1;
- }
- else if (fs->distro == 0 &&
- STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) {
- fs->distro = OS_DISTRO_MANDRIVA;
- r = 1;
- }
- else if (fs->distro == 0 &&
- STREQ (lines[i], "DISTRIB_ID=\"Mageia\"")) {
- fs->distro = OS_DISTRO_MAGEIA;
- r = 1;
- }
- else if (fs->distro == 0 &&
- STREQ (lines[i], "DISTRIB_ID=CoreOS")) {
- fs->distro = OS_DISTRO_COREOS;
- r = 1;
- }
- else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) {
- if (guestfs_int_version_from_x_y_or_x (g, &fs->version,
&lines[i][16]) == -1)
- return -1;
- }
- else if (fs->product_name == NULL &&
- (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") ||
- STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) {
- const size_t len = strlen (lines[i]) - 21 - 1;
- fs->product_name = safe_strndup (g, &lines[i][21], len);
- r = 1;
- }
- else if (fs->product_name == NULL &&
- STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) {
- const size_t len = strlen (lines[i]) - 20;
- fs->product_name = safe_strndup (g, &lines[i][20], len);
- r = 1;
- }
- }
-
- /* The unnecessary construct in the next line is required to
- * workaround -Wstrict-overflow warning in gcc 4.5.1.
- */
- return r ? 1 : 0;
-}
-
-static int
-parse_suse_release (guestfs_h *g, struct inspect_fs *fs, const char *filename)
-{
- int64_t size;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- int r = -1;
-
- /* Don't trust guestfs_head_n not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return -1;
- }
-
- lines = guestfs_head_n (g, 10, filename);
- if (lines == NULL)
- return -1;
-
- if (lines[0] == NULL)
- goto out;
-
- /* First line is dist release name */
- fs->product_name = safe_strdup (g, lines[0]);
-
- /* Match SLES first because openSuSE regex overlaps some SLES release strings
*/
- if (match (g, fs->product_name, re_sles) || match (g, fs->product_name,
re_nld)) {
- char *major, *minor;
-
- fs->distro = OS_DISTRO_SLES;
-
- /* Second line contains version string */
- if (lines[1] == NULL)
- goto out;
- major = match1 (g, lines[1], re_sles_version);
- if (major == NULL)
- goto out;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- goto out;
-
- /* Third line contains service pack string */
- if (lines[2] == NULL)
- goto out;
- minor = match1 (g, lines[2], re_sles_patchlevel);
- if (minor == NULL)
- goto out;
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- goto out;
- }
- else if (match (g, fs->product_name, re_opensuse)) {
- fs->distro = OS_DISTRO_OPENSUSE;
-
- /* Second line contains version string */
- if (lines[1] == NULL)
- goto out;
- if (guestfs_int_version_from_x_y_re (g, &fs->version, lines[1],
- re_opensuse_version) == -1)
- goto out;
- }
-
- r = 0;
-
- out:
- return r;
-}
-
-/* The currently mounted device is known to be a Linux root. Try to
- * determine from this the distro, version, etc. Also parse
- * /etc/fstab to determine the arrangement of mountpoints and
- * associated devices.
- */
-int
-guestfs_int_check_linux_root (guestfs_h *g, struct inspect_fs *fs)
-{
- int r;
- char *major, *minor;
-
- fs->type = OS_TYPE_LINUX;
-
- if (guestfs_is_file_opts (g, "/etc/os-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- r = parse_os_release (g, fs, "/etc/os-release");
- if (r == -1) /* error */
- return -1;
- if (r == 1) /* ok - detected the release from this file */
- goto skip_release_checks;
- }
-
- if (guestfs_is_file_opts (g, "/etc/lsb-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- r = parse_lsb_release (g, fs, "/etc/lsb-release");
- if (r == -1) /* error */
- return -1;
- if (r == 1) /* ok - detected the release from this file */
- goto skip_release_checks;
- }
-
- /* RHEL-based distros include a "/etc/redhat-release" file, hence
their
- * checks need to be performed before the Red-Hat one.
- */
- if (guestfs_is_file_opts (g, "/etc/oracle-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
-
- fs->distro = OS_DISTRO_ORACLE_LINUX;
-
- if (parse_release_file (g, fs, "/etc/oracle-release") == -1)
- return -1;
-
- if (match2 (g, fs->product_name, re_oracle_linux_old, &major,
&minor) ||
- match2 (g, fs->product_name, re_oracle_linux, &major,
&minor)) {
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1) {
- free (minor);
- return -1;
- }
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- return -1;
- } else if ((major = match1 (g, fs->product_name,
re_oracle_linux_no_minor)) != NULL) {
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- fs->version.v_minor = 0;
- }
- }
- else if (guestfs_is_file_opts (g, "/etc/centos-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_CENTOS;
-
- if (parse_release_file (g, fs, "/etc/centos-release") == -1)
- return -1;
-
- if (match2 (g, fs->product_name, re_centos_old, &major, &minor)
||
- match2 (g, fs->product_name, re_centos, &major, &minor)) {
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1) {
- free (minor);
- return -1;
- }
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- return -1;
- }
- else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) !=
NULL) {
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- fs->version.v_minor = 0;
- }
- }
- else if (guestfs_is_file_opts (g, "/etc/altlinux-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_ALTLINUX;
-
- if (parse_release_file (g, fs, "/etc/altlinux-release") == -1)
- return -1;
-
- if (guestfs_int_version_from_x_y_re (g, &fs->version,
fs->product_name,
- re_altlinux) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/redhat-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like.
*/
-
- if (parse_release_file (g, fs, "/etc/redhat-release") == -1)
- return -1;
-
- if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) {
- fs->distro = OS_DISTRO_FEDORA;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- }
- else if (match2 (g, fs->product_name, re_rhel_old, &major,
&minor) ||
- match2 (g, fs->product_name, re_rhel, &major, &minor))
{
- fs->distro = OS_DISTRO_RHEL;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1) {
- free (minor);
- return -1;
- }
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- return -1;
- }
- else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) !=
NULL) {
- fs->distro = OS_DISTRO_RHEL;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- fs->version.v_minor = 0;
- }
- else if (match2 (g, fs->product_name, re_centos_old, &major,
&minor) ||
- match2 (g, fs->product_name, re_centos, &major,
&minor)) {
- fs->distro = OS_DISTRO_CENTOS;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1) {
- free (minor);
- return -1;
- }
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- return -1;
- }
- else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) !=
NULL) {
- fs->distro = OS_DISTRO_CENTOS;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- fs->version.v_minor = 0;
- }
- else if (match2 (g, fs->product_name, re_scientific_linux_old,
&major, &minor) ||
- match2 (g, fs->product_name, re_scientific_linux, &major,
&minor)) {
- fs->distro = OS_DISTRO_SCIENTIFIC_LINUX;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1) {
- free (minor);
- return -1;
- }
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- free (minor);
- if (fs->version.v_minor == -1)
- return -1;
- }
- else if ((major = match1 (g, fs->product_name,
re_scientific_linux_no_minor)) != NULL) {
- fs->distro = OS_DISTRO_SCIENTIFIC_LINUX;
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- free (major);
- if (fs->version.v_major == -1)
- return -1;
- fs->version.v_minor = 0;
- }
- }
- else if (guestfs_is_file_opts (g, "/etc/debian_version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_DEBIAN;
-
- if (parse_release_file (g, fs, "/etc/debian_version") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/pardus-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_PARDUS;
-
- if (parse_release_file (g, fs, "/etc/pardus-release") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/arch-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_ARCHLINUX;
-
- /* /etc/arch-release file is empty and I can't see a way to
- * determine the actual release or product string.
- */
- }
- else if (guestfs_is_file_opts (g, "/etc/gentoo-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_GENTOO;
-
- if (parse_release_file (g, fs, "/etc/gentoo-release") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/meego-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_MEEGO;
-
- if (parse_release_file (g, fs, "/etc/meego-release") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/slackware-version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_SLACKWARE;
-
- if (parse_release_file (g, fs, "/etc/slackware-version") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/ttylinux-target",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_TTYLINUX;
-
- if (parse_release_file (g, fs, "/etc/ttylinux-target") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/SuSE-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_SUSE_BASED;
-
- if (parse_suse_release (g, fs, "/etc/SuSE-release") == -1)
- return -1;
-
- }
- /* CirrOS versions providing a own version file.
- */
- else if (guestfs_is_file_opts (g, "/etc/cirros/version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_CIRROS;
-
- if (parse_release_file (g, fs, "/etc/cirros/version") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- /* Buildroot (http://buildroot.net) is an embedded Linux distro
- * toolkit. It is used by specific distros such as Cirros.
- */
- else if (guestfs_is_file_opts (g, "/etc/br-version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- if (guestfs_is_file_opts (g, "/usr/share/cirros/logo",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) >
0)
- fs->distro = OS_DISTRO_CIRROS;
- else
- fs->distro = OS_DISTRO_BUILDROOT;
-
- /* /etc/br-version has the format YYYY.MM[-git/hg/svn release] */
- if (parse_release_file (g, fs, "/etc/br-version") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/alpine-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_ALPINE_LINUX;
-
- if (parse_release_file (g, fs, "/etc/alpine-release") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/frugalware-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_FRUGALWARE;
-
- if (parse_release_file (g, fs, "/etc/frugalware-release") == -1)
- return -1;
-
- if (guestfs_int_version_from_x_y_re (g, &fs->version,
fs->product_name,
- re_frugalware) == -1)
- return -1;
- }
- else if (guestfs_is_file_opts (g, "/etc/pld-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1)
> 0) {
- fs->distro = OS_DISTRO_PLD_LINUX;
-
- if (parse_release_file (g, fs, "/etc/pld-release") == -1)
- return -1;
-
- if (guestfs_int_version_from_x_y_re (g, &fs->version,
fs->product_name,
- re_pldlinux) == -1)
- return -1;
- }
-
- skip_release_checks:;
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* We already know /etc/fstab exists because it's part of the test
- * for Linux root above. We must now parse this file to determine
- * which filesystems are used by the operating system and how they
- * are mounted.
- */
- const char *configfiles[] = { "/etc/fstab",
"/etc/mdadm.conf", NULL };
- if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
- return -1;
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device looks like a Linux /usr. */
-int
-guestfs_int_check_linux_usr (guestfs_h *g, struct inspect_fs *fs)
-{
- int r;
-
- fs->type = OS_TYPE_LINUX;
- fs->role = OS_ROLE_USR;
-
- if (guestfs_is_file_opts (g, "/lib/os-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- r = parse_os_release (g, fs, "/lib/os-release");
- if (r == -1) /* error */
- return -1;
- if (r == 1) /* ok - detected the release from this file */
- goto skip_release_checks;
- }
-
- skip_release_checks:;
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- return 0;
-}
-
-/* The currently mounted device is known to be a FreeBSD root. */
-int
-guestfs_int_check_freebsd_root (guestfs_h *g, struct inspect_fs *fs)
-{
- fs->type = OS_TYPE_FREEBSD;
- fs->distro = OS_DISTRO_FREEBSD;
-
- /* FreeBSD has no authoritative version file. The version number is
- * in /etc/motd, which the system administrator might edit, but
- * we'll use that anyway.
- */
-
- if (guestfs_is_file_opts (g, "/etc/motd",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- if (parse_release_file (g, fs, "/etc/motd") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* We already know /etc/fstab exists because it's part of the test above.
*/
- const char *configfiles[] = { "/etc/fstab", NULL };
- if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
- return -1;
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device is maybe to be a *BSD root. */
-int
-guestfs_int_check_netbsd_root (guestfs_h *g, struct inspect_fs *fs)
-{
-
- if (guestfs_is_file_opts (g, "/etc/release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- int result;
- if (parse_release_file (g, fs, "/etc/release") == -1)
- return -1;
-
- result = guestfs_int_version_from_x_y_re (g, &fs->version,
- fs->product_name, re_netbsd);
- if (result == -1) {
- return -1;
- } else if (result == 1) {
- fs->type = OS_TYPE_NETBSD;
- fs->distro = OS_DISTRO_NETBSD;
- }
- } else {
- return -1;
- }
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* We already know /etc/fstab exists because it's part of the test above.
*/
- const char *configfiles[] = { "/etc/fstab", NULL };
- if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
- return -1;
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device may be an OpenBSD root. */
-int
-guestfs_int_check_openbsd_root (guestfs_h *g, struct inspect_fs *fs)
-{
- if (guestfs_is_file_opts (g, "/etc/motd",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- CLEANUP_FREE char *major = NULL, *minor = NULL;
-
- /* The first line of this file gets automatically updated at boot. */
- if (parse_release_file (g, fs, "/etc/motd") == -1)
- return -1;
-
- if (match2 (g, fs->product_name, re_openbsd, &major, &minor)) {
- fs->type = OS_TYPE_OPENBSD;
- fs->distro = OS_DISTRO_OPENBSD;
-
- /* Before the first boot, the first line will look like this:
- *
- * OpenBSD ?.? (UNKNOWN)
- */
- if ((fs->product_name[8] != '?') &&
(fs->product_name[10] != '?')) {
- fs->version.v_major = guestfs_int_parse_unsigned_int (g, major);
- if (fs->version.v_major == -1)
- return -1;
-
- fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor);
- if (fs->version.v_minor == -1)
- return -1;
- }
- }
- } else {
- return -1;
- }
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* We already know /etc/fstab exists because it's part of the test above.
*/
- const char *configfiles[] = { "/etc/fstab", NULL };
- if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
- return -1;
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device may be a Hurd root. Hurd has distros
- * just like Linux.
- */
-int
-guestfs_int_check_hurd_root (guestfs_h *g, struct inspect_fs *fs)
-{
- fs->type = OS_TYPE_HURD;
-
- if (guestfs_is_file_opts (g, "/etc/debian_version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- fs->distro = OS_DISTRO_DEBIAN;
-
- if (parse_release_file (g, fs, "/etc/debian_version") == -1)
- return -1;
-
- if (guestfs_int_parse_major_minor (g, fs) == -1)
- return -1;
- }
-
- /* Arch Hurd also exists, but inconveniently it doesn't have
- * the normal /etc/arch-release file. XXX
- */
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- if (guestfs_is_file (g, "/etc/fstab") > 0) {
- const char *configfiles[] = { "/etc/fstab", NULL };
- if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1)
- return -1;
- }
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device is maybe to be a Minix root. */
-int
-guestfs_int_check_minix_root (guestfs_h *g, struct inspect_fs *fs)
-{
- fs->type = OS_TYPE_MINIX;
-
- if (guestfs_is_file_opts (g, "/etc/version",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- if (parse_release_file (g, fs, "/etc/version") == -1)
- return -1;
-
- if (guestfs_int_version_from_x_y_re (g, &fs->version,
fs->product_name,
- re_minix) == -1)
- return -1;
- } else {
- return -1;
- }
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* TODO: enable fstab inspection once resolve_fstab_device implements
- * the proper mapping from the Minix device names to the appliance names
- */
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-/* The currently mounted device is a CoreOS root. From this partition we can
- * only determine the hostname. All immutable OS files are under a separate
- * read-only /usr partition.
- */
-int
-guestfs_int_check_coreos_root (guestfs_h *g, struct inspect_fs *fs)
-{
- fs->type = OS_TYPE_LINUX;
- fs->distro = OS_DISTRO_COREOS;
-
- /* Determine hostname. */
- if (check_hostname_unix (g, fs) == -1)
- return -1;
-
- /* CoreOS does not contain /etc/fstab to determine the mount points.
- * Associate this filesystem with the "/" mount point.
- */
- add_fstab_entry (g, fs, fs->mountable, "/");
-
- return 0;
-}
-
-/* The currently mounted device looks like a CoreOS /usr. In CoreOS
- * the read-only /usr contains the OS version. The /etc/os-release is a
- * link to /usr/share/coreos/os-release.
- */
-int
-guestfs_int_check_coreos_usr (guestfs_h *g, struct inspect_fs *fs)
-{
- int r;
-
- fs->type = OS_TYPE_LINUX;
- fs->distro = OS_DISTRO_COREOS;
- fs->role = OS_ROLE_USR;
-
- if (guestfs_is_file_opts (g, "/lib/os-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- r = parse_os_release (g, fs, "/lib/os-release");
- if (r == -1) /* error */
- return -1;
- if (r == 1) /* ok - detected the release from this file */
- goto skip_release_checks;
- }
-
- if (guestfs_is_file_opts (g, "/share/coreos/lsb-release",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0)
{
- r = parse_lsb_release (g, fs, "/share/coreos/lsb-release");
- if (r == -1) /* error */
- return -1;
- }
-
- skip_release_checks:;
-
- /* Determine the architecture. */
- check_architecture (g, fs);
-
- /* CoreOS does not contain /etc/fstab to determine the mount points.
- * Associate this filesystem with the "/usr" mount point.
- */
- add_fstab_entry (g, fs, fs->mountable, "/usr");
-
- return 0;
-}
-
-static void
-check_architecture (guestfs_h *g, struct inspect_fs *fs)
-{
- const char *binaries[] - { "/bin/bash", "/bin/ls",
"/bin/echo", "/bin/rm", "/bin/sh" };
- size_t i;
- char *arch = NULL;
-
- for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) {
- /* Allow symlinks when checking the binaries:,so in case they are
- * relative ones (which can be resolved within the same partition),
- * then we can check the architecture of their target.
- */
- if (guestfs_is_file_opts (g, binaries[i],
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) >
0) {
- CLEANUP_FREE char *resolved = NULL;
-
- /* Ignore errors from realpath and file_architecture calls. */
- guestfs_push_error_handler (g, NULL, NULL);
- resolved = guestfs_realpath (g, binaries[i]);
- /* If is_file above succeeded realpath should too, but better
- * be safe than sorry.
- */
- if (resolved)
- arch = guestfs_file_architecture (g, resolved);
- guestfs_pop_error_handler (g);
-
- if (arch) {
- /* String will be owned by handle, freed by
- * guestfs_int_free_inspect_info.
- */
- fs->arch = arch;
- break;
- }
- }
- }
-}
-
-/* Try several methods to determine the hostname from a Linux or
- * FreeBSD guest. Note that type and distro have been set, so we can
- * use that information to direct the search.
- */
-static int
-check_hostname_unix (guestfs_h *g, struct inspect_fs *fs)
-{
- switch (fs->type) {
- case OS_TYPE_LINUX:
- case OS_TYPE_HURD:
- /* Red Hat-derived would be in /etc/sysconfig/network or
- * /etc/hostname (RHEL 7+, F18+). Debian-derived in the file
- * /etc/hostname. Very old Debian and SUSE use /etc/HOSTNAME.
- * It's best to just look for each of these files in turn, rather
- * than try anything clever based on distro.
- */
- if (guestfs_is_file (g, "/etc/HOSTNAME")) {
- fs->hostname = guestfs_int_first_line_of_file (g,
"/etc/HOSTNAME");
- if (fs->hostname == NULL)
- return -1;
- if (STREQ (fs->hostname, "")) {
- free (fs->hostname);
- fs->hostname = NULL;
- }
- }
-
- if (!fs->hostname && guestfs_is_file (g,
"/etc/hostname")) {
- fs->hostname = guestfs_int_first_line_of_file (g,
"/etc/hostname");
- if (fs->hostname == NULL)
- return -1;
- if (STREQ (fs->hostname, "")) {
- free (fs->hostname);
- fs->hostname = NULL;
- }
- }
-
- if (!fs->hostname && guestfs_is_file (g,
"/etc/sysconfig/network")) {
- const char *configfiles[] = { "/etc/sysconfig/network", NULL };
- if (inspect_with_augeas (g, fs, configfiles,
- check_hostname_redhat) == -1)
- return -1;
- }
- break;
-
- case OS_TYPE_FREEBSD:
- case OS_TYPE_NETBSD:
- /* /etc/rc.conf contains the hostname, but there is no Augeas lens
- * for this file.
- */
- if (guestfs_is_file (g, "/etc/rc.conf")) {
- if (check_hostname_freebsd (g, fs) == -1)
- return -1;
- }
- break;
-
- case OS_TYPE_OPENBSD:
- if (guestfs_is_file (g, "/etc/myname")) {
- fs->hostname = guestfs_int_first_line_of_file (g,
"/etc/myname");
- if (fs->hostname == NULL)
- return -1;
- if (STREQ (fs->hostname, "")) {
- free (fs->hostname);
- fs->hostname = NULL;
- }
- }
- break;
-
- case OS_TYPE_MINIX:
- if (guestfs_is_file (g, "/etc/hostname.file")) {
- fs->hostname = guestfs_int_first_line_of_file (g,
"/etc/hostname.file");
- if (fs->hostname == NULL)
- return -1;
- if (STREQ (fs->hostname, "")) {
- free (fs->hostname);
- fs->hostname = NULL;
- }
- }
- break;
-
- case OS_TYPE_WINDOWS: /* not here, see check_windows_system_registry */
- case OS_TYPE_DOS:
- case OS_TYPE_UNKNOWN:
- /* nothing */;
- }
-
- return 0;
-}
-
-/* Parse the hostname from /etc/sysconfig/network. This must be
- * called from the inspect_with_augeas wrapper. Note that F18+ and
- * RHEL7+ use /etc/hostname just like Debian.
- */
-static int
-check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs)
-{
- char *hostname;
-
- /* Errors here are not fatal (RHBZ#726739), since it could be
- * just missing HOSTNAME field in the file.
- */
- guestfs_push_error_handler (g, NULL, NULL);
- hostname = guestfs_aug_get (g,
"/files/etc/sysconfig/network/HOSTNAME");
- guestfs_pop_error_handler (g);
-
- /* This is freed by guestfs_int_free_inspect_info. Note that hostname
- * could be NULL because we ignored errors above.
- */
- fs->hostname = hostname;
- return 0;
-}
-
-/* Parse the hostname from /etc/rc.conf. On FreeBSD this file
- * contains comments, blank lines and:
- * hostname="freebsd8.example.com"
- * ifconfig_re0="DHCP"
- * keymap="uk.iso"
- * sshd_enable="YES"
- */
-static int
-check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs)
-{
- const char *filename = "/etc/rc.conf";
- int64_t size;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- size_t i;
-
- /* Don't trust guestfs_read_lines not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return -1;
- }
-
- lines = guestfs_read_lines (g, filename);
- if (lines == NULL)
- return -1;
-
- for (i = 0; lines[i] != NULL; ++i) {
- if (STRPREFIX (lines[i], "hostname=\"") ||
- STRPREFIX (lines[i], "hostname='")) {
- const size_t len = strlen (lines[i]) - 10 - 1;
- fs->hostname = safe_strndup (g, &lines[i][10], len);
- break;
- } else if (STRPREFIX (lines[i], "hostname=")) {
- const size_t len = strlen (lines[i]) - 9;
- fs->hostname = safe_strndup (g, &lines[i][9], len);
- break;
- }
- }
-
- return 0;
-}
-
-static int
-check_fstab (guestfs_h *g, struct inspect_fs *fs)
-{
- CLEANUP_FREE_STRING_LIST char **entries = NULL;
- char **entry;
- char augpath[256];
- CLEANUP_HASH_FREE Hash_table *md_map = NULL;
- bool is_bsd = (fs->type == OS_TYPE_FREEBSD ||
- fs->type == OS_TYPE_NETBSD ||
- fs->type == OS_TYPE_OPENBSD);
-
- /* Generate a map of MD device paths listed in /etc/mdadm.conf to MD device
- * paths in the guestfs appliance */
- if (map_md_devices (g, &md_map) == -1) return -1;
-
- entries = guestfs_aug_match (g, "/files/etc/fstab/*[label() !=
'#comment']");
- if (entries == NULL)
- return -1;
-
- for (entry = entries; *entry != NULL; entry++) {
- CLEANUP_FREE char *spec = NULL;
- CLEANUP_FREE char *mp = NULL;
- CLEANUP_FREE char *mountable = NULL;
- CLEANUP_FREE char *vfstype = NULL;
-
- snprintf (augpath, sizeof augpath, "%s/spec", *entry);
- spec = guestfs_aug_get (g, augpath);
- if (spec == NULL)
- return -1;
-
- /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives.
- *
- * /dev/iso9660/FREEBSD_INSTALL can be found in FreeBSDs installation
- * discs.
- */
- if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7]))
||
- (STRPREFIX (spec, "/dev/cd") && c_isdigit (spec[7]))
||
- STREQ (spec, "/dev/floppy") ||
- STREQ (spec, "/dev/cdrom") ||
- STRPREFIX (spec, "/dev/iso9660/"))
- continue;
-
- snprintf (augpath, sizeof augpath, "%s/file", *entry);
- mp = guestfs_aug_get (g, augpath);
- if (mp == NULL)
- return -1;
-
- /* Canonicalize the path, so "///usr//local//" ->
"/usr/local" */
- canonical_mountpoint (mp);
-
- /* Ignore certain mountpoints. */
- if (STRPREFIX (mp, "/dev/") ||
- STREQ (mp, "/dev") ||
- STRPREFIX (mp, "/media/") ||
- STRPREFIX (mp, "/proc/") ||
- STREQ (mp, "/proc") ||
- STRPREFIX (mp, "/selinux/") ||
- STREQ (mp, "/selinux") ||
- STRPREFIX (mp, "/sys/") ||
- STREQ (mp, "/sys"))
- continue;
-
- /* Resolve UUID= and LABEL= to the actual device. */
- if (STRPREFIX (spec, "UUID=")) {
- CLEANUP_FREE char *s = guestfs_int_shell_unquote (&spec[5]);
- if (s == NULL) { perrorf (g, "guestfs_int_shell_unquote");
return -1; }
- mountable = guestfs_findfs_uuid (g, s);
- }
- else if (STRPREFIX (spec, "LABEL=")) {
- CLEANUP_FREE char *s = guestfs_int_shell_unquote (&spec[6]);
- if (s == NULL) { perrorf (g, "guestfs_int_shell_unquote");
return -1; }
- mountable = guestfs_findfs_label (g, s);
- }
- /* Ignore "/.swap" (Pardus) and pseudo-devices like
"tmpfs". */
- else if (STREQ (spec, "/dev/root") || (is_bsd && STREQ
(mp, "/")))
- /* Resolve /dev/root to the current device.
- * Do the same for the / partition of the *BSD systems, since the
- * BSD -> Linux device translation is not straight forward.
- */
- mountable = safe_strdup (g, fs->mountable);
- else if (STRPREFIX (spec, "/dev/"))
- /* Resolve guest block device names. */
- mountable = resolve_fstab_device (g, spec, md_map, fs->type);
- else if (match (g, spec, re_openbsd_duid)) {
- /* In OpenBSD's fstab you can specify partitions on a disk by
appending a
- * period and a partition letter to a Disklable Unique Identifier. The
- * DUID is a 16 hex digit field found in the OpenBSD's altered BSD
- * disklabel. For more info see here:
- * http://www.openbsd.org/faq/faq14.html#intro
- */
- char device[10]; /* /dev/sd[0-9][a-z] */
- char part = spec[17];
-
- /* We cannot peep into disklables, we can only assume that this is the
- * first disk.
- */
- snprintf(device, 10, "%s%c", "/dev/sd0", part);
- mountable = resolve_fstab_device (g, device, md_map, fs->type);
- }
-
- /* If we haven't resolved the device successfully by this point,
- * we don't care, just ignore it.
- */
- if (mountable == NULL)
- continue;
-
- snprintf (augpath, sizeof augpath, "%s/vfstype", *entry);
- vfstype = guestfs_aug_get (g, augpath);
- if (vfstype == NULL) return -1;
-
- if (STREQ (vfstype, "btrfs")) {
- size_t i;
-
- snprintf (augpath, sizeof augpath, "%s/opt", *entry);
- CLEANUP_FREE_STRING_LIST char **opts = guestfs_aug_match (g, augpath);
- if (opts == NULL) return -1;
-
- for (i = 0; opts[i] != NULL; ++i) {
- CLEANUP_FREE char *optname = NULL, *optvalue = NULL, *subvol = NULL;
- char *old_mountable;
-
- optname = guestfs_aug_get (g, opts[i]);
- if (optname == NULL) return -1;
-
- if (STREQ (optname, "subvol")) {
- optvalue = safe_asprintf (g, "%s/value", opts[i]);
-
- subvol = guestfs_aug_get (g, optvalue);
- if (subvol == NULL) return -1;
-
- old_mountable = mountable;
- mountable = safe_asprintf (g, "btrfsvol:%s/%s", mountable,
subvol);
- free (old_mountable);
- }
- }
- }
-
- add_fstab_entry (g, fs, mountable, mp);
- }
-
- return 0;
-}
-
-/* Add a filesystem and possibly a mountpoint entry for
- * the root filesystem 'fs'.
- *
- * 'spec' is the fstab spec field, which might be a device name or a
- * pseudodevice or 'UUID=...' or 'LABEL=...'.
- *
- * 'mp' is the mount point, which could also be 'swap' or
'none'.
- */
-static void
-add_fstab_entry (guestfs_h *g, struct inspect_fs *fs,
- const char *mountable, const char *mountpoint)
-{
- /* Add this to the fstab entry in 'fs'.
- * Note these are further filtered by guestfs_inspect_get_mountpoints
- * and guestfs_inspect_get_filesystems.
- */
- const size_t n = fs->nr_fstab + 1;
- struct inspect_fstab_entry *p;
-
- p = safe_realloc (g, fs->fstab, n * sizeof (struct inspect_fstab_entry));
-
- fs->fstab = p;
- fs->nr_fstab = n;
-
- /* These are owned by the handle and freed by guestfs_int_free_inspect_info.
*/
- fs->fstab[n-1].mountable = safe_strdup (g, mountable);
- fs->fstab[n-1].mountpoint = safe_strdup (g, mountpoint);
-
- debug (g, "fstab: mountable=%s mountpoint=%s", mountable,
mountpoint);
-}
-
-/* Compute a uuid hash as a simple xor of of its 4 32bit components */
-static size_t
-uuid_hash(const void *x, size_t table_size)
-{
- const md_uuid *a = x;
- size_t h, i;
-
- h = a->uuid[0];
- for (i = 1; i < 4; i++) {
- h ^= a->uuid[i];
- }
-
- return h % table_size;
-}
-
-static bool
-uuid_cmp(const void *x, const void *y)
-{
- const md_uuid *a = x;
- const md_uuid *b = y;
- size_t i;
-
- for (i = 0; i < 1; i++) {
- if (a->uuid[i] != b->uuid[i]) return 0;
- }
-
- return 1;
-}
-
-static void
-md_uuid_free(void *x)
-{
- md_uuid *a = x;
- free(a->path);
- free(a);
-}
-
-/* Taken from parse_uuid in mdadm */
-static int
-parse_uuid (const char *str, uint32_t *uuid)
-{
- size_t hit = 0; /* number of Hex digIT */
- char c;
- size_t i;
- int n;
-
- for (i = 0; i < 4; i++)
- uuid[i] = 0;
-
- while ((c = *str++)) {
- if (c >= '0' && c <= '9')
- n = c - '0';
- else if (c >= 'a' && c <= 'f')
- n = 10 + c - 'a';
- else if (c >= 'A' && c <= 'F')
- n = 10 + c - 'A';
- else if (strchr (":. -", c))
- continue;
- else
- return -1;
-
- if (hit < 32) {
- uuid[hit / 8] <<= 4;
- uuid[hit / 8] += n;
- }
- hit++;
- }
- if (hit == 32) return 0;
-
- return -1;
-}
-
-/* Create a mapping of uuids to appliance md device names */
-static ssize_t
-map_app_md_devices (guestfs_h *g, Hash_table **map)
-{
- CLEANUP_FREE_STRING_LIST char **mds = NULL;
- size_t n = 0;
- char **md;
-
- /* A hash mapping uuids to md device names */
- *map = hash_initialize(16, NULL, uuid_hash, uuid_cmp, md_uuid_free);
- if (*map == NULL) g->abort_cb();
-
- mds = guestfs_list_md_devices(g);
- if (mds == NULL) goto error;
-
- for (md = mds; *md != NULL; md++) {
- char **i;
- CLEANUP_FREE_STRING_LIST char **detail = guestfs_md_detail (g, *md);
- if (detail == NULL) goto error;
-
- /* Iterate over keys until we find uuid */
- for (i = detail; *i != NULL; i += 2) {
- if (STREQ(*i, "uuid")) break;
- }
-
- /* We found it */
- if (*i) {
- md_uuid *entry;
-
- /* Next item is the uuid value */
- i++;
-
- entry = safe_malloc(g, sizeof(md_uuid));
- entry->path = safe_strdup(g, *md);
-
- if (parse_uuid(*i, entry->uuid) == -1) {
- /* Invalid UUID is weird, but not fatal. */
- debug(g, "inspect-os: guestfs_md_detail returned invalid "
- "uuid for %s: %s", *md, *i);
- md_uuid_free(entry);
- continue;
- }
-
- const void *matched = NULL;
- switch (hash_insert_if_absent(*map, entry, &matched)) {
- case -1:
- g->abort_cb();
-
- case 0:
- /* Duplicate uuid in for md device is weird, but not fatal. */
- debug(g, "inspect-os: md devices %s and %s have the same uuid",
- ((md_uuid *)matched)->path, entry->path);
- md_uuid_free(entry);
- break;
-
- default:
- n++;
- }
- }
- }
-
- return n;
-
- error:
- hash_free (*map); *map = NULL;
-
- return -1;
-}
-
-static size_t
-mdadm_app_hash(const void *x, size_t table_size)
-{
- const mdadm_app *a = x;
- return hash_pjw(a->mdadm, table_size);
-}
-
-static bool
-mdadm_app_cmp(const void *x, const void *y)
-{
- const mdadm_app *a = x;
- const mdadm_app *b = y;
-
- return STREQ (a->mdadm, b->mdadm);
-}
-
-static void
-mdadm_app_free(void *x)
-{
- mdadm_app *a = x;
- free(a->mdadm);
- free(a->app);
- free(a);
-}
-
-/* Get a map of md device names in mdadm.conf to their device names in the
- * appliance */
-static int
-map_md_devices(guestfs_h *g, Hash_table **map)
-{
- CLEANUP_HASH_FREE Hash_table *app_map = NULL;
- CLEANUP_FREE_STRING_LIST char **matches = NULL;
- ssize_t n_app_md_devices;
-
- *map = NULL;
-
- /* Get a map of md device uuids to their device names in the appliance */
- n_app_md_devices = map_app_md_devices (g, &app_map);
- if (n_app_md_devices == -1) goto error;
-
- /* Nothing to do if there are no md devices */
- if (n_app_md_devices == 0)
- return 0;
-
- /* Get all arrays listed in mdadm.conf */
- matches = guestfs_aug_match(g, "/files/etc/mdadm.conf/array");
- if (!matches) goto error;
-
- /* Log a debug message if we've got md devices, but nothing in mdadm.conf
*/
- if (matches[0] == NULL) {
- debug(g, "Appliance has MD devices, but augeas returned no array
matches "
- "in mdadm.conf");
- return 0;
- }
-
- *map = hash_initialize(16, NULL, mdadm_app_hash, mdadm_app_cmp,
- mdadm_app_free);
- if (!*map) g->abort_cb();
-
- for (char **m = matches; *m != NULL; m++) {
- /* Get device name and uuid for each array */
- CLEANUP_FREE char *dev_path = safe_asprintf (g, "%s/devicename",
*m);
- char *dev = guestfs_aug_get (g, dev_path);
- if (!dev) goto error;
-
- CLEANUP_FREE char *uuid_path = safe_asprintf (g, "%s/uuid", *m);
- CLEANUP_FREE char *uuid = guestfs_aug_get (g, uuid_path);
- if (!uuid) {
- free (dev);
- continue;
- }
-
- /* Parse the uuid into an md_uuid structure so we can look it up in the
- * uuid->appliance device map */
- md_uuid mdadm;
- mdadm.path = dev;
- if (parse_uuid(uuid, mdadm.uuid) == -1) {
- /* Invalid uuid. Weird, but not fatal. */
- debug(g, "inspect-os: mdadm.conf contains invalid uuid for %s:
%s",
- dev, uuid);
- free (dev);
- continue;
- }
-
- /* If there's a corresponding uuid in the appliance, create a new
- * entry in the transitive map */
- md_uuid *app = hash_lookup(app_map, &mdadm);
- if (app) {
- mdadm_app *entry = safe_malloc(g, sizeof(mdadm_app));
- entry->mdadm = dev;
- entry->app = safe_strdup(g, app->path);
-
- switch (hash_insert_if_absent(*map, entry, NULL)) {
- case -1:
- g->abort_cb();
-
- case 0:
- /* Duplicate uuid in for md device is weird, but not fatal. */
- debug(g, "inspect-os: mdadm.conf contains multiple entries for %s",
- app->path);
- mdadm_app_free(entry);
- continue;
- }
- } else
- free (dev);
- }
-
- return 0;
-
- error:
- if (*map) hash_free (*map);
-
- return -1;
-}
-
-static int
-resolve_fstab_device_xdev (guestfs_h *g, const char *type, const char *disk,
- const char *part, char **device_ret)
-{
- CLEANUP_FREE char *name = NULL;
- char *device;
- CLEANUP_FREE_STRING_LIST char **devices = NULL;
- size_t i, count;
- struct drive *drv;
- const char *p;
-
- /* type: (h|s|v|xv)
- * disk: ([a-z]+)
- * part: (\d*)
- */
-
- devices = guestfs_list_devices (g);
- if (devices == NULL)
- return -1;
-
- /* Check any hints we were passed for a non-heuristic mapping */
- name = safe_asprintf (g, "%sd%s", type, disk);
- ITER_DRIVES (g, i, drv) {
- if (drv->name && STREQ (drv->name, name)) {
- device = safe_asprintf (g, "%s%s", devices[i], part);
- if (!guestfs_int_is_partition (g, device)) {
- free (device);
- return 0;
- }
- *device_ret = device;
- break;
- }
- }
-
- /* Guess the appliance device name if we didn't find a matching hint */
- if (!*device_ret) {
- /* Count how many disks the libguestfs appliance has */
- for (count = 0; devices[count] != NULL; count++)
- ;
-
- /* Calculate the numerical index of the disk */
- i = disk[0] - 'a';
- for (p = disk + 1; *p != '\0'; p++) {
- i += 1; i *= 26;
- i += *p - 'a';
- }
-
- /* Check the index makes sense wrt the number of disks the appliance has.
- * If it does, map it to an appliance disk.
- */
- if (i < count) {
- device = safe_asprintf (g, "%s%s", devices[i], part);
- if (!guestfs_int_is_partition (g, device)) {
- free (device);
- return 0;
- }
- *device_ret = device;
- }
- }
-
- return 0;
-}
-
-static int
-resolve_fstab_device_cciss (guestfs_h *g, const char *disk, const char *part,
- char **device_ret)
-{
- char *device;
- CLEANUP_FREE_STRING_LIST char **devices = NULL;
- size_t i;
- struct drive *drv;
-
- /* disk: (cciss/c\d+d\d+)
- * part: (\d+)?
- */
-
- devices = guestfs_list_devices (g);
- if (devices == NULL)
- return -1;
-
- /* Check any hints we were passed for a non-heuristic mapping */
- ITER_DRIVES (g, i, drv) {
- if (drv->name && STREQ (drv->name, disk)) {
- if (part) {
- device = safe_asprintf (g, "%s%s", devices[i], part);
- if (!guestfs_int_is_partition (g, device)) {
- free (device);
- return 0;
- }
- *device_ret = device;
- }
- else
- *device_ret = safe_strdup (g, devices[i]);
- break;
- }
- }
-
- /* We don't try to guess mappings for cciss devices */
- return 0;
-}
-
-static int
-resolve_fstab_device_diskbyid (guestfs_h *g, const char *part,
- char **device_ret)
-{
- int nr_devices;
- char *device;
-
- /* For /dev/disk/by-id there is a limit to what we can do because
- * original SCSI ID information has likely been lost. This
- * heuristic will only work for guests that have a single block
- * device.
- *
- * So the main task here is to make sure the assumptions above are
- * true.
- *
- * XXX Use hints from virt-p2v if available.
- * See also: https://bugzilla.redhat.com/show_bug.cgi?id=836573#c3
- */
-
- nr_devices = guestfs_nr_devices (g);
- if (nr_devices == -1)
- return -1;
-
- /* If #devices isn't 1, give up trying to translate this fstab entry. */
- if (nr_devices != 1)
- return 0;
-
- /* Make the partition name and check it exists. */
- device = safe_asprintf (g, "/dev/sda%s", part);
- if (!guestfs_int_is_partition (g, device)) {
- free (device);
- return 0;
- }
-
- *device_ret = device;
- return 0;
-}
-
-/* Resolve block device name to the libguestfs device name, eg.
- * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV. This
- * assumes that disks were added in the same order as they appear to
- * the real VM, which is a reasonable assumption to make. Return
- * anything we don't recognize unchanged.
- */
-static char *
-resolve_fstab_device (guestfs_h *g, const char *spec, Hash_table *md_map,
- enum inspect_os_type os_type)
-{
- char *device = NULL;
- char *type, *slice, *disk, *part;
- int r;
-
- if (STRPREFIX (spec, "/dev/mapper/")) {
- /* LVM2 does some strange munging on /dev/mapper paths for VGs and
- * LVs which contain '-' character:
- *
- * ><fs> lvcreate LV--test VG--test 32
- * ><fs> debug ls /dev/mapper
- * VG----test-LV----test
- *
- * This makes it impossible to reverse those paths directly, so
- * we have implemented lvm_canonical_lv_name in the daemon.
- */
- guestfs_push_error_handler (g, NULL, NULL);
- device = guestfs_lvm_canonical_lv_name (g, spec);
- guestfs_pop_error_handler (g);
- if (device == NULL) {
- if (guestfs_last_errno (g) == ENOENT) {
- /* Ignore devices that don't exist. (RHBZ#811872) */
- } else {
- guestfs_int_error_errno (g, guestfs_last_errno (g), "%s",
guestfs_last_error (g));
- return NULL;
- }
- }
- }
- else if (match3 (g, spec, re_xdev, &type, &disk, &part)) {
- r = resolve_fstab_device_xdev (g, type, disk, part, &device);
- free (type);
- free (disk);
- free (part);
- if (r == -1)
- return NULL;
- }
- else if (match2 (g, spec, re_cciss, &disk, &part)) {
- r = resolve_fstab_device_cciss (g, disk, part, &device);
- free (disk);
- free (part);
- if (r == -1)
- return NULL;
- }
- else if (md_map && (disk = match1 (g, spec, re_mdN)) != NULL) {
- mdadm_app entry;
- entry.mdadm = disk;
-
- mdadm_app *app = hash_lookup (md_map, &entry);
- if (app) device = safe_strdup (g, app->app);
-
- free (disk);
- }
- else if (match3 (g, spec, re_freebsd_gpt, &type, &disk, &part)) {
- /* If the FreeBSD disk contains GPT partitions, the translation to Linux
- * device names is straight forward. Partitions on a virtio disk are
- * prefixed with vtbd. IDE hard drives used to be prefixed with ad and now
- * are with ada.
- */
- const int disk_i = guestfs_int_parse_unsigned_int (g, disk);
- const int part_i = guestfs_int_parse_unsigned_int (g, part);
- free (type);
- free (disk);
- free (part);
-
- if (disk_i != -1 && disk_i <= 26 && part_i > 0
&& part_i <= 128)
- device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a',
part_i);
- }
- else if (match4 (g, spec, re_freebsd_mbr, &type, &disk, &slice,
&part)) {
- /* FreeBSD disks are organized quite differently. See:
- * http://www.freebsd.org/doc/handbook/disk-organization.html
- * FreeBSD "partitions" are exposed as quasi-extended partitions
- * numbered from 5 in Linux. I have no idea what happens when you
- * have multiple "slices" (the FreeBSD term for MBR partitions).
- */
- const int disk_i = guestfs_int_parse_unsigned_int (g, disk);
- const int slice_i = guestfs_int_parse_unsigned_int (g, slice);
- int part_i = part[0] - 'a' /* counting from 0 */;
- free (type);
- free (disk);
- free (slice);
- free (part);
-
- if (part_i > 2)
- /* Partition 'c' has the size of the enclosing slice. Not mapped
under Linux. */
- part_i -= 1;
-
- if (disk_i != -1 && disk_i <= 26 &&
- slice_i > 0 && slice_i <= 1 /* > 4 .. see comment
above */ &&
- part_i >= 0 && part_i < 25) {
- device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a',
part_i + 5);
- }
- }
- else if ((os_type == OS_TYPE_NETBSD) &&
- match3 (g, spec, re_netbsd_dev, &type, &disk, &part)) {
- const int disk_i = guestfs_int_parse_unsigned_int (g, disk);
- int part_i = part[0] - 'a'; /* counting from 0 */
- free (type);
- free (disk);
- free (part);
-
- if (part_i > 3)
- /* Partition 'c' is the disklabel partition and 'd' the
hard disk itself.
- * Not mapped under Linux.
- */
- part_i -= 2;
-
- if (disk_i != -1 && part_i >= 0 && part_i < 24)
- device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a',
part_i + 5);
- }
- else if ((os_type == OS_TYPE_OPENBSD) &&
- match3 (g, spec, re_openbsd_dev, &type, &disk, &part)) {
- const int disk_i = guestfs_int_parse_unsigned_int (g, disk);
- int part_i = part[0] - 'a'; /* counting from 0 */
- free (type);
- free (disk);
- free (part);
-
- if (part_i > 2)
- /* Partition 'c' is the hard disk itself. Not mapped under Linux
*/
- part_i -= 1;
-
- /* In OpenBSD MAXPARTITIONS is defined to 16 for all architectures */
- if (disk_i != -1 && part_i >= 0 && part_i < 15)
- device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a',
part_i + 5);
- }
- else if ((part = match1 (g, spec, re_diskbyid)) != NULL) {
- r = resolve_fstab_device_diskbyid (g, part, &device);
- free (part);
- if (r == -1)
- return NULL;
- }
- else if (match3 (g, spec, re_hurd_dev, &type, &disk, &part)) {
- /* Hurd disk devices are like /dev/hdNsM, where hdN is the
- * N-th disk and M is the M-th partition on that disk.
- * Turn the disk number into a letter-based identifier, so
- * we can resolve it easily.
- */
- const int disk_i = guestfs_int_parse_unsigned_int (g, disk);
- const char disk_as_letter[2] = { disk_i + 'a', 0 };
- r = resolve_fstab_device_xdev (g, type, disk_as_letter, part, &device);
- free (type);
- free (disk);
- free (part);
- if (r == -1)
- return NULL;
- }
-
- /* Didn't match device pattern, return original spec unchanged. */
- if (device == NULL)
- device = safe_strdup (g, spec);
-
- return device;
-}
-
-static char *make_augeas_path_expression (guestfs_h *g, const char
**configfiles);
-
-/* Call 'f' with Augeas opened and having parsed 'configfiles'
(these
- * files must exist). As a security measure, this bails if any file
- * is too large for a reasonable configuration file. After the call
- * to 'f' the Augeas handle is closed.
- */
-static int
-inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs,
- const char **configfiles,
- int (*f) (guestfs_h *, struct inspect_fs *))
-{
- size_t i;
- int64_t size;
- int r;
- CLEANUP_FREE char *pathexpr = NULL;
- CLEANUP_FREE_STRING_LIST char **matches = NULL;
- char **match;
-
- /* Security: Refuse to do this if a config file is too large. */
- for (i = 0; configfiles[i] != NULL; ++i) {
- if (guestfs_is_file_opts (g, configfiles[i],
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) == 0)
- continue;
-
- size = guestfs_filesize (g, configfiles[i]);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_AUGEAS_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- configfiles[i], size);
- return -1;
- }
- }
-
- if (guestfs_aug_init (g, "/", 16|32 /* AUG_SAVE_NOOP|AUG_NO_LOAD
*/) == -1)
- return -1;
-
- r = -1;
-
- /* Tell Augeas to only load configfiles and no other files. This
- * prevents a rogue guest from performing a denial of service attack
- * by having large, over-complicated configuration files which are
- * unrelated to the task at hand. (Thanks Dominic Cleal).
- * Note this requires Augeas >= 1.0.0 because of RHBZ#975412.
- */
- pathexpr = make_augeas_path_expression (g, configfiles);
- if (guestfs_aug_rm (g, pathexpr) == -1)
- goto out;
-
- if (guestfs_aug_load (g) == -1)
- goto out;
-
- /* Check that augeas did not get a parse error for any of the configfiles,
- * otherwise we are silently missing information.
- */
- matches = guestfs_aug_match (g, "/augeas/files//error");
- for (match = matches; *match != NULL; ++match) {
- for (i = 0; configfiles[i] != NULL; ++i) {
- CLEANUP_FREE char *errorpath - safe_asprintf (g,
"/augeas/files%s/error", configfiles[i]);
-
- if (STREQ (*match, errorpath)) {
- /* Get the various error details. */
- guestfs_push_error_handler (g, NULL, NULL);
- CLEANUP_FREE char *messagepath - safe_asprintf (g,
"%s/message", errorpath);
- CLEANUP_FREE char *message = guestfs_aug_get (g, messagepath);
- CLEANUP_FREE char *linepath - safe_asprintf (g,
"%s/line", errorpath);
- CLEANUP_FREE char *line = guestfs_aug_get (g, linepath);
- CLEANUP_FREE char *charpath - safe_asprintf (g,
"%s/char", errorpath);
- CLEANUP_FREE char *charp = guestfs_aug_get (g, charpath);
- guestfs_pop_error_handler (g);
-
- error (g, _("%s:%s:%s: augeas parse failure: %s"),
- configfiles[i],
- line ? : "<none>",
- charp ? : "<none>",
- message ? : "<none>");
- goto out;
- }
- }
- }
-
- r = f (g, fs);
-
- out:
- guestfs_aug_close (g);
-
- return r;
-}
-
-/* Explained here: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 */
-static char *
-make_augeas_path_expression (guestfs_h *g, const char **configfiles)
-{
- size_t i;
- size_t nr_files;
- CLEANUP_FREE_STRING_LIST char **subexprs = NULL;
- CLEANUP_FREE char *subexpr = NULL;
- char *ret;
-
- nr_files = guestfs_int_count_strings ((char **) configfiles);
- subexprs = safe_malloc (g, sizeof (char *) * (nr_files + 1));
-
- for (i = 0; i < nr_files; ++i) {
- subexprs[i] = /* v NB trailing '/' after filename */
- safe_asprintf (g, "\"%s/\" !~ regexp('^') +
glob(incl) + regexp('/.*')",
- configfiles[i]);
- }
- subexprs[nr_files] = NULL;
-
- subexpr = guestfs_int_join_strings (" and ", subexprs);
- if (subexpr == NULL)
- g->abort_cb ();
-
- ret = safe_asprintf (g, "/augeas/load/*[ %s ]", subexpr);
- debug (g, "augeas pathexpr = %s", ret);
- return ret;
-}
-
-/* Canonicalize the path, so "///usr//local//" ->
"/usr/local"
- *
- * The path is modified in place because the result is always
- * the same length or shorter than the argument passed.
- */
-static void
-canonical_mountpoint (char *s)
-{
- size_t len = strlen (s);
- char *orig = s;
-
- s = strchr (s, '/');
- while (s != NULL && *s != 0) {
- char *pos = s + 1;
- char *p = pos;
- /* Find how many consecutive slashes are there after the one found,
- * and shift the characters after them accordingly. */
- while (*p == '/')
- ++p;
- if (p - pos > 0) {
- memmove (pos, p, len - (p - orig) + 1);
- len -= p - pos;
- }
-
- s = strchr (pos, '/');
- }
- /* Ignore the trailing slash, but avoid removing it for "/". */
- if (len > 1 && orig[len-1] == '/')
- --len;
- orig[len] = 0;
-}
diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c
deleted file mode 100644
index 34f33c908..000000000
--- a/lib/inspect-fs-windows.c
+++ /dev/null
@@ -1,739 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010-2012 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <iconv.h>
-#include <inttypes.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-#ifdef HAVE_SYS_ENDIAN_H
-#include <sys/endian.h>
-#endif
-
-#if defined __APPLE__ && defined __MACH__
-#include <libkern/OSByteOrder.h>
-#define le32toh(x) OSSwapLittleToHostInt32(x)
-#define le64toh(x) OSSwapLittleToHostInt64(x)
-#endif
-
-#include <pcre.h>
-
-#include "c-ctype.h"
-#include "ignore-value.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-#include "guestfs-internal-actions.h"
-#include "structs-cleanups.h"
-
-COMPILE_REGEXP (re_windows_version, "^(\\d+)\\.(\\d+)", 0)
-COMPILE_REGEXP (re_boot_ini_os_header, "^\\[operating
systems\\]\\s*$", 0)
-COMPILE_REGEXP (re_boot_ini_os,
-
"^(multi|scsi)\\((\\d+)\\)disk\\((\\d+)\\)rdisk\\((\\d+)\\)partition\\((\\d+)\\)([^=]+)=",
0)
-
-static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
-static int check_windows_registry_paths (guestfs_h *g, struct inspect_fs *fs);
-static int check_windows_software_registry (guestfs_h *g, struct inspect_fs
*fs);
-static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs);
-static char *map_registry_disk_blob (guestfs_h *g, const void *blob);
-static char *map_registry_disk_blob_gpt (guestfs_h *g, const void *blob);
-static char *extract_guid_from_registry_blob (guestfs_h *g, const void *blob);
-
-/* XXX Handling of boot.ini in the Perl version was pretty broken. It
- * essentially didn't do anything for modern Windows guests.
- * Therefore I've omitted all that code.
- */
-
-/* Try to find Windows systemroot using some common locations.
- *
- * Notes:
- *
- * (1) We check for some directories inside to see if it is a real
- * systemroot, and not just a directory that happens to have the same
- * name.
- *
- * (2) If a Windows guest has multiple disks and applications are
- * installed on those other disks, then those other disks will contain
- * "/Program Files" and "/System Volume Information".
Those would
- * *not* be Windows root disks. (RHBZ#674130)
- */
-
-static int
-is_systemroot (guestfs_h *const g, const char *systemroot)
-{
- CLEANUP_FREE char *path1 = NULL, *path2 = NULL, *path3 = NULL;
-
- path1 = safe_asprintf (g, "%s/system32", systemroot);
- if (!guestfs_int_is_dir_nocase (g, path1))
- return 0;
-
- path2 = safe_asprintf (g, "%s/system32/config", systemroot);
- if (!guestfs_int_is_dir_nocase (g, path2))
- return 0;
-
- path3 = safe_asprintf (g, "%s/system32/cmd.exe", systemroot);
- if (!guestfs_int_is_file_nocase (g, path3))
- return 0;
-
- return 1;
-}
-
-char *
-guestfs_int_get_windows_systemroot (guestfs_h *g)
-{
- /* Check a predefined list of common windows system root locations */
- static const char *systemroots[] - { "/windows",
"/winnt", "/win32", "/win", "/reactos",
NULL };
-
- for (size_t i = 0; i < sizeof systemroots / sizeof systemroots[0]; ++i) {
- char *systemroot - guestfs_int_case_sensitive_path_silently (g,
systemroots[i]);
- if (!systemroot)
- continue;
-
- if (is_systemroot (g, systemroot)) {
- debug (g, "windows %%SYSTEMROOT%% = %s", systemroot);
-
- return systemroot;
- } else {
- free (systemroot);
- }
- }
-
- /* If the fs contains boot.ini, check it for non-standard
- * systemroot locations */
- CLEANUP_FREE char *boot_ini_path -
guestfs_int_case_sensitive_path_silently (g, "/boot.ini");
- if (boot_ini_path && guestfs_is_file (g, boot_ini_path) > 0) {
- CLEANUP_FREE_STRING_LIST char **boot_ini - guestfs_read_lines (g,
boot_ini_path);
- if (!boot_ini) {
- debug (g, "error reading %s", boot_ini_path);
- return NULL;
- }
-
- int found_os = 0;
- for (char **i = boot_ini; *i != NULL; i++) {
- CLEANUP_FREE char *controller_type = NULL;
- CLEANUP_FREE char *controller = NULL;
- CLEANUP_FREE char *disk = NULL;
- CLEANUP_FREE char *rdisk = NULL;
- CLEANUP_FREE char *partition = NULL;
- CLEANUP_FREE char *path = NULL;
-
- char *line = *i;
-
- if (!found_os) {
- if (match (g, line, re_boot_ini_os_header)) {
- found_os = 1;
- continue;
- }
- }
-
- /* See http://support.microsoft.com/kb/102873 for a discussion
- * of what this line means */
- if (match6 (g, line, re_boot_ini_os, &controller_type,
- &controller, &disk, &rdisk, &partition,
&path))
- {
- /* The Windows system root may be on any disk. However, there
- * are currently (at least) 2 practical problems preventing us
- * from locating it on another disk:
- *
- * 1. We don't have enough metadata about the disks we were
- * given to know if what controller they were on and what
- * index they had.
- *
- * 2. The way inspection of filesystems currently works, we
- * can't mark another filesystem, which we may have already
- * inspected, to be inspected for a specific Windows system
- * root.
- *
- * Solving 1 properly would require a new API at a minimum. We
- * might be able to fudge something practical without this,
- * though, e.g. by looking at the <partition>th partition of
- * every disk for the specific windows root.
- *
- * Solving 2 would probably require a significant refactoring
- * of the way filesystems are inspected. We should probably do
- * this some time.
- *
- * For the moment, we ignore all partition information and
- * assume the system root is on the current partition. In
- * practice, this will normally be correct.
- */
-
- /* Swap backslashes for forward slashes in the system root
- * path */
- for (char *j = path; *j != '\0'; j++) {
- if (*j == '\\') *j = '/';
- }
-
- char *systemroot = guestfs_int_case_sensitive_path_silently (g, path);
- if (systemroot && is_systemroot (g, systemroot)) {
- debug (g, "windows %%SYSTEMROOT%% = %s", systemroot);
-
- return systemroot;
- } else {
- free (systemroot);
- }
- }
- }
- }
-
- return NULL; /* not found */
-}
-
-int
-guestfs_int_check_windows_root (guestfs_h *g, struct inspect_fs *fs,
- char *const systemroot)
-{
- fs->type = OS_TYPE_WINDOWS;
- fs->distro = OS_DISTRO_WINDOWS;
-
- /* Freed by guestfs_int_free_inspect_info. */
- fs->windows_systemroot = systemroot;
-
- if (check_windows_arch (g, fs) == -1)
- return -1;
-
- /* Get system and software registry paths. */
- if (check_windows_registry_paths (g, fs) == -1)
- return -1;
-
- /* Product name and version. */
- if (check_windows_software_registry (g, fs) == -1)
- return -1;
-
- /* Hostname. */
- if (check_windows_system_registry (g, fs) == -1)
- return -1;
-
- return 0;
-}
-
-static int
-check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
-{
- CLEANUP_FREE char *cmd_exe - safe_asprintf (g,
"%s/system32/cmd.exe", fs->windows_systemroot);
-
- /* Should exist because of previous check above in get_windows_systemroot. */
- CLEANUP_FREE char *cmd_exe_path = guestfs_case_sensitive_path (g, cmd_exe);
- if (!cmd_exe_path)
- return -1;
-
- char *arch = guestfs_file_architecture (g, cmd_exe_path);
- if (!arch)
- return -1;
-
- fs->arch = arch; /* freed by guestfs_int_free_inspect_info */
-
- return 0;
-}
-
-static int
-check_windows_registry_paths (guestfs_h *g, struct inspect_fs *fs)
-{
- int r;
- CLEANUP_FREE char *software = NULL, *system = NULL;
-
- if (!fs->windows_systemroot)
- return 0;
-
- software = safe_asprintf (g, "%s/system32/config/software",
- fs->windows_systemroot);
-
- fs->windows_software_hive = guestfs_case_sensitive_path (g, software);
- if (!fs->windows_software_hive)
- return -1;
-
- r = guestfs_is_file (g, fs->windows_software_hive);
- if (r == -1) {
- free (fs->windows_software_hive);
- fs->windows_software_hive = NULL;
- return -1;
- }
-
- if (r == 0) { /* doesn't exist, or not a file */
- free (fs->windows_software_hive);
- fs->windows_software_hive = NULL;
- /*FALLTHROUGH*/
- }
-
- system = safe_asprintf (g, "%s/system32/config/system",
- fs->windows_systemroot);
-
- fs->windows_system_hive = guestfs_case_sensitive_path (g, system);
- if (!fs->windows_system_hive)
- return -1;
-
- r = guestfs_is_file (g, fs->windows_system_hive);
- if (r == -1) {
- free (fs->windows_system_hive);
- fs->windows_system_hive = NULL;
- return -1;
- }
-
- if (r == 0) { /* doesn't exist, or not a file */
- free (fs->windows_system_hive);
- fs->windows_system_hive = NULL;
- /*FALLTHROUGH*/
- }
-
- return 0;
-}
-
-/* At the moment, pull just the ProductName and version numbers from
- * the registry. In future there is a case for making many more
- * registry fields available to callers.
- */
-static int
-check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
-{
- int ret = -1;
- int64_t node;
- const char *hivepath[] - { "Microsoft", "Windows NT",
"CurrentVersion" };
- size_t i;
- CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL;
- bool ignore_currentversion = false;
-
- /* If the software hive doesn't exist, just accept that we cannot
- * find product_name etc.
- */
- if (!fs->windows_software_hive)
- return 0;
-
- if (guestfs_hivex_open (g, fs->windows_software_hive,
- GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose,
- GUESTFS_HIVEX_OPEN_UNSAFE, 1,
- -1) == -1)
- return -1;
-
- node = guestfs_hivex_root (g);
- for (i = 0; node > 0 && i < sizeof hivepath / sizeof
hivepath[0]; ++i)
- node = guestfs_hivex_node_get_child (g, node, hivepath[i]);
-
- if (node == -1)
- goto out;
-
- if (node == 0) {
- perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion");
- goto out;
- }
-
- values = guestfs_hivex_node_values (g, node);
-
- for (i = 0; i < values->len; ++i) {
- const int64_t value = values->val[i].hivex_value_h;
- CLEANUP_FREE char *key = guestfs_hivex_value_key (g, value);
- if (key == NULL)
- goto out;
-
- if (STRCASEEQ (key, "ProductName")) {
- fs->product_name = guestfs_hivex_value_utf8 (g, value);
- if (!fs->product_name)
- goto out;
- }
- else if (STRCASEEQ (key, "CurrentMajorVersionNumber")) {
- size_t vsize;
- const int64_t vtype = guestfs_hivex_value_type (g, value);
- CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value,
&vsize);
-
- if (vbuf == NULL)
- goto out;
- if (vtype != 4 || vsize != 4) {
- error (g, "hivex: expected CurrentVersion\\%s to be a DWORD
field",
- "CurrentMajorVersionNumber");
- goto out;
- }
-
- fs->version.v_major = le32toh (*(int32_t *)vbuf);
-
- /* Ignore CurrentVersion if we see it after this key. */
- ignore_currentversion = true;
- }
- else if (STRCASEEQ (key, "CurrentMinorVersionNumber")) {
- size_t vsize;
- const int64_t vtype = guestfs_hivex_value_type (g, value);
- CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value,
&vsize);
-
- if (vbuf == NULL)
- goto out;
- if (vtype != 4 || vsize != 4) {
- error (g, "hivex: expected CurrentVersion\\%s to be a DWORD
field",
- "CurrentMinorVersionNumber");
- goto out;
- }
-
- fs->version.v_minor = le32toh (*(int32_t *)vbuf);
-
- /* Ignore CurrentVersion if we see it after this key. */
- ignore_currentversion = true;
- }
- else if (!ignore_currentversion && STRCASEEQ (key,
"CurrentVersion")) {
- CLEANUP_FREE char *version = guestfs_hivex_value_utf8 (g, value);
- if (!version)
- goto out;
- if (guestfs_int_version_from_x_y_re (g, &fs->version, version,
- re_windows_version) == -1)
- goto out;
- }
- else if (STRCASEEQ (key, "InstallationType")) {
- fs->product_variant = guestfs_hivex_value_utf8 (g, value);
- if (!fs->product_variant)
- goto out;
- }
- }
-
- ret = 0;
-
- out:
- guestfs_hivex_close (g);
-
- return ret;
-}
-
-static int
-check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
-{
- static const char gpt_prefix[] = "DMIO:ID:";
- int ret = -1;
- int64_t root, node, value;
- CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL;
- CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values2 =
NULL;
- int32_t dword;
- size_t i, count;
- CLEANUP_FREE void *buf = NULL;
- size_t buflen;
- const char *hivepath[] - { NULL /* current control set */,
"Services", "Tcpip", "Parameters" };
-
- /* If the system hive doesn't exist, just accept that we cannot
- * find hostname etc.
- */
- if (!fs->windows_system_hive)
- return 0;
-
- if (guestfs_hivex_open (g, fs->windows_system_hive,
- GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose,
- GUESTFS_HIVEX_OPEN_UNSAFE, 1,
- -1) == -1)
- goto out;
-
- root = guestfs_hivex_root (g);
- if (root == 0)
- goto out;
-
- /* Get the CurrentControlSet. */
- node = guestfs_hivex_node_get_child (g, root, "Select");
- if (node == -1)
- goto out;
-
- if (node == 0) {
- error (g, "hivex: could not locate HKLM\\SYSTEM\\Select");
- goto out;
- }
-
- value = guestfs_hivex_node_get_value (g, node, "Current");
- if (value == -1)
- goto out;
-
- if (value == 0) {
- error (g, "hivex: HKLM\\System\\Select Default entry not found");
- goto out;
- }
-
- /* XXX Should check the type. */
- buf = guestfs_hivex_value_value (g, value, &buflen);
- if (buflen != 4) {
- error (g, "hivex: HKLM\\System\\Select\\Current expected to be
DWORD");
- goto out;
- }
- dword = le32toh (*(int32_t *)buf);
- fs->windows_current_control_set = safe_asprintf (g,
"ControlSet%03d", dword);
-
- /* Get the drive mappings.
- * This page explains the contents of HKLM\System\MountedDevices:
- * http://www.goodells.net/multiboot/partsigs.shtml
- */
- node = guestfs_hivex_node_get_child (g, root, "MountedDevices");
- if (node == -1)
- goto out;
-
- if (node == 0)
- /* Not found: skip getting drive letter mappings (RHBZ#803664). */
- goto skip_drive_letter_mappings;
-
- values = guestfs_hivex_node_values (g, node);
-
- /* Count how many DOS drive letter mappings there are. This doesn't
- * ignore removable devices, so it overestimates, but that doesn't
- * matter because it just means we'll allocate a few bytes extra.
- */
- for (i = count = 0; i < values->len; ++i) {
- CLEANUP_FREE char *key - guestfs_hivex_value_key (g,
values->val[i].hivex_value_h);
- if (key == NULL)
- goto out;
- if (STRCASEEQLEN (key, "\\DosDevices\\", 12) &&
- c_isalpha (key[12]) && key[13] == ':')
- count++;
- }
-
- fs->drive_mappings = safe_calloc (g, 2*count + 1, sizeof (char *));
-
- for (i = count = 0; i < values->len; ++i) {
- const int64_t v = values->val[i].hivex_value_h;
- CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v);
- if (key == NULL)
- goto out;
- if (STRCASEEQLEN (key, "\\DosDevices\\", 12) &&
- c_isalpha (key[12]) && key[13] == ':') {
- /* Get the binary value. Is it a fixed disk? */
- CLEANUP_FREE char *blob = NULL;
- char *device;
- int64_t type;
- bool is_gpt;
- size_t len;
-
- type = guestfs_hivex_value_type (g, v);
- blob = guestfs_hivex_value_value (g, v, &len);
- is_gpt = memcmp (blob, gpt_prefix, 8) == 0;
- if (blob != NULL && type == 3 && (len == 12 || is_gpt)) {
- /* Try to map the blob to a known disk and partition. */
- if (is_gpt)
- device = map_registry_disk_blob_gpt (g, blob);
- else
- device = map_registry_disk_blob (g, blob);
-
- if (device != NULL) {
- fs->drive_mappings[count++] = safe_strndup (g, &key[12], 1);
- fs->drive_mappings[count++] = device;
- }
- }
- }
- }
-
- skip_drive_letter_mappings:;
- /* Get the hostname. */
- hivepath[0] = fs->windows_current_control_set;
- for (node = root, i = 0;
- node > 0 && i < sizeof hivepath / sizeof hivepath[0];
- ++i) {
- node = guestfs_hivex_node_get_child (g, node, hivepath[i]);
- }
-
- if (node == -1)
- goto out;
-
- if (node == 0) {
- perrorf (g, "hivex: cannot locate
HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters",
- fs->windows_current_control_set);
- goto out;
- }
-
- values2 = guestfs_hivex_node_values (g, node);
- if (values2 == NULL)
- goto out;
-
- for (i = 0; i < values2->len; ++i) {
- const int64_t v = values2->val[i].hivex_value_h;
- CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v);
- if (key == NULL)
- goto out;
-
- if (STRCASEEQ (key, "Hostname")) {
- fs->hostname = guestfs_hivex_value_utf8 (g, v);
- if (!fs->hostname)
- goto out;
- }
- /* many other interesting fields here ... */
- }
-
- ret = 0;
-
- out:
- guestfs_hivex_close (g);
-
- return ret;
-}
-
-/* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data
- * to store partitions. This blob is described here:
- * http://www.goodells.net/multiboot/partsigs.shtml
- * The following function maps this blob to a libguestfs partition
- * name, if possible.
- */
-static char *
-map_registry_disk_blob (guestfs_h *g, const void *blob)
-{
- CLEANUP_FREE_STRING_LIST char **devices = NULL;
- CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *partitions = NULL;
- size_t i, j, len;
- uint64_t part_offset;
-
- /* First 4 bytes are the disk ID. Search all devices to find the
- * disk with this disk ID.
- */
- devices = guestfs_list_devices (g);
- if (devices == NULL)
- return NULL;
-
- for (i = 0; devices[i] != NULL; ++i) {
- /* Read the disk ID. */
- CLEANUP_FREE char *diskid - guestfs_pread_device (g, devices[i], 4,
0x01b8, &len);
- if (diskid == NULL)
- continue;
- if (len < 4)
- continue;
- if (memcmp (diskid, blob, 4) == 0) /* found it */
- goto found_disk;
- }
- return NULL;
-
- found_disk:
- /* Next 8 bytes are the offset of the partition in bytes(!) given as
- * a 64 bit little endian number. Luckily it's easy to get the
- * partition byte offset from guestfs_part_list.
- */
- memcpy (&part_offset, (char *) blob + 4, sizeof (part_offset));
- part_offset = le64toh (part_offset);
-
- partitions = guestfs_part_list (g, devices[i]);
- if (partitions == NULL)
- return NULL;
-
- for (j = 0; j < partitions->len; ++j) {
- if (partitions->val[j].part_start == part_offset) /* found it */
- goto found_partition;
- }
- return NULL;
-
- found_partition:
- /* Construct the full device name. */
- return safe_asprintf (g, "%s%d", devices[i],
partitions->val[j].part_num);
-}
-
-/* Matches Windows registry HKLM\SYSYTEM\MountedDevices\DosDevices blob to
- * to libguestfs GPT partition device. For GPT disks, the blob is made of
- * "DMIO:ID:" prefix followed by the GPT partition GUID.
- */
-static char *
-map_registry_disk_blob_gpt (guestfs_h *g, const void *blob)
-{
- CLEANUP_FREE_STRING_LIST char **parts = NULL;
- CLEANUP_FREE char *blob_guid = extract_guid_from_registry_blob (g, blob);
- size_t i;
-
- parts = guestfs_list_partitions (g);
- if (parts == NULL)
- return NULL;
-
- for (i = 0; parts[i] != NULL; ++i) {
- CLEANUP_FREE char *fs_guid = NULL;
- int partnum;
- CLEANUP_FREE char *device = NULL;
- CLEANUP_FREE char *type = NULL;
-
- partnum = guestfs_part_to_partnum (g, parts[i]);
- if (partnum == -1)
- continue;
-
- device = guestfs_part_to_dev (g, parts[i]);
- if (device == NULL)
- continue;
-
- type = guestfs_part_get_parttype (g, device);
- if (type == NULL)
- continue;
-
- if (STRCASENEQ (type, "gpt"))
- continue;
-
- /* get the GPT parition GUID from the partition block device */
- fs_guid = guestfs_part_get_gpt_guid (g, device, partnum);
- if (fs_guid == NULL)
- continue;
-
- /* if both GUIDs match, we have found the mapping for our device */
- if (STRCASEEQ (fs_guid, blob_guid))
- return safe_strdup (g, parts[i]);
- }
-
- return NULL;
-}
-
-/* Extracts the binary GUID stored in blob from Windows registry
- * HKLM\SYSTYEM\MountedDevices\DosDevices value and converts it to a
- * GUID string so that it can be matched against libguestfs partition
- * device GPT GUID.
- */
-static char *
-extract_guid_from_registry_blob (guestfs_h *g, const void *blob)
-{
- char guid_bytes[16];
- uint32_t data1;
- uint16_t data2, data3;
- uint64_t data4;
-
- /* get the GUID bytes from blob (skip 8 byte "DMIO:ID:" prefix) */
- memcpy (&guid_bytes, (char *) blob + 8, sizeof (guid_bytes));
-
- /* copy relevant sections from blob to respective ints */
- memcpy (&data1, guid_bytes, sizeof (data1));
- memcpy (&data2, guid_bytes + 4, sizeof (data2));
- memcpy (&data3, guid_bytes + 6, sizeof (data3));
- memcpy (&data4, guid_bytes + 8, sizeof (data4));
-
- /* ensure proper endianness */
- data1 = le32toh (data1);
- data2 = le16toh (data2);
- data3 = le16toh (data3);
- data4 = be64toh (data4);
-
- return safe_asprintf (g,
- "%08" PRIX32 "-%04" PRIX16 "-%04"
PRIX16 "-%04" PRIX64 "-%012" PRIX64,
- data1, data2, data3, data4 >> 48, data4 & 0xffffffffffff);
-}
-
-/* NB: This function DOES NOT test for the existence of the file. It
- * will return non-NULL even if the file/directory does not exist.
- * You have to call guestfs_is_file{,_opts} etc.
- */
-char *
-guestfs_int_case_sensitive_path_silently (guestfs_h *g, const char *path)
-{
- char *ret;
-
- guestfs_push_error_handler (g, NULL, NULL);
- ret = guestfs_case_sensitive_path (g, path);
- guestfs_pop_error_handler (g);
-
- return ret;
-}
diff --git a/lib/inspect-fs.c b/lib/inspect-fs.c
deleted file mode 100644
index e320b3e78..000000000
--- a/lib/inspect-fs.c
+++ /dev/null
@@ -1,758 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010-2012 Red Hat Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <libintl.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#include <pcre.h>
-
-#include "ignore-value.h"
-#include "xstrtol.h"
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-#include "structs-cleanups.h"
-
-static int check_filesystem (guestfs_h *g, const char *mountable,
- const struct guestfs_internal_mountable *m,
- int whole_device);
-static void extend_fses (guestfs_h *g);
-static int get_partition_context (guestfs_h *g, const char *partition, int
*partnum_ret, int *nr_partitions_ret);
-static int is_symlink_to (guestfs_h *g, const char *file, const char
*wanted_target);
-
-/* Find out if 'device' contains a filesystem. If it does, add
- * another entry in g->fses.
- */
-int
-guestfs_int_check_for_filesystem_on (guestfs_h *g, const char *mountable)
-{
- CLEANUP_FREE char *vfs_type = NULL;
- int is_swap, r;
- struct inspect_fs *fs;
- CLEANUP_FREE_INTERNAL_MOUNTABLE struct guestfs_internal_mountable *m = NULL;
- int whole_device = 0;
-
- /* Get vfs-type in order to check if it's a Linux(?) swap device.
- * If there's an error we should ignore it, so to do that we have to
- * temporarily replace the error handler with a null one.
- */
- guestfs_push_error_handler (g, NULL, NULL);
- vfs_type = guestfs_vfs_type (g, mountable);
- guestfs_pop_error_handler (g);
-
- is_swap = vfs_type && STREQ (vfs_type, "swap");
- debug (g, "check_for_filesystem_on: %s (%s)",
- mountable, vfs_type ? vfs_type : "failed to get vfs type");
-
- if (is_swap) {
- extend_fses (g);
- fs = &g->fses[g->nr_fses-1];
- fs->mountable = safe_strdup (g, mountable);
- return 0;
- }
-
- m = guestfs_internal_parse_mountable (g, mountable);
- if (m == NULL)
- return -1;
-
- /* If it's a whole device, see if it is an install ISO. */
- if (m->im_type == MOUNTABLE_DEVICE) {
- whole_device = guestfs_is_whole_device (g, m->im_device);
- if (whole_device == -1) {
- return -1;
- }
- }
-
- /* Try mounting the device. As above, ignore errors. */
- guestfs_push_error_handler (g, NULL, NULL);
- if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the
*BSDs. */
- /* FreeBSD fs is a variant of ufs called ufs2 ... */
- r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs",
mountable, "/");
- if (r == -1)
- /* while NetBSD and OpenBSD use another variant labeled 44bsd */
- r = guestfs_mount_vfs (g, "ro,ufstype=44bsd", "ufs",
mountable, "/");
- } else {
- r = guestfs_mount_ro (g, mountable, "/");
- }
- guestfs_pop_error_handler (g);
- if (r == -1)
- return 0;
-
- /* Do the rest of the checks. */
- r = check_filesystem (g, mountable, m, whole_device);
-
- /* Unmount the filesystem. */
- if (guestfs_umount_all (g) == -1)
- return -1;
-
- return r;
-}
-
-static int
-check_filesystem (guestfs_h *g, const char *mountable,
- const struct guestfs_internal_mountable *m,
- int whole_device)
-{
- int partnum = -1, nr_partitions = -1;
- /* Not CLEANUP_FREE, as it will be cleaned up with inspection info */
- char *windows_systemroot = NULL;
-
- extend_fses (g);
-
- if (!whole_device && m->im_type == MOUNTABLE_DEVICE &&
- guestfs_int_is_partition (g, m->im_device)) {
- if (get_partition_context (g, m->im_device,
- &partnum, &nr_partitions) == -1)
- return -1;
- }
-
- struct inspect_fs *fs = &g->fses[g->nr_fses-1];
-
- fs->mountable = safe_strdup (g, mountable);
-
- /* Optimize some of the tests by avoiding multiple tests of the same thing.
*/
- const int is_dir_etc = guestfs_is_dir (g, "/etc") > 0;
- const int is_dir_bin = guestfs_is_dir (g, "/bin") > 0;
- const int is_dir_share = guestfs_is_dir (g, "/share") > 0;
-
- /* Grub /boot? */
- if (guestfs_is_file (g, "/grub/menu.lst") > 0 ||
- guestfs_is_file (g, "/grub/grub.conf") > 0 ||
- guestfs_is_file (g, "/grub2/grub.cfg") > 0)
- ;
- /* FreeBSD root? */
- else if (is_dir_etc &&
- is_dir_bin &&
- guestfs_is_file (g, "/etc/freebsd-update.conf") > 0
&&
- guestfs_is_file (g, "/etc/fstab") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_freebsd_root (g, fs) == -1)
- return -1;
- }
- /* NetBSD root? */
- else if (is_dir_etc &&
- is_dir_bin &&
- guestfs_is_file (g, "/netbsd") > 0 &&
- guestfs_is_file (g, "/etc/fstab") > 0 &&
- guestfs_is_file (g, "/etc/release") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_netbsd_root (g, fs) == -1)
- return -1;
- }
- /* OpenBSD root? */
- else if (is_dir_etc &&
- is_dir_bin &&
- guestfs_is_file (g, "/bsd") > 0 &&
- guestfs_is_file (g, "/etc/fstab") > 0 &&
- guestfs_is_file (g, "/etc/motd") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_openbsd_root (g, fs) == -1)
- return -1;
- }
- /* Hurd root? */
- else if (guestfs_is_file (g, "/hurd/console") > 0 &&
- guestfs_is_file (g, "/hurd/hello") > 0 &&
- guestfs_is_file (g, "/hurd/null") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED; /* XXX could be more specific */
- if (guestfs_int_check_hurd_root (g, fs) == -1)
- return -1;
- }
- /* Minix root? */
- else if (is_dir_etc &&
- is_dir_bin &&
- guestfs_is_file (g, "/service/vm") > 0 &&
- guestfs_is_file (g, "/etc/fstab") > 0 &&
- guestfs_is_file (g, "/etc/version") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_minix_root (g, fs) == -1)
- return -1;
- }
- /* Linux root? */
- else if (is_dir_etc &&
- (is_dir_bin ||
- is_symlink_to (g, "/bin", "usr/bin") > 0)
&&
- (guestfs_is_file (g, "/etc/fstab") > 0 ||
- guestfs_is_file (g, "/etc/hosts") > 0)) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_linux_root (g, fs) == -1)
- return -1;
- }
- /* CoreOS root? */
- else if (is_dir_etc &&
- guestfs_is_dir (g, "/root") > 0 &&
- guestfs_is_dir (g, "/home") > 0 &&
- guestfs_is_dir (g, "/usr") > 0 &&
- guestfs_is_file (g, "/etc/coreos/update.conf") > 0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_coreos_root (g, fs) == -1)
- return -1;
- }
- /* Linux /usr/local? */
- else if (is_dir_etc &&
- is_dir_bin &&
- is_dir_share &&
- guestfs_is_dir (g, "/local") == 0 &&
- guestfs_is_file (g, "/etc/fstab") == 0)
- ;
- /* Linux /usr? */
- else if (is_dir_etc &&
- is_dir_bin &&
- is_dir_share &&
- guestfs_is_dir (g, "/local") > 0 &&
- guestfs_is_file (g, "/etc/fstab") == 0) {
- if (guestfs_int_check_linux_usr (g, fs) == -1)
- return -1;
- }
- /* CoreOS /usr? */
- else if (is_dir_bin &&
- is_dir_share &&
- guestfs_is_dir (g, "/local") > 0 &&
- guestfs_is_dir (g, "/share/coreos") > 0) {
- if (guestfs_int_check_coreos_usr (g, fs) == -1)
- return -1;
- }
- /* Linux /var? */
- else if (guestfs_is_dir (g, "/log") > 0 &&
- guestfs_is_dir (g, "/run") > 0 &&
- guestfs_is_dir (g, "/spool") > 0)
- ;
- /* Windows root? */
- else if ((windows_systemroot = guestfs_int_get_windows_systemroot (g)) !=
NULL)
- {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- if (guestfs_int_check_windows_root (g, fs, windows_systemroot) == -1)
- return -1;
- }
- /* Windows volume with installed applications (but not root)? */
- else if (guestfs_int_is_dir_nocase (g, "/System Volume
Information") > 0 &&
- guestfs_int_is_dir_nocase (g, "/Program Files") > 0)
- ;
- /* Windows volume (but not root)? */
- else if (guestfs_int_is_dir_nocase (g, "/System Volume
Information") > 0)
- ;
- /* FreeDOS? */
- else if (guestfs_int_is_dir_nocase (g, "/FDOS") > 0 &&
- guestfs_int_is_file_nocase (g, "/FDOS/FREEDOS.BSS") >
0) {
- fs->role = OS_ROLE_ROOT;
- fs->format = OS_FORMAT_INSTALLED;
- fs->type = OS_TYPE_DOS;
- fs->distro = OS_DISTRO_FREEDOS;
- /* FreeDOS is a mix of 16 and 32 bit, but assume it requires a
- * 32 bit i386 processor.
- */
- fs->arch = safe_strdup (g, "i386");
- }
-
- /* The above code should have set fs->type and fs->distro fields, so
- * we can now guess the package management system.
- */
- guestfs_int_check_package_format (g, fs);
- guestfs_int_check_package_management (g, fs);
-
- return 0;
-}
-
-static void
-extend_fses (guestfs_h *g)
-{
- const size_t n = g->nr_fses + 1;
- struct inspect_fs *p;
-
- p = safe_realloc (g, g->fses, n * sizeof (struct inspect_fs));
-
- g->fses = p;
- g->nr_fses = n;
-
- memset (&g->fses[n-1], 0, sizeof (struct inspect_fs));
-}
-
-/* Given a partition (eg. /dev/sda2) then return the partition number
- * (eg. 2) and the total number of other partitions.
- */
-static int
-get_partition_context (guestfs_h *g, const char *partition,
- int *partnum_ret, int *nr_partitions_ret)
-{
- int partnum, nr_partitions;
- CLEANUP_FREE char *device = NULL;
- CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *partitions = NULL;
-
- partnum = guestfs_part_to_partnum (g, partition);
- if (partnum == -1)
- return -1;
-
- device = guestfs_part_to_dev (g, partition);
- if (device == NULL)
- return -1;
-
- partitions = guestfs_part_list (g, device);
- if (partitions == NULL)
- return -1;
-
- nr_partitions = partitions->len;
-
- *partnum_ret = partnum;
- *nr_partitions_ret = nr_partitions;
- return 0;
-}
-
-static int
-is_symlink_to (guestfs_h *g, const char *file, const char *wanted_target)
-{
- CLEANUP_FREE char *target = NULL;
-
- if (guestfs_is_symlink (g, file) == 0)
- return 0;
-
- target = guestfs_readlink (g, file);
- /* This should not fail, but play safe. */
- if (target == NULL)
- return 0;
-
- return STREQ (target, wanted_target);
-}
-
-int
-guestfs_int_is_file_nocase (guestfs_h *g, const char *path)
-{
- CLEANUP_FREE char *p = NULL;
- int r;
-
- p = guestfs_int_case_sensitive_path_silently (g, path);
- if (!p)
- return 0;
- r = guestfs_is_file (g, p);
- return r > 0;
-}
-
-int
-guestfs_int_is_dir_nocase (guestfs_h *g, const char *path)
-{
- CLEANUP_FREE char *p = NULL;
- int r;
-
- p = guestfs_int_case_sensitive_path_silently (g, path);
- if (!p)
- return 0;
- r = guestfs_is_dir (g, p);
- return r > 0;
-}
-
-/* Parse small, unsigned ints, as used in version numbers. */
-int
-guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str)
-{
- long ret;
- const int r = xstrtol (str, NULL, 10, &ret, "");
- if (r != LONGINT_OK) {
- error (g, _("could not parse integer in version number: %s"),
str);
- return -1;
- }
- return ret;
-}
-
-/* Like parse_unsigned_int, but ignore trailing stuff. */
-int
-guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str)
-{
- long ret;
- const int r = xstrtol (str, NULL, 10, &ret, NULL);
- if (r != LONGINT_OK) {
- error (g, _("could not parse integer in version number: %s"),
str);
- return -1;
- }
- return ret;
-}
-
-/* Parse generic MAJOR.MINOR from the fs->product_name string. */
-int
-guestfs_int_parse_major_minor (guestfs_h *g, struct inspect_fs *fs)
-{
- if (guestfs_int_version_from_x_y (g, &fs->version,
fs->product_name) == -1)
- return -1;
-
- return 0;
-}
-
-/* At the moment, package format and package management is just a
- * simple function of the distro and version.v_major fields, so these
- * can never return an error. We might be cleverer in future.
- */
-void
-guestfs_int_check_package_format (guestfs_h *g, struct inspect_fs *fs)
-{
- switch (fs->distro) {
- case OS_DISTRO_FEDORA:
- case OS_DISTRO_MEEGO:
- case OS_DISTRO_REDHAT_BASED:
- case OS_DISTRO_RHEL:
- case OS_DISTRO_MAGEIA:
- case OS_DISTRO_MANDRIVA:
- case OS_DISTRO_SUSE_BASED:
- case OS_DISTRO_OPENSUSE:
- case OS_DISTRO_SLES:
- case OS_DISTRO_CENTOS:
- case OS_DISTRO_SCIENTIFIC_LINUX:
- case OS_DISTRO_ORACLE_LINUX:
- case OS_DISTRO_ALTLINUX:
- fs->package_format = OS_PACKAGE_FORMAT_RPM;
- break;
-
- case OS_DISTRO_DEBIAN:
- case OS_DISTRO_UBUNTU:
- case OS_DISTRO_LINUX_MINT:
- fs->package_format = OS_PACKAGE_FORMAT_DEB;
- break;
-
- case OS_DISTRO_ARCHLINUX:
- fs->package_format = OS_PACKAGE_FORMAT_PACMAN;
- break;
- case OS_DISTRO_GENTOO:
- fs->package_format = OS_PACKAGE_FORMAT_EBUILD;
- break;
- case OS_DISTRO_PARDUS:
- fs->package_format = OS_PACKAGE_FORMAT_PISI;
- break;
-
- case OS_DISTRO_ALPINE_LINUX:
- fs->package_format = OS_PACKAGE_FORMAT_APK;
- break;
-
- case OS_DISTRO_VOID_LINUX:
- fs->package_format = OS_PACKAGE_FORMAT_XBPS;
- break;
-
- case OS_DISTRO_SLACKWARE:
- case OS_DISTRO_TTYLINUX:
- case OS_DISTRO_COREOS:
- case OS_DISTRO_WINDOWS:
- case OS_DISTRO_BUILDROOT:
- case OS_DISTRO_CIRROS:
- case OS_DISTRO_FREEDOS:
- case OS_DISTRO_FREEBSD:
- case OS_DISTRO_NETBSD:
- case OS_DISTRO_OPENBSD:
- case OS_DISTRO_FRUGALWARE:
- case OS_DISTRO_PLD_LINUX:
- case OS_DISTRO_UNKNOWN:
- fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN;
- break;
- }
-}
-
-void
-guestfs_int_check_package_management (guestfs_h *g, struct inspect_fs *fs)
-{
- switch (fs->distro) {
- case OS_DISTRO_MEEGO:
- fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
- break;
-
- case OS_DISTRO_FEDORA:
- /* If Fedora >= 22 and dnf is installed, say "dnf". */
- if (guestfs_int_version_ge (&fs->version, 22, 0, 0) &&
- guestfs_is_file_opts (g, "/usr/bin/dnf",
- GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) >
0)
- fs->package_management = OS_PACKAGE_MANAGEMENT_DNF;
- else if (guestfs_int_version_ge (&fs->version, 1, 0, 0))
- fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
- else
- /* Probably parsing the release file failed, see RHBZ#1332025. */
- fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
- break;
-
- case OS_DISTRO_REDHAT_BASED:
- case OS_DISTRO_RHEL:
- case OS_DISTRO_CENTOS:
- case OS_DISTRO_SCIENTIFIC_LINUX:
- case OS_DISTRO_ORACLE_LINUX:
- if (guestfs_int_version_ge (&fs->version, 5, 0, 0))
- fs->package_management = OS_PACKAGE_MANAGEMENT_YUM;
- else if (guestfs_int_version_ge (&fs->version, 2, 0, 0))
- fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE;
- else
- /* Probably parsing the release file failed, see RHBZ#1332025. */
- fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
- break;
-
- case OS_DISTRO_DEBIAN:
- case OS_DISTRO_UBUNTU:
- case OS_DISTRO_LINUX_MINT:
- case OS_DISTRO_ALTLINUX:
- fs->package_management = OS_PACKAGE_MANAGEMENT_APT;
- break;
-
- case OS_DISTRO_ARCHLINUX:
- fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN;
- break;
- case OS_DISTRO_GENTOO:
- fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE;
- break;
- case OS_DISTRO_PARDUS:
- fs->package_management = OS_PACKAGE_MANAGEMENT_PISI;
- break;
- case OS_DISTRO_MAGEIA:
- case OS_DISTRO_MANDRIVA:
- fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI;
- break;
-
- case OS_DISTRO_SUSE_BASED:
- case OS_DISTRO_OPENSUSE:
- case OS_DISTRO_SLES:
- fs->package_management = OS_PACKAGE_MANAGEMENT_ZYPPER;
- break;
-
- case OS_DISTRO_ALPINE_LINUX:
- fs->package_management = OS_PACKAGE_MANAGEMENT_APK;
- break;
-
- case OS_DISTRO_VOID_LINUX:
- fs->package_management = OS_PACKAGE_MANAGEMENT_XBPS;
- break;
-
- case OS_DISTRO_SLACKWARE:
- case OS_DISTRO_TTYLINUX:
- case OS_DISTRO_COREOS:
- case OS_DISTRO_WINDOWS:
- case OS_DISTRO_BUILDROOT:
- case OS_DISTRO_CIRROS:
- case OS_DISTRO_FREEDOS:
- case OS_DISTRO_FREEBSD:
- case OS_DISTRO_NETBSD:
- case OS_DISTRO_OPENBSD:
- case OS_DISTRO_FRUGALWARE:
- case OS_DISTRO_PLD_LINUX:
- case OS_DISTRO_UNKNOWN:
- fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN;
- break;
- }
-}
-
-/* Get the first line of a small file, without any trailing newline
- * character.
- *
- * NOTE: If the file is completely empty or begins with a '\n'
- * character, this returns an empty string (not NULL). The caller
- * will usually need to check for this case.
- */
-char *
-guestfs_int_first_line_of_file (guestfs_h *g, const char *filename)
-{
- char **lines = NULL; /* sic: not CLEANUP_FREE_STRING_LIST */
- int64_t size;
- char *ret;
-
- /* Don't trust guestfs_head_n not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return NULL;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return NULL;
- }
-
- lines = guestfs_head_n (g, 1, filename);
- if (lines == NULL)
- return NULL;
- if (lines[0] == NULL) {
- guestfs_int_free_string_list (lines);
- /* Empty file: Return an empty string as explained above. */
- return safe_strdup (g, "");
- }
- /* lines[1] should be NULL because of '1' argument above ... */
-
- ret = lines[0]; /* caller frees */
-
- free (lines);
-
- return ret;
-}
-
-/* Get the first matching line (using egrep [-i]) of a small file,
- * without any trailing newline character.
- *
- * Returns: 1 = returned a line (in *ret)
- * 0 = no match
- * -1 = error
- */
-int
-guestfs_int_first_egrep_of_file (guestfs_h *g, const char *filename,
- const char *eregex, int iflag, char **ret)
-{
- char **lines;
- int64_t size;
- size_t i;
- struct guestfs_grep_opts_argv optargs;
-
- /* Don't trust guestfs_grep not to break with very large files.
- * Check the file size is something reasonable first.
- */
- size = guestfs_filesize (g, filename);
- if (size == -1)
- /* guestfs_filesize failed and has already set error in handle */
- return -1;
- if (size > MAX_SMALL_FILE_SIZE) {
- error (g, _("size of %s is unreasonably large (%" PRIi64 "
bytes)"),
- filename, size);
- return -1;
- }
-
- optargs.bitmask = GUESTFS_GREP_OPTS_EXTENDED_BITMASK;
- optargs.extended = 1;
- if (iflag) {
- optargs.bitmask |= GUESTFS_GREP_OPTS_INSENSITIVE_BITMASK;
- optargs.insensitive = 1;
- }
- lines = guestfs_grep_opts_argv (g, eregex, filename, &optargs);
- if (lines == NULL)
- return -1;
- if (lines[0] == NULL) {
- guestfs_int_free_string_list (lines);
- return 0;
- }
-
- *ret = lines[0]; /* caller frees */
-
- /* free up any other matches and the array itself */
- for (i = 1; lines[i] != NULL; ++i)
- free (lines[i]);
- free (lines);
-
- return 1;
-}
-
-/* Merge the missing OS inspection information found on the src inspect_fs into
- * the ones of the dst inspect_fs. This function is useful if the inspection
- * information for an OS are gathered by inspecting multiple filesystems.
- */
-void
-guestfs_int_merge_fs_inspections (guestfs_h *g, struct inspect_fs *dst, struct
inspect_fs *src)
-{
- size_t n, i, old;
- struct inspect_fstab_entry *fstab = NULL;
- char ** mappings = NULL;
-
- if (dst->type == 0)
- dst->type = src->type;
-
- if (dst->distro == 0)
- dst->distro = src->distro;
-
- if (dst->package_format == 0)
- dst->package_format = src->package_format;
-
- if (dst->package_management == 0)
- dst->package_management = src->package_management;
-
- if (dst->product_name == NULL) {
- dst->product_name = src->product_name;
- src->product_name = NULL;
- }
-
- if (dst->product_variant == NULL) {
- dst->product_variant= src->product_variant;
- src->product_variant = NULL;
- }
-
- if (version_is_null (&dst->version))
- dst->version = src->version;
-
- if (dst->arch == NULL) {
- dst->arch = src->arch;
- src->arch = NULL;
- }
-
- if (dst->hostname == NULL) {
- dst->hostname = src->hostname;
- src->hostname = NULL;
- }
-
- if (dst->windows_systemroot == NULL) {
- dst->windows_systemroot = src->windows_systemroot;
- src->windows_systemroot = NULL;
- }
-
- if (dst->windows_current_control_set == NULL) {
- dst->windows_current_control_set = src->windows_current_control_set;
- src->windows_current_control_set = NULL;
- }
-
- if (src->drive_mappings != NULL) {
- if (dst->drive_mappings == NULL) {
- /* Adopt the drive mappings of src */
- dst->drive_mappings = src->drive_mappings;
- src->drive_mappings = NULL;
- } else {
- n = 0;
- for (; dst->drive_mappings[n] != NULL; n++)
- ;
- old = n;
- for (; src->drive_mappings[n] != NULL; n++)
- ;
-
- /* Merge the src mappings to dst */
- mappings = safe_realloc (g, dst->drive_mappings,(n + 1) * sizeof (char
*));
-
- for (i = old; i < n; i++)
- mappings[i] = src->drive_mappings[i - old];
-
- mappings[n] = NULL;
- dst->drive_mappings = mappings;
-
- free(src->drive_mappings);
- src->drive_mappings = NULL;
- }
- }
-
- if (src->nr_fstab > 0) {
- n = dst->nr_fstab + src->nr_fstab;
- fstab = safe_realloc (g, dst->fstab, n * sizeof (struct
inspect_fstab_entry));
-
- for (i = 0; i < src->nr_fstab; i++) {
- fstab[dst->nr_fstab + i].mountable = src->fstab[i].mountable;
- fstab[dst->nr_fstab + i].mountpoint = src->fstab[i].mountpoint;
- }
- free(src->fstab);
- src->fstab = NULL;
- src->nr_fstab = 0;
-
- dst->fstab = fstab;
- dst->nr_fstab = n;
- }
-}
diff --git a/lib/inspect-icon.c b/lib/inspect-icon.c
index 89c232f5b..f4f5f0660 100644
--- a/lib/inspect-icon.c
+++ b/lib/inspect-icon.c
@@ -51,22 +51,24 @@
* An icon was found. 'ret' points to the icon buffer, and *size_r
* is the size.
*/
-static char *icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r);
-static char *icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
-static char *icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
-static char *icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
-static char *icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
-static char *icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
-static char *icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r);
+static char *icon_favicon (guestfs_h *g, const char *type, size_t *size_r);
+static char *icon_fedora (guestfs_h *g, size_t *size_r);
+static char *icon_rhel (guestfs_h *g, int major, size_t *size_r);
+static char *icon_debian (guestfs_h *g, size_t *size_r);
+static char *icon_ubuntu (guestfs_h *g, size_t *size_r);
+static char *icon_mageia (guestfs_h *g, size_t *size_r);
+static char *icon_opensuse (guestfs_h *g, size_t *size_r);
#if CAN_DO_CIRROS
-static char *icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+static char *icon_cirros (guestfs_h *g, size_t *size_r);
#endif
-static char *icon_voidlinux (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r);
-static char *icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r);
+static char *icon_voidlinux (guestfs_h *g, size_t *size_r);
+static char *icon_altlinux (guestfs_h *g, size_t *size_r);
#if CAN_DO_WINDOWS
-static char *icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r);
+static char *icon_windows (guestfs_h *g, const char *root, size_t *size_r);
#endif
+static char *case_sensitive_path_silently (guestfs_h *g, const char *path);
+
/* Dummy static object. */
static char *NOT_FOUND = (char *) "not_found";
@@ -82,13 +84,17 @@ char *
guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r,
const struct guestfs_inspect_get_icon_argv *optargs)
{
- struct inspect_fs *fs;
char *r = NOT_FOUND;
int favicon, highquality;
size_t size;
+ CLEANUP_FREE char *type = NULL;
+ CLEANUP_FREE char *distro = NULL;
- fs = guestfs_int_search_for_root (g, root);
- if (!fs)
+ type = guestfs_inspect_get_type (g, root);
+ if (!type)
+ return NULL;
+ distro = guestfs_inspect_get_distro (g, root);
+ if (!distro)
return NULL;
/* Get optargs, or defaults. */
@@ -106,7 +112,7 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char
*root, size_t *size_r,
/* Try looking for a favicon first. */
if (favicon) {
- r = icon_favicon (g, fs, &size);
+ r = icon_favicon (g, type, &size);
if (!r)
return NULL;
@@ -120,96 +126,52 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char
*root, size_t *size_r,
/* Favicon failed, so let's try a method based on the detected operating
* system.
*/
- switch (fs->type) {
- case OS_TYPE_LINUX:
- case OS_TYPE_HURD:
- switch (fs->distro) {
- case OS_DISTRO_FEDORA:
- r = icon_fedora (g, fs, &size);
- break;
-
- case OS_DISTRO_RHEL:
- case OS_DISTRO_REDHAT_BASED:
- case OS_DISTRO_CENTOS:
- case OS_DISTRO_SCIENTIFIC_LINUX:
- case OS_DISTRO_ORACLE_LINUX:
- r = icon_rhel (g, fs, &size);
- break;
-
- case OS_DISTRO_DEBIAN:
- r = icon_debian (g, fs, &size);
- break;
-
- case OS_DISTRO_UBUNTU:
+ if (STREQ (type, "linux") || STREQ (type, "hurd")) {
+ if (STREQ (distro, "fedora")) {
+ r = icon_fedora (g, &size);
+ }
+ else if (STREQ (distro, "rhel") ||
+ STREQ (distro, "redhat-based") ||
+ STREQ (distro, "centos") ||
+ STREQ (distro, "scientificlinux") ||
+ STREQ (distro, "oraclelinux")) {
+ r = icon_rhel (g, guestfs_inspect_get_major_version (g, root),
&size);
+ }
+ else if (STREQ (distro, "debian")) {
+ r = icon_debian (g, &size);
+ }
+ else if (STREQ (distro, "ubuntu")) {
if (!highquality)
- r = icon_ubuntu (g, fs, &size);
- break;
-
- case OS_DISTRO_MAGEIA:
- r = icon_mageia (g, fs, &size);
- break;
-
- case OS_DISTRO_SUSE_BASED:
- case OS_DISTRO_OPENSUSE:
- case OS_DISTRO_SLES:
- r = icon_opensuse (g, fs, &size);
- break;
-
- case OS_DISTRO_CIRROS:
+ r = icon_ubuntu (g, &size);
+ }
+ else if (STREQ (distro, "mageia")) {
+ r = icon_mageia (g, &size);
+ }
+ else if (STREQ (distro, "suse-based") ||
+ STREQ (distro, "opensuse") ||
+ STREQ (distro, "sles")) {
+ r = icon_opensuse (g, &size);
+ }
+ else if (STREQ (distro, "cirros")) {
#if CAN_DO_CIRROS
- r = icon_cirros (g, fs, &size);
+ r = icon_cirros (g, &size);
#endif
- break;
-
- case OS_DISTRO_VOID_LINUX:
- r = icon_voidlinux (g, fs, &size);
- break;
-
- case OS_DISTRO_ALTLINUX:
- r = icon_altlinux (g, fs, &size);
- break;
-
- /* These are just to keep gcc warnings happy. */
- case OS_DISTRO_ARCHLINUX:
- case OS_DISTRO_BUILDROOT:
- case OS_DISTRO_COREOS:
- case OS_DISTRO_FREEDOS:
- case OS_DISTRO_GENTOO:
- case OS_DISTRO_LINUX_MINT:
- case OS_DISTRO_MANDRIVA:
- case OS_DISTRO_MEEGO:
- case OS_DISTRO_PARDUS:
- case OS_DISTRO_SLACKWARE:
- case OS_DISTRO_TTYLINUX:
- case OS_DISTRO_WINDOWS:
- case OS_DISTRO_FREEBSD:
- case OS_DISTRO_NETBSD:
- case OS_DISTRO_OPENBSD:
- case OS_DISTRO_ALPINE_LINUX:
- case OS_DISTRO_FRUGALWARE:
- case OS_DISTRO_PLD_LINUX:
- case OS_DISTRO_UNKNOWN:
- ; /* nothing */
}
- break;
-
- case OS_TYPE_WINDOWS:
+ else if (STREQ (distro, "voidlinux")) {
+ r = icon_voidlinux (g, &size);
+ }
+ else if (STREQ (distro, "altlinux")) {
+ r = icon_altlinux (g, &size);
+ }
+ }
+ else if (STREQ (type, "windows")) {
#if CAN_DO_WINDOWS
/* We don't know how to get high quality icons from a Windows guest,
* so disable this if high quality was specified.
*/
if (!highquality)
- r = icon_windows (g, fs, &size);
+ r = icon_windows (g, root, &size);
#endif
- break;
-
- case OS_TYPE_FREEBSD:
- case OS_TYPE_NETBSD:
- case OS_TYPE_DOS:
- case OS_TYPE_OPENBSD:
- case OS_TYPE_MINIX:
- case OS_TYPE_UNKNOWN:
- ; /* nothing */
}
if (r == NOT_FOUND) {
@@ -229,8 +191,7 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char
*root, size_t *size_r,
* If it is, download and return it.
*/
static char *
-get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename,
- size_t *size_r, size_t max_size)
+get_png (guestfs_h *g, const char *filename, size_t *size_r, size_t max_size)
{
char *ret;
CLEANUP_FREE char *real = NULL;
@@ -270,7 +231,7 @@ get_png (guestfs_h *g, struct inspect_fs *fs, const char
*filename,
if (max_size == 0)
max_size = 4 * w * h;
- local = guestfs_int_download_to_tmp (g, fs, real, "icon",
max_size);
+ local = guestfs_int_download_to_tmp (g, real, "icon", max_size);
if (!local)
return NOT_FOUND;
@@ -285,20 +246,20 @@ get_png (guestfs_h *g, struct inspect_fs *fs, const char
*filename,
* it has a reasonable size and format.
*/
static char *
-icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_favicon (guestfs_h *g, const char *type, size_t *size_r)
{
char *ret;
char *filename = safe_strdup (g, "/etc/favicon.png");
- if (fs->type == OS_TYPE_WINDOWS) {
- char *f = guestfs_int_case_sensitive_path_silently (g, filename);
+ if (STREQ (type, "windows")) {
+ char *f = case_sensitive_path_silently (g, filename);
if (f) {
free (filename);
filename = f;
}
}
- ret = get_png (g, fs, filename, size_r, 0);
+ ret = get_png (g, filename, size_r, 0);
free (filename);
return ret;
}
@@ -309,9 +270,9 @@ icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
#define FEDORA_ICON
"/usr/share/icons/hicolor/96x96/apps/fedora-logo-icon.png"
static char *
-icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_fedora (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, FEDORA_ICON, size_r, 0);
+ return get_png (g, FEDORA_ICON, size_r, 0);
}
/* RHEL 3, 4:
@@ -330,28 +291,28 @@ icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
* RHEL clones have different sizes.
*/
static char *
-icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_rhel (guestfs_h *g, int major, size_t *size_r)
{
const char *shadowman;
- if (!guestfs_int_version_ge (&fs->version, 7, 0, 0))
+ if (major < 7)
shadowman =
"/usr/share/pixmaps/redhat/shadowman-transparent.png";
else
shadowman = "/usr/share/pixmaps/fedora-logo-sprite.png";
- return get_png (g, fs, shadowman, size_r, 102400);
+ return get_png (g, shadowman, size_r, 102400);
}
#define DEBIAN_ICON "/usr/share/pixmaps/debian-logo.png"
static char *
-icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_debian (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, DEBIAN_ICON, size_r, 2048);
+ return get_png (g, DEBIAN_ICON, size_r, 2048);
}
static char *
-icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_ubuntu (guestfs_h *g, size_t *size_r)
{
const char *icons[] = {
"/usr/share/icons/gnome/24x24/places/ubuntu-logo.png",
@@ -366,7 +327,7 @@ icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
char *ret;
for (i = 0; icons[i] != NULL; ++i) {
- ret = get_png (g, fs, icons[i], size_r, 2048);
+ ret = get_png (g, icons[i], size_r, 2048);
if (ret == NULL)
return NULL;
if (ret != NOT_FOUND)
@@ -378,17 +339,17 @@ icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
#define MAGEIA_ICON "/usr/share/icons/mageia.png"
static char *
-icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_mageia (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, MAGEIA_ICON, size_r, 2048);
+ return get_png (g, MAGEIA_ICON, size_r, 2048);
}
#define OPENSUSE_ICON
"/usr/share/icons/hicolor/24x24/apps/distributor.png"
static char *
-icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_opensuse (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, OPENSUSE_ICON, size_r, 2048);
+ return get_png (g, OPENSUSE_ICON, size_r, 2048);
}
#if CAN_DO_CIRROS
@@ -397,7 +358,7 @@ icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
#define CIRROS_LOGO "/usr/share/cirros/logo"
static char *
-icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_cirros (guestfs_h *g, size_t *size_r)
{
char *ret;
CLEANUP_FREE char *type = NULL;
@@ -421,7 +382,7 @@ icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
if (!STRPREFIX (type, "ASCII text"))
return NOT_FOUND;
- local = guestfs_int_download_to_tmp (g, fs, CIRROS_LOGO, "icon",
1024);
+ local = guestfs_int_download_to_tmp (g, CIRROS_LOGO, "icon", 1024);
if (!local)
return NOT_FOUND;
@@ -450,17 +411,17 @@ icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
#define VOIDLINUX_ICON "/usr/share/void-artwork/void-logo.png"
static char *
-icon_voidlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_voidlinux (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, VOIDLINUX_ICON, size_r, 20480);
+ return get_png (g, VOIDLINUX_ICON, size_r, 20480);
}
#define ALTLINUX_ICON
"/usr/share/icons/hicolor/48x48/apps/altlinux.png"
static char *
-icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_altlinux (guestfs_h *g, size_t *size_r)
{
- return get_png (g, fs, ALTLINUX_ICON, size_r, 20480);
+ return get_png (g, ALTLINUX_ICON, size_r, 20480);
}
#if CAN_DO_WINDOWS
@@ -481,7 +442,7 @@ icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
*/
static char *
-icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_windows_xp (guestfs_h *g, const char *systemroot, size_t *size_r)
{
CLEANUP_FREE char *filename = NULL;
CLEANUP_FREE char *filename_case = NULL;
@@ -492,7 +453,7 @@ icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
char *ret;
/* Download %systemroot%\explorer.exe */
- filename = safe_asprintf (g, "%s/explorer.exe",
fs->windows_systemroot);
+ filename = safe_asprintf (g, "%s/explorer.exe", systemroot);
filename_case = guestfs_case_sensitive_path (g, filename);
if (filename_case == NULL)
return NULL;
@@ -505,7 +466,7 @@ icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
if (r == 0)
return NOT_FOUND;
- filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case,
+ filename_downloaded = guestfs_int_download_to_tmp (g, filename_case,
"explorer.exe",
MAX_WINDOWS_EXPLORER_SIZE);
if (filename_downloaded == NULL)
@@ -543,7 +504,7 @@ static const char *win7_explorer[] = {
};
static char *
-icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_windows_7 (guestfs_h *g, const char *systemroot, size_t *size_r)
{
size_t i;
CLEANUP_FREE char *filename_case = NULL;
@@ -556,11 +517,10 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs,
size_t *size_r)
for (i = 0; win7_explorer[i] != NULL; ++i) {
CLEANUP_FREE char *filename = NULL;
- filename = safe_asprintf (g, "%s/%s",
- fs->windows_systemroot, win7_explorer[i]);
+ filename = safe_asprintf (g, "%s/%s", systemroot,
win7_explorer[i]);
free (filename_case);
- filename_case = guestfs_int_case_sensitive_path_silently (g, filename);
+ filename_case = case_sensitive_path_silently (g, filename);
if (filename_case == NULL)
continue;
@@ -575,7 +535,7 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
if (win7_explorer[i] == NULL)
return NOT_FOUND;
- filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case,
+ filename_downloaded = guestfs_int_download_to_tmp (g, filename_case,
"explorer.exe",
MAX_WINDOWS_EXPLORER_SIZE);
if (filename_downloaded == NULL)
@@ -609,14 +569,14 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs,
size_t *size_r)
* - /Windows/System32/slui.exe --type=14 group icon #2
*/
static char *
-icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_windows_8 (guestfs_h *g, size_t *size_r)
{
CLEANUP_FREE char *filename_case = NULL;
CLEANUP_FREE char *filename_downloaded = NULL;
int r;
char *ret;
- filename_case = guestfs_int_case_sensitive_path_silently
+ filename_case = case_sensitive_path_silently
(g, "/ProgramData/Microsoft/Windows Live/WLive48x48.png");
if (filename_case == NULL)
return NOT_FOUND; /* Not an error since a parent dir might not exist. */
@@ -629,7 +589,7 @@ icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t
*size_r)
if (r == 0)
return NOT_FOUND;
- filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case,
+ filename_downloaded = guestfs_int_download_to_tmp (g, filename_case,
"wlive48x48.png", 8192);
if (filename_downloaded == NULL)
return NOT_FOUND;
@@ -641,25 +601,46 @@ icon_windows_8 (guestfs_h *g, struct inspect_fs *fs,
size_t *size_r)
}
static char *
-icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+icon_windows (guestfs_h *g, const char *root, size_t *size_r)
{
- if (fs->windows_systemroot == NULL)
+ CLEANUP_FREE char *systemroot + guestfs_inspect_get_windows_systemroot (g,
root);
+ int major = guestfs_inspect_get_major_version (g, root);
+ int minor = guestfs_inspect_get_minor_version (g, root);
+
+ if (systemroot == NULL)
return NOT_FOUND;
/* Windows XP. */
- if (fs->version.v_major == 5 && fs->version.v_minor == 1)
- return icon_windows_xp (g, fs, size_r);
+ if (major == 5 && minor == 1)
+ return icon_windows_xp (g, systemroot, size_r);
/* Windows 7. */
- else if (fs->version.v_major == 6 && fs->version.v_minor == 1)
- return icon_windows_7 (g, fs, size_r);
+ else if (major == 6 && minor == 1)
+ return icon_windows_7 (g, systemroot, size_r);
/* Windows 8. */
- else if (fs->version.v_major == 6 && fs->version.v_minor == 2)
- return icon_windows_8 (g, fs, size_r);
+ else if (major == 6 && minor == 2)
+ return icon_windows_8 (g, size_r);
/* Not (yet) a supported version of Windows. */
else return NOT_FOUND;
}
#endif /* CAN_DO_WINDOWS */
+
+/* NB: This function DOES NOT test for the existence of the file. It
+ * will return non-NULL even if the file/directory does not exist.
+ * You have to call guestfs_is_file{,_opts} etc.
+ */
+static char *
+case_sensitive_path_silently (guestfs_h *g, const char *path)
+{
+ char *ret;
+
+ guestfs_push_error_handler (g, NULL, NULL);
+ ret = guestfs_case_sensitive_path (g, path);
+ guestfs_pop_error_handler (g);
+
+ return ret;
+}
diff --git a/lib/inspect.c b/lib/inspect.c
index 1cc0942f1..f2d64b61e 100644
--- a/lib/inspect.c
+++ b/lib/inspect.c
@@ -43,688 +43,6 @@
#include "guestfs-internal.h"
#include "guestfs-internal-actions.h"
-COMPILE_REGEXP (re_primary_partition, "^/dev/(?:h|s|v)d.[1234]$", 0)
-
-static void check_for_duplicated_bsd_root (guestfs_h *g);
-static void collect_coreos_inspection_info (guestfs_h *g);
-static void collect_linux_inspection_info (guestfs_h *g);
-static void collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs
*root);
-
-/**
- * The main inspection API.
- */
-char **
-guestfs_impl_inspect_os (guestfs_h *g)
-{
- CLEANUP_FREE_STRING_LIST char **fses = NULL;
- char **fs, **ret;
-
- /* Remove any information previously stored in the handle. */
- guestfs_int_free_inspect_info (g);
-
- if (guestfs_umount_all (g) == -1)
- return NULL;
-
- /* Iterate over all detected filesystems. Inspect each one in turn
- * and add that information to the handle.
- */
-
- fses = guestfs_list_filesystems (g);
- if (fses == NULL) return NULL;
-
- for (fs = fses; *fs; fs += 2) {
- if (guestfs_int_check_for_filesystem_on (g, *fs)) {
- guestfs_int_free_inspect_info (g);
- return NULL;
- }
- }
-
- /* The OS inspection information for CoreOS are gathered by inspecting
- * multiple filesystems. Gather all the inspected information in the
- * inspect_fs struct of the root filesystem.
- */
- collect_coreos_inspection_info (g);
-
- /* Check if the same filesystem was listed twice as root in g->fses.
- * This may happen for the *BSD root partition where an MBR partition
- * is a shadow of the real root partition probably /dev/sda5
- */
- check_for_duplicated_bsd_root (g);
-
- /* For Linux guests with a separate /usr filesyste, merge some of the
- * inspected information in that partition to the inspect_fs struct
- * of the root filesystem.
- */
- collect_linux_inspection_info (g);
-
- /* At this point we have, in the handle, a list of all filesystems
- * found and data about each one. Now we assemble the list of
- * filesystems which are root devices and return that to the user.
- * Fall through to guestfs_inspect_get_roots to do that.
- */
- ret = guestfs_inspect_get_roots (g);
- if (ret == NULL)
- guestfs_int_free_inspect_info (g);
- return ret;
-}
-
-/**
- * Traverse through the filesystem list and find out if it contains
- * the C</> and C</usr> filesystems of a CoreOS image. If this is
the
- * case, sum up all the collected information on the root fs.
- */
-static void
-collect_coreos_inspection_info (guestfs_h *g)
-{
- size_t i;
- struct inspect_fs *root = NULL, *usr = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro == OS_DISTRO_COREOS && fs->role ==
OS_ROLE_ROOT)
- root = fs;
- }
-
- if (root == NULL)
- return;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro != OS_DISTRO_COREOS || fs->role != OS_ROLE_USR)
- continue;
-
- /* CoreOS is designed to contain 2 /usr partitions (USR-A, USR-B):
- * https://coreos.com/docs/sdk-distributors/sdk/disk-partitions/
- * One is active and one passive. During the initial boot, the passive
- * partition is empty and it gets filled up when an update is performed.
- * Then, when the system reboots, the boot loader is instructed to boot
- * from the passive partition. If both partitions are valid, we cannot
- * determine which the active and which the passive is, unless we peep into
- * the boot loader. As a workaround, we check the OS versions and pick the
- * one with the higher version as active.
- */
- if (usr && guestfs_int_version_cmp_ge (&usr->version,
&fs->version))
- continue;
-
- usr = fs;
- }
-
- if (usr == NULL)
- return;
-
- guestfs_int_merge_fs_inspections (g, root, usr);
-}
-
-/**
- * Traverse through the filesystems and find the /usr filesystem for
- * the specified C<root>: if found, merge its basic inspection details
- * to the root when they were set (i.e. because the /usr had os-release
- * or other ways to identify the OS).
- */
-static void
-collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs *root)
-{
- size_t i;
- struct inspect_fs *usr = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
- size_t j;
-
- if (!(fs->distro == root->distro || fs->distro ==
OS_DISTRO_UNKNOWN) ||
- fs->role != OS_ROLE_USR)
- continue;
-
- for (j = 0; j < root->nr_fstab; ++j) {
- if (STREQ (fs->mountable, root->fstab[j].mountable)) {
- usr = fs;
- goto got_usr;
- }
- }
- }
-
- assert (usr == NULL);
- return;
-
- got_usr:
- /* If the version information in /usr is not null, then most probably
- * there was an os-release file there, so reset what is in root
- * and pick the results from /usr.
- */
- if (!version_is_null (&usr->version)) {
- root->distro = OS_DISTRO_UNKNOWN;
- free (root->product_name);
- root->product_name = NULL;
- }
-
- guestfs_int_merge_fs_inspections (g, root, usr);
-}
-
-/**
- * Traverse through the filesystem list and find out if it contains
- * the C</> and C</usr> filesystems of a Linux image (but not
CoreOS,
- * for which there is a separate C<collect_coreos_inspection_info>).
- * If this is the case, sum up all the collected information on each
- * root fs from the respective /usr filesystems.
- */
-static void
-collect_linux_inspection_info (guestfs_h *g)
-{
- size_t i;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro != OS_DISTRO_COREOS && fs->role ==
OS_ROLE_ROOT)
- collect_linux_inspection_info_for (g, fs);
- }
-}
-
-/**
- * On *BSD systems, sometimes F</dev/sda[1234]> is a shadow of the
- * real root filesystem that is probably F</dev/sda5> (see:
- * L<http://www.freebsd.org/doc/handbook/disk-organization.html>)
- */
-static void
-check_for_duplicated_bsd_root (guestfs_h *g)
-{
- size_t i;
- struct inspect_fs *bsd_primary = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- bool is_bsd;
- struct inspect_fs *fs = &g->fses[i];
-
- is_bsd - fs->type == OS_TYPE_FREEBSD ||
- fs->type == OS_TYPE_NETBSD ||
- fs->type == OS_TYPE_OPENBSD;
-
- if (fs->role == OS_ROLE_ROOT && is_bsd &&
- match (g, fs->mountable, re_primary_partition)) {
- bsd_primary = fs;
- continue;
- }
-
- if (fs->role == OS_ROLE_ROOT && bsd_primary &&
- bsd_primary->type == fs->type) {
- /* remove the root role from the bsd_primary */
- bsd_primary->role = OS_ROLE_UNKNOWN;
- bsd_primary->format = OS_FORMAT_UNKNOWN;
- return;
- }
- }
-}
-
-static int
-compare_strings (const void *vp1, const void *vp2)
-{
- const char *s1 = * (char * const *) vp1;
- const char *s2 = * (char * const *) vp2;
-
- return strcmp (s1, s2);
-}
-
-char **
-guestfs_impl_inspect_get_roots (guestfs_h *g)
-{
- size_t i;
- DECLARE_STRINGSBUF (ret);
-
- /* NB. Doesn't matter if g->nr_fses == 0. We just return an empty
- * list in this case.
- */
- for (i = 0; i < g->nr_fses; ++i) {
- if (g->fses[i].role == OS_ROLE_ROOT)
- guestfs_int_add_string (g, &ret, g->fses[i].mountable);
- }
- guestfs_int_end_stringsbuf (g, &ret);
-
- qsort (ret.argv, ret.size-1, sizeof (char *), compare_strings);
-
- return ret.argv;
-}
-
-char *
-guestfs_impl_inspect_get_type (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- char *ret = NULL;
-
- if (!fs)
- return NULL;
-
- switch (fs->type) {
- case OS_TYPE_DOS: ret = safe_strdup (g, "dos"); break;
- case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
- case OS_TYPE_HURD: ret = safe_strdup (g, "hurd"); break;
- case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
- case OS_TYPE_MINIX: ret = safe_strdup (g, "minix"); break;
- case OS_TYPE_NETBSD: ret = safe_strdup (g, "netbsd"); break;
- case OS_TYPE_OPENBSD: ret = safe_strdup (g, "openbsd"); break;
- case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
- case OS_TYPE_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_arch (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->arch ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_distro (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- char *ret = NULL;
-
- if (!fs)
- return NULL;
-
- switch (fs->distro) {
- case OS_DISTRO_ALPINE_LINUX: ret = safe_strdup (g, "alpinelinux");
break;
- case OS_DISTRO_ALTLINUX: ret = safe_strdup (g, "altlinux"); break;
- case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux");
break;
- case OS_DISTRO_BUILDROOT: ret = safe_strdup (g, "buildroot");
break;
- case OS_DISTRO_CENTOS: ret = safe_strdup (g, "centos"); break;
- case OS_DISTRO_CIRROS: ret = safe_strdup (g, "cirros"); break;
- case OS_DISTRO_COREOS: ret = safe_strdup (g, "coreos"); break;
- case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
- case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
- case OS_DISTRO_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
- case OS_DISTRO_FREEDOS: ret = safe_strdup (g, "freedos"); break;
- case OS_DISTRO_FRUGALWARE: ret = safe_strdup (g, "frugalware");
break;
- case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
- case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint");
break;
- case OS_DISTRO_MAGEIA: ret = safe_strdup (g, "mageia"); break;
- case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
- case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
- case OS_DISTRO_NETBSD: ret = safe_strdup (g, "netbsd"); break;
- case OS_DISTRO_OPENBSD: ret = safe_strdup (g, "openbsd"); break;
- case OS_DISTRO_OPENSUSE: ret = safe_strdup (g, "opensuse"); break;
- case OS_DISTRO_ORACLE_LINUX: ret = safe_strdup (g, "oraclelinux");
break;
- case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
- case OS_DISTRO_PLD_LINUX: ret = safe_strdup (g, "pldlinux"); break;
- case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based");
break;
- case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
- case OS_DISTRO_SCIENTIFIC_LINUX: ret = safe_strdup (g,
"scientificlinux"); break;
- case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware");
break;
- case OS_DISTRO_SLES: ret = safe_strdup (g, "sles"); break;
- case OS_DISTRO_SUSE_BASED: ret = safe_strdup (g, "suse-based");
break;
- case OS_DISTRO_TTYLINUX: ret = safe_strdup (g, "ttylinux"); break;
- case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
- case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
- case OS_DISTRO_VOID_LINUX: ret = safe_strdup (g, "voidlinux");
break;
- case OS_DISTRO_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-int
-guestfs_impl_inspect_get_major_version (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->version.v_major;
-}
-
-int
-guestfs_impl_inspect_get_minor_version (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->version.v_minor;
-}
-
-char *
-guestfs_impl_inspect_get_product_name (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->product_name ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_product_variant (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->product_variant ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_windows_systemroot (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_systemroot) {
- error (g, _("not a Windows guest, or systemroot could not be
determined"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_systemroot);
-}
-
-char *
-guestfs_impl_inspect_get_windows_software_hive (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_software_hive) {
- error (g, _("not a Windows guest, or software hive not found"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_software_hive);
-}
-
-char *
-guestfs_impl_inspect_get_windows_system_hive (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_system_hive) {
- error (g, _("not a Windows guest, or system hive not found"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_system_hive);
-}
-
-char *
-guestfs_impl_inspect_get_windows_current_control_set (guestfs_h *g,
- const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_current_control_set) {
- error (g, _("not a Windows guest, or CurrentControlSet could not be
determined"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_current_control_set);
-}
-
-char *
-guestfs_impl_inspect_get_format (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->format) {
- case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed");
break;
- case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer");
break;
- case OS_FORMAT_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-int
-guestfs_impl_inspect_is_live (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_live_disk;
-}
-
-int
-guestfs_impl_inspect_is_netinst (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_netinst_disk;
-}
-
-int
-guestfs_impl_inspect_is_multipart (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_multipart_disk;
-}
-
-char **
-guestfs_impl_inspect_get_mountpoints (guestfs_h *g, const char *root)
-{
- char **ret;
- size_t i, count, nr;
- struct inspect_fs *fs;
-
- fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
-#define CRITERION(fs, i) fs->fstab[i].mountpoint[0] == '/'
-
- nr = fs->nr_fstab;
-
- if (nr == 0)
- count = 1;
- else {
- count = 0;
- for (i = 0; i < nr; ++i)
- if (CRITERION (fs, i))
- count++;
- }
-
- /* Hashtables have 2N+1 entries. */
- ret = calloc (2*count+1, sizeof (char *));
- if (ret == NULL) {
- perrorf (g, "calloc");
- return NULL;
- }
-
- /* If no fstab information (Windows) return just the root. */
- if (nr == 0) {
- ret[0] = safe_strdup (g, "/");
- ret[1] = safe_strdup (g, root);
- ret[2] = NULL;
- return ret;
- }
-
- count = 0;
- for (i = 0; i < nr; ++i)
- if (CRITERION (fs, i)) {
- ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
- ret[2*count+1] = safe_strdup (g, fs->fstab[i].mountable);
- count++;
- }
-#undef CRITERION
-
- return ret;
-}
-
-char **
-guestfs_impl_inspect_get_filesystems (guestfs_h *g, const char *root)
-{
- char **ret;
- size_t i, nr;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
-
- if (!fs)
- return NULL;
-
- nr = fs->nr_fstab;
- ret = calloc (nr == 0 ? 2 : nr+1, sizeof (char *));
- if (ret == NULL) {
- perrorf (g, "calloc");
- return NULL;
- }
-
- /* If no fstab information (Windows) return just the root. */
- if (nr == 0) {
- ret[0] = safe_strdup (g, root);
- ret[1] = NULL;
- return ret;
- }
-
- for (i = 0; i < nr; ++i)
- ret[i] = safe_strdup (g, fs->fstab[i].mountable);
-
- return ret;
-}
-
-char **
-guestfs_impl_inspect_get_drive_mappings (guestfs_h *g, const char *root)
-{
- DECLARE_STRINGSBUF (ret);
- size_t i;
- struct inspect_fs *fs;
-
- fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (fs->drive_mappings) {
- for (i = 0; fs->drive_mappings[i] != NULL; ++i)
- guestfs_int_add_string (g, &ret, fs->drive_mappings[i]);
- }
-
- guestfs_int_end_stringsbuf (g, &ret);
- return ret.argv;
-}
-
-char *
-guestfs_impl_inspect_get_package_format (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->package_format) {
- case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
- case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
- case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman");
break;
- case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild");
break;
- case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
- case OS_PACKAGE_FORMAT_PKGSRC: ret = safe_strdup (g, "pkgsrc");
break;
- case OS_PACKAGE_FORMAT_APK: ret = safe_strdup (g, "apk"); break;
- case OS_PACKAGE_FORMAT_XBPS: ret = safe_strdup (g, "xbps"); break;
- case OS_PACKAGE_FORMAT_UNKNOWN:
- ret = safe_strdup (g, "unknown");
- break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_package_management (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->package_management) {
- case OS_PACKAGE_MANAGEMENT_APK: ret = safe_strdup (g, "apk");
break;
- case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt");
break;
- case OS_PACKAGE_MANAGEMENT_DNF: ret = safe_strdup (g, "dnf");
break;
- case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman");
break;
- case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi");
break;
- case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g,
"portage"); break;
- case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g,
"up2date"); break;
- case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi");
break;
- case OS_PACKAGE_MANAGEMENT_XBPS: ret = safe_strdup (g, "xbps");
break;
- case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum");
break;
- case OS_PACKAGE_MANAGEMENT_ZYPPER: ret = safe_strdup (g, "zypper");
break;
- case OS_PACKAGE_MANAGEMENT_UNKNOWN:
- ret = safe_strdup (g, "unknown");
- break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_hostname (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->hostname ? : "unknown");
-}
-
-void
-guestfs_int_free_inspect_info (guestfs_h *g)
-{
- size_t i, j;
-
- for (i = 0; i < g->nr_fses; ++i) {
- free (g->fses[i].mountable);
- free (g->fses[i].product_name);
- free (g->fses[i].product_variant);
- free (g->fses[i].arch);
- free (g->fses[i].hostname);
- free (g->fses[i].windows_systemroot);
- free (g->fses[i].windows_software_hive);
- free (g->fses[i].windows_system_hive);
- free (g->fses[i].windows_current_control_set);
- for (j = 0; j < g->fses[i].nr_fstab; ++j) {
- free (g->fses[i].fstab[j].mountable);
- free (g->fses[i].fstab[j].mountpoint);
- }
- free (g->fses[i].fstab);
- if (g->fses[i].drive_mappings)
- guestfs_int_free_string_list (g->fses[i].drive_mappings);
- }
- free (g->fses);
- g->nr_fses = 0;
- g->fses = NULL;
-}
-
/**
* Download a guest file to a local temporary file. The file is
* cached in the temporary directory, and is not downloaded again.
@@ -740,7 +58,7 @@ guestfs_int_free_inspect_info (guestfs_h *g)
* handle the case of multiple roots.
*/
char *
-guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs,
+guestfs_int_download_to_tmp (guestfs_h *g,
const char *filename,
const char *basename, uint64_t max_size)
{
@@ -749,10 +67,7 @@ guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs
*fs,
char devfd[32];
int64_t size;
- /* Make the basename unique by prefixing it with the fs number.
- * This also ensures there is one cache per filesystem.
- */
- if (asprintf (&r, "%s/%td-%s", g->tmpdir, fs - g->fses,
basename) == -1) {
+ if (asprintf (&r, "%s/%s", g->tmpdir, basename) == -1) {
perrorf (g, "asprintf");
return NULL;
}
@@ -798,46 +113,3 @@ guestfs_int_download_to_tmp (guestfs_h *g, struct
inspect_fs *fs,
free (r);
return NULL;
}
-
-struct inspect_fs *
-guestfs_int_search_for_root (guestfs_h *g, const char *root)
-{
- size_t i;
-
- if (g->nr_fses == 0) {
- error (g, _("no inspection data: call guestfs_inspect_os
first"));
- return NULL;
- }
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
- if (fs->role == OS_ROLE_ROOT && STREQ (root, fs->mountable))
- return fs;
- }
-
- error (g, _("%s: root device not found: only call this function with a
root device previously returned by guestfs_inspect_os"),
- root);
- return NULL;
-}
-
-int
-guestfs_int_is_partition (guestfs_h *g, const char *partition)
-{
- CLEANUP_FREE char *device = NULL;
-
- guestfs_push_error_handler (g, NULL, NULL);
-
- if ((device = guestfs_part_to_dev (g, partition)) == NULL) {
- guestfs_pop_error_handler (g);
- return 0;
- }
-
- if (guestfs_device_index (g, device) == -1) {
- guestfs_pop_error_handler (g);
- return 0;
- }
-
- guestfs_pop_error_handler (g);
-
- return 1;
-}
diff --git a/lib/version.c b/lib/version.c
index 60ffe1e89..86bb0b1f0 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -24,14 +24,42 @@
#include <string.h>
#include <unistd.h>
+#include <libintl.h>
#include "ignore-value.h"
+#include "xstrtol.h"
#include "guestfs.h"
#include "guestfs-internal.h"
COMPILE_REGEXP (re_major_minor, "(\\d+)\\.(\\d+)", 0)
+/* Parse small, unsigned ints, as used in version numbers. */
+int
+guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str)
+{
+ long ret;
+ const int r = xstrtol (str, NULL, 10, &ret, "");
+ if (r != LONGINT_OK) {
+ error (g, _("could not parse integer in version number: %s"),
str);
+ return -1;
+ }
+ return ret;
+}
+
+/* Like parse_unsigned_int, but ignore trailing stuff. */
+int
+guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str)
+{
+ long ret;
+ const int r = xstrtol (str, NULL, 10, &ret, NULL);
+ if (r != LONGINT_OK) {
+ error (g, _("could not parse integer in version number: %s"),
str);
+ return -1;
+ }
+ return ret;
+}
+
static int version_from_x_y_or_x (guestfs_h *g, struct version *v, const char
*str, const pcre *re, bool allow_only_x);
void
--
2.13.0
Possibly Parallel Threads
- [PATCH v12 00/11] Reimplement inspection in the daemon.
- [PATCH v10 00/10] Reimplement inspection in the daemon.
- [PATCH v9 00/11] Reimplement inspection in the daemon.
- [PATCH v11 00/10] Reimplement inspection in the daemon.
- [PATCH v8 00/42] Refactor utilities and reimplement inspection.