Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 00/42] Refactor utilities and reimplement inspection.
v7 was: https://www.redhat.com/archives/libguestfs/2017-June/msg00169.html https://www.redhat.com/archives/libguestfs/2017-June/msg00184.html I believe this addresses all comments received so far. Also it now passes a test where I compared about 100 disk images processed with old and new virt-inspector binaries. The output is identical in all cases except one which is caused by a bug in blkid (https://bugzilla.redhat.com/show_bug.cgi?id=1463615). Rich.
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 01/42] mllib: Move Visit OCaml bindings to common/mlvisit.
The ‘Visit’ module is a self-contained library with the only dependencies being: - the C ‘visit’ implementation - the guestfs OCaml bindings Move it to a separate ‘common/mlvisit’ directory. This change is not entirely refactoring. Two other fixes are made: - remove unsafe use of CLEANUP_FREE from a function which could raise an OCaml exception (cleanup handlers would not be called correctly if the exception is thrown) - don't link directly to common/visit/visit.c, but instead use the library (common/visit/libvisit.la) --- .gitignore | 3 +- Makefile.am | 5 +- common/mlvisit/Makefile.am | 152 +++++++++++++++++++++++++++++++ common/mlvisit/dummy.c | 2 + {mllib => common/mlvisit}/visit-c.c | 6 +- {mllib => common/mlvisit}/visit.ml | 0 {mllib => common/mlvisit}/visit.mli | 0 {mllib => common/mlvisit}/visit_tests.ml | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 3 +- docs/guestfs-hacking.pod | 4 + mllib/Makefile.am | 30 +----- sysprep/Makefile.am | 10 +- 13 files changed, 180 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 7ccc928bb..63e0e0778 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,8 @@ Makefile.in /common/errnostring/errnostring-gperf.gperf /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 +/common/mlvisit/.depend +/common/mlvisit/visit_tests /common/protocol/guestfs_protocol.c /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x @@ -361,7 +363,6 @@ Makefile.in /mllib/JSON_tests /mllib/libdir.ml /mllib/oUnit-* -/mllib/visit_tests /ocaml/bindtests.bc /ocaml/bindtests.opt /ocaml/bindtests.ml diff --git a/Makefile.am b/Makefile.am index ae77cdda2..499a1d279 100644 --- a/Makefile.am +++ b/Makefile.am @@ -150,10 +150,11 @@ endif # Unconditional because nothing is built yet. SUBDIRS += csharp -# OCaml tools. Note 'mllib' and 'customize' contain shared code used -# by other OCaml tools, so these must come first. +# 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/mlvisit \ mllib \ customize \ builder builder/templates \ diff --git a/common/mlvisit/Makefile.am b/common/mlvisit/Makefile.am new file mode 100644 index 000000000..51cbd2de6 --- /dev/null +++ b/common/mlvisit/Makefile.am @@ -0,0 +1,152 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) \ + visit_tests.ml + +SOURCES_MLI = \ + visit.mli + +SOURCES_ML = \ + visit.ml + +SOURCES_C = \ + visit-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlvisit.a + +if !HAVE_OCAMLOPT +MLVISIT_CMA = mlvisit.cma +else +MLVISIT_CMA = mlvisit.cmxa +endif + +noinst_DATA = $(MLVISIT_CMA) + +libmlvisit_a_SOURCES = $(SOURCES_C) +libmlvisit_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/common/visit +libmlvisit_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +# -I $(top_builddir)/lib/.libs is a hack which forces corresponding -L +# option to be passed to gcc, so we don't try linking against an +# installed copy of libguestfs. +OCAMLPACKAGES = \ + -package str,unix \ + -I $(top_builddir)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/visit/.libs \ + -I $(builddir) +OCAMLPACKAGES_TESTS = $(MLVISIT_CMA) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlvisit_a_DEPENDENCIES = $(OBJECTS) + +$(MLVISIT_CMA): $(OBJECTS) libmlvisit.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlvisit_a_OBJECTS) -cclib -lvisit -o mlvisit + +# Tests. + +visit_tests_SOURCES = dummy.c +visit_tests_BOBJECTS = visit_tests.cmo +visit_tests_XOBJECTS = $(visit_tests_BOBJECTS:.cmo=.cmx) + +# Can't call the following as <test>_OBJECTS because automake gets confused. +if !HAVE_OCAMLOPT +visit_tests_THEOBJECTS = $(visit_tests_BOBJECTS) +visit_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +else +visit_tests_THEOBJECTS = $(visit_tests_XOBJECTS) +visit_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +endif + +OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) + +visit_tests_DEPENDENCIES = \ + $(visit_tests_THEOBJECTS) \ + $(MLVISIT_CMA) \ + $(top_srcdir)/ocaml-link.sh +visit_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh \ + -cclib '-lvisit -lutils $(LIBXML2_LIBS) -lgnu' -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ + $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ + $(visit_tests_THEOBJECTS) -o $@ + +TESTS_ENVIRONMENT = $(top_builddir)/run --test + +check_PROGRAMS +TESTS + +if ENABLE_APPLIANCE +check_PROGRAMS += visit_tests +TESTS += visit_tests +endif + +check-valgrind: + $(MAKE) VG="@VG@" check + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/common/mlvisit/dummy.c b/common/mlvisit/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlvisit/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/mllib/visit-c.c b/common/mlvisit/visit-c.c similarity index 98% rename from mllib/visit-c.c rename to common/mlvisit/visit-c.c index b1c12166c..4cd1c6cea 100644 --- a/mllib/visit-c.c +++ b/common/mlvisit/visit-c.c @@ -31,7 +31,6 @@ #include <caml/mlvalues.h> #include "guestfs.h" -#include "guestfs-internal.h" #include "visit.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -59,7 +58,7 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) /* The dir string could move around when we call the * visitor_function, so we have to take a full copy of it. */ - CLEANUP_FREE char *dir = strdup (String_val (dirv)); + char *dir = strdup (String_val (dirv)); /* This stack address is used to point to the exception, if one is * raised in the visitor_function. */ @@ -71,6 +70,8 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) args.fvp = &fv; if (visit (g, dir, visitor_function_wrapper, &args) == -1) { + free (dir); + if (exn != Val_unit) { /* The failure was caused by visitor_function raising an * exception. Re-raise it here. @@ -84,6 +85,7 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) */ caml_failwith ("visit"); } + free (dir); CAMLreturn (Val_unit); } diff --git a/mllib/visit.ml b/common/mlvisit/visit.ml similarity index 100% rename from mllib/visit.ml rename to common/mlvisit/visit.ml diff --git a/mllib/visit.mli b/common/mlvisit/visit.mli similarity index 100% rename from mllib/visit.mli rename to common/mlvisit/visit.mli diff --git a/mllib/visit_tests.ml b/common/mlvisit/visit_tests.ml similarity index 100% rename from mllib/visit_tests.ml rename to common/mlvisit/visit_tests.ml diff --git a/configure.ac b/configure.ac index cbb5101fc..4fc226123 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,7 @@ AC_CONFIG_FILES([Makefile common/errnostring/Makefile common/edit/Makefile common/miniexpect/Makefile + common/mlvisit/Makefile common/options/Makefile common/parallel/Makefile common/progress/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 15abec124..578126403 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -15,6 +15,8 @@ common/edit/file-edit.c common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h +common/mlvisit/dummy.c +common/mlvisit/visit-c.c common/options/config.c common/options/decrypt.c common/options/display-options.c @@ -340,7 +342,6 @@ mllib/getopt-c.c mllib/progress-c.c mllib/unix_utils-c.c mllib/uri-c.c -mllib/visit-c.c mllib/xml-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index d3621c7e6..ac6d1ccbf 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlvisit> + +OCaml bindings for the visit functions (see F<common/visit>). + =item F<common/options> Common options parsing for guestfish, guestmount and some virt tools. diff --git a/mllib/Makefile.am b/mllib/Makefile.am index ee2f1a7a8..cb79f50f5 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -24,7 +24,6 @@ EXTRA_DIST = \ common_utils_tests.ml \ getopt_tests.ml \ JSON_tests.ml \ - visit_tests.ml \ test-getopt.sh SOURCES_MLI = \ @@ -41,8 +40,7 @@ SOURCES_MLI = \ regedit.mli \ registry.mli \ stringMap.mli \ - URI.mli \ - visit.mli + URI.mli SOURCES_ML = \ guestfs_config.ml \ @@ -55,7 +53,6 @@ SOURCES_ML = \ common_utils.ml \ progress.ml \ URI.ml \ - visit.ml \ planner.ml \ registry.ml \ regedit.ml \ @@ -66,7 +63,6 @@ SOURCES_ML = \ xpath_helpers.ml SOURCES_C = \ - ../common/visit/visit.c \ ../common/options/decrypt.c \ ../common/options/keys.c \ ../common/options/uri.c \ @@ -76,7 +72,6 @@ SOURCES_C = \ progress-c.c \ unix_utils-c.c \ uri-c.c \ - visit-c.c \ xml-c.c if HAVE_OCAML @@ -104,7 +99,6 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/visit \ -I$(top_srcdir)/common/options \ -I$(top_srcdir)/common/progress libmllib_a_CFLAGS = \ @@ -187,10 +181,6 @@ JSON_tests_SOURCES = dummy.c JSON_tests_BOBJECTS = JSON_tests.cmo JSON_tests_XOBJECTS = $(JSON_tests_BOBJECTS:.cmo=.cmx) -visit_tests_SOURCES = dummy.c -visit_tests_BOBJECTS = visit_tests.cmo -visit_tests_XOBJECTS = $(visit_tests_BOBJECTS:.cmo=.cmx) - # Can't call the following as <test>_OBJECTS because automake gets confused. if !HAVE_OCAMLOPT common_utils_tests_THEOBJECTS = $(common_utils_tests_BOBJECTS) @@ -210,9 +200,6 @@ getopt_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) JSON_tests_THEOBJECTS = $(JSON_tests_XOBJECTS) JSON_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) - -visit_tests_THEOBJECTS = $(visit_tests_XOBJECTS) -visit_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) endif OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) @@ -247,16 +234,6 @@ JSON_tests_LINK = \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(JSON_tests_THEOBJECTS) -o $@ -visit_tests_DEPENDENCIES = \ - $(visit_tests_THEOBJECTS) \ - $(MLLIB_CMA) \ - $(top_srcdir)/ocaml-link.sh -visit_tests_LINK = \ - $(top_srcdir)/ocaml-link.sh -cclib '-lutils $(LIBXML2_LIBS) -lgnu' -- \ - $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ - $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ - $(visit_tests_THEOBJECTS) -o $@ - TESTS_ENVIRONMENT = $(top_builddir)/run --test TESTS = \ @@ -269,11 +246,6 @@ check_PROGRAMS += common_utils_tests JSON_tests TESTS += common_utils_tests JSON_tests endif -if ENABLE_APPLIANCE -check_PROGRAMS += visit_tests -TESTS += visit_tests -endif - check-valgrind: $(MAKE) VG="@VG@" check diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 2a1ca25fd..68cb1814a 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -111,6 +111,8 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/visit/.libs \ + -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize if HAVE_OCAML_PKG_GETTEXT @@ -118,6 +120,7 @@ OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lvisit \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ @@ -133,7 +136,12 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + mlvisit.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 02/42] mllib: Move Progress OCaml bindings to common/mlprogress.
The ‘Progress’ module is a self-contained library with the only dependencies being: - the C ‘progress’ implementation Move it to a separate ‘common/mlprogress’ directory. This change is pure code refactoring. --- .gitignore | 1 + Makefile.am | 1 + common/mlprogress/Makefile.am | 111 ++++++++++++++++++++++++++++++ {mllib => common/mlprogress}/progress-c.c | 0 {mllib => common/mlprogress}/progress.ml | 0 {mllib => common/mlprogress}/progress.mli | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 2 +- docs/guestfs-hacking.pod | 4 ++ mllib/Makefile.am | 7 +- resize/Makefile.am | 9 ++- sparsify/Makefile.am | 9 ++- 12 files changed, 136 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 63e0e0778..6c84643bb 100644 --- a/.gitignore +++ b/.gitignore @@ -123,6 +123,7 @@ Makefile.in /common/errnostring/errnostring-gperf.gperf /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 +/common/mlprogress/.depend /common/mlvisit/.depend /common/mlvisit/visit_tests /common/protocol/guestfs_protocol.c diff --git a/Makefile.am b/Makefile.am index 499a1d279..bd0fc94e7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -154,6 +154,7 @@ SUBDIRS += csharp # shared code used by other OCaml tools, so these must come first. if HAVE_OCAML SUBDIRS += \ + common/mlprogress \ common/mlvisit \ mllib \ customize \ diff --git a/common/mlprogress/Makefile.am b/common/mlprogress/Makefile.am new file mode 100644 index 000000000..d4a229451 --- /dev/null +++ b/common/mlprogress/Makefile.am @@ -0,0 +1,111 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + progress.mli + +SOURCES_ML = \ + progress.ml + +SOURCES_C = \ + progress-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlprogress.a + +if !HAVE_OCAMLOPT +MLPROGRESS_CMA = mlprogress.cma +else +MLPROGRESS_CMA = mlprogress.cmxa +endif + +noinst_DATA = $(MLPROGRESS_CMA) + +libmlprogress_a_SOURCES = $(SOURCES_C) +libmlprogress_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/utils \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/common/progress +libmlprogress_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +# -I $(top_builddir)/lib/.libs is a hack which forces corresponding -L +# option to be passed to gcc, so we don't try linking against an +# installed copy of libguestfs. +OCAMLPACKAGES = \ + -package str,unix \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/ocaml \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlprogress_a_DEPENDENCIES = $(OBJECTS) + +$(MLPROGRESS_CMA): $(OBJECTS) libmlprogress.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlprogress_a_OBJECTS) \ + -cclib -lprogress \ + -o mlprogress + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/mllib/progress-c.c b/common/mlprogress/progress-c.c similarity index 100% rename from mllib/progress-c.c rename to common/mlprogress/progress-c.c diff --git a/mllib/progress.ml b/common/mlprogress/progress.ml similarity index 100% rename from mllib/progress.ml rename to common/mlprogress/progress.ml diff --git a/mllib/progress.mli b/common/mlprogress/progress.mli similarity index 100% rename from mllib/progress.mli rename to common/mlprogress/progress.mli diff --git a/configure.ac b/configure.ac index 4fc226123..1abb72671 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,7 @@ AC_CONFIG_FILES([Makefile common/errnostring/Makefile common/edit/Makefile common/miniexpect/Makefile + common/mlprogress/Makefile common/mlvisit/Makefile common/options/Makefile common/parallel/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 578126403..1e2a99e7e 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -15,6 +15,7 @@ common/edit/file-edit.c common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h +common/mlprogress/progress-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/options/config.c @@ -339,7 +340,6 @@ make-fs/make-fs.c mllib/common_utils-c.c mllib/dummy.c mllib/getopt-c.c -mllib/progress-c.c mllib/unix_utils-c.c mllib/uri-c.c mllib/xml-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index ac6d1ccbf..f9cb88f05 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlprogress> + +OCaml bindings for the progress bar functions (see F<common/progress>). + =item F<common/mlvisit> OCaml bindings for the visit functions (see F<common/visit>). diff --git a/mllib/Makefile.am b/mllib/Makefile.am index cb79f50f5..42f450323 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -36,7 +36,6 @@ SOURCES_MLI = \ getopt.mli \ JSON.mli \ planner.mli \ - progress.mli \ regedit.mli \ registry.mli \ stringMap.mli \ @@ -51,7 +50,6 @@ SOURCES_ML = \ getopt.ml \ unix_utils.ml \ common_utils.ml \ - progress.ml \ URI.ml \ planner.ml \ registry.ml \ @@ -66,10 +64,8 @@ SOURCES_C = \ ../common/options/decrypt.c \ ../common/options/keys.c \ ../common/options/uri.c \ - ../common/progress/progress.c \ common_utils-c.c \ getopt-c.c \ - progress-c.c \ unix_utils-c.c \ uri-c.c \ xml-c.c @@ -99,8 +95,7 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/options \ - -I$(top_srcdir)/common/progress + -I$(top_srcdir)/common/options libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ diff --git a/resize/Makefile.am b/resize/Makefile.am index e097dd611..c35c3a78a 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -57,15 +57,18 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lprogress \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBXML2_LIBS) \ @@ -80,7 +83,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlprogress.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_resize_DEPENDENCIES = \ $(OBJECTS) \ diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index b7229ffdd..97236829e 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -62,15 +62,18 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lprogress \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBXML2_LIBS) \ @@ -85,7 +88,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlprogress.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 03/42] mllib: Move Xml (libxml2) OCaml bindings to common/mlxml.
The ‘Xml’ module is a self-contained library of bindings for libxml2, with no other dependencies. Move it to a separate ‘common/mlxml’ directory. This is not pure refactoring. For unclear reasons, the previous version of ‘Xml.parse_file’ read the whole file into memory and then called ‘xmlReadMemory’. This was quite inefficient, and unnecessary because we could use ‘xmlReadFile’ to read and parse the file efficiently. Changing the code to use ‘xmlReadFile’ also removes the unnecessary dependency on ‘Common_utils.read_whole_file’. --- .gitignore | 1 + Makefile.am | 1 + common/mlxml/Makefile.am | 107 ++++++++++++++++++++++++++++++++++++++++ {mllib => common/mlxml}/xml-c.c | 21 ++++++++ {mllib => common/mlxml}/xml.ml | 8 +-- {mllib => common/mlxml}/xml.mli | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 2 +- docs/guestfs-hacking.pod | 4 ++ mllib/Makefile.am | 13 +++-- v2v/Makefile.am | 11 ++++- v2v/test-harness/Makefile.am | 3 +- 12 files changed, 158 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 6c84643bb..80637523d 100644 --- a/.gitignore +++ b/.gitignore @@ -126,6 +126,7 @@ Makefile.in /common/mlprogress/.depend /common/mlvisit/.depend /common/mlvisit/visit_tests +/common/mlxml/.depend /common/protocol/guestfs_protocol.c /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x diff --git a/Makefile.am b/Makefile.am index bd0fc94e7..48f538475 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,6 +156,7 @@ if HAVE_OCAML SUBDIRS += \ common/mlprogress \ common/mlvisit \ + common/mlxml \ mllib \ customize \ builder builder/templates \ diff --git a/common/mlxml/Makefile.am b/common/mlxml/Makefile.am new file mode 100644 index 000000000..1a989949f --- /dev/null +++ b/common/mlxml/Makefile.am @@ -0,0 +1,107 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + xml.mli + +SOURCES_ML = \ + xml.ml + +SOURCES_C = \ + xml-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlxml.a + +if !HAVE_OCAMLOPT +MLXML_CMA = mlxml.cma +else +MLXML_CMA = mlxml.cmxa +endif + +noinst_DATA = $(MLXML_CMA) + +libmlxml_a_SOURCES = $(SOURCES_C) +libmlxml_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) +libmlxml_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +# -I $(top_builddir)/lib/.libs is a hack which forces corresponding -L +# option to be passed to gcc, so we don't try linking against an +# installed copy of libguestfs. +OCAMLPACKAGES = \ + -package str,unix \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlxml_a_DEPENDENCIES = $(OBJECTS) + +$(MLXML_CMA): $(OBJECTS) libmlxml.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlxml_a_OBJECTS) \ + -cclib '$(LIBXML2_LIBS)' \ + -o mlxml + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/mllib/xml-c.c b/common/mlxml/xml-c.c similarity index 95% rename from mllib/xml-c.c rename to common/mlxml/xml-c.c index d3e893076..92388d940 100644 --- a/mllib/xml-c.c +++ b/common/mlxml/xml-c.c @@ -129,6 +129,27 @@ mllib_xml_parse_memory (value xmlv) } value +mllib_xml_parse_file (value filenamev) +{ + CAMLparam1 (filenamev); + CAMLlocal1 (docv); + xmlDocPtr doc; + + /* For security reasons, call xmlReadFile (not xmlParseFile) and + * pass XML_PARSE_NONET. See commit 845daded5fddc70f. + */ + doc = xmlReadFile (String_val (filenamev), NULL, XML_PARSE_NONET); + if (doc == NULL) + caml_invalid_argument ("parse_file: unable to parse XML from file"); + + docv = caml_alloc_custom (&docptr_custom_operations, sizeof (xmlDocPtr), + 0, 1); + docptr_val (docv) = doc; + + CAMLreturn (docv); +} + +value mllib_xml_copy_doc (value docv, value recursivev) { CAMLparam2 (docv, recursivev); diff --git a/mllib/xml.ml b/common/mlxml/xml.ml similarity index 97% rename from mllib/xml.ml rename to common/mlxml/xml.ml index 78e75b8f2..5ccf42ddd 100644 --- a/mllib/xml.ml +++ b/common/mlxml/xml.ml @@ -67,9 +67,11 @@ let parse_memory xml Gc.finalise free_docptr docptr; docptr -let parse_file file - let xml = Common_utils.read_whole_file file in - parse_memory xml +external _parse_file : string -> docptr = "mllib_xml_parse_file" +let parse_file filename + let docptr = _parse_file filename in + Gc.finalise free_docptr docptr; + docptr external _copy_doc : docptr -> recursive:bool -> docptr = "mllib_xml_copy_doc" let copy_doc docptr ~recursive diff --git a/mllib/xml.mli b/common/mlxml/xml.mli similarity index 100% rename from mllib/xml.mli rename to common/mlxml/xml.mli diff --git a/configure.ac b/configure.ac index 1abb72671..7d0f0a1dd 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,7 @@ AC_CONFIG_FILES([Makefile common/miniexpect/Makefile common/mlprogress/Makefile common/mlvisit/Makefile + common/mlxml/Makefile common/options/Makefile common/parallel/Makefile common/progress/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 1e2a99e7e..f6ac73047 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -18,6 +18,7 @@ common/miniexpect/miniexpect.h common/mlprogress/progress-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c +common/mlxml/xml-c.c common/options/config.c common/options/decrypt.c common/options/display-options.c @@ -342,7 +343,6 @@ mllib/dummy.c mllib/getopt-c.c mllib/unix_utils-c.c mllib/uri-c.c -mllib/xml-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c ocaml/guestfs-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index f9cb88f05..1ff496381 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -108,6 +108,10 @@ OCaml bindings for the progress bar functions (see F<common/progress>). OCaml bindings for the visit functions (see F<common/visit>). +=item F<common/mlxml> + +OCaml bindings for the libxml2 library. + =item F<common/options> Common options parsing for guestfish, guestmount and some virt tools. diff --git a/mllib/Makefile.am b/mllib/Makefile.am index 42f450323..c84f5f36d 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -27,8 +27,6 @@ EXTRA_DIST = \ test-getopt.sh SOURCES_MLI = \ - xml.mli \ - xpath_helpers.mli \ checksums.mli \ unix_utils.mli \ common_utils.mli \ @@ -39,7 +37,8 @@ SOURCES_MLI = \ regedit.mli \ registry.mli \ stringMap.mli \ - URI.mli + URI.mli \ + xpath_helpers.mli SOURCES_ML = \ guestfs_config.ml \ @@ -57,7 +56,6 @@ SOURCES_ML = \ JSON.ml \ curl.ml \ checksums.ml \ - xml.ml \ xpath_helpers.ml SOURCES_C = \ @@ -67,8 +65,7 @@ SOURCES_C = \ common_utils-c.c \ getopt-c.c \ unix_utils-c.c \ - uri-c.c \ - xml-c.c + uri-c.c if HAVE_OCAML @@ -95,7 +92,8 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/options + -I$(top_srcdir)/common/options \ + -I$(top_srcdir)/common/mlxml libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -113,6 +111,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 5b6618adf..2de99ceb9 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -146,6 +146,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize if HAVE_OCAML_PKG_GETTEXT @@ -168,7 +169,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlxml.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_v2v_DEPENDENCIES = $(OBJECTS) $(top_srcdir)/ocaml-link.sh virt_v2v_LINK = \ @@ -205,6 +210,7 @@ endif virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ + ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_v2v_copy_to_local_LINK = \ @@ -489,6 +495,7 @@ endif v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ + ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh v2v_unit_tests_LINK = \ @@ -503,7 +510,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/test-harness/Makefile.am b/v2v/test-harness/Makefile.am index 8ce441222..9a548022a 100644 --- a/v2v/test-harness/Makefile.am +++ b/v2v/test-harness/Makefile.am @@ -42,6 +42,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/v2v @@ -128,7 +129,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 04/42] mllib: Split ‘Common_utils’ into ‘Std_utils’ + ‘Common_utils’.
The new module ‘Std_utils’ contains only functions which are pure OCaml and depend only on the OCaml stdlib. Therefore these functions may be used by the generator. The new module is moved to ‘common/mlstdutils’. This also removes the "<stdlib>" hack, and the code which copied the library around. Also ‘Guestfs_config’, ‘Libdir’ and ‘StringMap’ modules are moved since these are essentially the same. The bulk of this change is just updating files which use ‘open Common_utils’ to add ‘open Std_utils’ where necessary. --- .gitignore | 8 +- Makefile.am | 2 +- builder/Makefile.am | 12 +- builder/builder.ml | 1 + builder/cache.ml | 3 +- builder/cmdline.ml | 3 +- builder/downloader.ml | 3 +- builder/index.ml | 3 +- builder/index_parser.ml | 3 +- builder/ini_reader.ml | 1 + builder/languages.ml | 1 + builder/list_entries.ml | 3 +- builder/paths.ml | 1 + builder/sigchecker.ml | 3 +- builder/simplestreams_parser.ml | 3 +- builder/sources.ml | 3 +- builder/yajl.ml | 3 +- common/mlstdutils/Makefile.am | 151 +++++ common/mlstdutils/dummy.c | 2 + {mllib => common/mlstdutils}/guestfs_config.ml.in | 0 common/mlstdutils/std_utils.ml | 664 +++++++++++++++++++++ common/mlstdutils/std_utils.mli | 338 +++++++++++ common/mlstdutils/std_utils_tests.ml | 95 +++ {mllib => common/mlstdutils}/stringMap.ml | 0 {mllib => common/mlstdutils}/stringMap.mli | 0 configure.ac | 3 +- customize/Makefile.am | 8 +- customize/SELinux_relabel.ml | 3 +- customize/append_line.ml | 1 + customize/customize_main.ml | 3 +- customize/customize_run.ml | 3 +- customize/firstboot.ml | 1 + customize/hostname.ml | 1 + customize/password.ml | 3 +- customize/perl_edit.ml | 1 + customize/ssh_key.ml | 7 +- customize/subscription_manager.ml | 3 +- dib/Makefile.am | 10 +- dib/cmdline.ml | 3 +- dib/dib.ml | 3 +- dib/elements.ml | 3 +- dib/output_format.ml | 1 + dib/output_format_qcow2.ml | 1 + dib/utils.ml | 3 +- docs/C_SOURCE_FILES | 1 + docs/guestfs-hacking.pod | 4 + generator/GObject.ml | 2 +- generator/Makefile.am | 34 +- generator/OCaml.ml | 2 +- generator/UEFI.ml | 2 +- generator/XDR.ml | 2 +- generator/actions.ml | 2 +- generator/authors.ml | 2 +- generator/bindtests.ml | 2 +- generator/c.ml | 2 +- generator/checks.ml | 2 +- generator/csharp.ml | 2 +- generator/customize.ml | 3 +- generator/daemon.ml | 2 +- generator/docstrings.ml | 2 +- generator/erlang.ml | 2 +- generator/errnostring.ml | 2 +- generator/events.ml | 2 +- generator/fish.ml | 2 +- generator/golang.ml | 2 +- generator/haskell.ml | 2 +- generator/java.ml | 2 +- generator/lua.ml | 2 +- generator/main.ml | 2 +- generator/optgroups.ml | 2 +- generator/perl.ml | 2 +- generator/php.ml | 2 +- generator/pr.ml | 2 +- generator/python.ml | 2 +- generator/ruby.ml | 2 +- generator/structs.ml | 2 +- generator/tests_c_api.ml | 2 +- generator/utils.ml | 2 +- get-kernel/Makefile.am | 10 +- get-kernel/get_kernel.ml | 3 +- mllib/Makefile.am | 25 +- mllib/checksums.ml | 3 +- mllib/common_utils.ml | 676 +--------------------- mllib/common_utils.mli | 347 ----------- mllib/common_utils_tests.ml | 61 +- mllib/curl.ml | 1 + mllib/getopt_tests.ml | 1 + mllib/regedit.ml | 1 + mllib/registry.ml | 3 +- mllib/xpath_helpers.ml | 3 +- resize/Makefile.am | 5 +- resize/resize.ml | 1 + sparsify/Makefile.am | 5 +- sparsify/cmdline.ml | 3 +- sparsify/copying.ml | 1 + sparsify/in_place.ml | 1 + sparsify/utils.ml | 2 +- sysprep/Makefile.am | 5 +- sysprep/main.ml | 1 + sysprep/sysprep_operation.ml | 4 +- sysprep/sysprep_operation_backup_files.ml | 3 +- sysprep/sysprep_operation_cron_spool.ml | 6 +- sysprep/sysprep_operation_net_hostname.ml | 4 +- sysprep/sysprep_operation_net_hwaddr.ml | 4 +- sysprep/sysprep_operation_script.ml | 3 +- sysprep/sysprep_operation_user_account.ml | 1 + v2v/DOM.ml | 1 + v2v/Makefile.am | 6 +- v2v/changeuid.ml | 3 +- v2v/cmdline.ml | 3 +- v2v/convert_linux.ml | 3 +- v2v/convert_windows.ml | 3 +- v2v/copy_to_local.ml | 3 +- v2v/create_libvirt_xml.ml | 3 +- v2v/create_ovf.ml | 7 +- v2v/input_disk.ml | 3 +- v2v/input_libvirtxml.ml | 3 +- v2v/input_ova.ml | 3 +- v2v/input_vmx.ml | 3 +- v2v/inspect_source.ml | 1 + v2v/linux.ml | 3 +- v2v/linux_bootloaders.ml | 3 +- v2v/linux_kernels.ml | 3 +- v2v/modules_list.ml | 2 +- v2v/output_glance.ml | 3 +- v2v/output_libvirt.ml | 3 +- v2v/output_local.ml | 3 +- v2v/output_null.ml | 3 +- v2v/output_qemu.ml | 3 +- v2v/output_rhv.ml | 3 +- v2v/output_vdsm.ml | 3 +- v2v/parse_libvirt_xml.ml | 7 +- v2v/parse_ovf_from_ova.ml | 3 +- v2v/parse_vmx.ml | 1 + v2v/target_bus_assignment.ml | 1 + v2v/test-harness/Makefile.am | 3 +- v2v/test-harness/v2v_test_harness.ml | 1 + v2v/utils.ml | 3 +- v2v/v2v.ml | 3 +- v2v/v2v_unit_tests.ml | 8 +- v2v/vCenter.ml | 1 + v2v/windows_virtio.ml | 3 +- 142 files changed, 1526 insertions(+), 1237 deletions(-) diff --git a/.gitignore b/.gitignore index 80637523d..3cd1fbb0d 100644 --- a/.gitignore +++ b/.gitignore @@ -124,6 +124,11 @@ Makefile.in /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 /common/mlprogress/.depend +/common/mlstdutils/.depend +/common/mlstdutils/guestfs_config.ml +/common/mlstdutils/libdir.ml +/common/mlstdutils/oUnit-* +/common/mlstdutils/std_utils_tests /common/mlvisit/.depend /common/mlvisit/visit_tests /common/mlxml/.depend @@ -273,7 +278,6 @@ Makefile.in /generator/common_utils.mli /generator/files-generated.txt /generator/generator -/generator/guestfs_config.ml /generator/.pod2text.data* /generator/stamp-generator /get-kernel/.depend @@ -361,9 +365,7 @@ Makefile.in /mllib/common_gettext.ml /mllib/common_utils_tests /mllib/getopt_tests -/mllib/guestfs_config.ml /mllib/JSON_tests -/mllib/libdir.ml /mllib/oUnit-* /ocaml/bindtests.bc /ocaml/bindtests.opt diff --git a/Makefile.am b/Makefile.am index 48f538475..64ac23f2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ include $(top_srcdir)/common-rules.mk ACLOCAL_AMFLAGS = -I m4 # The generator - must be before anything else. -SUBDIRS = generator +SUBDIRS = common/mlstdutils generator # Must be the first tests that run. if ENABLE_APPLIANCE diff --git a/builder/Makefile.am b/builder/Makefile.am index d56b394b7..5f0606ca4 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -124,6 +124,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize OCAMLPACKAGES_TESTS @@ -153,10 +154,16 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_builder_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -228,6 +235,7 @@ endif yajl_tests_DEPENDENCIES = \ $(yajl_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -299,7 +307,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/builder/builder.ml b/builder/builder.ml index b0a48ea89..0e02bab3b 100644 --- a/builder/builder.ml +++ b/builder/builder.ml @@ -20,6 +20,7 @@ open Common_gettext.Gettext module G = Guestfs +open Std_utils open Common_utils open Unix_utils open Password diff --git a/builder/cache.ml b/builder/cache.ml index 19fcd15e2..494796edb 100644 --- a/builder/cache.ml +++ b/builder/cache.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/cmdline.ml b/builder/cmdline.ml index f20c0936c..a1f901144 100644 --- a/builder/cmdline.ml +++ b/builder/cmdline.ml @@ -18,8 +18,9 @@ (* Command line argument parsing. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Customize_cmdline diff --git a/builder/downloader.ml b/builder/downloader.ml index ef3cd67cb..d6b27c8c7 100644 --- a/builder/downloader.ml +++ b/builder/downloader.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/index.ml b/builder/index.ml index 8c59de651..54af6e719 100644 --- a/builder/index.ml +++ b/builder/index.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/index_parser.ml b/builder/index_parser.ml index 468805cf8..fb546831f 100644 --- a/builder/index_parser.ml +++ b/builder/index_parser.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/ini_reader.ml b/builder/ini_reader.ml index 0470d173d..2d8ff7e59 100644 --- a/builder/ini_reader.ml +++ b/builder/ini_reader.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils type sections = section list diff --git a/builder/languages.ml b/builder/languages.ml index 66f49cb06..d94f97c5c 100644 --- a/builder/languages.ml +++ b/builder/languages.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils let split_locale loc diff --git a/builder/list_entries.ml b/builder/list_entries.ml index 2a1aef4c8..ea607107c 100644 --- a/builder/list_entries.ml +++ b/builder/list_entries.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/builder/paths.ml b/builder/paths.ml index cbd9d4bd0..e0fb9a024 100644 --- a/builder/paths.ml +++ b/builder/paths.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils let xdg_cache_home diff --git a/builder/sigchecker.ml b/builder/sigchecker.ml index 6c1e691ee..f72c21ab0 100644 --- a/builder/sigchecker.ml +++ b/builder/sigchecker.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Utils diff --git a/builder/simplestreams_parser.ml b/builder/simplestreams_parser.ml index 8844d476b..c550675ba 100644 --- a/builder/simplestreams_parser.ml +++ b/builder/simplestreams_parser.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Yajl open Utils diff --git a/builder/sources.ml b/builder/sources.ml index 290151c3a..4c9ea0fff 100644 --- a/builder/sources.ml +++ b/builder/sources.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf open Unix diff --git a/builder/yajl.ml b/builder/yajl.ml index d933b5246..5ae1c5d9b 100644 --- a/builder/yajl.ml +++ b/builder/yajl.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type yajl_val | Yajl_null diff --git a/common/mlstdutils/Makefile.am b/common/mlstdutils/Makefile.am new file mode 100644 index 000000000..9e0b34d42 --- /dev/null +++ b/common/mlstdutils/Makefile.am @@ -0,0 +1,151 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + std_utils_tests.ml + +SOURCES_MLI = \ + std_utils.mli \ + stringMap.mli + +SOURCES_ML = \ + guestfs_config.ml \ + $(OCAML_BYTES_COMPAT_ML) \ + libdir.ml \ + stringMap.ml \ + std_utils.ml + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlstdutils.a + +if !HAVE_OCAMLOPT +MLSTDUTILS_CMA = mlstdutils.cma +else +MLSTDUTILS_CMA = mlstdutils.cmxa +endif + +noinst_DATA = $(MLSTDUTILS_CMA) + +libmlstdutils_a_SOURCES = dummy.c +libmlstdutils_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) +libmlstdutils_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +OCAMLPACKAGES = \ + -package str,unix \ + -I $(builddir) +OCAMLPACKAGES_TESTS = $(MLSTDUTILS_CMA) +if HAVE_OCAML_PKG_OUNIT +OCAMLPACKAGES_TESTS += -package oUnit +endif + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlstdutils_a_DEPENDENCIES = $(OBJECTS) + +$(MLSTDUTILS_CMA): $(OBJECTS) + $(OCAMLFIND) mklib $(OCAMLPACKAGES) $(OBJECTS) -o mlstdutils + +# This OCaml module has to be generated by make (configure will put +# unexpanded prefix macro in). + +libdir.ml: Makefile + echo 'let libdir = "$(libdir)"' > $@-t + mv $@-t $@ + +# Tests. + +std_utils_tests_SOURCES = dummy.c +std_utils_tests_CPPFLAGS = \ + -I. \ + -I$(top_builddir) +std_utils_tests_BOBJECTS = std_utils_tests.cmo +std_utils_tests_XOBJECTS = $(std_utils_tests_BOBJECTS:.cmo=.cmx) + +# Can't call the following as <test>_OBJECTS because automake gets confused. +if !HAVE_OCAMLOPT +std_utils_tests_THEOBJECTS = $(std_utils_tests_BOBJECTS) +std_utils_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +else +std_utils_tests_THEOBJECTS = $(std_utils_tests_XOBJECTS) +std_utils_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +endif + +OCAMLLINKFLAGS = $(LINK_CUSTOM_OCAMLC_ONLY) + +std_utils_tests_DEPENDENCIES = \ + $(std_utils_tests_THEOBJECTS) \ + $(MLSTDUTILS_CMA) \ + $(top_srcdir)/ocaml-link.sh +std_utils_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ + $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ + $(std_utils_tests_THEOBJECTS) -o $@ + +TESTS_ENVIRONMENT = $(top_builddir)/run --test + +TESTS +check_PROGRAMS + +if HAVE_OCAML_PKG_OUNIT +check_PROGRAMS += std_utils_tests +TESTS += std_utils_tests +endif + +check-valgrind: + $(MAKE) VG="@VG@" check + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/common/mlstdutils/dummy.c b/common/mlstdutils/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlstdutils/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/mllib/guestfs_config.ml.in b/common/mlstdutils/guestfs_config.ml.in similarity index 100% rename from mllib/guestfs_config.ml.in rename to common/mlstdutils/guestfs_config.ml.in diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml new file mode 100644 index 000000000..b91785a9c --- /dev/null +++ b/common/mlstdutils/std_utils.ml @@ -0,0 +1,664 @@ +(* Common utilities for OCaml tools in libguestfs. + * Copyright (C) 2010-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 + +module Char = struct + include Char + + let lowercase_ascii c + if (c >= 'A' && c <= 'Z') + then unsafe_chr (code c + 32) + else c + + let uppercase_ascii c + if (c >= 'a' && c <= 'z') + then unsafe_chr (code c - 32) + else c + + let isspace c + c = ' ' + (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *) + + let isdigit = function + | '0'..'9' -> true + | _ -> false + + let isxdigit = function + | '0'..'9' -> true + | 'a'..'f' -> true + | 'A'..'F' -> true + | _ -> false + + let isalpha = function + | 'a'..'z' -> true + | 'A'..'Z' -> true + | _ -> false + + let isalnum = function + | '0'..'9' -> true + | 'a'..'z' -> true + | 'A'..'Z' -> true + | _ -> false + + let hexdigit = function + | '0' -> 0 + | '1' -> 1 + | '2' -> 2 + | '3' -> 3 + | '4' -> 4 + | '5' -> 5 + | '6' -> 6 + | '7' -> 7 + | '8' -> 8 + | '9' -> 9 + | 'a' | 'A' -> 10 + | 'b' | 'B' -> 11 + | 'c' | 'C' -> 12 + | 'd' | 'D' -> 13 + | 'e' | 'E' -> 14 + | 'f' | 'F' -> 15 + | _ -> -1 +end + +module String = struct + include String + + let map f s + let len = String.length s in + let b = Bytes.create len in + for i = 0 to len-1 do + Bytes.unsafe_set b i (f (unsafe_get s i)) + done; + Bytes.to_string b + + let lowercase_ascii s = map Char.lowercase_ascii s + let uppercase_ascii s = map Char.uppercase_ascii s + + let capitalize_ascii s + let b = Bytes.of_string s in + Bytes.unsafe_set b 0 (Char.uppercase_ascii (Bytes.unsafe_get b 0)); + Bytes.to_string b + + let is_prefix str prefix + let n = length prefix in + length str >= n && sub str 0 n = prefix + + let is_suffix str suffix + let sufflen = length suffix + and len = length str in + len >= sufflen && sub str (len - sufflen) sufflen = suffix + + let rec find s sub + let len = length s in + let sublen = length sub in + let rec loop i + if i <= len-sublen then ( + let rec loop2 j + if j < sublen then ( + if s.[i+j] = sub.[j] then loop2 (j+1) + else -1 + ) else + i (* found *) + in + let r = loop2 0 in + if r = -1 then loop (i+1) else r + ) else + -1 (* not found *) + in + loop 0 + + let rec replace s s1 s2 + let len = length s in + let sublen = length s1 in + let i = find s s1 in + if i = -1 then s + else ( + let s' = sub s 0 i in + let s'' = sub s (i+sublen) (len-i-sublen) in + s' ^ s2 ^ replace s'' s1 s2 + ) + + let replace_char s c1 c2 + let b2 = Bytes.of_string s in + let r = ref false in + for i = 0 to Bytes.length b2 - 1 do + if Bytes.unsafe_get b2 i = c1 then ( + Bytes.unsafe_set b2 i c2; + r := true + ) + done; + if not !r then s else Bytes.to_string b2 + + let rec nsplit sep str + let len = length str in + let seplen = length sep in + let i = find str sep in + if i = -1 then [str] + else ( + let s' = sub str 0 i in + let s'' = sub str (i+seplen) (len-i-seplen) in + s' :: nsplit sep s'' + ) + + let split sep str + let len = length sep in + let seplen = length str in + let i = find str sep in + if i = -1 then str, "" + else ( + sub str 0 i, sub str (i + len) (seplen - i - len) + ) + + let rec lines_split str + let buf = Buffer.create 16 in + let len = length str in + let rec loop start len + try + let i = index_from str start '\n' in + if i > 0 && str.[i-1] = '\\' then ( + Buffer.add_substring buf str start (i-start-1); + Buffer.add_char buf '\n'; + loop (i+1) len + ) else ( + Buffer.add_substring buf str start (i-start); + i+1 + ) + with Not_found -> + if len > 0 && str.[len-1] = '\\' then ( + Buffer.add_substring buf str start (len-start-1); + Buffer.add_char buf '\n' + ) else + Buffer.add_substring buf str start (len-start); + len+1 + in + let endi = loop 0 len in + let line = Buffer.contents buf in + if endi > len then + [line] + else + line :: lines_split (sub str endi (len-endi)) + + let random8 + let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in + fun () -> + concat "" ( + List.map ( + fun _ -> + let c = Random.int 36 in + let c = chars.[c] in + make 1 c + ) [1;2;3;4;5;6;7;8] + ) + + let triml ?(test = Char.isspace) str + let i = ref 0 in + let n = ref (String.length str) in + while !n > 0 && test str.[!i]; do + decr n; + incr i + done; + if !i = 0 then str + else String.sub str !i !n + + let trimr ?(test = Char.isspace) str + let n = ref (String.length str) in + while !n > 0 && test str.[!n-1]; do + decr n + done; + if !n = String.length str then str + else String.sub str 0 !n + + let trim ?(test = Char.isspace) str + trimr ~test (triml ~test str) + + let count_chars c str + let count = ref 0 in + for i = 0 to String.length str - 1 do + if c = String.unsafe_get str i then incr count + done; + !count + + let explode str + let r = ref [] in + for i = 0 to String.length str - 1 do + let c = String.unsafe_get str i in + r := c :: !r; + done; + List.rev !r + + let map_chars f str + List.map f (explode str) + + let spaces n = String.make n ' ' +end + +let (//) = Filename.concat +let quote = Filename.quote + +let subdirectory parent path + if path = parent then + "" + else if String.is_prefix path (parent // "") then ( + let len = String.length parent in + String.sub path (len+1) (String.length path - len-1) + ) else + invalid_arg (sprintf "%S is not a path prefix of %S" parent path) + +let ( +^ ) = Int64.add +let ( -^ ) = Int64.sub +let ( *^ ) = Int64.mul +let ( /^ ) = Int64.div +let ( &^ ) = Int64.logand +let ( ~^ ) = Int64.lognot + +external identity : 'a -> 'a = "%identity" + +let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) +let div_roundup64 i a = (i +^ a -^ 1L) /^ a + +let int_of_le32 str + assert (String.length str = 4); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) +^ + (Int64.shift_left (Int64.of_int c2) 16) +^ + (Int64.shift_left (Int64.of_int c3) 24) + +let le32_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let b = Bytes.create 4 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.to_string b + +type wrap_break_t = WrapEOS | WrapSpace | WrapNL + +let rec wrap ?(chan = stdout) ?(indent = 0) str + let len = String.length str in + _wrap chan indent 0 0 len str + +and _wrap chan indent column i len str + if i < len then ( + let (j, break) = _wrap_find_next_break i len str in + let next_column + if column + (j-i) >= 76 then ( + output_char chan '\n'; + output_spaces chan indent; + indent + (j-i) + 1 + ) + else column + (j-i) + 1 in + output chan (Bytes.of_string str) i (j-i); + match break with + | WrapEOS -> () + | WrapSpace -> + output_char chan ' '; + _wrap chan indent next_column (j+1) len str + | WrapNL -> + output_char chan '\n'; + output_spaces chan indent; + _wrap chan indent indent (j+1) len str + ) + +and _wrap_find_next_break i len str + if i >= len then (len, WrapEOS) + else if String.unsafe_get str i = ' ' then (i, WrapSpace) + else if String.unsafe_get str i = '\n' then (i, WrapNL) + else _wrap_find_next_break (i+1) len str + +and output_spaces chan n = for i = 0 to n-1 do output_char chan ' ' done + +let (|>) x f = f x + +(* Drop elements from a list while a predicate is true. *) +let rec dropwhile f = function + | [] -> [] + | x :: xs when f x -> dropwhile f xs + | xs -> xs + +(* Take elements from a list while a predicate is true. *) +let rec takewhile f = function + | x :: xs when f x -> x :: takewhile f xs + | _ -> [] + +let rec filter_map f = function + | [] -> [] + | x :: xs -> + match f x with + | Some y -> y :: filter_map f xs + | None -> filter_map f xs + +let rec find_map f = function + | [] -> raise Not_found + | x :: xs -> + match f x with + | Some y -> y + | None -> find_map f xs + +let iteri f xs + let rec loop i = function + | [] -> () + | x :: xs -> f i x; loop (i+1) xs + in + loop 0 xs + +let rec mapi i f + function + | [] -> [] + | a::l -> + let r = f i a in + r :: mapi (i + 1) f l +let mapi f l = mapi 0 f l + +let rec combine3 xs ys zs + match xs, ys, zs with + | [], [], [] -> [] + | x::xs, y::ys, z::zs -> (x, y, z) :: combine3 xs ys zs + | _ -> invalid_arg "combine3" + +let rec assoc ?(cmp = compare) ~default x = function + | [] -> default + | (y, y') :: _ when cmp x y = 0 -> y' + | _ :: ys -> assoc ~cmp ~default x ys + +let uniq ?(cmp = Pervasives.compare) xs + let rec loop acc = function + | [] -> acc + | [x] -> x :: acc + | x :: (y :: _ as xs) when cmp x y = 0 -> + loop acc xs + | x :: (y :: _ as xs) -> + loop (x :: acc) xs + in + List.rev (loop [] xs) + +let sort_uniq ?(cmp = Pervasives.compare) xs + let xs = List.sort cmp xs in + let xs = uniq ~cmp xs in + xs + +let remove_duplicates xs + let h = Hashtbl.create (List.length xs) in + let rec loop = function + | [] -> [] + | x :: xs when Hashtbl.mem h x -> xs + | x :: xs -> Hashtbl.add h x true; x :: loop xs + in + loop xs + +let push_back xsp x = xsp := !xsp @ [x] +let push_front x xsp = xsp := x :: !xsp +let pop_back xsp + let x, xs + match List.rev !xsp with + | x :: xs -> x, xs + | [] -> failwith "pop" in + xsp := List.rev xs; + x +let pop_front xsp + let x, xs + match !xsp with + | x :: xs -> x, xs + | [] -> failwith "shift" in + xsp := xs; + x + +let append xsp xs = xsp := !xsp @ xs +let prepend xs xsp = xsp := xs @ !xsp + +let unique = let i = ref 0 in fun () -> incr i; !i + +let may f = function + | None -> () + | Some x -> f x + +type ('a, 'b) maybe = Either of 'a | Or of 'b + +let protect ~f ~finally + let r + try Either (f ()) + with exn -> Or exn in + finally (); + match r with Either ret -> ret | Or exn -> raise exn + +let failwithf fs = ksprintf failwith fs + +exception Executable_not_found of string (* executable *) + +let which executable + let paths + try String.nsplit ":" (Sys.getenv "PATH") + with Not_found -> [] in + let paths = filter_map ( + fun p -> + let path = p // executable in + try Unix.access path [Unix.X_OK]; Some path + with Unix.Unix_error _ -> None + ) paths in + match paths with + | [] -> raise (Executable_not_found executable) + | x :: _ -> x + +(* Program name. *) +let prog = Filename.basename Sys.executable_name + +(* Stores the colours (--colours), quiet (--quiet), trace (-x) and + * verbose (-v) flags in a global variable. + *) +let colours = ref false +let set_colours () = colours := true +let colours () = !colours + +let quiet = ref false +let set_quiet () = quiet := true +let quiet () = !quiet + +let trace = ref false +let set_trace () = trace := true +let trace () = !trace + +let verbose = ref false +let set_verbose () = verbose := true +let verbose () = !verbose + +let read_whole_file path + let buf = Buffer.create 16384 in + let chan = open_in path in + let maxlen = 16384 in + let b = Bytes.create maxlen in + let rec loop () + let r = input chan b 0 maxlen in + if r > 0 then ( + Buffer.add_substring buf (Bytes.to_string b) 0 r; + loop () + ) + in + loop (); + close_in chan; + Buffer.contents buf + +(* Compare two version strings intelligently. *) +let rex_numbers = Str.regexp "^\\([0-9]+\\)\\(.*\\)$" +let rex_letters = Str.regexp_case_fold "^\\([a-z]+\\)\\(.*\\)$" + +let compare_version v1 v2 + let rec split_version = function + | "" -> [] + | str -> + let first, rest + if Str.string_match rex_numbers str 0 then ( + let n = Str.matched_group 1 str in + let rest = Str.matched_group 2 str in + let n + try `Number (int_of_string n) + with Failure _ -> `String n in + n, rest + ) + else if Str.string_match rex_letters str 0 then + `String (Str.matched_group 1 str), Str.matched_group 2 str + else ( + let len = String.length str in + `Char str.[0], String.sub str 1 (len-1) + ) in + first :: split_version rest + in + compare (split_version v1) (split_version v2) + +(* Annoying LVM2 returns a differing UUID strings for different + * function calls (sometimes containing or not containing '-' + * characters), so we have to normalize each string before + * comparison. c.f. 'compare_pvuuids' in virt-filesystem. + *) +let compare_lvm2_uuids uuid1 uuid2 + let n1 = String.length uuid1 and n2 = String.length uuid2 in + let rec loop i1 i2 + if i1 = n1 && i2 = n2 then 0 (* matching *) + else if i1 >= n1 then 1 (* different lengths *) + else if i2 >= n2 then -1 + else if uuid1.[i1] = '-' then loop (i1+1) i2 (* ignore '-' characters *) + else if uuid2.[i2] = '-' then loop i1 (i2+1) + else ( + let c = compare uuid1.[i1] uuid2.[i2] in + if c <> 0 then c (* not matching *) + else loop (i1+1) (i2+1) + ) + in + loop 0 0 + +let stringify_args args + let rec quote_args = function + | [] -> "" + | x :: xs -> " " ^ Filename.quote x ^ quote_args xs + in + match args with + | [] -> "" + | app :: xs -> app ^ quote_args xs + +(* Unlink a temporary file on exit. *) +let unlink_on_exit + let files = ref [] in + let registered_handlers = ref false in + + let rec unlink_files () + List.iter ( + fun file -> try Unix.unlink file with _ -> () + ) !files + and register_handlers () + (* Unlink on exit. *) + at_exit unlink_files + in + + fun file -> + files := file :: !files; + if not !registered_handlers then ( + register_handlers (); + registered_handlers := true + ) + +let is_block_device file + try (Unix.stat file).Unix.st_kind = Unix.S_BLK + with Unix.Unix_error _ -> false + +let is_char_device file + try (Unix.stat file).Unix.st_kind = Unix.S_CHR + with Unix.Unix_error _ -> false + +(* Annoyingly Sys.is_directory throws an exception on failure + * (RHBZ#1022431). + *) +let is_directory path + try Sys.is_directory path + with Sys_error _ -> false + +let absolute_path path + if not (Filename.is_relative path) then path + else Sys.getcwd () // path + +let qemu_input_filename filename + (* If the filename is something like "file:foo" then qemu-img will + * try to interpret that as "foo" in the file:/// protocol. To + * avoid that, if the path is relative prefix it with "./" since + * qemu-img won't try to interpret such a path. + *) + if String.length filename > 0 && filename.[0] <> '/' then + "./" ^ filename + else + filename + +let rec mkdir_p path permissions + try Unix.mkdir path permissions + with + | Unix.Unix_error (Unix.EEXIST, _, _) -> () + | Unix.Unix_error (Unix.ENOENT, _, _) -> + (* A component in the path does not exist, so first try + * creating the parent directory, and then again the requested + * directory. *) + mkdir_p (Filename.dirname path) permissions; + Unix.mkdir path permissions + +let normalize_arch = function + | "i486" | "i586" | "i686" -> "i386" + | "amd64" -> "x86_64" + | "powerpc" -> "ppc" + | "powerpc64" -> "ppc64" + | "powerpc64le" -> "ppc64le" + | arch -> arch + +(* Are guest arch and host_cpu compatible, in terms of being able + * to run commands in the libguestfs appliance? + *) +let guest_arch_compatible guest_arch + let own = normalize_arch Guestfs_config.host_cpu in + let guest_arch = normalize_arch guest_arch in + match own, guest_arch with + | x, y when x = y -> true + | "x86_64", "i386" -> true + | _ -> false + +(* Is the guest OS "Unix-like"? *) +let unix_like = function + | "hurd" + | "linux" + | "minix" -> true + | typ when String.is_suffix typ "bsd" -> true + | _ -> false + +(** Return the last part of a string, after the specified separator. *) +let last_part_of str sep + try + let i = String.rindex str sep in + Some (String.sub str (i+1) (String.length str - (i+1))) + with Not_found -> None + +let read_first_line_from_file filename + let chan = open_in filename in + let line = try input_line chan with End_of_file -> "" in + close_in chan; + line + +let is_regular_file path = (* NB: follows symlinks. *) + try (Unix.stat path).Unix.st_kind = Unix.S_REG + with Unix.Unix_error _ -> false diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli new file mode 100644 index 000000000..c0f1da30c --- /dev/null +++ b/common/mlstdutils/std_utils.mli @@ -0,0 +1,338 @@ +(* Common utilities for OCaml tools in libguestfs. + * Copyright (C) 2010-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. + *) + +module Char : sig + type t = char + val chr : int -> char + val code : char -> int + val compare: t -> t -> int + val escaped : char -> string + val unsafe_chr : int -> char + + val lowercase_ascii : char -> char + val uppercase_ascii : char -> char + + val isspace : char -> bool + (** Return true if char is a whitespace character. *) + val isdigit : char -> bool + (** Return true if the character is a digit [[0-9]]. *) + val isxdigit : char -> bool + (** Return true if the character is a hex digit [[0-9a-fA-F]]. *) + val isalpha : char -> bool + (** Return true if the character is a US ASCII 7 bit alphabetic. *) + val isalnum : char -> bool + (** Return true if the character is a US ASCII 7 bit alphanumeric. *) + + val hexdigit : char -> int + (** Return the value of a hex digit. If the char is not in + the set [[0-9a-fA-F]] then this returns [-1]. *) +end +(** Override the Char module from stdlib. *) + +module String : sig + type t = string + val compare: t -> t -> int + val concat : string -> string list -> string + val contains : string -> char -> bool + val contains_from : string -> int -> char -> bool + val copy : string -> string + val escaped : string -> string + val get : string -> int -> char + val index : string -> char -> int + val index_from : string -> int -> char -> int + val iter : (char -> unit) -> string -> unit + val length : string -> int + val make : int -> char -> string + val rcontains_from : string -> int -> char -> bool + val rindex : string -> char -> int + val rindex_from : string -> int -> char -> int + val sub : string -> int -> int -> string + val unsafe_get : string -> int -> char + + val map : (char -> char) -> string -> string + + val lowercase_ascii : string -> string + val uppercase_ascii : string -> string + val capitalize_ascii : string -> string + + val is_prefix : string -> string -> bool + (** [is_prefix str prefix] returns true if [prefix] is a prefix of [str]. *) + val is_suffix : string -> string -> bool + (** [is_suffix str suffix] returns true if [suffix] is a suffix of [str]. *) + val find : string -> string -> int + (** [find str sub] searches for [sub] as a substring of [str]. If + found it returns the index. If not found, it returns [-1]. *) + val replace : string -> string -> string -> string + (** [replace str s1 s2] replaces all instances of [s1] appearing in + [str] with [s2]. *) + val replace_char : string -> char -> char -> string + (** Replace character in string. *) + val nsplit : string -> string -> string list + (** [nsplit sep str] splits [str] into multiple strings at each + separator [sep]. *) + val split : string -> string -> string * string + (** [split sep str] splits [str] at the first occurrence of the + separator [sep], returning the part before and the part after. + If separator is not found, return the whole string and an + empty string. *) + val lines_split : string -> string list + (** [lines_split str] splits [str] into lines, keeping continuation + characters (i.e. [\] at the end of lines) into account. *) + val random8 : unit -> string + (** Return a string of 8 random printable characters. *) + val triml : ?test:(char -> bool) -> string -> string + (** Trim left. *) + val trimr : ?test:(char -> bool) -> string -> string + (** Trim right. *) + val trim : ?test:(char -> bool) -> string -> string + (** Trim left and right. *) + val count_chars : char -> string -> int + (** Count number of times the character occurs in string. *) + val explode : string -> char list + (** Explode a string into a list of characters. *) + val map_chars : (char -> 'a) -> string -> 'a list + (** Explode string, then map function over the characters. *) + val spaces : int -> string + (** [spaces n] creates a string of n spaces. *) +end +(** Override the String module from stdlib. *) + +val ( // ) : string -> string -> string +(** Concatenate directory and filename. *) + +val quote : string -> string +(** Shell-safe quoting of a string (alias for {!Filename.quote}). *) + +val subdirectory : string -> string -> string +(** [subdirectory parent path] returns subdirectory part of [path] relative + to the [parent]. If [path] and [parent] point to the same directory empty + string is returned. + + Note: path normalization on arguments is {b not} performed! + + If [parent] is not a path prefix of [path] the function raises + [Invalid_argument]. *) + +val ( +^ ) : int64 -> int64 -> int64 +val ( -^ ) : int64 -> int64 -> int64 +val ( *^ ) : int64 -> int64 -> int64 +val ( /^ ) : int64 -> int64 -> int64 +val ( &^ ) : int64 -> int64 -> int64 +val ( ~^ ) : int64 -> int64 +(** Various int64 operators. *) + +external identity : 'a -> 'a = "%identity" + +val roundup64 : int64 -> int64 -> int64 +(** [roundup64 i a] returns [i] rounded up to the next multiple of [a]. *) +val div_roundup64 : int64 -> int64 -> int64 +(** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], + with the result divided by [a]. *) +val int_of_le32 : string -> int64 +(** Unpack a 4 byte string as a little endian 32 bit integer. *) +val le32_of_int : int64 -> string +(** Pack a 32 bit integer a 4 byte string stored little endian. *) + +val wrap : ?chan:out_channel -> ?indent:int -> string -> unit +(** Wrap text. *) + +val output_spaces : out_channel -> int -> unit +(** Write [n] spaces to [out_channel]. *) + +val (|>) : 'a -> ('a -> 'b) -> 'b +(** Added in OCaml 4.01, we can remove our definition when we + can assume this minimum version of OCaml. *) + +val dropwhile : ('a -> bool) -> 'a list -> 'a list +(** [dropwhile f xs] drops leading elements from [xs] until + [f] returns false. *) +val takewhile : ('a -> bool) -> 'a list -> 'a list +(** [takewhile f xs] takes leading elements from [xs] until + [f] returns false. + + For any list [xs] and function [f], + [xs = takewhile f xs @ dropwhile f xs] *) +val filter_map : ('a -> 'b option) -> 'a list -> 'b list +(** [filter_map f xs] applies [f] to each element of [xs]. If + [f x] returns [Some y] then [y] is added to the returned list. *) +val find_map : ('a -> 'b option) -> 'a list -> 'b +(** [find_map f xs] applies [f] to each element of [xs] until + [f x] returns [Some y]. It returns [y]. If we exhaust the + list then this raises [Not_found]. *) +val iteri : (int -> 'a -> 'b) -> 'a list -> unit +(** [iteri f xs] calls [f i x] for each element, with [i] counting from [0]. *) +val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list +(** [mapi f xs] calls [f i x] for each element, with [i] counting from [0], + forming the return values from [f] into another list. *) + +val combine3 : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list +(** Like {!List.combine} but for triples. All lists must be the same length. *) + +val assoc : ?cmp:('a -> 'a -> int) -> default:'b -> 'a -> ('a * 'b) list -> 'b +(** Like {!List.assoc} but with a user-defined comparison function, and + instead of raising [Not_found], it returns the [~default] value. *) + +val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list +(** Uniquify a list (the list must be sorted first). *) + +val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list +(** Sort and uniquify a list. *) + +val remove_duplicates : 'a list -> 'a list +(** Remove duplicates from an unsorted list; useful when the order + of the elements matter. + + Please use [sort_uniq] when the order does not matter. *) + +val push_back : 'a list ref -> 'a -> unit +val push_front : 'a -> 'a list ref -> unit +val pop_back : 'a list ref -> 'a +val pop_front : 'a list ref -> 'a +(** Imperative list manipulation functions, similar to C++ STL + functions with the same names. (Although the names are similar, + the computational complexity of the functions is quite different.) + + These operate on list references, and each function modifies the + list reference that is passed to it. + + [push_back xsp x] appends the element [x] to the end of the list + [xsp]. This function is not tail-recursive. + + [push_front x xsp] prepends the element [x] to the head of the + list [xsp]. (The arguments are reversed compared to the same Perl + function, but OCaml is type safe so that's OK.) + + [pop_back xsp] removes the last element of the list [xsp] and + returns it. The list is modified to become the list minus the + final element. If a zero-length list is passed in, this raises + [Failure "pop_back"]. This function is not tail-recursive. + + [pop_front xsp] removes the head element of the list [xsp] and + returns it. The list is modified to become the tail of the list. + If a zero-length list is passed in, this raises [Failure + "pop_front"]. *) + +val append : 'a list ref -> 'a list -> unit +val prepend : 'a list -> 'a list ref -> unit +(** More imperative list manipulation functions. + + [append] is like {!push_back} above, except it appends a list to + the list reference. This function is not tail-recursive. + + [prepend] is like {!push_front} above, except it prepends a list + to the list reference. *) + +val unique : unit -> int +(** Returns a unique number each time called. *) + +val may : ('a -> unit) -> 'a option -> unit +(** [may f (Some x)] runs [f x]. [may f None] does nothing. *) + +type ('a, 'b) maybe = Either of 'a | Or of 'b +(** Like the Haskell [Either] type. *) + +val protect : f:(unit -> 'a) -> finally:(unit -> unit) -> 'a +(** Execute [~f] and afterwards execute [~finally]. + + If [~f] throws an exception then [~finally] is run and the + original exception from [~f] is re-raised. + + If [~finally] throws an exception, then the original exception + is lost. (NB: Janestreet core {!Exn.protectx}, on which this + function is modelled, doesn't throw away the exception in this + case, but requires a lot more work by the caller. Perhaps we + will change this in future.) *) + +val failwithf : ('a, unit, string, 'b) format4 -> 'a +(** Like [failwith] but supports printf-like arguments. *) + +exception Executable_not_found of string (* executable *) +(** Exception thrown by [which] when the specified executable is not found + in [$PATH]. *) + +val which : string -> string +(** Return the full path of the specified executable from [$PATH]. + + Throw [Executable_not_found] if not available. *) + +val prog : string +(** The program name (derived from {!Sys.executable_name}). *) + +val set_colours : unit -> unit +val colours : unit -> bool +val set_quiet : unit -> unit +val quiet : unit -> bool +val set_trace : unit -> unit +val trace : unit -> bool +val set_verbose : unit -> unit +val verbose : unit -> bool +(** Stores the colours ([--colours]), quiet ([--quiet]), trace ([-x]) + and verbose ([-v]) flags in global variables. *) + +val read_whole_file : string -> string +(** Read in the whole file as a string. *) + +val compare_version : string -> string -> int +(** Compare two version strings. *) + +val compare_lvm2_uuids : string -> string -> int +(** Compare two LVM2 UUIDs, ignoring '-' characters. *) + +val stringify_args : string list -> string +(** Create a "pretty-print" representation of a program invocation + (i.e. executable and its arguments). *) + +val unlink_on_exit : string -> unit +(** Unlink a temporary file on exit. *) + +val is_block_device : string -> bool +val is_char_device : string -> bool +val is_directory : string -> bool +(** These don't throw exceptions, unlike the [Sys] functions. *) + +val absolute_path : string -> string +(** Convert any path to an absolute path. *) + +val qemu_input_filename : string -> string +(** Sanitizes a filename for passing it safely to qemu/qemu-img. *) + +val mkdir_p : string -> int -> unit +(** Creates a directory, and its parents if missing. *) + +val normalize_arch : string -> string +(** Normalize the architecture name, i.e. maps it into a defined + identifier for it -- e.g. i386, i486, i586, and i686 are + normalized as i386. *) + +val guest_arch_compatible : string -> bool +(** Are guest arch and host_cpu compatible, in terms of being able + to run commands in the libguestfs appliance? *) + +val unix_like : string -> bool +(** Is the guest OS "Unix-like"? Call this with the result of + {!Guestfs.inspect_get_type}. *) + +val last_part_of : string -> char -> string option +(** Return the last part of a string, after the specified separator. *) + +val read_first_line_from_file : string -> string +(** Read only the first line (i.e. until the first newline character) + of a file. If the file is empty this returns an empty string. *) + +val is_regular_file : string -> bool +(** Checks whether the file is a regular file. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml new file mode 100644 index 000000000..1003f931c --- /dev/null +++ b/common/mlstdutils/std_utils_tests.ml @@ -0,0 +1,95 @@ +(* Utilities for OCaml tools in libguestfs. + * Copyright (C) 2011-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 file tests the Std_utils module. *) + +open OUnit2 +open Std_utils + +(* Utils. *) +let assert_equal_string = assert_equal ~printer:(fun x -> x) +let assert_equal_int = assert_equal ~printer:(fun x -> string_of_int x) +let assert_equal_int64 = assert_equal ~printer:(fun x -> Int64.to_string x) +let assert_equal_stringlist = assert_equal ~printer:(fun x -> "(" ^ (String.escaped (String.concat "," x)) ^ ")") + +let test_subdirectory ctx + assert_equal_string "" (subdirectory "/foo" "/foo"); + assert_equal_string "" (subdirectory "/foo" "/foo/"); + assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); + assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") + +(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) +let test_le32 ctx + assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); + assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) + +(* Test Std_utils.String.is_prefix. *) +let test_string_is_prefix ctx + assert_bool "String.is_prefix,," (String.is_prefix "" ""); + assert_bool "String.is_prefix,foo," (String.is_prefix "foo" ""); + assert_bool "String.is_prefix,foo,foo" (String.is_prefix "foo" "foo"); + assert_bool "String.is_prefix,foo123,foo" (String.is_prefix "foo123" "foo"); + assert_bool "not (String.is_prefix,,foo" (not (String.is_prefix "" "foo")) + +(* Test Std_utils.String.is_suffix. *) +let test_string_is_suffix ctx + assert_bool "String.is_suffix,," (String.is_suffix "" ""); + assert_bool "String.is_suffix,foo," (String.is_suffix "foo" ""); + assert_bool "String.is_suffix,foo,foo" (String.is_suffix "foo" "foo"); + assert_bool "String.is_suffix,123foo,foo" (String.is_suffix "123foo" "foo"); + assert_bool "not String.is_suffix,,foo" (not (String.is_suffix "" "foo")) + +(* Test Std_utils.String.find. *) +let test_string_find ctx + assert_equal_int 0 (String.find "" ""); + assert_equal_int 0 (String.find "foo" ""); + assert_equal_int 1 (String.find "foo" "o"); + assert_equal_int 3 (String.find "foobar" "bar"); + assert_equal_int (-1) (String.find "" "baz"); + assert_equal_int (-1) (String.find "foobar" "baz") + +(* Test Std_utils.String.lines_split. *) +let test_string_lines_split ctx + assert_equal_stringlist [""] (String.lines_split ""); + assert_equal_stringlist ["A"] (String.lines_split "A"); + assert_equal_stringlist ["A"; ""] (String.lines_split "A\n"); + assert_equal_stringlist ["A"; "B"] (String.lines_split "A\nB"); + assert_equal_stringlist ["A"; "B"; "C"] (String.lines_split "A\nB\nC"); + assert_equal_stringlist ["A"; "B"; "C"; "D"] (String.lines_split "A\nB\nC\nD"); + assert_equal_stringlist ["A\n"] (String.lines_split "A\\"); + assert_equal_stringlist ["A\nB"] (String.lines_split "A\\\nB"); + assert_equal_stringlist ["A"; "B\nC"] (String.lines_split "A\nB\\\nC"); + assert_equal_stringlist ["A"; "B\nC"; "D"] (String.lines_split "A\nB\\\nC\nD"); + assert_equal_stringlist ["A"; "B\nC\nD"] (String.lines_split "A\nB\\\nC\\\nD"); + assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); + assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") + +(* Suites declaration. *) +let suite + "mllib Std_utils" >::: + [ + "subdirectory" >:: test_subdirectory; + "numeric.le32" >:: test_le32; + "strings.is_prefix" >:: test_string_is_prefix; + "strings.is_suffix" >:: test_string_is_suffix; + "strings.find" >:: test_string_find; + "strings.lines_split" >:: test_string_lines_split; + ] + +let () + run_test_tt_main suite diff --git a/mllib/stringMap.ml b/common/mlstdutils/stringMap.ml similarity index 100% rename from mllib/stringMap.ml rename to common/mlstdutils/stringMap.ml diff --git a/mllib/stringMap.mli b/common/mlstdutils/stringMap.mli similarity index 100% rename from mllib/stringMap.mli rename to common/mlstdutils/stringMap.mli diff --git a/configure.ac b/configure.ac index 7d0f0a1dd..eba149241 100644 --- a/configure.ac +++ b/configure.ac @@ -186,6 +186,8 @@ AC_CONFIG_FILES([Makefile common/edit/Makefile common/miniexpect/Makefile common/mlprogress/Makefile + common/mlstdutils/Makefile + common/mlstdutils/guestfs_config.ml common/mlvisit/Makefile common/mlxml/Makefile common/options/Makefile @@ -230,7 +232,6 @@ AC_CONFIG_FILES([Makefile lua/examples/Makefile make-fs/Makefile mllib/Makefile - mllib/guestfs_config.ml ocaml/META ocaml/Makefile ocaml/examples/Makefile diff --git a/customize/Makefile.am b/customize/Makefile.am index 07398b2e8..674134b70 100644 --- a/customize/Makefile.am +++ b/customize/Makefile.am @@ -123,6 +123,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib \ -I $(builddir) if HAVE_OCAML_PKG_GETTEXT @@ -149,7 +150,12 @@ else CUSTOMIZE_THEOBJECTS = $(CUSTOMIZE_XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) OCAMLCLIBS = \ -lutils \ diff --git a/customize/SELinux_relabel.ml b/customize/SELinux_relabel.ml index 11999299b..ab373b33a 100644 --- a/customize/SELinux_relabel.ml +++ b/customize/SELinux_relabel.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/customize/append_line.ml b/customize/append_line.ml index e967b4201..405080617 100644 --- a/customize/append_line.ml +++ b/customize/append_line.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/customize/customize_main.ml b/customize/customize_main.ml index 5b4641237..55ec3cb78 100644 --- a/customize/customize_main.ml +++ b/customize/customize_main.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Customize_cmdline diff --git a/customize/customize_run.ml b/customize/customize_run.ml index f71ae3535..5564684b4 100644 --- a/customize/customize_run.ml +++ b/customize/customize_run.ml @@ -19,8 +19,9 @@ open Unix open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Customize_cmdline open Password diff --git a/customize/firstboot.ml b/customize/firstboot.ml index 9208daa0a..41aa52dac 100644 --- a/customize/firstboot.ml +++ b/customize/firstboot.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/customize/hostname.ml b/customize/hostname.ml index 23c149402..b49db8714 100644 --- a/customize/hostname.ml +++ b/customize/hostname.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Printf diff --git a/customize/password.ml b/customize/password.ml index 4ab5a14d1..d26b94865 100644 --- a/customize/password.ml +++ b/customize/password.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/customize/perl_edit.ml b/customize/perl_edit.ml index 5cd250b49..bb44ea062 100644 --- a/customize/perl_edit.ml +++ b/customize/perl_edit.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils external c_edit_file : verbose:bool -> Guestfs.t -> int64 -> string -> string -> unit diff --git a/customize/ssh_key.ml b/customize/ssh_key.ml index 4302a8e92..185536d1d 100644 --- a/customize/ssh_key.ml +++ b/customize/ssh_key.ml @@ -16,13 +16,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext -open Common_utils - open Printf open Sys open Unix +open Std_utils +open Common_utils +open Common_gettext.Gettext + module G = Guestfs type ssh_key_selector diff --git a/customize/subscription_manager.ml b/customize/subscription_manager.ml index a23efe546..56ba28ab9 100644 --- a/customize/subscription_manager.ml +++ b/customize/subscription_manager.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type sm_credentials = { sm_username : string; diff --git a/dib/Makefile.am b/dib/Makefile.am index 6780ee249..b10fa94c9 100644 --- a/dib/Makefile.am +++ b/dib/Makefile.am @@ -79,6 +79,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -99,10 +100,15 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_dib_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_dib_LINK = \ @@ -138,7 +144,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/dib/cmdline.ml b/dib/cmdline.ml index 67194704e..549f01546 100644 --- a/dib/cmdline.ml +++ b/dib/cmdline.ml @@ -18,8 +18,9 @@ (* Command line argument parsing. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/dib/dib.ml b/dib/dib.ml index eca47fa46..d083eed9f 100644 --- a/dib/dib.ml +++ b/dib/dib.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Cmdline open Utils diff --git a/dib/elements.ml b/dib/elements.ml index 4c2875ae1..d237eeb7f 100644 --- a/dib/elements.ml +++ b/dib/elements.ml @@ -18,8 +18,9 @@ (* Parsing and handling of elements. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/dib/output_format.ml b/dib/output_format.ml index 851cefc43..6499ee259 100644 --- a/dib/output_format.ml +++ b/dib/output_format.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/dib/output_format_qcow2.ml b/dib/output_format_qcow2.ml index afb564ce7..a32b2a4f9 100644 --- a/dib/output_format_qcow2.ml +++ b/dib/output_format_qcow2.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/dib/utils.ml b/dib/utils.ml index afa2ec944..8b6bb1576 100644 --- a/dib/utils.ml +++ b/dib/utils.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index f6ac73047..71c17603f 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -16,6 +16,7 @@ common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h common/mlprogress/progress-c.c +common/mlstdutils/dummy.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/mlxml/xml-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index 1ff496381..beb44d2dc 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlstdutils> + +A library of pure OCaml utility functions used in many places. + =item F<common/mlprogress> OCaml bindings for the progress bar functions (see F<common/progress>). diff --git a/generator/GObject.ml b/generator/GObject.ml index bb95b170c..8fa17c219 100644 --- a/generator/GObject.ml +++ b/generator/GObject.ml @@ -22,7 +22,7 @@ open Printf -open Common_utils +open Std_utils open Actions open Docstrings open Events diff --git a/generator/Makefile.am b/generator/Makefile.am index 81b49cab1..401029d34 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -53,8 +53,6 @@ sources = \ c.mli \ checks.ml \ checks.mli \ - common_utils.ml \ - common_utils.mli \ csharp.ml \ csharp.mli \ customize.ml \ @@ -77,7 +75,6 @@ sources = \ GObject.mli \ golang.ml \ golang.mli \ - guestfs_config.ml \ haskell.ml \ haskell.mli \ java.ml \ @@ -118,8 +115,8 @@ sources = \ # In build dependency order. objects = \ $(OCAML_GENERATOR_BYTES_COMPAT_CMO) \ - guestfs_config.cmo \ - common_utils.cmo \ + ../common/mlstdutils/guestfs_config.cmo \ + ../common/mlstdutils/std_utils.cmo \ types.cmo \ utils.cmo \ proc_nr.cmo \ @@ -170,7 +167,12 @@ objects = \ EXTRA_DIST = $(sources) files-generated.txt -OCAMLPACKAGES = -package unix,str -I $(srcdir) -I . +OCAMLPACKAGES = \ + -package unix,str \ + -I $(srcdir) \ + -I . \ + -I $(top_srcdir)/common/mlstdutils \ + -I $(top_builddir)/common/mlstdutils OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) noinst_PROGRAM = generator @@ -183,9 +185,9 @@ generator: $(objects) # Dependencies. depend: .depend -.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) common_utils.ml common_utils.mli guestfs_config.ml +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) $(wildcard $(abs_srcdir)/common/mlstdutils/*.mli) $(wildcard $(abs_srcdir)/common/mlstdutils/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) $^ | \ + $(OCAMLFIND) ocamldep -I ../common/mlstdutils -I $(abs_srcdir) $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ @@ -224,22 +226,6 @@ stamp-generator: generator cd $(top_srcdir) && $(abs_builddir)/generator touch $@ -# We share common_utils.ml{,i} with the mllib directory. However we -# have to remove functions which depend on any modules which are not -# part of the OCaml stdlib. -common_utils.ml: $(top_srcdir)/mllib/common_utils.ml - rm -f $@ $@-t - echo '(* This file is generated from mllib/common_utils.ml *)' > $@-t - sed -n '/^(\*<stdlib>\*)$$/,/^(\*<\/stdlib>\*)$$/p' $< >> $@-t - mv $@-t $@ -common_utils.mli: $(top_srcdir)/mllib/common_utils.mli - rm -f $@ $@-t - echo '(* This file is generated from mllib/common_utils.mli *)' > $@-t - sed -n '/^(\*<stdlib>\*)$$/,/^(\*<\/stdlib>\*)$$/p' $< >> $@-t - mv $@-t $@ -guestfs_config.ml: ../mllib/guestfs_config.ml - cp $< $@ - CLEANFILES += $(noinst_DATA) $(noinst_PROGRAM) DISTCLEANFILES += .pod2text.data.version.2 diff --git a/generator/OCaml.ml b/generator/OCaml.ml index 955da6f09..f6a4292b9 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/UEFI.ml b/generator/UEFI.ml index 95797aad9..5c5e02bab 100644 --- a/generator/UEFI.ml +++ b/generator/UEFI.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils open Pr open Docstrings diff --git a/generator/XDR.ml b/generator/XDR.ml index 2d799929b..4b0a552d1 100644 --- a/generator/XDR.ml +++ b/generator/XDR.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/actions.ml b/generator/actions.ml index 2722f3dcd..a9b3b5906 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils diff --git a/generator/authors.ml b/generator/authors.ml index d4547bdb1..ca5242983 100644 --- a/generator/authors.ml +++ b/generator/authors.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils open Pr open Docstrings diff --git a/generator/bindtests.ml b/generator/bindtests.ml index c3caebfce..d225146c0 100644 --- a/generator/bindtests.ml +++ b/generator/bindtests.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/c.ml b/generator/c.ml index 1f099a221..27bf1ebf9 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/checks.ml b/generator/checks.ml index 881069489..be7b272a3 100644 --- a/generator/checks.ml +++ b/generator/checks.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils open Actions diff --git a/generator/csharp.ml b/generator/csharp.ml index 6a280011a..0eab21f0d 100644 --- a/generator/csharp.ml +++ b/generator/csharp.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/customize.ml b/generator/customize.ml index b158eb5d9..381ed0627 100644 --- a/generator/customize.ml +++ b/generator/customize.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Docstrings open Pr @@ -623,6 +623,7 @@ and generate_customize_cmdline_ml () open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/generator/daemon.ml b/generator/daemon.ml index 84686973c..0300dc54b 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/docstrings.ml b/generator/docstrings.ml index 2ce595dae..696f1c52a 100644 --- a/generator/docstrings.ml +++ b/generator/docstrings.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/erlang.ml b/generator/erlang.ml index 602380966..03cca3368 100644 --- a/generator/erlang.ml +++ b/generator/erlang.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/errnostring.ml b/generator/errnostring.ml index b3d718815..e5f4c69f8 100644 --- a/generator/errnostring.ml +++ b/generator/errnostring.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/events.ml b/generator/events.ml index 7188e1203..f3b682a5a 100644 --- a/generator/events.ml +++ b/generator/events.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils (* NB: DO NOT REORDER THESE, as doing so will change the ABI. Only diff --git a/generator/fish.ml b/generator/fish.ml index 45289132f..3d99c9081 100644 --- a/generator/fish.ml +++ b/generator/fish.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/golang.ml b/generator/golang.ml index f32ccf2c1..67f360839 100644 --- a/generator/golang.ml +++ b/generator/golang.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/haskell.ml b/generator/haskell.ml index 592d817fa..ec3f311df 100644 --- a/generator/haskell.ml +++ b/generator/haskell.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/java.ml b/generator/java.ml index c44e669a0..7c3212a49 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/lua.ml b/generator/lua.ml index c4ab4cc47..b40c51753 100644 --- a/generator/lua.ml +++ b/generator/lua.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/main.ml b/generator/main.ml index d4316c085..0e1c01f74 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Pr open Actions open Structs diff --git a/generator/optgroups.ml b/generator/optgroups.ml index e9a37e19c..4b9b66f77 100644 --- a/generator/optgroups.ml +++ b/generator/optgroups.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils open Actions diff --git a/generator/perl.ml b/generator/perl.ml index bf2dc4a81..8e3dad75e 100644 --- a/generator/perl.ml +++ b/generator/perl.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/php.ml b/generator/php.ml index 48cd89fdc..0721e431a 100644 --- a/generator/php.ml +++ b/generator/php.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/pr.ml b/generator/pr.ml index e8b32b67d..0c56f3e67 100644 --- a/generator/pr.ml +++ b/generator/pr.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Utils (* Output channel, 'pr' prints to this. *) diff --git a/generator/python.ml b/generator/python.ml index 4cae24757..c6c237241 100644 --- a/generator/python.ml +++ b/generator/python.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/ruby.ml b/generator/ruby.ml index 4d2ebbf0d..825cab32a 100644 --- a/generator/ruby.ml +++ b/generator/ruby.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/structs.ml b/generator/structs.ml index 834fa9c54..57975b564 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index f9f14f6dc..a680521f4 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/utils.ml b/generator/utils.ml index a745a02b7..b818a0b3c 100644 --- a/generator/utils.ml +++ b/generator/utils.ml @@ -23,7 +23,7 @@ * makes this a bit harder than it should be. *) -open Common_utils +open Std_utils open Unix open Printf diff --git a/get-kernel/Makefile.am b/get-kernel/Makefile.am index bda3a8db1..c6454d7a4 100644 --- a/get-kernel/Makefile.am +++ b/get-kernel/Makefile.am @@ -63,6 +63,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -83,10 +84,15 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_get_kernel_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_get_kernel_LINK = \ @@ -121,7 +127,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/get-kernel/get_kernel.ml b/get-kernel/get_kernel.ml index e45838811..1c9ece44b 100644 --- a/get-kernel/get_kernel.ml +++ b/get-kernel/get_kernel.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName module G = Guestfs diff --git a/mllib/Makefile.am b/mllib/Makefile.am index c84f5f36d..5f6f7fa85 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -19,7 +19,7 @@ include $(top_srcdir)/subdir-rules.mk EXTRA_DIST = \ $(SOURCES_MLI) \ - $(filter-out guestfs_config.ml libdir.ml,$(SOURCES_ML)) \ + $(SOURCES_ML) \ $(SOURCES_C) \ common_utils_tests.ml \ getopt_tests.ml \ @@ -36,15 +36,11 @@ SOURCES_MLI = \ planner.mli \ regedit.mli \ registry.mli \ - stringMap.mli \ URI.mli \ xpath_helpers.mli SOURCES_ML = \ - guestfs_config.ml \ $(OCAML_BYTES_COMPAT_ML) \ - libdir.ml \ - stringMap.ml \ common_gettext.ml \ getopt.ml \ unix_utils.ml \ @@ -93,7 +89,8 @@ libmllib_a_CPPFLAGS = \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/options \ - -I$(top_srcdir)/common/mlxml + -I$(top_srcdir)/common/mlxml \ + -I$(top_srcdir)/common/mlstdutils libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -112,6 +109,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlxml \ + -I $(top_builddir)/common/mlstdutils \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT @@ -144,13 +142,6 @@ $(MLLIB_CMA): $(OBJECTS) libmllib.a $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ $(OBJECTS) $(libmllib_a_OBJECTS) -o mllib -# This OCaml module has to be generated by make (configure will put -# unexpanded prefix macro in). - -libdir.ml: Makefile - echo 'let libdir = "$(libdir)"' > $@-t - mv $@-t $@ - # Tests. common_utils_tests_SOURCES = dummy.c @@ -196,10 +187,14 @@ JSON_tests_THEOBJECTS = $(JSON_tests_XOBJECTS) JSON_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) common_utils_tests_DEPENDENCIES = \ $(common_utils_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh common_utils_tests_LINK = \ @@ -210,6 +205,7 @@ common_utils_tests_LINK = \ getopt_tests_DEPENDENCIES = \ $(getopt_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh getopt_tests_LINK = \ @@ -220,6 +216,7 @@ getopt_tests_LINK = \ JSON_tests_DEPENDENCIES = \ $(JSON_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh JSON_tests_LINK = \ diff --git a/mllib/checksums.ml b/mllib/checksums.ml index 61deac2d1..f4c414f57 100644 --- a/mllib/checksums.ml +++ b/mllib/checksums.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/mllib/common_utils.ml b/mllib/common_utils.ml index 89bbfe397..73c6e2473 100644 --- a/mllib/common_utils.ml +++ b/mllib/common_utils.ml @@ -16,14 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* The parts between <stdlib>..</stdlib> are copied into the - * generator/common_utils.ml file. These parts must ONLY use - * functions from the OCaml stdlib. - *) -(*<stdlib>*) open Printf -(*</stdlib>*) +open Std_utils open Common_gettext.Gettext open Getopt.OptionName @@ -31,474 +26,6 @@ external c_inspect_decrypt : Guestfs.t -> int64 -> unit = "guestfs_int_mllib_ins external c_set_echo_keys : unit -> unit = "guestfs_int_mllib_set_echo_keys" "noalloc" external c_set_keys_from_stdin : unit -> unit = "guestfs_int_mllib_set_keys_from_stdin" "noalloc" -(*<stdlib>*) - -module Char = struct - include Char - - let lowercase_ascii c - if (c >= 'A' && c <= 'Z') - then unsafe_chr (code c + 32) - else c - - let uppercase_ascii c - if (c >= 'a' && c <= 'z') - then unsafe_chr (code c - 32) - else c - - let isspace c - c = ' ' - (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *) - - let isdigit = function - | '0'..'9' -> true - | _ -> false - - let isxdigit = function - | '0'..'9' -> true - | 'a'..'f' -> true - | 'A'..'F' -> true - | _ -> false - - let isalpha = function - | 'a'..'z' -> true - | 'A'..'Z' -> true - | _ -> false - - let isalnum = function - | '0'..'9' -> true - | 'a'..'z' -> true - | 'A'..'Z' -> true - | _ -> false - - let hexdigit = function - | '0' -> 0 - | '1' -> 1 - | '2' -> 2 - | '3' -> 3 - | '4' -> 4 - | '5' -> 5 - | '6' -> 6 - | '7' -> 7 - | '8' -> 8 - | '9' -> 9 - | 'a' | 'A' -> 10 - | 'b' | 'B' -> 11 - | 'c' | 'C' -> 12 - | 'd' | 'D' -> 13 - | 'e' | 'E' -> 14 - | 'f' | 'F' -> 15 - | _ -> -1 -end - -module String = struct - include String - - let map f s - let len = String.length s in - let b = Bytes.create len in - for i = 0 to len-1 do - Bytes.unsafe_set b i (f (unsafe_get s i)) - done; - Bytes.to_string b - - let lowercase_ascii s = map Char.lowercase_ascii s - let uppercase_ascii s = map Char.uppercase_ascii s - - let capitalize_ascii s - let b = Bytes.of_string s in - Bytes.unsafe_set b 0 (Char.uppercase_ascii (Bytes.unsafe_get b 0)); - Bytes.to_string b - - let is_prefix str prefix - let n = length prefix in - length str >= n && sub str 0 n = prefix - - let is_suffix str suffix - let sufflen = length suffix - and len = length str in - len >= sufflen && sub str (len - sufflen) sufflen = suffix - - let rec find s sub - let len = length s in - let sublen = length sub in - let rec loop i - if i <= len-sublen then ( - let rec loop2 j - if j < sublen then ( - if s.[i+j] = sub.[j] then loop2 (j+1) - else -1 - ) else - i (* found *) - in - let r = loop2 0 in - if r = -1 then loop (i+1) else r - ) else - -1 (* not found *) - in - loop 0 - - let rec replace s s1 s2 - let len = length s in - let sublen = length s1 in - let i = find s s1 in - if i = -1 then s - else ( - let s' = sub s 0 i in - let s'' = sub s (i+sublen) (len-i-sublen) in - s' ^ s2 ^ replace s'' s1 s2 - ) - - let replace_char s c1 c2 - let b2 = Bytes.of_string s in - let r = ref false in - for i = 0 to Bytes.length b2 - 1 do - if Bytes.unsafe_get b2 i = c1 then ( - Bytes.unsafe_set b2 i c2; - r := true - ) - done; - if not !r then s else Bytes.to_string b2 - - let rec nsplit sep str - let len = length str in - let seplen = length sep in - let i = find str sep in - if i = -1 then [str] - else ( - let s' = sub str 0 i in - let s'' = sub str (i+seplen) (len-i-seplen) in - s' :: nsplit sep s'' - ) - - let split sep str - let len = length sep in - let seplen = length str in - let i = find str sep in - if i = -1 then str, "" - else ( - sub str 0 i, sub str (i + len) (seplen - i - len) - ) - - let rec lines_split str - let buf = Buffer.create 16 in - let len = length str in - let rec loop start len - try - let i = index_from str start '\n' in - if i > 0 && str.[i-1] = '\\' then ( - Buffer.add_substring buf str start (i-start-1); - Buffer.add_char buf '\n'; - loop (i+1) len - ) else ( - Buffer.add_substring buf str start (i-start); - i+1 - ) - with Not_found -> - if len > 0 && str.[len-1] = '\\' then ( - Buffer.add_substring buf str start (len-start-1); - Buffer.add_char buf '\n' - ) else - Buffer.add_substring buf str start (len-start); - len+1 - in - let endi = loop 0 len in - let line = Buffer.contents buf in - if endi > len then - [line] - else - line :: lines_split (sub str endi (len-endi)) - - let random8 - let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in - fun () -> - concat "" ( - List.map ( - fun _ -> - let c = Random.int 36 in - let c = chars.[c] in - make 1 c - ) [1;2;3;4;5;6;7;8] - ) - - let triml ?(test = Char.isspace) str - let i = ref 0 in - let n = ref (String.length str) in - while !n > 0 && test str.[!i]; do - decr n; - incr i - done; - if !i = 0 then str - else String.sub str !i !n - - let trimr ?(test = Char.isspace) str - let n = ref (String.length str) in - while !n > 0 && test str.[!n-1]; do - decr n - done; - if !n = String.length str then str - else String.sub str 0 !n - - let trim ?(test = Char.isspace) str - trimr ~test (triml ~test str) - - let count_chars c str - let count = ref 0 in - for i = 0 to String.length str - 1 do - if c = String.unsafe_get str i then incr count - done; - !count - - let explode str - let r = ref [] in - for i = 0 to String.length str - 1 do - let c = String.unsafe_get str i in - r := c :: !r; - done; - List.rev !r - - let map_chars f str - List.map f (explode str) - - let spaces n = String.make n ' ' -end - -let (//) = Filename.concat -let quote = Filename.quote - -let subdirectory parent path - if path = parent then - "" - else if String.is_prefix path (parent // "") then ( - let len = String.length parent in - String.sub path (len+1) (String.length path - len-1) - ) else - invalid_arg (sprintf "%S is not a path prefix of %S" parent path) - -let ( +^ ) = Int64.add -let ( -^ ) = Int64.sub -let ( *^ ) = Int64.mul -let ( /^ ) = Int64.div -let ( &^ ) = Int64.logand -let ( ~^ ) = Int64.lognot - -external identity : 'a -> 'a = "%identity" - -let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) -let div_roundup64 i a = (i +^ a -^ 1L) /^ a - -let int_of_le32 str - assert (String.length str = 4); - let c0 = Char.code (String.unsafe_get str 0) in - let c1 = Char.code (String.unsafe_get str 1) in - let c2 = Char.code (String.unsafe_get str 2) in - let c3 = Char.code (String.unsafe_get str 3) in - Int64.of_int c0 +^ - (Int64.shift_left (Int64.of_int c1) 8) +^ - (Int64.shift_left (Int64.of_int c2) 16) +^ - (Int64.shift_left (Int64.of_int c3) 24) - -let le32_of_int i - let c0 = i &^ 0xffL in - let c1 = Int64.shift_right (i &^ 0xff00L) 8 in - let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in - let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in - let b = Bytes.create 4 in - Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); - Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); - Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); - Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); - Bytes.to_string b - -type wrap_break_t = WrapEOS | WrapSpace | WrapNL - -let rec wrap ?(chan = stdout) ?(indent = 0) str - let len = String.length str in - _wrap chan indent 0 0 len str - -and _wrap chan indent column i len str - if i < len then ( - let (j, break) = _wrap_find_next_break i len str in - let next_column - if column + (j-i) >= 76 then ( - output_char chan '\n'; - output_spaces chan indent; - indent + (j-i) + 1 - ) - else column + (j-i) + 1 in - output chan (Bytes.of_string str) i (j-i); - match break with - | WrapEOS -> () - | WrapSpace -> - output_char chan ' '; - _wrap chan indent next_column (j+1) len str - | WrapNL -> - output_char chan '\n'; - output_spaces chan indent; - _wrap chan indent indent (j+1) len str - ) - -and _wrap_find_next_break i len str - if i >= len then (len, WrapEOS) - else if String.unsafe_get str i = ' ' then (i, WrapSpace) - else if String.unsafe_get str i = '\n' then (i, WrapNL) - else _wrap_find_next_break (i+1) len str - -and output_spaces chan n = for i = 0 to n-1 do output_char chan ' ' done - -let (|>) x f = f x - -(* Drop elements from a list while a predicate is true. *) -let rec dropwhile f = function - | [] -> [] - | x :: xs when f x -> dropwhile f xs - | xs -> xs - -(* Take elements from a list while a predicate is true. *) -let rec takewhile f = function - | x :: xs when f x -> x :: takewhile f xs - | _ -> [] - -let rec filter_map f = function - | [] -> [] - | x :: xs -> - match f x with - | Some y -> y :: filter_map f xs - | None -> filter_map f xs - -let rec find_map f = function - | [] -> raise Not_found - | x :: xs -> - match f x with - | Some y -> y - | None -> find_map f xs - -let iteri f xs - let rec loop i = function - | [] -> () - | x :: xs -> f i x; loop (i+1) xs - in - loop 0 xs - -let rec mapi i f - function - | [] -> [] - | a::l -> - let r = f i a in - r :: mapi (i + 1) f l -let mapi f l = mapi 0 f l - -let rec combine3 xs ys zs - match xs, ys, zs with - | [], [], [] -> [] - | x::xs, y::ys, z::zs -> (x, y, z) :: combine3 xs ys zs - | _ -> invalid_arg "combine3" - -let rec assoc ?(cmp = compare) ~default x = function - | [] -> default - | (y, y') :: _ when cmp x y = 0 -> y' - | _ :: ys -> assoc ~cmp ~default x ys - -let uniq ?(cmp = Pervasives.compare) xs - let rec loop acc = function - | [] -> acc - | [x] -> x :: acc - | x :: (y :: _ as xs) when cmp x y = 0 -> - loop acc xs - | x :: (y :: _ as xs) -> - loop (x :: acc) xs - in - List.rev (loop [] xs) - -let sort_uniq ?(cmp = Pervasives.compare) xs - let xs = List.sort cmp xs in - let xs = uniq ~cmp xs in - xs - -let remove_duplicates xs - let h = Hashtbl.create (List.length xs) in - let rec loop = function - | [] -> [] - | x :: xs when Hashtbl.mem h x -> xs - | x :: xs -> Hashtbl.add h x true; x :: loop xs - in - loop xs - -let push_back xsp x = xsp := !xsp @ [x] -let push_front x xsp = xsp := x :: !xsp -let pop_back xsp - let x, xs - match List.rev !xsp with - | x :: xs -> x, xs - | [] -> failwith "pop" in - xsp := List.rev xs; - x -let pop_front xsp - let x, xs - match !xsp with - | x :: xs -> x, xs - | [] -> failwith "shift" in - xsp := xs; - x - -let append xsp xs = xsp := !xsp @ xs -let prepend xs xsp = xsp := xs @ !xsp - -let unique = let i = ref 0 in fun () -> incr i; !i - -let may f = function - | None -> () - | Some x -> f x - -type ('a, 'b) maybe = Either of 'a | Or of 'b - -let protect ~f ~finally - let r - try Either (f ()) - with exn -> Or exn in - finally (); - match r with Either ret -> ret | Or exn -> raise exn - -let failwithf fs = ksprintf failwith fs - -exception Executable_not_found of string (* executable *) - -let which executable - let paths - try String.nsplit ":" (Sys.getenv "PATH") - with Not_found -> [] in - let paths = filter_map ( - fun p -> - let path = p // executable in - try Unix.access path [Unix.X_OK]; Some path - with Unix.Unix_error _ -> None - ) paths in - match paths with - | [] -> raise (Executable_not_found executable) - | x :: _ -> x - -(* Program name. *) -let prog = Filename.basename Sys.executable_name - -(* Stores the colours (--colours), quiet (--quiet), trace (-x) and - * verbose (-v) flags in a global variable. - *) -let colours = ref false -let set_colours () = colours := true -let colours () = !colours - -let quiet = ref false -let set_quiet () = quiet := true -let quiet () = !quiet - -let trace = ref false -let set_trace () = trace := true -let trace () = !trace - -let verbose = ref false -let set_verbose () = verbose := true -let verbose () = !verbose - (* ANSI terminal colours. *) let istty chan Unix.isatty (Unix.descr_of_out_channel chan) @@ -514,8 +41,6 @@ let ansi_magenta ?(chan = stdout) () let ansi_restore ?(chan = stdout) () if colours () || istty chan then output_string chan "\x1b[0m" -(*</stdlib>*) - (* Timestamped progress messages, used for ordinary messages when not * --quiet. *) @@ -630,26 +155,6 @@ let virt_tools_data_dir ) in fun () -> Lazy.force dir -(*<stdlib>*) - -let read_whole_file path - let buf = Buffer.create 16384 in - let chan = open_in path in - let maxlen = 16384 in - let b = Bytes.create maxlen in - let rec loop () - let r = input chan b 0 maxlen in - if r > 0 then ( - Buffer.add_substring buf (Bytes.to_string b) 0 r; - loop () - ) - in - loop (); - close_in chan; - Buffer.contents buf - -(*</stdlib>*) - (* Parse a size field, eg. "10G". *) let parse_size let const_re = Str.regexp "^\\([.0-9]+\\)\\([bKMG]\\)$" in @@ -764,67 +269,6 @@ let create_standard_options argspec ?anon_fun ?(key_opts = false) usage_msg else []) in Getopt.create argspec ?anon_fun usage_msg -(*<stdlib>*) - -(* Compare two version strings intelligently. *) -let rex_numbers = Str.regexp "^\\([0-9]+\\)\\(.*\\)$" -let rex_letters = Str.regexp_case_fold "^\\([a-z]+\\)\\(.*\\)$" - -let compare_version v1 v2 - let rec split_version = function - | "" -> [] - | str -> - let first, rest - if Str.string_match rex_numbers str 0 then ( - let n = Str.matched_group 1 str in - let rest = Str.matched_group 2 str in - let n - try `Number (int_of_string n) - with Failure _ -> `String n in - n, rest - ) - else if Str.string_match rex_letters str 0 then - `String (Str.matched_group 1 str), Str.matched_group 2 str - else ( - let len = String.length str in - `Char str.[0], String.sub str 1 (len-1) - ) in - first :: split_version rest - in - compare (split_version v1) (split_version v2) - -(* Annoying LVM2 returns a differing UUID strings for different - * function calls (sometimes containing or not containing '-' - * characters), so we have to normalize each string before - * comparison. c.f. 'compare_pvuuids' in virt-filesystem. - *) -let compare_lvm2_uuids uuid1 uuid2 - let n1 = String.length uuid1 and n2 = String.length uuid2 in - let rec loop i1 i2 - if i1 = n1 && i2 = n2 then 0 (* matching *) - else if i1 >= n1 then 1 (* different lengths *) - else if i2 >= n2 then -1 - else if uuid1.[i1] = '-' then loop (i1+1) i2 (* ignore '-' characters *) - else if uuid2.[i2] = '-' then loop i1 (i2+1) - else ( - let c = compare uuid1.[i1] uuid2.[i2] in - if c <> 0 then c (* not matching *) - else loop (i1+1) (i2+1) - ) - in - loop 0 0 - -let stringify_args args - let rec quote_args = function - | [] -> "" - | x :: xs -> " " ^ Filename.quote x ^ quote_args xs - in - match args with - | [] -> "" - | app :: xs -> app ^ quote_args xs - -(*</stdlib>*) - (* Run an external command, slurp up the output as a list of lines. *) let external_command ?(echo_cmd = true) cmd if echo_cmd then @@ -942,31 +386,6 @@ let uuidgen () if len < 10 then assert false; (* sanity check on uuidgen *) uuid -(*<stdlib>*) - -(* Unlink a temporary file on exit. *) -let unlink_on_exit - let files = ref [] in - let registered_handlers = ref false in - - let rec unlink_files () - List.iter ( - fun file -> try Unix.unlink file with _ -> () - ) !files - and register_handlers () - (* Unlink on exit. *) - at_exit unlink_files - in - - fun file -> - files := file :: !files; - if not !registered_handlers then ( - register_handlers (); - registered_handlers := true - ) - -(*</stdlib>*) - (* Remove a temporary directory on exit. *) let rmdir_on_exit let dirs = ref [] in @@ -1103,18 +522,6 @@ let detect_file_type filename close_in chan; ret -(*<stdlib>*) - -let is_block_device file - try (Unix.stat file).Unix.st_kind = Unix.S_BLK - with Unix.Unix_error _ -> false - -let is_char_device file - try (Unix.stat file).Unix.st_kind = Unix.S_CHR - with Unix.Unix_error _ -> false - -(*</stdlib>*) - let is_partition dev try if not (is_block_device dev) then false @@ -1128,87 +535,6 @@ let is_partition dev ) with Unix.Unix_error _ -> false -(*<stdlib>*) - -(* Annoyingly Sys.is_directory throws an exception on failure - * (RHBZ#1022431). - *) -let is_directory path - try Sys.is_directory path - with Sys_error _ -> false - -let absolute_path path - if not (Filename.is_relative path) then path - else Sys.getcwd () // path - -let qemu_input_filename filename - (* If the filename is something like "file:foo" then qemu-img will - * try to interpret that as "foo" in the file:/// protocol. To - * avoid that, if the path is relative prefix it with "./" since - * qemu-img won't try to interpret such a path. - *) - if String.length filename > 0 && filename.[0] <> '/' then - "./" ^ filename - else - filename - -let rec mkdir_p path permissions - try Unix.mkdir path permissions - with - | Unix.Unix_error (Unix.EEXIST, _, _) -> () - | Unix.Unix_error (Unix.ENOENT, _, _) -> - (* A component in the path does not exist, so first try - * creating the parent directory, and then again the requested - * directory. *) - mkdir_p (Filename.dirname path) permissions; - Unix.mkdir path permissions - -let normalize_arch = function - | "i486" | "i586" | "i686" -> "i386" - | "amd64" -> "x86_64" - | "powerpc" -> "ppc" - | "powerpc64" -> "ppc64" - | "powerpc64le" -> "ppc64le" - | arch -> arch - -(* Are guest arch and host_cpu compatible, in terms of being able - * to run commands in the libguestfs appliance? - *) -let guest_arch_compatible guest_arch - let own = normalize_arch Guestfs_config.host_cpu in - let guest_arch = normalize_arch guest_arch in - match own, guest_arch with - | x, y when x = y -> true - | "x86_64", "i386" -> true - | _ -> false - -(* Is the guest OS "Unix-like"? *) -let unix_like = function - | "hurd" - | "linux" - | "minix" -> true - | typ when String.is_suffix typ "bsd" -> true - | _ -> false - -(** Return the last part of a string, after the specified separator. *) -let last_part_of str sep - try - let i = String.rindex str sep in - Some (String.sub str (i+1) (String.length str - (i+1))) - with Not_found -> None - -let read_first_line_from_file filename - let chan = open_in filename in - let line = try input_line chan with End_of_file -> "" in - close_in chan; - line - -let is_regular_file path = (* NB: follows symlinks. *) - try (Unix.stat path).Unix.st_kind = Unix.S_REG - with Unix.Unix_error _ -> false - -(*</stdlib>*) - let inspect_mount_root g ?mount_opts_fn root let mps = g#inspect_get_mountpoints root in let cmp (a,_) (b,_) diff --git a/mllib/common_utils.mli b/mllib/common_utils.mli index 376274849..fa4d15054 100644 --- a/mllib/common_utils.mli +++ b/mllib/common_utils.mli @@ -16,280 +16,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* The parts between <stdlib>..</stdlib> are copied into the - * generator/common_utils.ml file. These parts must ONLY use - * functions from the OCaml stdlib. - *) -(*<stdlib>*) - -module Char : sig - type t = char - val chr : int -> char - val code : char -> int - val compare: t -> t -> int - val escaped : char -> string - val unsafe_chr : int -> char - - val lowercase_ascii : char -> char - val uppercase_ascii : char -> char - - val isspace : char -> bool - (** Return true if char is a whitespace character. *) - val isdigit : char -> bool - (** Return true if the character is a digit [[0-9]]. *) - val isxdigit : char -> bool - (** Return true if the character is a hex digit [[0-9a-fA-F]]. *) - val isalpha : char -> bool - (** Return true if the character is a US ASCII 7 bit alphabetic. *) - val isalnum : char -> bool - (** Return true if the character is a US ASCII 7 bit alphanumeric. *) - - val hexdigit : char -> int - (** Return the value of a hex digit. If the char is not in - the set [[0-9a-fA-F]] then this returns [-1]. *) -end -(** Override the Char module from stdlib. *) - -module String : sig - type t = string - val compare: t -> t -> int - val concat : string -> string list -> string - val contains : string -> char -> bool - val contains_from : string -> int -> char -> bool - val copy : string -> string - val escaped : string -> string - val get : string -> int -> char - val index : string -> char -> int - val index_from : string -> int -> char -> int - val iter : (char -> unit) -> string -> unit - val length : string -> int - val make : int -> char -> string - val rcontains_from : string -> int -> char -> bool - val rindex : string -> char -> int - val rindex_from : string -> int -> char -> int - val sub : string -> int -> int -> string - val unsafe_get : string -> int -> char - - val map : (char -> char) -> string -> string - - val lowercase_ascii : string -> string - val uppercase_ascii : string -> string - val capitalize_ascii : string -> string - - val is_prefix : string -> string -> bool - (** [is_prefix str prefix] returns true if [prefix] is a prefix of [str]. *) - val is_suffix : string -> string -> bool - (** [is_suffix str suffix] returns true if [suffix] is a suffix of [str]. *) - val find : string -> string -> int - (** [find str sub] searches for [sub] as a substring of [str]. If - found it returns the index. If not found, it returns [-1]. *) - val replace : string -> string -> string -> string - (** [replace str s1 s2] replaces all instances of [s1] appearing in - [str] with [s2]. *) - val replace_char : string -> char -> char -> string - (** Replace character in string. *) - val nsplit : string -> string -> string list - (** [nsplit sep str] splits [str] into multiple strings at each - separator [sep]. *) - val split : string -> string -> string * string - (** [split sep str] splits [str] at the first occurrence of the - separator [sep], returning the part before and the part after. - If separator is not found, return the whole string and an - empty string. *) - val lines_split : string -> string list - (** [lines_split str] splits [str] into lines, keeping continuation - characters (i.e. [\] at the end of lines) into account. *) - val random8 : unit -> string - (** Return a string of 8 random printable characters. *) - val triml : ?test:(char -> bool) -> string -> string - (** Trim left. *) - val trimr : ?test:(char -> bool) -> string -> string - (** Trim right. *) - val trim : ?test:(char -> bool) -> string -> string - (** Trim left and right. *) - val count_chars : char -> string -> int - (** Count number of times the character occurs in string. *) - val explode : string -> char list - (** Explode a string into a list of characters. *) - val map_chars : (char -> 'a) -> string -> 'a list - (** Explode string, then map function over the characters. *) - val spaces : int -> string - (** [spaces n] creates a string of n spaces. *) -end -(** Override the String module from stdlib. *) - -val ( // ) : string -> string -> string -(** Concatenate directory and filename. *) - -val quote : string -> string -(** Shell-safe quoting of a string (alias for {!Filename.quote}). *) - -val subdirectory : string -> string -> string -(** [subdirectory parent path] returns subdirectory part of [path] relative - to the [parent]. If [path] and [parent] point to the same directory empty - string is returned. - - Note: path normalization on arguments is {b not} performed! - - If [parent] is not a path prefix of [path] the function raises - [Invalid_argument]. *) - -val ( +^ ) : int64 -> int64 -> int64 -val ( -^ ) : int64 -> int64 -> int64 -val ( *^ ) : int64 -> int64 -> int64 -val ( /^ ) : int64 -> int64 -> int64 -val ( &^ ) : int64 -> int64 -> int64 -val ( ~^ ) : int64 -> int64 -(** Various int64 operators. *) - -external identity : 'a -> 'a = "%identity" - -val roundup64 : int64 -> int64 -> int64 -(** [roundup64 i a] returns [i] rounded up to the next multiple of [a]. *) -val div_roundup64 : int64 -> int64 -> int64 -(** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], - with the result divided by [a]. *) -val int_of_le32 : string -> int64 -(** Unpack a 4 byte string as a little endian 32 bit integer. *) -val le32_of_int : int64 -> string -(** Pack a 32 bit integer a 4 byte string stored little endian. *) - -val wrap : ?chan:out_channel -> ?indent:int -> string -> unit -(** Wrap text. *) - -val output_spaces : out_channel -> int -> unit -(** Write [n] spaces to [out_channel]. *) - -val (|>) : 'a -> ('a -> 'b) -> 'b -(** Added in OCaml 4.01, we can remove our definition when we - can assume this minimum version of OCaml. *) - -val dropwhile : ('a -> bool) -> 'a list -> 'a list -(** [dropwhile f xs] drops leading elements from [xs] until - [f] returns false. *) -val takewhile : ('a -> bool) -> 'a list -> 'a list -(** [takewhile f xs] takes leading elements from [xs] until - [f] returns false. - - For any list [xs] and function [f], - [xs = takewhile f xs @ dropwhile f xs] *) -val filter_map : ('a -> 'b option) -> 'a list -> 'b list -(** [filter_map f xs] applies [f] to each element of [xs]. If - [f x] returns [Some y] then [y] is added to the returned list. *) -val find_map : ('a -> 'b option) -> 'a list -> 'b -(** [find_map f xs] applies [f] to each element of [xs] until - [f x] returns [Some y]. It returns [y]. If we exhaust the - list then this raises [Not_found]. *) -val iteri : (int -> 'a -> 'b) -> 'a list -> unit -(** [iteri f xs] calls [f i x] for each element, with [i] counting from [0]. *) -val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list -(** [mapi f xs] calls [f i x] for each element, with [i] counting from [0], - forming the return values from [f] into another list. *) - -val combine3 : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list -(** Like {!List.combine} but for triples. All lists must be the same length. *) - -val assoc : ?cmp:('a -> 'a -> int) -> default:'b -> 'a -> ('a * 'b) list -> 'b -(** Like {!List.assoc} but with a user-defined comparison function, and - instead of raising [Not_found], it returns the [~default] value. *) - -val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -(** Uniquify a list (the list must be sorted first). *) - -val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -(** Sort and uniquify a list. *) - -val remove_duplicates : 'a list -> 'a list -(** Remove duplicates from an unsorted list; useful when the order - of the elements matter. - - Please use [sort_uniq] when the order does not matter. *) - -val push_back : 'a list ref -> 'a -> unit -val push_front : 'a -> 'a list ref -> unit -val pop_back : 'a list ref -> 'a -val pop_front : 'a list ref -> 'a -(** Imperative list manipulation functions, similar to C++ STL - functions with the same names. (Although the names are similar, - the computational complexity of the functions is quite different.) - - These operate on list references, and each function modifies the - list reference that is passed to it. - - [push_back xsp x] appends the element [x] to the end of the list - [xsp]. This function is not tail-recursive. - - [push_front x xsp] prepends the element [x] to the head of the - list [xsp]. (The arguments are reversed compared to the same Perl - function, but OCaml is type safe so that's OK.) - - [pop_back xsp] removes the last element of the list [xsp] and - returns it. The list is modified to become the list minus the - final element. If a zero-length list is passed in, this raises - [Failure "pop_back"]. This function is not tail-recursive. - - [pop_front xsp] removes the head element of the list [xsp] and - returns it. The list is modified to become the tail of the list. - If a zero-length list is passed in, this raises [Failure - "pop_front"]. *) - -val append : 'a list ref -> 'a list -> unit -val prepend : 'a list -> 'a list ref -> unit -(** More imperative list manipulation functions. - - [append] is like {!push_back} above, except it appends a list to - the list reference. This function is not tail-recursive. - - [prepend] is like {!push_front} above, except it prepends a list - to the list reference. *) - -val unique : unit -> int -(** Returns a unique number each time called. *) - -val may : ('a -> unit) -> 'a option -> unit -(** [may f (Some x)] runs [f x]. [may f None] does nothing. *) - -type ('a, 'b) maybe = Either of 'a | Or of 'b -(** Like the Haskell [Either] type. *) - -val protect : f:(unit -> 'a) -> finally:(unit -> unit) -> 'a -(** Execute [~f] and afterwards execute [~finally]. - - If [~f] throws an exception then [~finally] is run and the - original exception from [~f] is re-raised. - - If [~finally] throws an exception, then the original exception - is lost. (NB: Janestreet core {!Exn.protectx}, on which this - function is modelled, doesn't throw away the exception in this - case, but requires a lot more work by the caller. Perhaps we - will change this in future.) *) - -val failwithf : ('a, unit, string, 'b) format4 -> 'a -(** Like [failwith] but supports printf-like arguments. *) - -exception Executable_not_found of string (* executable *) -(** Exception thrown by [which] when the specified executable is not found - in [$PATH]. *) - -val which : string -> string -(** Return the full path of the specified executable from [$PATH]. - - Throw [Executable_not_found] if not available. *) - -val prog : string -(** The program name (derived from {!Sys.executable_name}). *) - -val set_quiet : unit -> unit -val quiet : unit -> bool -val set_trace : unit -> unit -val trace : unit -> bool -val set_verbose : unit -> unit -val verbose : unit -> bool -(** Stores the quiet ([--quiet]), trace ([-x]) and verbose ([-v]) flags - in global variables. *) - -(*</stdlib>*) - val message : ('a, unit, string, unit) format4 -> 'a (** Timestamped progress messages. Used for ordinary messages when not [--quiet]. *) @@ -328,13 +54,6 @@ val virt_tools_data_dir : unit -> string the environment variable is not set, a default value is calculated based on configure settings. *) -(*<stdlib>*) - -val read_whole_file : string -> string -(** Read in the whole file as a string. *) - -(*</stdlib>*) - val parse_size : string -> int64 (** Parse a size field, eg. [10G] *) @@ -354,20 +73,6 @@ val create_standard_options : Getopt.speclist -> ?anon_fun:Getopt.anon_fun -> ?k Returns a new [Getopt.t] handle. *) -(*<stdlib>*) - -val compare_version : string -> string -> int -(** Compare two version strings. *) - -val compare_lvm2_uuids : string -> string -> int -(** Compare two LVM2 UUIDs, ignoring '-' characters. *) - -val stringify_args : string list -> string -(** Create a "pretty-print" representation of a program invocation - (i.e. executable and its arguments). *) - -(*</stdlib>*) - val external_command : ?echo_cmd:bool -> string -> string list (** Run an external command, slurp up the output as a list of lines. @@ -408,13 +113,6 @@ val shell_command : ?echo_cmd:bool -> string -> int val uuidgen : unit -> string (** Run uuidgen to return a random UUID. *) -(*<stdlib>*) - -val unlink_on_exit : string -> unit -(** Unlink a temporary file on exit. *) - -(*</stdlib>*) - val rmdir_on_exit : string -> unit (** Remove a temporary directory on exit (using [rm -rf]). *) @@ -450,55 +148,10 @@ val debug_augeas_errors : Guestfs.guestfs -> unit val detect_file_type : string -> [`GZip | `Tar | `XZ | `Zip | `Unknown] (** Detect type of a file (for a very limited range of file types). *) -(*<stdlib>*) - -val is_block_device : string -> bool -val is_char_device : string -> bool -val is_directory : string -> bool -(** These don't throw exceptions, unlike the [Sys] functions. *) - -(*</stdlib>*) - val is_partition : string -> bool (** Return true if the host device [dev] is a partition. If it's anything else, or missing, returns false. *) -(*<stdlib>*) - -val absolute_path : string -> string -(** Convert any path to an absolute path. *) - -val qemu_input_filename : string -> string -(** Sanitizes a filename for passing it safely to qemu/qemu-img. *) - -val mkdir_p : string -> int -> unit -(** Creates a directory, and its parents if missing. *) - -val normalize_arch : string -> string -(** Normalize the architecture name, i.e. maps it into a defined - identifier for it -- e.g. i386, i486, i586, and i686 are - normalized as i386. *) - -val guest_arch_compatible : string -> bool -(** Are guest arch and host_cpu compatible, in terms of being able - to run commands in the libguestfs appliance? *) - -val unix_like : string -> bool -(** Is the guest OS "Unix-like"? Call this with the result of - {!Guestfs.inspect_get_type}. *) - -val last_part_of : string -> char -> string option -(** Return the last part of a string, after the specified separator. *) - -val read_first_line_from_file : string -> string -(** Read only the first line (i.e. until the first newline character) - of a file. If the file is empty this returns an empty string. *) - -val is_regular_file : string -> bool -(** Checks whether the file is a regular file. *) - -(*</stdlib>*) - val inspect_mount_root : Guestfs.guestfs -> ?mount_opts_fn:(string -> string) -> string -> unit (** Mounts all the mount points of the specified root, just like [guestfish -i] does. diff --git a/mllib/common_utils_tests.ml b/mllib/common_utils_tests.ml index 4c9f53fdf..ee8a60463 100644 --- a/mllib/common_utils_tests.ml +++ b/mllib/common_utils_tests.ml @@ -19,26 +19,16 @@ (* This file tests the Common_utils module. *) open OUnit2 + +open Std_utils open Common_utils (* Utils. *) let assert_equal_string = assert_equal ~printer:(fun x -> x) let assert_equal_int = assert_equal ~printer:(fun x -> string_of_int x) let assert_equal_int64 = assert_equal ~printer:(fun x -> Int64.to_string x) -let assert_equal_stringlist = assert_equal ~printer:(fun x -> "(" ^ (String.escaped (String.concat "," x)) ^ ")") let assert_equal_intlist = assert_equal ~printer:(fun x -> "(" ^ (String.concat ";" (List.map string_of_int x)) ^ ")") -let test_subdirectory ctx - assert_equal_string "" (subdirectory "/foo" "/foo"); - assert_equal_string "" (subdirectory "/foo" "/foo/"); - assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); - assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") - -(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) -let test_le32 ctx - assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); - assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) - (* Test Common_utils.parse_size. *) let test_parse_resize ctx (* For absolute sizes, oldsize is ignored. *) @@ -91,47 +81,6 @@ let test_human_size ctx assert_equal_string "3.4G" (human_size 3650722201_L); assert_equal_string "-3.4G" (human_size (-3650722201_L)) -(* Test Common_utils.String.is_prefix. *) -let test_string_is_prefix ctx - assert_bool "String.is_prefix,," (String.is_prefix "" ""); - assert_bool "String.is_prefix,foo," (String.is_prefix "foo" ""); - assert_bool "String.is_prefix,foo,foo" (String.is_prefix "foo" "foo"); - assert_bool "String.is_prefix,foo123,foo" (String.is_prefix "foo123" "foo"); - assert_bool "not (String.is_prefix,,foo" (not (String.is_prefix "" "foo")) - -(* Test Common_utils.String.is_suffix. *) -let test_string_is_suffix ctx - assert_bool "String.is_suffix,," (String.is_suffix "" ""); - assert_bool "String.is_suffix,foo," (String.is_suffix "foo" ""); - assert_bool "String.is_suffix,foo,foo" (String.is_suffix "foo" "foo"); - assert_bool "String.is_suffix,123foo,foo" (String.is_suffix "123foo" "foo"); - assert_bool "not String.is_suffix,,foo" (not (String.is_suffix "" "foo")) - -(* Test Common_utils.String.find. *) -let test_string_find ctx - assert_equal_int 0 (String.find "" ""); - assert_equal_int 0 (String.find "foo" ""); - assert_equal_int 1 (String.find "foo" "o"); - assert_equal_int 3 (String.find "foobar" "bar"); - assert_equal_int (-1) (String.find "" "baz"); - assert_equal_int (-1) (String.find "foobar" "baz") - -(* Test Common_utils.String.lines_split. *) -let test_string_lines_split ctx - assert_equal_stringlist [""] (String.lines_split ""); - assert_equal_stringlist ["A"] (String.lines_split "A"); - assert_equal_stringlist ["A"; ""] (String.lines_split "A\n"); - assert_equal_stringlist ["A"; "B"] (String.lines_split "A\nB"); - assert_equal_stringlist ["A"; "B"; "C"] (String.lines_split "A\nB\nC"); - assert_equal_stringlist ["A"; "B"; "C"; "D"] (String.lines_split "A\nB\nC\nD"); - assert_equal_stringlist ["A\n"] (String.lines_split "A\\"); - assert_equal_stringlist ["A\nB"] (String.lines_split "A\\\nB"); - assert_equal_stringlist ["A"; "B\nC"] (String.lines_split "A\nB\\\nC"); - assert_equal_stringlist ["A"; "B\nC"; "D"] (String.lines_split "A\nB\\\nC\nD"); - assert_equal_stringlist ["A"; "B\nC\nD"] (String.lines_split "A\nB\\\nC\\\nD"); - assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); - assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") - (* Test Common_utils.run_command. *) let test_run_command ctx assert_equal_int 0 (run_command ["true"]); @@ -203,14 +152,8 @@ let test_run_commands ctx let suite "mllib Common_utils" >::: [ - "subdirectory" >:: test_subdirectory; - "numeric.le32" >:: test_le32; "sizes.parse_resize" >:: test_parse_resize; "sizes.human_size" >:: test_human_size; - "strings.is_prefix" >:: test_string_is_prefix; - "strings.is_suffix" >:: test_string_is_suffix; - "strings.find" >:: test_string_find; - "strings.lines_split" >:: test_string_lines_split; "run_command" >:: test_run_command; "run_commands" >:: test_run_commands; ] diff --git a/mllib/curl.ml b/mllib/curl.ml index ed0b8960a..ccf98acef 100644 --- a/mllib/curl.ml +++ b/mllib/curl.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils type t = { diff --git a/mllib/getopt_tests.ml b/mllib/getopt_tests.ml index 9d432e922..22e4282fa 100644 --- a/mllib/getopt_tests.ml +++ b/mllib/getopt_tests.ml @@ -22,6 +22,7 @@ open Printf +open Std_utils open Common_utils open Getopt.OptionName diff --git a/mllib/regedit.ml b/mllib/regedit.ml index dd03f5a23..e07700bb1 100644 --- a/mllib/regedit.ml +++ b/mllib/regedit.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/mllib/registry.ml b/mllib/registry.ml index 767092c6d..8d62e3bb5 100644 --- a/mllib/registry.ml +++ b/mllib/registry.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type node = int64 type value = int64 diff --git a/mllib/xpath_helpers.ml b/mllib/xpath_helpers.ml index d651fab23..e6185bf3d 100644 --- a/mllib/xpath_helpers.ml +++ b/mllib/xpath_helpers.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext (* Parse an xpath expression and return a string/int. Returns * [Some v], or [None] if the expression doesn't match. diff --git a/resize/Makefile.am b/resize/Makefile.am index c35c3a78a..3707d73b4 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -61,6 +61,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT @@ -84,6 +85,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -91,6 +93,7 @@ OCAMLLINKFLAGS = \ virt_resize_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_resize_LINK = \ @@ -135,7 +138,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/resize/resize.ml b/resize/resize.ml index c52db199f..fbb2d021b 100644 --- a/resize/resize.ml +++ b/resize/resize.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Unix_utils diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index 97236829e..a1395ccbd 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -66,6 +66,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT @@ -89,6 +90,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -96,6 +98,7 @@ OCAMLLINKFLAGS = \ virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_sparsify_LINK = \ @@ -142,7 +145,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sparsify/cmdline.ml b/sparsify/cmdline.ml index 4629aa7a4..6e0594f12 100644 --- a/sparsify/cmdline.ml +++ b/sparsify/cmdline.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/sparsify/copying.ml b/sparsify/copying.ml index 9042bd53d..02a53b9b4 100644 --- a/sparsify/copying.ml +++ b/sparsify/copying.ml @@ -23,6 +23,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Unix_utils diff --git a/sparsify/in_place.ml b/sparsify/in_place.ml index 88f30c0b3..1f3da2c70 100644 --- a/sparsify/in_place.ml +++ b/sparsify/in_place.ml @@ -21,6 +21,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/sparsify/utils.ml b/sparsify/utils.ml index 3bb64b737..27723c3a2 100644 --- a/sparsify/utils.ml +++ b/sparsify/utils.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils module G = Guestfs diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 68cb1814a..c2adb1a6e 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -112,6 +112,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/visit/.libs \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -137,6 +138,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ mlvisit.$(MLARCHIVE) \ @@ -145,6 +147,7 @@ OCAMLLINKFLAGS = \ virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -213,7 +216,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sysprep/main.ml b/sysprep/main.ml index 82164c62f..ab631c479 100644 --- a/sysprep/main.ml +++ b/sysprep/main.ml @@ -19,6 +19,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/sysprep/sysprep_operation.ml b/sysprep/sysprep_operation.ml index b2286f642..17d298fc1 100644 --- a/sysprep/sysprep_operation.ml +++ b/sysprep/sysprep_operation.ml @@ -16,10 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_utils - open Printf +open Std_utils +open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/sysprep/sysprep_operation_backup_files.ml b/sysprep/sysprep_operation_backup_files.ml index 6b1a100e6..64df8d758 100644 --- a/sysprep/sysprep_operation_backup_files.ml +++ b/sysprep/sysprep_operation_backup_files.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Visit open Unix_utils.Fnmatch open Sysprep_operation diff --git a/sysprep/sysprep_operation_cron_spool.ml b/sysprep/sysprep_operation_cron_spool.ml index 063f75a83..f48a5201a 100644 --- a/sysprep/sysprep_operation_cron_spool.ml +++ b/sysprep/sysprep_operation_cron_spool.ml @@ -16,9 +16,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Sysprep_operation -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext + +open Sysprep_operation module G = Guestfs diff --git a/sysprep/sysprep_operation_net_hostname.ml b/sysprep/sysprep_operation_net_hostname.ml index 7284d630f..b455e5c93 100644 --- a/sysprep/sysprep_operation_net_hostname.ml +++ b/sysprep/sysprep_operation_net_hostname.ml @@ -16,10 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils -open Sysprep_operation open Common_gettext.Gettext +open Sysprep_operation + module G = Guestfs let net_hostname_perform (g : Guestfs.guestfs) root side_effects diff --git a/sysprep/sysprep_operation_net_hwaddr.ml b/sysprep/sysprep_operation_net_hwaddr.ml index 439da6d81..21cae1be4 100644 --- a/sysprep/sysprep_operation_net_hwaddr.ml +++ b/sysprep/sysprep_operation_net_hwaddr.ml @@ -16,10 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils -open Sysprep_operation open Common_gettext.Gettext +open Sysprep_operation + module G = Guestfs let net_hwaddr_perform (g : Guestfs.guestfs) root side_effects diff --git a/sysprep/sysprep_operation_script.ml b/sysprep/sysprep_operation_script.ml index aa656727e..cf911043a 100644 --- a/sysprep/sysprep_operation_script.ml +++ b/sysprep/sysprep_operation_script.ml @@ -19,9 +19,10 @@ open Printf open Unix -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Getopt.OptionName open Sysprep_operation diff --git a/sysprep/sysprep_operation_user_account.ml b/sysprep/sysprep_operation_user_account.ml index 6f44b9dfd..2a633f5d8 100644 --- a/sysprep/sysprep_operation_user_account.ml +++ b/sysprep/sysprep_operation_user_account.ml @@ -19,6 +19,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/v2v/DOM.ml b/v2v/DOM.ml index 29ce64fa6..9986fc912 100644 --- a/v2v/DOM.ml +++ b/v2v/DOM.ml @@ -18,6 +18,7 @@ (* Poor man's XML DOM, mutable for ease of modification. *) +open Std_utils open Common_utils open Printf diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 2de99ceb9..8a831a700 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -146,6 +146,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -170,6 +171,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlxml.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -210,6 +212,7 @@ endif virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -495,6 +498,7 @@ endif v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -510,7 +514,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/changeuid.ml b/v2v/changeuid.ml index dbb05bc94..639fcfe12 100644 --- a/v2v/changeuid.ml +++ b/v2v/changeuid.ml @@ -21,9 +21,10 @@ open Unix open Printf +open Std_utils open Common_utils -open Common_gettext.Gettext open Unix_utils +open Common_gettext.Gettext open Utils diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml index 70301ab40..a19510b3f 100644 --- a/v2v/cmdline.ml +++ b/v2v/cmdline.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Types diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index 42a19947b..ffb43564f 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -28,8 +28,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils open Types diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml index dfb90d079..2c8708878 100644 --- a/v2v/convert_windows.ml +++ b/v2v/convert_windows.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils open Types diff --git a/v2v/copy_to_local.ml b/v2v/copy_to_local.ml index 88fd9abde..0a2b7ed75 100644 --- a/v2v/copy_to_local.ml +++ b/v2v/copy_to_local.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index 246cacd21..3f22f3764 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/create_ovf.ml b/v2v/create_ovf.ml index 6c7aba6d7..fd7ec5fe8 100644 --- a/v2v/create_ovf.ml +++ b/v2v/create_ovf.ml @@ -18,12 +18,13 @@ (* Create OVF and related files for RHV. *) -open Common_gettext.Gettext -open Common_utils - open Unix open Printf +open Std_utils +open Common_utils +open Common_gettext.Gettext + open Types open Utils open DOM diff --git a/v2v/input_disk.ml b/v2v/input_disk.ml index d28f45ece..a92f3a602 100644 --- a/v2v/input_disk.ml +++ b/v2v/input_disk.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/input_libvirtxml.ml b/v2v/input_libvirtxml.ml index d829ee523..570541d7d 100644 --- a/v2v/input_libvirtxml.ml +++ b/v2v/input_libvirtxml.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Parse_libvirt_xml diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml index b509326dd..e8be68ed7 100644 --- a/v2v/input_ova.ml +++ b/v2v/input_ova.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml index c48a0155a..bb1650ae9 100644 --- a/v2v/input_vmx.ml +++ b/v2v/input_vmx.ml @@ -19,8 +19,9 @@ open Printf open Scanf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/inspect_source.ml b/v2v/inspect_source.ml index 7476c3d85..e5d1fd3aa 100644 --- a/v2v/inspect_source.ml +++ b/v2v/inspect_source.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/linux.ml b/v2v/linux.ml index 5f40c4196..799654511 100644 --- a/v2v/linux.ml +++ b/v2v/linux.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/linux_bootloaders.ml b/v2v/linux_bootloaders.ml index 33a6dc4e9..b5ad25508 100644 --- a/v2v/linux_bootloaders.ml +++ b/v2v/linux_bootloaders.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/linux_kernels.ml b/v2v/linux_kernels.ml index e8c3a93c6..6e1ca4bf1 100644 --- a/v2v/linux_kernels.ml +++ b/v2v/linux_kernels.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types diff --git a/v2v/modules_list.ml b/v2v/modules_list.ml index 3ee0bd7dc..e3c6d5934 100644 --- a/v2v/modules_list.ml +++ b/v2v/modules_list.ml @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_utils +open Std_utils let input_modules = ref [] and output_modules = ref [] diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml index 3feb2e493..e26bc0732 100644 --- a/v2v/output_glance.ml +++ b/v2v/output_glance.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml index b3e695387..61e1efddb 100644 --- a/v2v/output_libvirt.ml +++ b/v2v/output_libvirt.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_local.ml b/v2v/output_local.ml index 9c105ef8d..3553150ff 100644 --- a/v2v/output_local.ml +++ b/v2v/output_local.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_null.ml b/v2v/output_null.ml index b0e99b4de..9b31c2d00 100644 --- a/v2v/output_null.ml +++ b/v2v/output_null.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml index 031279cb3..00814e8f0 100644 --- a/v2v/output_qemu.ml +++ b/v2v/output_qemu.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_rhv.ml b/v2v/output_rhv.ml index 82e745a94..0c02df612 100644 --- a/v2v/output_rhv.ml +++ b/v2v/output_rhv.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Unix open Printf diff --git a/v2v/output_vdsm.ml b/v2v/output_vdsm.ml index d8cd20156..361a8e555 100644 --- a/v2v/output_vdsm.ml +++ b/v2v/output_vdsm.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Unix open Printf diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index 4ac9b51a5..56f9ea297 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -18,12 +18,13 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils - -open Types +open Common_gettext.Gettext open Xpath_helpers +open Types + type parsed_disk = { p_source_disk : source_disk; p_source : parsed_source; diff --git a/v2v/parse_ovf_from_ova.ml b/v2v/parse_ovf_from_ova.ml index 2a3752776..6dc032407 100644 --- a/v2v/parse_ovf_from_ova.ml +++ b/v2v/parse_ovf_from_ova.ml @@ -18,9 +18,10 @@ (* Parse OVF from an externally produced OVA file. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/parse_vmx.ml b/v2v/parse_vmx.ml index 33ec17d3d..770dc29d3 100644 --- a/v2v/parse_vmx.ml +++ b/v2v/parse_vmx.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/target_bus_assignment.ml b/v2v/target_bus_assignment.ml index a9010c245..de6b0148d 100644 --- a/v2v/target_bus_assignment.ml +++ b/v2v/target_bus_assignment.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/test-harness/Makefile.am b/v2v/test-harness/Makefile.am index 9a548022a..bcfcdf21d 100644 --- a/v2v/test-harness/Makefile.am +++ b/v2v/test-harness/Makefile.am @@ -42,6 +42,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/v2v @@ -129,7 +130,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/test-harness/v2v_test_harness.ml b/v2v/test-harness/v2v_test_harness.ml index 3c29a9430..ba8c5eeab 100644 --- a/v2v/test-harness/v2v_test_harness.ml +++ b/v2v/test-harness/v2v_test_harness.ml @@ -23,6 +23,7 @@ module D = Libvirt.Domain open Unix open Printf +open Std_utils open Common_utils type test_plan = { diff --git a/v2v/utils.ml b/v2v/utils.ml index e0275db53..0dab5816e 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext external drive_name : int -> string = "v2v_utils_drive_name" external drive_index : string -> int = "v2v_utils_drive_index" diff --git a/v2v/v2v.ml b/v2v/v2v.ml index 59f5ef17e..f1ce9335a 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -19,9 +19,10 @@ open Unix open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/v2v_unit_tests.ml b/v2v/v2v_unit_tests.ml index 7f98e09d3..be0bf0172 100644 --- a/v2v/v2v_unit_tests.ml +++ b/v2v/v2v_unit_tests.ml @@ -18,13 +18,15 @@ (* This file tests individual virt-v2v functions. *) -open OUnit2 -open Types - open Printf +open OUnit2 + +open Std_utils open Common_utils +open Types + let inspect_defaults = { i_type = ""; i_distro = ""; i_arch = ""; i_major_version = 0; i_minor_version = 0; diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml index 468261d3d..d84bf8b58 100644 --- a/v2v/vCenter.ml +++ b/v2v/vCenter.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/windows_virtio.ml b/v2v/windows_virtio.ml index 9891a770c..76af7ab2f 100644 --- a/v2v/windows_virtio.ml +++ b/v2v/windows_virtio.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Regedit -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 05/42] common/mlstdutils: Implement complete set of byte swapping functions.
This implements all of: val int_of_le16 : string -> int64 val le16_of_int : int64 -> string val int_of_be16 : string -> int64 val be16_of_int : int64 -> string val int_of_le32 : string -> int64 val le32_of_int : int64 -> string val int_of_be32 : string -> int64 val be32_of_int : int64 -> string val int_of_le64 : string -> int64 val le64_of_int : int64 -> string val int_of_be64 : string -> int64 val be64_of_int : int64 -> string and tests. --- common/mlstdutils/std_utils.ml | 131 +++++++++++++++++++++++++++++++++++ common/mlstdutils/std_utils.mli | 23 +++++- common/mlstdutils/std_utils_tests.ml | 22 ++++-- 3 files changed, 169 insertions(+), 7 deletions(-) diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml index b91785a9c..363b761ce 100644 --- a/common/mlstdutils/std_utils.ml +++ b/common/mlstdutils/std_utils.ml @@ -272,6 +272,21 @@ external identity : 'a -> 'a = "%identity" let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) let div_roundup64 i a = (i +^ a -^ 1L) /^ a +let int_of_le16 str + assert (String.length str = 2); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) + +let le16_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let b = Bytes.create 2 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.to_string b + let int_of_le32 str assert (String.length str = 4); let c0 = Char.code (String.unsafe_get str 0) in @@ -295,6 +310,122 @@ let le32_of_int i Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); Bytes.to_string b +let int_of_le64 str + assert (String.length str = 8); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + let c4 = Char.code (String.unsafe_get str 4) in + let c5 = Char.code (String.unsafe_get str 5) in + let c6 = Char.code (String.unsafe_get str 6) in + let c7 = Char.code (String.unsafe_get str 7) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) +^ + (Int64.shift_left (Int64.of_int c2) 16) +^ + (Int64.shift_left (Int64.of_int c3) 24) +^ + (Int64.shift_left (Int64.of_int c4) 32) +^ + (Int64.shift_left (Int64.of_int c5) 40) +^ + (Int64.shift_left (Int64.of_int c6) 48) +^ + (Int64.shift_left (Int64.of_int c7) 56) + +let le64_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let c4 = Int64.shift_right (i &^ 0xff00000000L) 32 in + let c5 = Int64.shift_right (i &^ 0xff0000000000L) 40 in + let c6 = Int64.shift_right (i &^ 0xff000000000000L) 48 in + let c7 = Int64.shift_right (i &^ 0xff00000000000000L) 56 in + let b = Bytes.create 8 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 4 (Char.unsafe_chr (Int64.to_int c4)); + Bytes.unsafe_set b 5 (Char.unsafe_chr (Int64.to_int c5)); + Bytes.unsafe_set b 6 (Char.unsafe_chr (Int64.to_int c6)); + Bytes.unsafe_set b 7 (Char.unsafe_chr (Int64.to_int c7)); + Bytes.to_string b + +let int_of_be16 str + assert (String.length str = 2); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + Int64.of_int c1 +^ + (Int64.shift_left (Int64.of_int c0) 8) + +let be16_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let b = Bytes.create 2 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + +let int_of_be32 str + assert (String.length str = 4); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + Int64.of_int c3 +^ + (Int64.shift_left (Int64.of_int c2) 8) +^ + (Int64.shift_left (Int64.of_int c1) 16) +^ + (Int64.shift_left (Int64.of_int c0) 24) + +let be32_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let b = Bytes.create 4 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + +let int_of_be64 str + assert (String.length str = 8); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + let c4 = Char.code (String.unsafe_get str 4) in + let c5 = Char.code (String.unsafe_get str 5) in + let c6 = Char.code (String.unsafe_get str 6) in + let c7 = Char.code (String.unsafe_get str 7) in + Int64.of_int c7 +^ + (Int64.shift_left (Int64.of_int c6) 8) +^ + (Int64.shift_left (Int64.of_int c5) 16) +^ + (Int64.shift_left (Int64.of_int c4) 24) +^ + (Int64.shift_left (Int64.of_int c3) 32) +^ + (Int64.shift_left (Int64.of_int c2) 40) +^ + (Int64.shift_left (Int64.of_int c1) 48) +^ + (Int64.shift_left (Int64.of_int c0) 56) + +let be64_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let c4 = Int64.shift_right (i &^ 0xff00000000L) 32 in + let c5 = Int64.shift_right (i &^ 0xff0000000000L) 40 in + let c6 = Int64.shift_right (i &^ 0xff000000000000L) 48 in + let c7 = Int64.shift_right (i &^ 0xff00000000000000L) 56 in + let b = Bytes.create 8 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c7)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c6)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c5)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c4)); + Bytes.unsafe_set b 4 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 5 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 6 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 7 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + type wrap_break_t = WrapEOS | WrapSpace | WrapNL let rec wrap ?(chan = stdout) ?(indent = 0) str diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli index c0f1da30c..a07c1e6dc 100644 --- a/common/mlstdutils/std_utils.mli +++ b/common/mlstdutils/std_utils.mli @@ -143,10 +143,29 @@ val roundup64 : int64 -> int64 -> int64 val div_roundup64 : int64 -> int64 -> int64 (** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], with the result divided by [a]. *) + +val int_of_le16 : string -> int64 +val le16_of_int : int64 -> string +val int_of_be16 : string -> int64 +val be16_of_int : int64 -> string val int_of_le32 : string -> int64 -(** Unpack a 4 byte string as a little endian 32 bit integer. *) val le32_of_int : int64 -> string -(** Pack a 32 bit integer a 4 byte string stored little endian. *) +val int_of_be32 : string -> int64 +val be32_of_int : int64 -> string +val int_of_le64 : string -> int64 +val le64_of_int : int64 -> string +val int_of_be64 : string -> int64 +val be64_of_int : int64 -> string +(** [int_of_X] functions unpack a string and return the equivalent integer. + + [X_of_int] functions pack an integer into a string. + + The value of [X] encodes whether the string is stored as + little endian [le] or big endian [be] and the size in bits + [16], [32] or [64]. + + On the OCaml side, 64 bit integers are always used so that you + can use the [.^] operators on them for bit manipulation. *) val wrap : ?chan:out_channel -> ?indent:int -> string -> unit (** Wrap text. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml index 1003f931c..6bc74fb63 100644 --- a/common/mlstdutils/std_utils_tests.ml +++ b/common/mlstdutils/std_utils_tests.ml @@ -33,10 +33,22 @@ let test_subdirectory ctx assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") -(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) -let test_le32 ctx - assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); - assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) +(* Test Std_utils.int_of_X and Std_utils.X_of_int byte swapping + * functions. + *) +let rec test_byteswap ctx + test_swap int_of_le16 le16_of_int 0x2040L "\x40\x20"; + test_swap int_of_le32 le32_of_int 0x20406080L "\x80\x60\x40\x20"; + test_swap int_of_le64 le64_of_int + 0x20406080A0C0E0F0L "\xF0\xE0\xC0\xA0\x80\x60\x40\x20"; + test_swap int_of_be16 be16_of_int 0x2040L "\x20\x40"; + test_swap int_of_be32 be32_of_int 0x20406080L "\x20\x40\x60\x80"; + test_swap int_of_be64 be64_of_int + 0x20406080A0C0E0F0L "\x20\x40\x60\x80\xA0\xC0\xE0\xF0" + +and test_swap int_of_x x_of_int i s + assert_equal_int64 i (int_of_x s); + assert_equal_string s (x_of_int i) (* Test Std_utils.String.is_prefix. *) let test_string_is_prefix ctx @@ -84,7 +96,7 @@ let suite "mllib Std_utils" >::: [ "subdirectory" >:: test_subdirectory; - "numeric.le32" >:: test_le32; + "numeric.byteswap" >:: test_byteswap; "strings.is_prefix" >:: test_string_is_prefix; "strings.is_suffix" >:: test_string_is_suffix; "strings.find" >:: test_string_find; -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 06/42] common/mlstdutils: Implement ‘Char.mem’, ‘String.span’ and ‘String.cspan’.
Char.mem tells you if a byte is a member of a string. String.span and String.cspan are like the C functions strspn and strcspn. --- common/mlstdutils/std_utils.ml | 20 ++++++++++++++++++++ common/mlstdutils/std_utils.mli | 15 +++++++++++++++ common/mlstdutils/std_utils_tests.ml | 21 +++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml index 363b761ce..374507662 100644 --- a/common/mlstdutils/std_utils.ml +++ b/common/mlstdutils/std_utils.ml @@ -74,6 +74,8 @@ module Char = struct | 'e' | 'E' -> 14 | 'f' | 'F' -> 15 | _ -> -1 + + let mem c str = String.contains str c end module String = struct @@ -246,6 +248,24 @@ module String = struct List.map f (explode str) let spaces n = String.make n ' ' + + let span str accept + let len = String.length str in + let rec loop i + if i >= len then len + else if Char.mem (String.unsafe_get str i) accept then loop (i+1) + else i + in + loop 0 + + let cspan str reject + let len = String.length str in + let rec loop i + if i >= len then len + else if Char.mem (String.unsafe_get str i) reject then i + else loop (i+1) + in + loop 0 end let (//) = Filename.concat diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli index a07c1e6dc..0b7d90736 100644 --- a/common/mlstdutils/std_utils.mli +++ b/common/mlstdutils/std_utils.mli @@ -41,6 +41,12 @@ module Char : sig val hexdigit : char -> int (** Return the value of a hex digit. If the char is not in the set [[0-9a-fA-F]] then this returns [-1]. *) + + val mem : char -> string -> bool + (** [mem c str] returns true if the byte [c] is contained in [str]. + + This is actually the same as {!String.contains} with the + parameters reversed. *) end (** Override the Char module from stdlib. *) @@ -109,6 +115,15 @@ module String : sig (** Explode string, then map function over the characters. *) val spaces : int -> string (** [spaces n] creates a string of n spaces. *) + val span : string -> string -> int + val cspan : string -> string -> int + (** [span str accept] returns the length in bytes of the initial + segment of [str] which contains only bytes in [accept]. + + [cspan str reject] returns the length in bytes of the initial + segment of [str] which contains only bytes {!i not} in [reject]. + + These work exactly like the C functions [strspn] and [strcspn]. *) end (** Override the String module from stdlib. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml index 6bc74fb63..2789766c6 100644 --- a/common/mlstdutils/std_utils_tests.ml +++ b/common/mlstdutils/std_utils_tests.ml @@ -50,6 +50,14 @@ and test_swap int_of_x x_of_int i s assert_equal_int64 i (int_of_x s); assert_equal_string s (x_of_int i) +(* Test Std_utils.Char.mem. *) +let test_char_mem ctx + assert_bool "Char.mem" (Char.mem 'a' "abc"); + assert_bool "Char.mem" (Char.mem 'b' "abc"); + assert_bool "Char.mem" (Char.mem 'c' "abc"); + assert_bool "Char.mem" (not (Char.mem 'd' "abc")); + assert_bool "Char.mem" (not (Char.mem 'a' "")) + (* Test Std_utils.String.is_prefix. *) let test_string_is_prefix ctx assert_bool "String.is_prefix,," (String.is_prefix "" ""); @@ -91,16 +99,29 @@ let test_string_lines_split ctx assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") +(* Test Std_utils.String.span and cspan. *) +let test_string_span ctx + assert_equal_int 3 (String.span "aaabb" "a"); + assert_equal_int 3 (String.span "aaaba" "a"); + assert_equal_int 3 (String.span "aba" "ab"); + assert_equal_int 0 (String.span "" "ab"); + assert_equal_int 3 (String.cspan "defab" "ab"); + assert_equal_int 3 (String.cspan "defba" "ab"); + assert_equal_int 3 (String.cspan "def" "ab"); + assert_equal_int 0 (String.cspan "" "ab") + (* Suites declaration. *) let suite "mllib Std_utils" >::: [ "subdirectory" >:: test_subdirectory; "numeric.byteswap" >:: test_byteswap; + "char.mem" >:: test_char_mem; "strings.is_prefix" >:: test_string_is_prefix; "strings.is_suffix" >:: test_string_is_suffix; "strings.find" >:: test_string_find; "strings.lines_split" >:: test_string_lines_split; + "strings.span" >:: test_string_span; ] let () -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 07/42] common/utils: Move ‘uefi.c’ to ‘lib/’.
This was only used inside the library, so move it there. --- .gitignore | 2 +- common/utils/Makefile.am | 4 +--- common/utils/guestfs-internal-frontend.h | 12 ------------ docs/C_SOURCE_FILES | 2 +- generator/UEFI.ml | 3 ++- generator/main.ml | 2 +- lib/Makefile.am | 3 ++- lib/appliance-uefi.c | 2 +- lib/guestfs-internal.h | 12 ++++++++++++ 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 3cd1fbb0d..772c12b59 100644 --- a/.gitignore +++ b/.gitignore @@ -140,7 +140,6 @@ Makefile.in /common/utils/structs-cleanup.c /common/utils/structs-print.c /common/utils/structs-print.h -/common/utils/uefi.c /compile /config.cache /config.guess @@ -337,6 +336,7 @@ Makefile.in /lib/structs-copy.c /lib/structs-free.c /lib/unit-tests +/lib/uefi.c /libguestfs.spec /libguestfs-*.tar.gz /libtool diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 81a567b86..93f42293a 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -21,8 +21,7 @@ generator_built = \ guestfs-internal-frontend-cleanups.h \ structs-cleanup.c \ structs-print.c \ - structs-print.h \ - uefi.c + structs-print.h BUILT_SOURCES = \ $(generator_built) @@ -40,7 +39,6 @@ libutils_la_SOURCES = \ structs-cleanup.c \ structs-print.c \ structs-print.h \ - uefi.c \ utils.c libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index e48f4eb49..489b54ffd 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -102,18 +102,6 @@ extern void guestfs_int_fadvise_noreuse (int fd); //extern void guestfs_int_fadvise_willneed (int fd); extern char *guestfs_int_shell_unquote (const char *str); -/* uefi.c */ -struct uefi_firmware { - const char *code; /* code file (NULL = end of list) */ - const char *code_debug; /* code file with debugging msgs (may be NULL)*/ - const char *vars; /* vars template file */ - int flags; /* various flags, see below */ -#define UEFI_FLAG_SECURE_BOOT_REQUIRED 1 /* secure boot (see RHBZ#1367615) */ -}; -extern struct uefi_firmware guestfs_int_uefi_i386_firmware[]; -extern struct uefi_firmware guestfs_int_uefi_x86_64_firmware[]; -extern struct uefi_firmware guestfs_int_uefi_aarch64_firmware[]; - /* These functions are used internally by the CLEANUP_* macros. * Don't call them directly. */ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 71c17603f..ccb3a091a 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -48,7 +48,6 @@ common/utils/guestfs-internal-frontend.h common/utils/structs-cleanup.c common/utils/structs-print.c common/utils/structs-print.h -common/utils/uefi.c common/utils/utils.c common/visit/visit.c common/visit/visit.h @@ -331,6 +330,7 @@ lib/structs-copy.c lib/structs-free.c lib/tmpdirs.c lib/tsk.c +lib/uefi.c lib/umask.c lib/unit-tests.c lib/version.c diff --git a/generator/UEFI.ml b/generator/UEFI.ml index 5c5e02bab..17418f473 100644 --- a/generator/UEFI.ml +++ b/generator/UEFI.ml @@ -86,7 +86,8 @@ let generate_uefi_c () pr "\n"; pr "#include <stdio.h>\n"; pr "\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"guestfs.h\"\n"; + pr "#include \"guestfs-internal.h\"\n"; List.iter ( fun arch -> diff --git a/generator/main.ml b/generator/main.ml index 0e1c01f74..8ff698130 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -94,7 +94,7 @@ Run it from the top source directory using the command C.generate_client_structs_print_c; output_to "common/utils/structs-print.h" C.generate_client_structs_print_h; - output_to "common/utils/uefi.c" + output_to "lib/uefi.c" UEFI.generate_uefi_c; output_to "lib/guestfs.h" C.generate_guestfs_h; diff --git a/lib/Makefile.am b/lib/Makefile.am index 360ce9c92..81841e6e1 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -124,10 +124,11 @@ libguestfs_la_SOURCES = \ structs-free.c \ tmpdirs.c \ tsk.c \ + uefi.c \ umask.c \ + version.c \ wait.c \ whole-file.c \ - version.c \ yara.c \ libguestfs.syms diff --git a/lib/appliance-uefi.c b/lib/appliance-uefi.c index 1612c5db5..986989e67 100644 --- a/lib/appliance-uefi.c +++ b/lib/appliance-uefi.c @@ -19,7 +19,7 @@ /** * Find the UEFI firmware needed to boot the appliance. * - * See also F<common/utils/uefi.c> (autogenerated file) containing the + * See also F<lib/uefi.c> (autogenerated file) containing the * firmware file locations. */ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index ec70336e2..190b5cdd2 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -1011,4 +1011,16 @@ extern bool guestfs_int_version_cmp_ge (const struct version *a, const struct ve #define version_init_null(v) guestfs_int_version_from_values (v, 0, 0, 0) #define version_is_null(v) ((v)->v_major == 0 && (v)->v_minor == 0 && (v)->v_micro == 0) +/* uefi.c */ +struct uefi_firmware { + const char *code; /* code file (NULL = end of list) */ + const char *code_debug; /* code file with debugging msgs (may be NULL)*/ + const char *vars; /* vars template file */ + int flags; /* various flags, see below */ +#define UEFI_FLAG_SECURE_BOOT_REQUIRED 1 /* secure boot (see RHBZ#1367615) */ +}; +extern struct uefi_firmware guestfs_int_uefi_i386_firmware[]; +extern struct uefi_firmware guestfs_int_uefi_x86_64_firmware[]; +extern struct uefi_firmware guestfs_int_uefi_aarch64_firmware[]; + #endif /* GUESTFS_INTERNAL_H_ */ -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 08/42] common/utils: Refactor stdlib, gnulib and libxml2 cleanup functions.
This refactoring change just moves the cleanup functions around in the common/utils directory. libxml2 cleanups are moved to a separate object file, so that we can still link to libutils even if the main program is not using libxml2 anywhere. cleanup.c is renamed to cleanups.c. A new header file cleanups.h is introduced which will replace guestfs-internal-frontend-cleanups.h (fully replaced in a later commit). --- .gitignore | 3 +- common/utils/Makefile.am | 4 +- common/utils/{cleanup.c => cleanups.c} | 105 ++++++------------------------- common/utils/cleanups.h | 82 ++++++++++++++++++++++++ common/utils/guestfs-internal-frontend.h | 58 +---------------- common/utils/libxml2-cleanups.c | 94 +++++++++++++++++++++++++++ docs/C_SOURCE_FILES | 4 +- ocaml/Makefile.am | 1 + python/Makefile.am | 15 +++-- 9 files changed, 216 insertions(+), 150 deletions(-) diff --git a/.gitignore b/.gitignore index 772c12b59..eb85f74c0 100644 --- a/.gitignore +++ b/.gitignore @@ -483,6 +483,8 @@ Makefile.in /python/bindtests.py /python/build /python/c-ctype.h +/python/cleanups.c +/python/cleanups.h /python/config.h /python/dist /python/examples/guestfs-python.3 @@ -491,7 +493,6 @@ Makefile.in /python/guestfs.pyc /python/guestfs.pyo /python/guestfs-internal-all.h -/python/guestfs-internal-frontend-cleanups.h /python/guestfs-internal-frontend.h /python/ignore-value.h /python/MANIFEST diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 93f42293a..5c9728797 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -33,9 +33,11 @@ noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ ../../lib/guestfs.h \ - cleanup.c \ + cleanups.c \ + cleanups.h \ guestfs-internal-frontend.h \ guestfs-internal-frontend-cleanups.h \ + libxml2-cleanups.c \ structs-cleanup.c \ structs-print.c \ structs-print.h \ diff --git a/common/utils/cleanup.c b/common/utils/cleanups.c similarity index 70% rename from common/utils/cleanup.c rename to common/utils/cleanups.c index 6c4558c39..c9a34c2ef 100644 --- a/common/utils/cleanup.c +++ b/common/utils/cleanups.c @@ -1,5 +1,5 @@ /* libguestfs - * Copyright (C) 2013 Red Hat Inc. + * Copyright (C) 2013-2017 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 @@ -63,16 +63,12 @@ #include <unistd.h> #include <string.h> -#include <libxml/uri.h> -#include <libxml/tree.h> -#include <libxml/xpath.h> -#include <libxml/xmlwriter.h> - #include "hash.h" -#include "guestfs.h" #include "guestfs-internal-frontend.h" +/* Stdlib cleanups. */ + void guestfs_int_cleanup_free (void *ptr) { @@ -80,21 +76,6 @@ guestfs_int_cleanup_free (void *ptr) } void -guestfs_int_cleanup_free_string_list (char ***ptr) -{ - guestfs_int_free_string_list (*ptr); -} - -void -guestfs_int_cleanup_hash_free (void *ptr) -{ - Hash_table *h = * (Hash_table **) ptr; - - if (h) - hash_free (h); -} - -void guestfs_int_cleanup_unlink_free (char **ptr) { char *filename = *ptr; @@ -106,69 +87,6 @@ guestfs_int_cleanup_unlink_free (char **ptr) } void -guestfs_int_cleanup_xmlFree (void *ptr) -{ - xmlChar *buf = * (xmlChar **) ptr; - - if (buf) - xmlFree (buf); -} - -void -guestfs_int_cleanup_xmlBufferFree (void *ptr) -{ - xmlBufferPtr xb = * (xmlBufferPtr *) ptr; - - if (xb) - xmlBufferFree (xb); -} - -void -guestfs_int_cleanup_xmlFreeDoc (void *ptr) -{ - xmlDocPtr doc = * (xmlDocPtr *) ptr; - - if (doc) - xmlFreeDoc (doc); -} - -void -guestfs_int_cleanup_xmlFreeURI (void *ptr) -{ - xmlURIPtr uri = * (xmlURIPtr *) ptr; - - if (uri) - xmlFreeURI (uri); -} - -void -guestfs_int_cleanup_xmlFreeTextWriter (void *ptr) -{ - xmlTextWriterPtr xo = * (xmlTextWriterPtr *) ptr; - - if (xo) - xmlFreeTextWriter (xo); -} - -void -guestfs_int_cleanup_xmlXPathFreeContext (void *ptr) -{ - xmlXPathContextPtr ctx = * (xmlXPathContextPtr *) ptr; - - if (ctx) - xmlXPathFreeContext (ctx); -} - -void -guestfs_int_cleanup_xmlXPathFreeObject (void *ptr) -{ - xmlXPathObjectPtr obj = * (xmlXPathObjectPtr *) ptr; - - if (obj) - xmlXPathFreeObject (obj); -} - -void guestfs_int_cleanup_fclose (void *ptr) { FILE *f = * (FILE **) ptr; @@ -185,3 +103,20 @@ guestfs_int_cleanup_pclose (void *ptr) if (f) pclose (f); } + +void +guestfs_int_cleanup_free_string_list (char ***ptr) +{ + guestfs_int_free_string_list (*ptr); +} + +/* Gnulib cleanups. */ + +void +guestfs_int_cleanup_hash_free (void *ptr) +{ + Hash_table *h = * (Hash_table **) ptr; + + if (h) + hash_free (h); +} diff --git a/common/utils/cleanups.h b/common/utils/cleanups.h new file mode 100644 index 000000000..df62cafd6 --- /dev/null +++ b/common/utils/cleanups.h @@ -0,0 +1,82 @@ +/* libguestfs + * Copyright (C) 2013-2017 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 + */ + +#ifndef GUESTFS_CLEANUPS_H_ +#define GUESTFS_CLEANUPS_H_ + +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_free))) +#define CLEANUP_HASH_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_hash_free))) +#define CLEANUP_UNLINK_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) +#define CLEANUP_FCLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_fclose))) +#define CLEANUP_PCLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_pclose))) +#define CLEANUP_FREE_STRING_LIST \ + __attribute__((cleanup(guestfs_int_cleanup_free_string_list))) +#define CLEANUP_XMLFREE \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFree))) +#define CLEANUP_XMLBUFFERFREE \ + __attribute__((cleanup(guestfs_int_cleanup_xmlBufferFree))) +#define CLEANUP_XMLFREEDOC \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeDoc))) +#define CLEANUP_XMLFREEURI \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeURI))) +#define CLEANUP_XMLFREETEXTWRITER \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeTextWriter))) +#define CLEANUP_XMLXPATHFREECONTEXT \ + __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeContext))) +#define CLEANUP_XMLXPATHFREEOBJECT \ + __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeObject))) +#else +#define CLEANUP_FREE +#define CLEANUP_HASH_FREE +#define CLEANUP_UNLINK_FREE +#define CLEANUP_FCLOSE +#define CLEANUP_PCLOSE +#define CLEANUP_FREE_STRING_LIST +#define CLEANUP_XMLFREE +#define CLEANUP_XMLBUFFERFREE +#define CLEANUP_XMLFREEDOC +#define CLEANUP_XMLFREEURI +#define CLEANUP_XMLFREETEXTWRITER +#define CLEANUP_XMLXPATHFREECONTEXT +#define CLEANUP_XMLXPATHFREEOBJECT +#endif + +/* These functions are used internally by the CLEANUP_* macros. + * Don't call them directly. + */ +extern void guestfs_int_cleanup_free (void *ptr); +extern void guestfs_int_cleanup_hash_free (void *ptr); +extern void guestfs_int_cleanup_unlink_free (char **ptr); +extern void guestfs_int_cleanup_fclose (void *ptr); +extern void guestfs_int_cleanup_pclose (void *ptr); +extern void guestfs_int_cleanup_free_string_list (char ***ptr); +extern void guestfs_int_cleanup_xmlFree (void *ptr); +extern void guestfs_int_cleanup_xmlBufferFree (void *ptr); +extern void guestfs_int_cleanup_xmlFreeDoc (void *ptr); +extern void guestfs_int_cleanup_xmlFreeURI (void *ptr); +extern void guestfs_int_cleanup_xmlFreeTextWriter (void *ptr); +extern void guestfs_int_cleanup_xmlXPathFreeContext (void *ptr); +extern void guestfs_int_cleanup_xmlXPathFreeObject (void *ptr); + +#endif /* GUESTFS_CLEANUPS_H_ */ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index 489b54ffd..1d5a50664 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -35,50 +35,11 @@ #include <stdbool.h> #include "guestfs-internal-all.h" +#include "cleanups.h" #define _(str) dgettext(PACKAGE, (str)) #define N_(str) dgettext(PACKAGE, (str)) -#ifdef HAVE_ATTRIBUTE_CLEANUP -#define CLEANUP_FREE __attribute__((cleanup(guestfs_int_cleanup_free))) -#define CLEANUP_FREE_STRING_LIST \ - __attribute__((cleanup(guestfs_int_cleanup_free_string_list))) -#define CLEANUP_HASH_FREE \ - __attribute__((cleanup(guestfs_int_cleanup_hash_free))) -#define CLEANUP_UNLINK_FREE \ - __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) -#define CLEANUP_XMLFREE \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFree))) -#define CLEANUP_XMLBUFFERFREE \ - __attribute__((cleanup(guestfs_int_cleanup_xmlBufferFree))) -#define CLEANUP_XMLFREEDOC \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeDoc))) -#define CLEANUP_XMLFREEURI \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeURI))) -#define CLEANUP_XMLFREETEXTWRITER \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeTextWriter))) -#define CLEANUP_XMLXPATHFREECONTEXT \ - __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeContext))) -#define CLEANUP_XMLXPATHFREEOBJECT \ - __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeObject))) -#define CLEANUP_FCLOSE __attribute__((cleanup(guestfs_int_cleanup_fclose))) -#define CLEANUP_PCLOSE __attribute__((cleanup(guestfs_int_cleanup_pclose))) -#else -#define CLEANUP_FREE -#define CLEANUP_FREE_STRING_LIST -#define CLEANUP_HASH_FREE -#define CLEANUP_UNLINK_FREE -#define CLEANUP_XMLFREE -#define CLEANUP_XMLBUFFERFREE -#define CLEANUP_XMLFREEDOC -#define CLEANUP_XMLFREEURI -#define CLEANUP_XMLFREETEXTWRITER -#define CLEANUP_XMLXPATHFREECONTEXT -#define CLEANUP_XMLXPATHFREEOBJECT -#define CLEANUP_FCLOSE -#define CLEANUP_PCLOSE -#endif - /* utils.c */ extern void guestfs_int_free_string_list (char **); extern size_t guestfs_int_count_strings (char *const *); @@ -102,23 +63,6 @@ extern void guestfs_int_fadvise_noreuse (int fd); //extern void guestfs_int_fadvise_willneed (int fd); extern char *guestfs_int_shell_unquote (const char *str); -/* These functions are used internally by the CLEANUP_* macros. - * Don't call them directly. - */ -extern void guestfs_int_cleanup_free (void *ptr); -extern void guestfs_int_cleanup_free_string_list (char ***ptr); -extern void guestfs_int_cleanup_hash_free (void *ptr); -extern void guestfs_int_cleanup_unlink_free (char **ptr); -extern void guestfs_int_cleanup_xmlFree (void *ptr); -extern void guestfs_int_cleanup_xmlBufferFree (void *ptr); -extern void guestfs_int_cleanup_xmlFreeDoc (void *ptr); -extern void guestfs_int_cleanup_xmlFreeURI (void *ptr); -extern void guestfs_int_cleanup_xmlFreeTextWriter (void *ptr); -extern void guestfs_int_cleanup_xmlXPathFreeContext (void *ptr); -extern void guestfs_int_cleanup_xmlXPathFreeObject (void *ptr); -extern void guestfs_int_cleanup_fclose (void *ptr); -extern void guestfs_int_cleanup_pclose (void *ptr); - /* These are in a separate header so the header can be generated. * Don't include the following file directly: */ diff --git a/common/utils/libxml2-cleanups.c b/common/utils/libxml2-cleanups.c new file mode 100644 index 000000000..066573fef --- /dev/null +++ b/common/utils/libxml2-cleanups.c @@ -0,0 +1,94 @@ +/* libguestfs + * Copyright (C) 2013-2017 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 <libxml/uri.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlwriter.h> + +#include "cleanups.h" + +void +guestfs_int_cleanup_xmlFree (void *ptr) +{ + xmlChar *buf = * (xmlChar **) ptr; + + if (buf) + xmlFree (buf); +} + +void +guestfs_int_cleanup_xmlBufferFree (void *ptr) +{ + xmlBufferPtr xb = * (xmlBufferPtr *) ptr; + + if (xb) + xmlBufferFree (xb); +} + +void +guestfs_int_cleanup_xmlFreeDoc (void *ptr) +{ + xmlDocPtr doc = * (xmlDocPtr *) ptr; + + if (doc) + xmlFreeDoc (doc); +} + +void +guestfs_int_cleanup_xmlFreeURI (void *ptr) +{ + xmlURIPtr uri = * (xmlURIPtr *) ptr; + + if (uri) + xmlFreeURI (uri); +} + +void +guestfs_int_cleanup_xmlFreeTextWriter (void *ptr) +{ + xmlTextWriterPtr xo = * (xmlTextWriterPtr *) ptr; + + if (xo) + xmlFreeTextWriter (xo); +} + +void +guestfs_int_cleanup_xmlXPathFreeContext (void *ptr) +{ + xmlXPathContextPtr ctx = * (xmlXPathContextPtr *) ptr; + + if (ctx) + xmlXPathFreeContext (ctx); +} + +void +guestfs_int_cleanup_xmlXPathFreeObject (void *ptr) +{ + xmlXPathObjectPtr obj = * (xmlXPathObjectPtr *) ptr; + + if (obj) + xmlXPathFreeObject (obj); +} diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index ccb3a091a..f690fb473 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -42,9 +42,11 @@ common/progress/progress.h common/qemuopts/qemuopts-tests.c common/qemuopts/qemuopts.c common/qemuopts/qemuopts.h -common/utils/cleanup.c +common/utils/cleanups.c +common/utils/cleanups.h common/utils/guestfs-internal-frontend-cleanups.h common/utils/guestfs-internal-frontend.h +common/utils/libxml2-cleanups.c common/utils/structs-cleanup.c common/utils/structs-print.c common/utils/structs-print.h diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am index 57ecd608b..91be080bf 100644 --- a/ocaml/Makefile.am +++ b/ocaml/Makefile.am @@ -94,6 +94,7 @@ libguestfsocaml_a_SOURCES = \ guestfs-c.c \ guestfs-c-actions.c \ guestfs-c-errnos.c \ + ../common/utils/cleanups.c \ ../common/utils/utils.c if HAVE_OCAMLDOC diff --git a/python/Makefile.am b/python/Makefile.am index ae90aa01d..7e91bbb7e 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -97,9 +97,10 @@ setup-install: setup.py stamp-extra-files # to hard-link any extra files we need into the local directory. stamp-extra-files: \ c-ctype.h \ + cleanups.c \ + cleanups.h \ config.h \ guestfs-internal-all.h \ - guestfs-internal-frontend-cleanups.h \ guestfs-internal-frontend.h \ ignore-value.h \ utils.c @@ -111,15 +112,18 @@ config.h: c-ctype.h: ln $(top_srcdir)/gnulib/lib/c-ctype.h $@ +cleanups.c: + ln $(top_srcdir)/common/utils/cleanups.c $@ + +cleanups.h: + ln $(top_srcdir)/common/utils/cleanups.h $@ + ignore-value.h: ln $(top_srcdir)/gnulib/lib/ignore-value.h $@ guestfs-internal-all.h: ln $(top_srcdir)/lib/guestfs-internal-all.h $@ -guestfs-internal-frontend-cleanups.h: - ln $(top_srcdir)/common/utils/guestfs-internal-frontend-cleanups.h $@ - guestfs-internal-frontend.h: ln $(top_srcdir)/common/utils/guestfs-internal-frontend.h $@ @@ -145,8 +149,9 @@ CLEANFILES += \ t/*~ t/*.pyc \ c-ctype.h \ config.h \ + cleanups.c \ + cleanups.h \ guestfs-internal-all.h \ - guestfs-internal-frontend-cleanups.h \ guestfs-internal-frontend.h \ ignore-value.h \ stamp-extra-files \ -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 09/42] common/utils: cleanups: Add CLEANUP_CLOSE function.
This is present in the daemon, but the function could be used throughout the code. --- common/utils/cleanups.c | 9 +++++++++ common/utils/cleanups.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/common/utils/cleanups.c b/common/utils/cleanups.c index c9a34c2ef..47da587bb 100644 --- a/common/utils/cleanups.c +++ b/common/utils/cleanups.c @@ -87,6 +87,15 @@ guestfs_int_cleanup_unlink_free (char **ptr) } void +guestfs_int_cleanup_close (void *ptr) +{ + const int fd = * (int *) ptr; + + if (fd >= 0) + close (fd); +} + +void guestfs_int_cleanup_fclose (void *ptr) { FILE *f = * (FILE **) ptr; diff --git a/common/utils/cleanups.h b/common/utils/cleanups.h index df62cafd6..75df0074c 100644 --- a/common/utils/cleanups.h +++ b/common/utils/cleanups.h @@ -26,6 +26,8 @@ __attribute__((cleanup(guestfs_int_cleanup_hash_free))) #define CLEANUP_UNLINK_FREE \ __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) +#define CLEANUP_CLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_close))) #define CLEANUP_FCLOSE \ __attribute__((cleanup(guestfs_int_cleanup_fclose))) #define CLEANUP_PCLOSE \ @@ -50,6 +52,7 @@ #define CLEANUP_FREE #define CLEANUP_HASH_FREE #define CLEANUP_UNLINK_FREE +#define CLEANUP_CLOSE #define CLEANUP_FCLOSE #define CLEANUP_PCLOSE #define CLEANUP_FREE_STRING_LIST @@ -68,6 +71,7 @@ extern void guestfs_int_cleanup_free (void *ptr); extern void guestfs_int_cleanup_hash_free (void *ptr); extern void guestfs_int_cleanup_unlink_free (char **ptr); +extern void guestfs_int_cleanup_close (void *ptr); extern void guestfs_int_cleanup_fclose (void *ptr); extern void guestfs_int_cleanup_pclose (void *ptr); extern void guestfs_int_cleanup_free_string_list (char ***ptr); -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 10/42] utils: Split out structs cleanups and printing into common/structs.
These won't be used by the daemon, so interferes with us using common/utils in the daemon, so they are moved to a different library. --- .gitignore | 8 +++--- Makefile.am | 4 ++- align/Makefile.am | 2 ++ align/scan.c | 1 + cat/Makefile.am | 10 ++++++++ cat/filesystems.c | 1 + cat/log.c | 1 + cat/tail.c | 1 + common/mlvisit/Makefile.am | 3 ++- common/structs/Makefile.am | 44 ++++++++++++++++++++++++++++++++ common/utils/Makefile.am | 16 ------------ common/utils/guestfs-internal-frontend.h | 5 ---- common/visit/Makefile.am | 3 ++- common/visit/visit.c | 1 + configure.ac | 1 + df/Makefile.am | 2 ++ df/df.c | 1 + diff/Makefile.am | 1 + docs/C_SOURCE_FILES | 8 +++--- docs/guestfs-hacking.pod | 5 ++++ fish/Makefile.am | 4 +++ generator/c.ml | 17 +++++------- generator/c.mli | 4 +-- generator/java.ml | 1 + generator/main.ml | 12 ++++----- generator/tests_c_api.ml | 1 + inspector/Makefile.am | 2 ++ inspector/inspector.c | 1 + java/Makefile.am | 2 ++ lib/Makefile.am | 6 ++++- lib/file.c | 1 + lib/fuse.c | 1 + lib/inspect-apps.c | 1 + lib/inspect-fs-windows.c | 1 + lib/inspect-fs.c | 1 + lib/launch.c | 1 + lib/listfs.c | 1 + lib/mountable.c | 2 +- make-fs/Makefile.am | 2 ++ make-fs/make-fs.c | 1 + sysprep/Makefile.am | 2 ++ tests/c-api/Makefile.am | 2 ++ tests/c-api/tests-main.c | 1 + 43 files changed, 133 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index eb85f74c0..5cf0d8dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -136,10 +136,10 @@ Makefile.in /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x /common/qemuopts/qemuopts-tests -/common/utils/guestfs-internal-frontend-cleanups.h -/common/utils/structs-cleanup.c -/common/utils/structs-print.c -/common/utils/structs-print.h +/common/structs/structs-cleanups.c +/common/structs/structs-cleanups.h +/common/structs/structs-print.c +/common/structs/structs-print.h /compile /config.cache /config.guess diff --git a/Makefile.am b/Makefile.am index 64ac23f2e..b14ce4813 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,9 @@ SUBDIRS += gnulib/tests endif # Basic source for the library. -SUBDIRS += common/errnostring common/protocol common/qemuopts common/utils +SUBDIRS += common/errnostring common/protocol common/qemuopts +SUBDIRS += common/utils +SUBDIRS += common/structs SUBDIRS += lib docs examples po # The daemon and the appliance. diff --git a/align/Makefile.am b/align/Makefile.am index 8d4fce11b..cc8df13f7 100644 --- a/align/Makefile.am +++ b/align/Makefile.am @@ -31,6 +31,7 @@ virt_alignment_scan_SOURCES = \ virt_alignment_scan_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -46,6 +47,7 @@ virt_alignment_scan_CFLAGS = \ virt_alignment_scan_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/parallel/libparallel.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/align/scan.c b/align/scan.c index 4fa95c0a3..b9f29868c 100644 --- a/align/scan.c +++ b/align/scan.c @@ -41,6 +41,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" #include "parallel.h" diff --git a/cat/Makefile.am b/cat/Makefile.am index 4b9171937..3fb579769 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -39,6 +39,7 @@ virt_cat_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -51,6 +52,7 @@ virt_cat_CFLAGS = \ virt_cat_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ @@ -65,6 +67,7 @@ virt_filesystems_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -77,6 +80,7 @@ virt_filesystems_CFLAGS = \ virt_filesystems_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ @@ -91,6 +95,7 @@ virt_log_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -102,6 +107,7 @@ virt_log_CFLAGS = \ virt_log_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ @@ -116,6 +122,7 @@ virt_ls_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/visit \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -129,6 +136,7 @@ virt_ls_CFLAGS = \ virt_ls_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ @@ -143,6 +151,7 @@ virt_tail_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -155,6 +164,7 @@ virt_tail_CFLAGS = \ virt_tail_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/cat/filesystems.c b/cat/filesystems.c index f3dd265ab..74e994169 100644 --- a/cat/filesystems.c +++ b/cat/filesystems.c @@ -37,6 +37,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/cat/log.c b/cat/log.c index 92272b8d1..6e445af0b 100644 --- a/cat/log.c +++ b/cat/log.c @@ -38,6 +38,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/cat/tail.c b/cat/tail.c index 4ac73ad36..e932820e6 100644 --- a/cat/tail.c +++ b/cat/tail.c @@ -37,6 +37,7 @@ #include "ignore-value.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" #include "windows.h" diff --git a/common/mlvisit/Makefile.am b/common/mlvisit/Makefile.am index 51cbd2de6..2019efd75 100644 --- a/common/mlvisit/Makefile.am +++ b/common/mlvisit/Makefile.am @@ -74,6 +74,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/structs/.libs \ -I $(top_builddir)/common/visit/.libs \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLVISIT_CMA) @@ -115,7 +116,7 @@ visit_tests_DEPENDENCIES = \ $(top_srcdir)/ocaml-link.sh visit_tests_LINK = \ $(top_srcdir)/ocaml-link.sh \ - -cclib '-lvisit -lutils $(LIBXML2_LIBS) -lgnu' -- \ + -cclib '-lvisit -lstructs -lutils $(LIBXML2_LIBS) -lgnu' -- \ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(visit_tests_THEOBJECTS) -o $@ diff --git a/common/structs/Makefile.am b/common/structs/Makefile.am new file mode 100644 index 000000000..1762af276 --- /dev/null +++ b/common/structs/Makefile.am @@ -0,0 +1,44 @@ +# libguestfs +# 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 $(top_srcdir)/subdir-rules.mk + +generator_built = \ + structs-cleanups.c \ + structs-cleanups.h \ + structs-print.c \ + structs-print.h + +BUILT_SOURCES = \ + $(generator_built) + +EXTRA_DIST = \ + $(BUILT_SOURCES) + +noinst_LTLIBRARIES = libstructs.la + +libstructs_la_SOURCES = \ + ../../lib/guestfs.h \ + $(BUILT_SOURCES) +libstructs_la_CPPFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ + -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/lib -I$(top_builddir)/lib +libstructs_la_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(GCC_VISIBILITY_HIDDEN) diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 5c9728797..a86b715f6 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -17,18 +17,6 @@ include $(top_srcdir)/subdir-rules.mk -generator_built = \ - guestfs-internal-frontend-cleanups.h \ - structs-cleanup.c \ - structs-print.c \ - structs-print.h - -BUILT_SOURCES = \ - $(generator_built) - -EXTRA_DIST = \ - $(BUILT_SOURCES) - noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ @@ -36,11 +24,7 @@ libutils_la_SOURCES = \ cleanups.c \ cleanups.h \ guestfs-internal-frontend.h \ - guestfs-internal-frontend-cleanups.h \ libxml2-cleanups.c \ - structs-cleanup.c \ - structs-print.c \ - structs-print.h \ utils.c libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index 1d5a50664..3594d593e 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -63,11 +63,6 @@ extern void guestfs_int_fadvise_noreuse (int fd); //extern void guestfs_int_fadvise_willneed (int fd); extern char *guestfs_int_shell_unquote (const char *str); -/* These are in a separate header so the header can be generated. - * Don't include the following file directly: - */ -#include "guestfs-internal-frontend-cleanups.h" - /* Not all language bindings know how to deal with Pointer arguments. * Those that don't will use this macro which complains noisily and * returns NULL. diff --git a/common/visit/Makefile.am b/common/visit/Makefile.am index e95954a11..8b9136780 100644 --- a/common/visit/Makefile.am +++ b/common/visit/Makefile.am @@ -27,7 +27,8 @@ libvisit_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils + -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs libvisit_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(GCC_VISIBILITY_HIDDEN) diff --git a/common/visit/visit.c b/common/visit/visit.c index 5045f9f71..491f9dda3 100644 --- a/common/visit/visit.c +++ b/common/visit/visit.c @@ -37,6 +37,7 @@ #include "guestfs.h" #include "guestfs-internal-frontend.h" +#include "structs-cleanups.h" #include "visit.h" diff --git a/configure.ac b/configure.ac index eba149241..8c782368f 100644 --- a/configure.ac +++ b/configure.ac @@ -195,6 +195,7 @@ AC_CONFIG_FILES([Makefile common/progress/Makefile common/protocol/Makefile common/qemuopts/Makefile + common/structs/Makefile common/utils/Makefile common/visit/Makefile common/windows/Makefile diff --git a/df/Makefile.am b/df/Makefile.am index 8725402bc..89ec12a64 100644 --- a/df/Makefile.am +++ b/df/Makefile.am @@ -36,6 +36,7 @@ virt_df_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -50,6 +51,7 @@ virt_df_CFLAGS = \ virt_df_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/parallel/libparallel.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/df/df.c b/df/df.c index a13cc5910..66b1d8334 100644 --- a/df/df.c +++ b/df/df.c @@ -27,6 +27,7 @@ #include <errno.h> #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "domains.h" #include "virt-df.h" diff --git a/diff/Makefile.am b/diff/Makefile.am index 5e71b74de..574981f8b 100644 --- a/diff/Makefile.am +++ b/diff/Makefile.am @@ -44,6 +44,7 @@ virt_diff_CFLAGS = \ virt_diff_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index f690fb473..1f0758106 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -42,14 +42,14 @@ common/progress/progress.h common/qemuopts/qemuopts-tests.c common/qemuopts/qemuopts.c common/qemuopts/qemuopts.h +common/structs/structs-cleanups.c +common/structs/structs-cleanups.h +common/structs/structs-print.c +common/structs/structs-print.h common/utils/cleanups.c common/utils/cleanups.h -common/utils/guestfs-internal-frontend-cleanups.h common/utils/guestfs-internal-frontend.h common/utils/libxml2-cleanups.c -common/utils/structs-cleanup.c -common/utils/structs-print.c -common/utils/structs-print.h common/utils/utils.c common/visit/visit.c common/visit/visit.h diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index beb44d2dc..fa00a9ab6 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -137,6 +137,11 @@ and the daemon running inside the appliance is defined here. Mini-library for writing qemu command lines and qemu config files. +=item F<common/structs> + +Common code for printing and freeing libguestfs structs, used by the +library and some tools. + =item F<common/utils> Various utility functions used throughout the library and tools. diff --git a/fish/Makefile.am b/fish/Makefile.am index 9c07761e2..a6efbb439 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -108,10 +108,12 @@ librc_protocol_la_CFLAGS = -Wall -Wno-unused -fno-strict-aliasing libcmds_la_SOURCES = cmds-gperf.c libcmds_la_CPPFLAGS = \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib libcmds_la_CFLAGS libcmds_la_LIBADD = \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(LTLIBINTL) @@ -124,6 +126,7 @@ guestfish_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/edit -I$(top_builddir)/common/edit \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -141,6 +144,7 @@ guestfish_LDADD = \ $(top_builddir)/common/edit/libedit.la \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/progress/libprogress.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/generator/c.ml b/generator/c.ml index 27bf1ebf9..c9fd867de 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -794,20 +794,17 @@ and generate_internal_actions_h () pr "\n"; pr "#endif /* GUESTFS_INTERNAL_ACTIONS_H_ */\n" -(* Generate guestfs-internal-frontend-cleanups.h file. *) -and generate_internal_frontend_cleanups_h () +(* Generate structs-cleanups.h file. *) +and generate_client_structs_cleanups_h () generate_header CStyle LGPLv2plus; pr "\ /* These CLEANUP_* macros automatically free the struct or struct list * pointed to by the local variable at the end of the current scope. - * - * Don't include this file directly! To use these cleanups in library - * bindings and tools, include \"guestfs-internal-frontend.h\" only. */ -#ifndef GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ -#define GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ +#ifndef GUESTFS_STRUCTS_CLEANUPS_H_ +#define GUESTFS_STRUCTS_CLEANUPS_H_ #ifdef HAVE_ATTRIBUTE_CLEANUP "; @@ -846,7 +843,7 @@ and generate_internal_frontend_cleanups_h () ) structs; pr "\n"; - pr "#endif /* GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ */\n" + pr "#endif /* GUESTFS_STRUCTS_CLEANUPS_H_ */\n" (* Functions to free structures. *) and generate_client_structs_free () @@ -1166,7 +1163,7 @@ and generate_client_structs_copy () ) structs (* Functions to free structures used by the CLEANUP_* macros. *) -and generate_client_structs_cleanup () +and generate_client_structs_cleanups_c () generate_header CStyle LGPLv2plus; pr "\ @@ -1176,7 +1173,7 @@ and generate_client_structs_cleanup () #include <stdlib.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" "; diff --git a/generator/c.mli b/generator/c.mli index 0884a8dfe..6d8cae891 100644 --- a/generator/c.mli +++ b/generator/c.mli @@ -26,7 +26,8 @@ val generate_actions_pod : unit -> unit val generate_availability_pod : unit -> unit val generate_client_actions : Types.action list -> unit -> unit val generate_client_actions_variants : unit -> unit -val generate_client_structs_cleanup : unit -> unit +val generate_client_structs_cleanups_h : unit -> unit +val generate_client_structs_cleanups_c : unit -> unit val generate_client_structs_compare : unit -> unit val generate_client_structs_copy : unit -> unit val generate_client_structs_free : unit -> unit @@ -35,7 +36,6 @@ val generate_client_structs_print_c : unit -> unit val generate_event_string_c : unit -> unit val generate_guestfs_h : unit -> unit val generate_internal_actions_h : unit -> unit -val generate_internal_frontend_cleanups_h : unit -> unit val generate_linker_script : unit -> unit val generate_max_proc_nr : unit -> unit val generate_structs_pod : unit -> unit diff --git a/generator/java.ml b/generator/java.ml index 7c3212a49..a7d0ed359 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -586,6 +586,7 @@ and generate_java_c actions () #include \"com_redhat_et_libguestfs_GuestFS.h\" #include \"guestfs.h\" #include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" /* Note that this function returns. The exception is not thrown * until after the wrapper function returns. diff --git a/generator/main.ml b/generator/main.ml index 8ff698130..33fe2b2ee 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -86,13 +86,13 @@ Run it from the top source directory using the command Errnostring.generate_errnostring_h; output_to "common/protocol/guestfs_protocol.x" XDR.generate_xdr; - output_to "common/utils/guestfs-internal-frontend-cleanups.h" - C.generate_internal_frontend_cleanups_h; - output_to "common/utils/structs-cleanup.c" - C.generate_client_structs_cleanup; - output_to "common/utils/structs-print.c" + output_to "common/structs/structs-cleanups.h" + C.generate_client_structs_cleanups_h; + output_to "common/structs/structs-cleanups.c" + C.generate_client_structs_cleanups_c; + output_to "common/structs/structs-print.c" C.generate_client_structs_print_c; - output_to "common/utils/structs-print.h" + output_to "common/structs/structs-print.h" C.generate_client_structs_print_h; output_to "lib/uefi.c" UEFI.generate_uefi_c; diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index a680521f4..c3cb62c4d 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -48,6 +48,7 @@ let rec generate_c_api_tests () #include \"guestfs.h\" #include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" #include \"tests.h\" diff --git a/inspector/Makefile.am b/inspector/Makefile.am index 753e2c93c..9402270c9 100644 --- a/inspector/Makefile.am +++ b/inspector/Makefile.am @@ -59,6 +59,7 @@ virt_inspector_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/fish \ @@ -70,6 +71,7 @@ virt_inspector_CFLAGS = \ virt_inspector_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/inspector/inspector.c b/inspector/inspector.c index b00c85208..104310d1f 100644 --- a/inspector/inspector.c +++ b/inspector/inspector.c @@ -40,6 +40,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/java/Makefile.am b/java/Makefile.am index ea64f5525..a4fcc38cf 100644 --- a/java/Makefile.am +++ b/java/Makefile.am @@ -109,6 +109,7 @@ libguestfs_jni_la_SOURCES = \ libguestfs_jni_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib libguestfs_jni_la_CFLAGS = \ @@ -116,6 +117,7 @@ libguestfs_jni_la_CFLAGS = \ $(JNI_CFLAGS) libguestfs_jni_la_LIBADD = \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la diff --git a/lib/Makefile.am b/lib/Makefile.am index 81841e6e1..3e71f3084 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -58,7 +58,7 @@ libguestfs_la_SOURCES = \ ../common/protocol/guestfs_protocol.h \ ../common/qemuopts/qemuopts.h \ ../common/utils/guestfs-internal-frontend.h \ - ../common/utils/guestfs-internal-frontend-cleanups.h \ + ../common/structs/structs-cleanups.h \ guestfs.h \ guestfs-internal.h \ guestfs-internal-all.h \ @@ -140,6 +140,7 @@ libguestfs_la_CPPFLAGS = \ -I$(top_srcdir)/common/protocol -I$(top_builddir)/common/protocol \ -I$(top_srcdir)/common/qemuopts -I$(top_builddir)/common/qemuopts \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib libguestfs_la_CFLAGS = \ @@ -155,6 +156,7 @@ libguestfs_la_LIBADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ ../common/qemuopts/libqemuopts.la \ + ../common/structs/libstructs.la \ ../common/utils/libutils.la \ $(PCRE_LIBS) $(MAGIC_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ @@ -216,6 +218,7 @@ unit_tests_SOURCES = unit-tests.c unit_tests_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I. unit_tests_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) @@ -223,6 +226,7 @@ unit_tests_CFLAGS = \ # non-exported functions we have to link with the objects not the # library. unit_tests_LDADD = \ + ../common/structs/libstructs.la \ ../common/utils/libutils.la \ $(libguestfs_la_OBJECTS) \ $(libguestfs_la_LIBADD) diff --git a/lib/file.c b/lib/file.c index 53b859d4b..73c983c2b 100644 --- a/lib/file.c +++ b/lib/file.c @@ -32,6 +32,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" static int compare (const void *vp1, const void *vp2) diff --git a/lib/fuse.c b/lib/fuse.c index cde3783d9..de8c4d8f2 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -54,6 +54,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" #if HAVE_FUSE diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c index c324f3bf0..25192340c 100644 --- a/lib/inspect-apps.c +++ b/lib/inspect-apps.c @@ -43,6 +43,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" #ifdef DB_DUMP static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs); diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c index 35f7cc821..b14dc2e14 100644 --- a/lib/inspect-fs-windows.c +++ b/lib/inspect-fs-windows.c @@ -48,6 +48,7 @@ #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) diff --git a/lib/inspect-fs.c b/lib/inspect-fs.c index 9f7630bcf..2da73d310 100644 --- a/lib/inspect-fs.c +++ b/lib/inspect-fs.c @@ -35,6 +35,7 @@ #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, diff --git a/lib/launch.c b/lib/launch.c index 04d69d867..70e7f8897 100644 --- a/lib/launch.c +++ b/lib/launch.c @@ -45,6 +45,7 @@ #include "guestfs-internal.h" #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" +#include "structs-cleanups.h" static struct backend { struct backend *next; diff --git a/lib/listfs.c b/lib/listfs.c index 88446cc9f..60aff3305 100644 --- a/lib/listfs.c +++ b/lib/listfs.c @@ -25,6 +25,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" /* List filesystems. * diff --git a/lib/mountable.c b/lib/mountable.c index 9f7b451fd..6e4a0c293 100644 --- a/lib/mountable.c +++ b/lib/mountable.c @@ -23,7 +23,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" - +#include "structs-cleanups.h" char * guestfs_impl_mountable_device (guestfs_h *g, const char *mountable) diff --git a/make-fs/Makefile.am b/make-fs/Makefile.am index bef0e7bf8..28c274868 100644 --- a/make-fs/Makefile.am +++ b/make-fs/Makefile.am @@ -31,6 +31,7 @@ virt_make_fs_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/fish \ @@ -42,6 +43,7 @@ virt_make_fs_CFLAGS = \ virt_make_fs_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/make-fs/make-fs.c b/make-fs/make-fs.c index 27cdb9fd2..e30745a0f 100644 --- a/make-fs/make-fs.c +++ b/make-fs/make-fs.c @@ -40,6 +40,7 @@ #include "xstrtol.h" #include "getprogname.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index c2adb1a6e..834baee6a 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -108,6 +108,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/structs/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -122,6 +123,7 @@ endif OCAMLCLIBS = \ -lvisit \ + -lstructs \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am index cb653f7a6..d79b8ed52 100644 --- a/tests/c-api/Makefile.am +++ b/tests/c-api/Makefile.am @@ -93,12 +93,14 @@ tests_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/lib -I$(top_builddir)/lib tests_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(PCRE_CFLAGS) tests_LDADD = \ $(PCRE_LIBS) \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c index 0fd895423..f202a70ff 100644 --- a/tests/c-api/tests-main.c +++ b/tests/c-api/tests-main.c @@ -38,6 +38,7 @@ #include "guestfs.h" #include "guestfs-internal-frontend.h" +#include "structs-cleanups.h" #include "tests.h" -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 11/42] mllib, v2v: Split out OCaml utils bindings ‘common/mlutils’.
Create a module ‘C_utils’ containing functions like ‘drive_name’ and ‘shell_unquote’ which come from the C utilities. The new directory ‘common/mlutils’ also contains the ‘Unix_utils’ wrappers around POSIX functions missing from the OCaml stdlib. --- .gitignore | 3 + Makefile.am | 24 ++--- builder/Makefile.am | 6 +- common/mlutils/Makefile.am | 154 ++++++++++++++++++++++++++++ v2v/utils-c.c => common/mlutils/c_utils-c.c | 6 +- common/mlutils/c_utils.ml | 26 +++++ common/mlutils/c_utils.mli | 30 ++++++ common/mlutils/c_utils_unit_tests.ml | 81 +++++++++++++++ common/mlutils/dummy.c | 2 + {mllib => common/mlutils}/unix_utils-c.c | 0 {mllib => common/mlutils}/unix_utils.ml | 0 {mllib => common/mlutils}/unix_utils.mli | 0 configure.ac | 1 + customize/Makefile.am | 5 +- dib/Makefile.am | 5 +- docs/C_SOURCE_FILES | 5 +- docs/guestfs-hacking.pod | 5 + get-kernel/Makefile.am | 5 +- mllib/Makefile.am | 8 +- resize/Makefile.am | 5 +- sparsify/Makefile.am | 5 +- sysprep/Makefile.am | 5 +- v2v/Makefile.am | 12 ++- v2v/convert_linux.ml | 3 +- v2v/create_libvirt_xml.ml | 1 + v2v/parse_libvirt_xml.ml | 3 +- v2v/utils.ml | 5 - v2v/utils.mli | 11 -- v2v/v2v.ml | 1 + v2v/v2v_unit_tests.ml | 46 --------- 30 files changed, 366 insertions(+), 97 deletions(-) diff --git a/.gitignore b/.gitignore index 5cf0d8dbc..eadeda311 100644 --- a/.gitignore +++ b/.gitignore @@ -129,6 +129,9 @@ Makefile.in /common/mlstdutils/libdir.ml /common/mlstdutils/oUnit-* /common/mlstdutils/std_utils_tests +/common/mlutils/.depend +/common/mlutils/c_utils_unit_tests +/common/mlutils/oUnit-* /common/mlvisit/.depend /common/mlvisit/visit_tests /common/mlxml/.depend diff --git a/Makefile.am b/Makefile.am index b14ce4813..9122d44ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -155,18 +155,18 @@ 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/mlprogress \ - common/mlvisit \ - common/mlxml \ - mllib \ - customize \ - builder builder/templates \ - get-kernel \ - resize \ - sparsify \ - sysprep \ - v2v +SUBDIRS += common/mlutils +SUBDIRS += common/mlprogress +SUBDIRS += common/mlvisit +SUBDIRS += common/mlxml +SUBDIRS += mllib +SUBDIRS += customize +SUBDIRS += builder builder/templates +SUBDIRS += get-kernel +SUBDIRS += resize +SUBDIRS += sparsify +SUBDIRS += sysprep +SUBDIRS += v2v if HAVE_OCAML_PKG_LIBVIRT SUBDIRS += v2v/test-harness endif diff --git a/builder/Makefile.am b/builder/Makefile.am index 5f0606ca4..09ae4ae3c 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -125,6 +125,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize OCAMLPACKAGES_TESTS @@ -157,6 +158,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -164,6 +166,7 @@ OCAMLLINKFLAGS = \ virt_builder_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -236,6 +239,7 @@ endif yajl_tests_DEPENDENCIES = \ $(yajl_tests_THEOBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -307,7 +311,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mlutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/common/mlutils/Makefile.am b/common/mlutils/Makefile.am new file mode 100644 index 000000000..684f0ce33 --- /dev/null +++ b/common/mlutils/Makefile.am @@ -0,0 +1,154 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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 $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + c_utils.mli \ + unix_utils.mli + +SOURCES_ML = \ + c_utils.ml \ + unix_utils.ml + +SOURCES_C = \ + c_utils-c.c \ + unix_utils-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlcutils.a + +if !HAVE_OCAMLOPT +MLCUTILS_CMA = mlcutils.cma +else +MLCUTILS_CMA = mlcutils.cmxa +endif + +noinst_DATA = $(MLCUTILS_CMA) + +# lib/guestfs-internal-all.h header is used here. It probably +# shouldn't be located under lib. XXX +libmlcutils_a_SOURCES = $(SOURCES_C) +libmlcutils_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -I$(shell $(OCAMLC) -where) +libmlcutils_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +OCAMLPACKAGES = \ + -package str,unix \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/mlstdutils \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlcutils_a_DEPENDENCIES = $(OBJECTS) + +$(MLCUTILS_CMA): $(OBJECTS) libmlcutils.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlcutils_a_OBJECTS) \ + -cclib -lutils \ + -o mlcutils + +# Tests. + +TESTS +check_PROGRAMS + +if HAVE_OCAML_PKG_OUNIT +TESTS += c_utils_unit_tests +check_PROGRAMS += c_utils_unit_tests +endif + +c_utils_unit_tests_BOBJECTS = \ + c_utils_unit_tests.cmo +c_utils_unit_tests_XOBJECTS = $(c_utils_unit_tests_BOBJECTS:.cmo=.cmx) + +c_utils_unit_tests_SOURCES = dummy.c +c_utils_unit_tests_CPPFLAGS = $(libmlcutils_a_CPPFLAGS) +c_utils_unit_tests_CFLAGS = $(libmlcutils_a_CFLAGS) + +if !HAVE_OCAMLOPT +# Can't call this c_utils_unit_tests_OBJECTS because automake gets confused. +c_utils_unit_tests_THEOBJECTS = $(c_utils_unit_tests_BOBJECTS) +c_utils_unit_tests.cmo: OCAMLPACKAGES += -package oUnit +else +c_utils_unit_tests_THEOBJECTS = $(c_utils_unit_tests_XOBJECTS) +c_utils_unit_tests.cmx: OCAMLPACKAGES += -package oUnit +endif + +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) + +c_utils_unit_tests_DEPENDENCIES = \ + $(c_utils_unit_tests_THEOBJECTS) \ + ../mlstdutils/mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ + $(top_srcdir)/ocaml-link.sh +c_utils_unit_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh -cclib '-lutils -lgnu' -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) \ + $(OCAMLPACKAGES) -package oUnit \ + $(OCAMLLINKFLAGS) \ + $(c_utils_unit_tests_THEOBJECTS) -o $@ + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/v2v/utils-c.c b/common/mlutils/c_utils-c.c similarity index 93% rename from v2v/utils-c.c rename to common/mlutils/c_utils-c.c index 69b070fc2..32edbd4a7 100644 --- a/v2v/utils-c.c +++ b/common/mlutils/c_utils-c.c @@ -41,7 +41,7 @@ extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; #pragma GCC diagnostic ignored "-Wmissing-prototypes" value -v2v_utils_drive_name (value indexv) +guestfs_int_mlutils_drive_name (value indexv) { CAMLparam1 (indexv); CAMLlocal1 (namev); @@ -54,7 +54,7 @@ v2v_utils_drive_name (value indexv) } value -v2v_utils_drive_index (value strv) +guestfs_int_mlutils_drive_index (value strv) { CAMLparam1 (strv); ssize_t r; @@ -67,7 +67,7 @@ v2v_utils_drive_index (value strv) } value -v2v_utils_shell_unquote (value strv) +guestfs_int_mlutils_shell_unquote (value strv) { CAMLparam1 (strv); CAMLlocal1 (retv); diff --git a/common/mlutils/c_utils.ml b/common/mlutils/c_utils.ml new file mode 100644 index 000000000..e4263962d --- /dev/null +++ b/common/mlutils/c_utils.ml @@ -0,0 +1,26 @@ +(* virt-v2v + * 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. + *) + +(* OCaml bindings for C utility functions in [common/utils]. *) + +open Printf + +external drive_name : int -> string = "guestfs_int_mlutils_drive_name" +external drive_index : string -> int = "guestfs_int_mlutils_drive_index" + +external shell_unquote : string -> string = "guestfs_int_mlutils_shell_unquote" diff --git a/common/mlutils/c_utils.mli b/common/mlutils/c_utils.mli new file mode 100644 index 000000000..7824f9658 --- /dev/null +++ b/common/mlutils/c_utils.mli @@ -0,0 +1,30 @@ +(* virt-v2v + * 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. + *) + +(** OCaml bindings for C utility functions in [common/utils]. *) + +val drive_name : int -> string +val drive_index : string -> int + +val shell_unquote : string -> string +(** If the string looks like a shell quoted string, then attempt to + unquote it. + + This is just intended to deal with quoting in configuration files + (like ones under /etc/sysconfig), and it doesn't deal with some + situations such as $variable interpolation. *) diff --git a/common/mlutils/c_utils_unit_tests.ml b/common/mlutils/c_utils_unit_tests.ml new file mode 100644 index 000000000..8840d6620 --- /dev/null +++ b/common/mlutils/c_utils_unit_tests.ml @@ -0,0 +1,81 @@ +(* virt-v2v + * Copyright (C) 2011-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 file tests individual OCaml bindings for C utility functions. *) + +open Printf + +open OUnit2 + +open Std_utils +open C_utils + +let test_drive_name ctx + let printer = identity in + assert_equal ~printer "a" (drive_name 0); + assert_equal ~printer "z" (drive_name 25); + assert_equal ~printer "aa" (drive_name 26); + assert_equal ~printer "ab" (drive_name 27); + assert_equal ~printer "az" (drive_name 51); + assert_equal ~printer "ba" (drive_name 52); + assert_equal ~printer "zz" (drive_name 701); + assert_equal ~printer "aaa" (drive_name 702); + assert_equal ~printer "zzz" (drive_name 18277) + +let test_drive_index ctx + let printer = string_of_int in + assert_equal ~printer 0 (drive_index "a"); + assert_equal ~printer 25 (drive_index "z"); + assert_equal ~printer 26 (drive_index "aa"); + assert_equal ~printer 27 (drive_index "ab"); + assert_equal ~printer 51 (drive_index "az"); + assert_equal ~printer 52 (drive_index "ba"); + assert_equal ~printer 701 (drive_index "zz"); + assert_equal ~printer 702 (drive_index "aaa"); + assert_equal ~printer 18277 (drive_index "zzz"); + let exn = Invalid_argument "drive_index: invalid parameter" in + assert_raises exn (fun () -> drive_index ""); + assert_raises exn (fun () -> drive_index "abc123"); + assert_raises exn (fun () -> drive_index "123"); + assert_raises exn (fun () -> drive_index "Z"); + assert_raises exn (fun () -> drive_index "aB") + +let test_shell_unquote ctx + let printer = identity in + assert_equal ~printer "a" (shell_unquote "a"); + assert_equal ~printer "b" (shell_unquote "'b'"); + assert_equal ~printer "c" (shell_unquote "\"c\""); + assert_equal ~printer "dd" (shell_unquote "\"dd\""); + assert_equal ~printer "e\\e" (shell_unquote "\"e\\\\e\""); + assert_equal ~printer "f\\" (shell_unquote "\"f\\\\\""); + assert_equal ~printer "\\g" (shell_unquote "\"\\\\g\""); + assert_equal ~printer "h\\-h" (shell_unquote "\"h\\-h\""); + assert_equal ~printer "i`" (shell_unquote "\"i\\`\""); + assert_equal ~printer "j\"" (shell_unquote "\"j\\\"\"") + +(* Suites declaration. *) +let suite + "C_utils" >::: + [ + "C_utils.drive_name" >:: test_drive_name; + "C_utils.drive_index" >:: test_drive_index; + "C_utils.shell_unquote" >:: test_shell_unquote; + ] + +let () + run_test_tt_main suite diff --git a/common/mlutils/dummy.c b/common/mlutils/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlutils/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/mllib/unix_utils-c.c b/common/mlutils/unix_utils-c.c similarity index 100% rename from mllib/unix_utils-c.c rename to common/mlutils/unix_utils-c.c diff --git a/mllib/unix_utils.ml b/common/mlutils/unix_utils.ml similarity index 100% rename from mllib/unix_utils.ml rename to common/mlutils/unix_utils.ml diff --git a/mllib/unix_utils.mli b/common/mlutils/unix_utils.mli similarity index 100% rename from mllib/unix_utils.mli rename to common/mlutils/unix_utils.mli diff --git a/configure.ac b/configure.ac index 8c782368f..7daf23162 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,7 @@ AC_CONFIG_FILES([Makefile common/mlprogress/Makefile common/mlstdutils/Makefile common/mlstdutils/guestfs_config.ml + common/mlutils/Makefile common/mlvisit/Makefile common/mlxml/Makefile common/options/Makefile diff --git a/customize/Makefile.am b/customize/Makefile.am index 674134b70..fb8946ce1 100644 --- a/customize/Makefile.am +++ b/customize/Makefile.am @@ -124,6 +124,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib \ -I $(builddir) if HAVE_OCAML_PKG_GETTEXT @@ -153,6 +154,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -172,6 +174,7 @@ virt_customize_DEPENDENCIES = \ $(top_srcdir)/ocaml-link.sh \ $(CUSTOMIZE_THEOBJECTS) \ $(CUSTOMIZE_CMA) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) virt_customize_LINK = \ $(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \ @@ -304,7 +307,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/dib/Makefile.am b/dib/Makefile.am index b10fa94c9..cc1287b35 100644 --- a/dib/Makefile.am +++ b/dib/Makefile.am @@ -80,6 +80,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -103,12 +104,14 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_dib_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_dib_LINK = \ @@ -144,7 +147,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 1f0758106..6bf0c3544 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -17,6 +17,9 @@ common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h common/mlprogress/progress-c.c common/mlstdutils/dummy.c +common/mlutils/c_utils-c.c +common/mlutils/dummy.c +common/mlutils/unix_utils-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/mlxml/xml-c.c @@ -344,7 +347,6 @@ make-fs/make-fs.c mllib/common_utils-c.c mllib/dummy.c mllib/getopt-c.c -mllib/unix_utils-c.c mllib/uri-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c @@ -410,4 +412,3 @@ utils/qemu-speed-test/qemu-speed-test.c v2v/libvirt_utils-c.c v2v/qemuopts-c.c v2v/test-harness/dummy.c -v2v/utils-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index fa00a9ab6..bfbe4526d 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -108,6 +108,11 @@ A library of pure OCaml utility functions used in many places. OCaml bindings for the progress bar functions (see F<common/progress>). +=item F<common/mlutils> + +OCaml bindings for C functions in C<common/utils>, and some POSIX +bindings which are missing from the OCaml stdlib. + =item F<common/mlvisit> OCaml bindings for the visit functions (see F<common/visit>). diff --git a/get-kernel/Makefile.am b/get-kernel/Makefile.am index c6454d7a4..6cc7ec2fb 100644 --- a/get-kernel/Makefile.am +++ b/get-kernel/Makefile.am @@ -64,6 +64,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -87,12 +88,14 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_get_kernel_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_get_kernel_LINK = \ @@ -127,7 +130,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/mllib/Makefile.am b/mllib/Makefile.am index 5f6f7fa85..ad86ac58e 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -28,7 +28,6 @@ EXTRA_DIST = \ SOURCES_MLI = \ checksums.mli \ - unix_utils.mli \ common_utils.mli \ curl.mli \ getopt.mli \ @@ -43,7 +42,6 @@ SOURCES_ML = \ $(OCAML_BYTES_COMPAT_ML) \ common_gettext.ml \ getopt.ml \ - unix_utils.ml \ common_utils.ml \ URI.ml \ planner.ml \ @@ -60,7 +58,6 @@ SOURCES_C = \ ../common/options/uri.c \ common_utils-c.c \ getopt-c.c \ - unix_utils-c.c \ uri-c.c if HAVE_OCAML @@ -90,7 +87,8 @@ libmllib_a_CPPFLAGS = \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/options \ -I$(top_srcdir)/common/mlxml \ - -I$(top_srcdir)/common/mlstdutils + -I$(top_srcdir)/common/mlstdutils \ + -I$(top_srcdir)/common/mlutils libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -110,6 +108,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT @@ -189,6 +188,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) diff --git a/resize/Makefile.am b/resize/Makefile.am index 3707d73b4..af65acbf5 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -63,6 +63,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -88,12 +89,14 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_resize_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_resize_LINK = \ @@ -138,7 +141,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index a1395ccbd..c8062f353 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -68,6 +68,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -93,12 +94,14 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_sparsify_LINK = \ @@ -145,7 +148,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 834baee6a..f42722453 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -114,6 +114,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/visit/.libs \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -142,6 +143,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ mlvisit.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ @@ -150,6 +152,7 @@ OCAMLLINKFLAGS = \ virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -218,7 +221,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 8a831a700..a4f4300cb 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -111,8 +111,7 @@ SOURCES_ML = \ SOURCES_C = \ libvirt_utils-c.c \ - qemuopts-c.c \ - utils-c.c + qemuopts-c.c if HAVE_OCAML @@ -147,6 +146,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -174,6 +174,7 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlxml.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -184,8 +185,7 @@ virt_v2v_LINK = \ $(OBJECTS) -o $@ virt_v2v_copy_to_local_SOURCES = \ - libvirt_utils-c.c \ - utils-c.c + libvirt_utils-c.c virt_v2v_copy_to_local_CPPFLAGS = \ -I. \ -I$(top_builddir) \ @@ -214,6 +214,7 @@ virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_v2v_copy_to_local_LINK = \ @@ -500,6 +501,7 @@ v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh v2v_unit_tests_LINK = \ @@ -514,7 +516,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index ffb43564f..c34bf3e91 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -28,6 +28,7 @@ open Printf +open C_utils open Std_utils open Common_utils open Common_gettext.Gettext @@ -186,7 +187,7 @@ let rec convert (g : G.guestfs) inspect source output rcaps fun line -> if Str.string_match rex line 0 then ( let path = Str.matched_group 1 line in - let path = Utils.shell_unquote path in + let path = shell_unquote path in if String.length path >= 1 && path.[0] = '/' then ( let vboxuninstall = path ^ "/uninstall.sh" in Some vboxuninstall diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index 3f22f3764..f5dca2d57 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -19,6 +19,7 @@ open Printf open Std_utils +open C_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index 56f9ea297..285cf2f44 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -18,6 +18,7 @@ open Printf +open C_utils open Std_utils open Common_utils open Common_gettext.Gettext @@ -39,7 +40,7 @@ and parsed_source *) let get_drive_slot str offset let name = String.sub str offset (String.length str - offset) in - try Some (Utils.drive_index name) + try Some (drive_index name) with Invalid_argument _ -> warning (f_"could not parse device name ‘%s’ from the source libvirt XML") str; None diff --git a/v2v/utils.ml b/v2v/utils.ml index 0dab5816e..0c6a1f444 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -24,11 +24,6 @@ open Std_utils open Common_utils open Common_gettext.Gettext -external drive_name : int -> string = "v2v_utils_drive_name" -external drive_index : string -> int = "v2v_utils_drive_index" - -external shell_unquote : string -> string = "v2v_utils_shell_unquote" - (* Map guest architecture found by inspection to the architecture * that KVM must emulate. Note for x86 we assume a 64 bit hypervisor. *) diff --git a/v2v/utils.mli b/v2v/utils.mli index 4906f0023..f267717d4 100644 --- a/v2v/utils.mli +++ b/v2v/utils.mli @@ -18,17 +18,6 @@ (** Utilities used in virt-v2v only. *) -val drive_name : int -> string -val drive_index : string -> int - -val shell_unquote : string -> string -(** If the string looks like a shell quoted string, then attempt to - unquote it. - - This is just intended to deal with quoting in configuration files - (like ones under /etc/sysconfig), and it doesn't deal with some - situations such as $variable interpolation. *) - val kvm_arch : string -> string (** Map guest architecture found by inspection to the architecture that KVM must emulate. Note for x86 we assume a 64 bit hypervisor. *) diff --git a/v2v/v2v.ml b/v2v/v2v.ml index f1ce9335a..00fbff2bc 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -19,6 +19,7 @@ open Unix open Printf +open C_utils open Std_utils open Common_utils open Unix_utils diff --git a/v2v/v2v_unit_tests.ml b/v2v/v2v_unit_tests.ml index be0bf0172..76f04f6fe 100644 --- a/v2v/v2v_unit_tests.ml +++ b/v2v/v2v_unit_tests.ml @@ -110,36 +110,6 @@ let test_get_ostype ctx i_product_variant = "Server"; i_arch = "x86_64" }) -let test_drive_name ctx - let printer = identity in - assert_equal ~printer "a" (Utils.drive_name 0); - assert_equal ~printer "z" (Utils.drive_name 25); - assert_equal ~printer "aa" (Utils.drive_name 26); - assert_equal ~printer "ab" (Utils.drive_name 27); - assert_equal ~printer "az" (Utils.drive_name 51); - assert_equal ~printer "ba" (Utils.drive_name 52); - assert_equal ~printer "zz" (Utils.drive_name 701); - assert_equal ~printer "aaa" (Utils.drive_name 702); - assert_equal ~printer "zzz" (Utils.drive_name 18277) - -let test_drive_index ctx - let printer = string_of_int in - assert_equal ~printer 0 (Utils.drive_index "a"); - assert_equal ~printer 25 (Utils.drive_index "z"); - assert_equal ~printer 26 (Utils.drive_index "aa"); - assert_equal ~printer 27 (Utils.drive_index "ab"); - assert_equal ~printer 51 (Utils.drive_index "az"); - assert_equal ~printer 52 (Utils.drive_index "ba"); - assert_equal ~printer 701 (Utils.drive_index "zz"); - assert_equal ~printer 702 (Utils.drive_index "aaa"); - assert_equal ~printer 18277 (Utils.drive_index "zzz"); - let exn = Invalid_argument "drive_index: invalid parameter" in - assert_raises exn (fun () -> Utils.drive_index ""); - assert_raises exn (fun () -> Utils.drive_index "abc123"); - assert_raises exn (fun () -> Utils.drive_index "123"); - assert_raises exn (fun () -> Utils.drive_index "Z"); - assert_raises exn (fun () -> Utils.drive_index "aB") - let test_virtio_iso_path_matches_guest_os ctx (* Windows OSes fake inspection data. *) let make_win name major minor variant arch = { @@ -779,19 +749,6 @@ let test_virtio_iso_path_matches_guest_os ctx ) all_windows ) paths -let test_shell_unquote ctx - let printer = identity in - assert_equal ~printer "a" (Utils.shell_unquote "a"); - assert_equal ~printer "b" (Utils.shell_unquote "'b'"); - assert_equal ~printer "c" (Utils.shell_unquote "\"c\""); - assert_equal ~printer "dd" (Utils.shell_unquote "\"dd\""); - assert_equal ~printer "e\\e" (Utils.shell_unquote "\"e\\\\e\""); - assert_equal ~printer "f\\" (Utils.shell_unquote "\"f\\\\\""); - assert_equal ~printer "\\g" (Utils.shell_unquote "\"\\\\g\""); - assert_equal ~printer "h\\-h" (Utils.shell_unquote "\"h\\-h\""); - assert_equal ~printer "i`" (Utils.shell_unquote "\"i\\`\""); - assert_equal ~printer "j\"" (Utils.shell_unquote "\"j\\\"\"") - let test_qemu_img_supports ctx (* No assertion here, we don't know if qemu-img supports the * feature, so just run the code and make sure it doesn't crash. @@ -945,11 +902,8 @@ let suite "virt-v2v" >::: [ "Create_ovf.get_ostype" >:: test_get_ostype; - "Utils.drive_name" >:: test_drive_name; - "Utils.drive_index" >:: test_drive_index; "Windows_virtio.virtio_iso_path_matches_guest_os" >:: test_virtio_iso_path_matches_guest_os; - "Utils.shell_unquote" >:: test_shell_unquote; "Utils.qemu_img_supports" >:: test_qemu_img_supports; "Parse_vmx.parse_string" >::test_vmx_parse_string; ] -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 12/42] utils: Rename ‘guestfs-internal-frontend.h’ to ‘guestfs-utils.h’.
The reason it's not just ‘utils.h’ is because Pino is worried that we might pick up /usr/include/utils.h from a rogue library. --- .gitignore | 2 +- builder/index-validate.c | 2 +- builder/pxzcat-c.c | 2 +- common/edit/file-edit.c | 2 +- common/mlutils/c_utils-c.c | 2 +- common/options/options.h | 2 +- common/options/uri.c | 2 +- common/parallel/domains.c | 2 +- common/parallel/estimate-max-threads.c | 2 +- common/parallel/parallel.c | 2 +- common/progress/progress.c | 2 +- common/utils/Makefile.am | 2 +- common/utils/cleanups.c | 2 +- common/utils/{guestfs-internal-frontend.h => guestfs-utils.h} | 6 +++--- common/utils/utils.c | 2 +- common/visit/visit.c | 2 +- common/windows/windows.c | 2 +- docs/C_SOURCE_FILES | 2 +- erlang/main.c | 2 +- fish/fish.h | 2 +- fuse/guestunmount.c | 2 +- fuse/test-fuse.c | 2 +- fuse/test-guestmount-fd.c | 2 +- fuse/test-guestunmount-fd.c | 2 +- generator/OCaml.ml | 2 +- generator/erlang.ml | 6 +++--- generator/fish.ml | 4 ++-- generator/java.ml | 2 +- generator/lua.ml | 2 +- generator/php.ml | 2 +- generator/python.ml | 2 +- generator/ruby.ml | 2 +- generator/tests_c_api.ml | 2 +- java/handle.c | 2 +- lib/Makefile.am | 2 +- lib/guestfs-internal-all.h | 9 ++++----- lib/guestfs-internal.h | 5 ++--- lib/unit-tests.c | 2 +- make-fs/make-fs.c | 2 +- mllib/getopt-c.c | 2 +- mllib/uri-c.c | 2 +- ocaml/guestfs-c.c | 2 +- p2v/p2v.h | 4 ++-- python/MANIFEST.in | 4 ++-- python/Makefile.am | 10 +++++----- rescue/escape.c | 2 +- rescue/rescue.c | 2 +- rescue/suggest.c | 2 +- test-tool/test-tool.c | 2 +- tests/c-api/test-add-drive-opts.c | 2 +- tests/c-api/test-add-libvirt-dom.c | 2 +- tests/c-api/test-backend-settings.c | 2 +- tests/c-api/test-config.c | 2 +- tests/c-api/test-create-handle.c | 2 +- tests/c-api/test-debug-to-file.c | 2 +- tests/c-api/test-environment.c | 2 +- tests/c-api/test-event-string.c | 2 +- tests/c-api/test-last-errno.c | 2 +- tests/c-api/test-private-data.c | 2 +- tests/c-api/test-user-cancel.c | 2 +- tests/c-api/tests-main.c | 2 +- tests/charsets/test-charset-fidelity.c | 2 +- tests/disks/test-add-disks.c | 2 +- tests/events/test-libvirt-auth-callbacks.c | 2 +- tests/mount-local/test-parallel-mount-local.c | 2 +- tests/parallel/test-parallel.c | 2 +- tests/regressions/rhbz1055452.c | 2 +- tests/regressions/rhbz501893.c | 2 +- tests/regressions/rhbz790721.c | 2 +- tests/regressions/rhbz914931.c | 2 +- tests/regressions/test-big-heap.c | 2 +- utils/boot-analysis/boot-analysis-timeline.c | 2 +- utils/boot-analysis/boot-analysis-utils.c | 2 +- utils/boot-analysis/boot-analysis.c | 2 +- utils/boot-benchmark/boot-benchmark.c | 2 +- utils/qemu-boot/qemu-boot.c | 2 +- utils/qemu-speed-test/qemu-speed-test.c | 2 +- v2v/libvirt_utils-c.c | 2 +- 78 files changed, 93 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index eadeda311..8afd06d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -496,7 +496,7 @@ Makefile.in /python/guestfs.pyc /python/guestfs.pyo /python/guestfs-internal-all.h -/python/guestfs-internal-frontend.h +/python/guestfs-utils.h /python/ignore-value.h /python/MANIFEST /python/module.c diff --git a/builder/index-validate.c b/builder/index-validate.c index 224cd674a..98c4e8279 100644 --- a/builder/index-validate.c +++ b/builder/index-validate.c @@ -31,7 +31,7 @@ #include <guestfs.h> #include "getprogname.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "index-struct.h" #include "index-parse.h" diff --git a/builder/pxzcat-c.c b/builder/pxzcat-c.c index ef37849ed..436914ba6 100644 --- a/builder/pxzcat-c.c +++ b/builder/pxzcat-c.c @@ -36,7 +36,7 @@ #include <caml/mlvalues.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" diff --git a/common/edit/file-edit.c b/common/edit/file-edit.c index b0347e78f..171703844 100644 --- a/common/edit/file-edit.c +++ b/common/edit/file-edit.c @@ -41,7 +41,7 @@ #include <utime.h> #include <sys/wait.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "file-edit.h" diff --git a/common/mlutils/c_utils-c.c b/common/mlutils/c_utils-c.c index 32edbd4a7..8c4571a6e 100644 --- a/common/mlutils/c_utils-c.c +++ b/common/mlutils/c_utils-c.c @@ -36,7 +36,7 @@ extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" diff --git a/common/options/options.h b/common/options/options.h index 6bf2c5863..f4482e478 100644 --- a/common/options/options.h +++ b/common/options/options.h @@ -23,7 +23,7 @@ #include <stdbool.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" /* Provided by guestfish or guestmount. */ extern guestfs_h *g; diff --git a/common/options/uri.c b/common/options/uri.c index e7ba7a51a..ac36bccb2 100644 --- a/common/options/uri.c +++ b/common/options/uri.c @@ -37,7 +37,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "uri.h" static int is_uri (const char *arg); diff --git a/common/parallel/domains.c b/common/parallel/domains.c index d5d3ae9a1..442cc223b 100644 --- a/common/parallel/domains.c +++ b/common/parallel/domains.c @@ -41,7 +41,7 @@ #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "domains.h" #if defined(HAVE_LIBVIRT) diff --git a/common/parallel/estimate-max-threads.c b/common/parallel/estimate-max-threads.c index 27ed80db9..a0e7ed6d7 100644 --- a/common/parallel/estimate-max-threads.c +++ b/common/parallel/estimate-max-threads.c @@ -26,7 +26,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "estimate-max-threads.h" static char *read_line_from (const char *cmd); diff --git a/common/parallel/parallel.c b/common/parallel/parallel.c index 97b9a3a0c..c89bea8b3 100644 --- a/common/parallel/parallel.c +++ b/common/parallel/parallel.c @@ -47,7 +47,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "options.h" #include "domains.h" #include "estimate-max-threads.h" diff --git a/common/progress/progress.c b/common/progress/progress.c index 20c15eab4..29e900d12 100644 --- a/common/progress/progress.c +++ b/common/progress/progress.c @@ -32,7 +32,7 @@ #include <langinfo.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "progress.h" diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index a86b715f6..02357a591 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -23,7 +23,7 @@ libutils_la_SOURCES = \ ../../lib/guestfs.h \ cleanups.c \ cleanups.h \ - guestfs-internal-frontend.h \ + guestfs-utils.h \ libxml2-cleanups.c \ utils.c libutils_la_CPPFLAGS = \ diff --git a/common/utils/cleanups.c b/common/utils/cleanups.c index 47da587bb..457aee57f 100644 --- a/common/utils/cleanups.c +++ b/common/utils/cleanups.c @@ -65,7 +65,7 @@ #include "hash.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" /* Stdlib cleanups. */ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-utils.h similarity index 97% rename from common/utils/guestfs-internal-frontend.h rename to common/utils/guestfs-utils.h index 3594d593e..5758059ec 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-utils.h @@ -29,8 +29,8 @@ * B<not> be here! */ -#ifndef GUESTFS_INTERNAL_FRONTEND_H_ -#define GUESTFS_INTERNAL_FRONTEND_H_ +#ifndef GUESTFS_UTILS_H_ +#define GUESTFS_UTILS_H_ #include <stdbool.h> @@ -103,4 +103,4 @@ extern char *guestfs_int_shell_unquote (const char *str); fputs ("\033[0m", (fp)); \ } while (0) -#endif /* GUESTFS_INTERNAL_FRONTEND_H_ */ +#endif /* GUESTFS_UTILS_H_ */ diff --git a/common/utils/utils.c b/common/utils/utils.c index bdc8449a2..58953e58a 100644 --- a/common/utils/utils.c +++ b/common/utils/utils.c @@ -47,7 +47,7 @@ /* NB: MUST NOT include "guestfs-internal.h". */ #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" void guestfs_int_free_string_list (char **argv) diff --git a/common/visit/visit.c b/common/visit/visit.c index 491f9dda3..18380ab43 100644 --- a/common/visit/visit.c +++ b/common/visit/visit.c @@ -36,7 +36,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "structs-cleanups.h" #include "visit.h" diff --git a/common/windows/windows.c b/common/windows/windows.c index 1fd94e0e6..6c3cfb08e 100644 --- a/common/windows/windows.c +++ b/common/windows/windows.c @@ -34,7 +34,7 @@ #include <langinfo.h> #include <libintl.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "c-ctype.h" diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 6bf0c3544..bfb837178 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -51,7 +51,7 @@ common/structs/structs-print.c common/structs/structs-print.h common/utils/cleanups.c common/utils/cleanups.h -common/utils/guestfs-internal-frontend.h +common/utils/guestfs-utils.h common/utils/libxml2-cleanups.c common/utils/utils.c common/visit/visit.c diff --git a/erlang/main.c b/erlang/main.c index 54df4822f..bfb268647 100644 --- a/erlang/main.c +++ b/erlang/main.c @@ -36,7 +36,7 @@ instead of erl_interface. #include "full-write.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" guestfs_h *g; diff --git a/fish/fish.h b/fish/fish.h index df22e34e7..c610a0a2f 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -21,7 +21,7 @@ #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "fish-cmds.h" diff --git a/fuse/guestunmount.c b/fuse/guestunmount.c index 2b2c493f8..9560fe740 100644 --- a/fuse/guestunmount.c +++ b/fuse/guestunmount.c @@ -35,7 +35,7 @@ #include <sys/wait.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" #include "getprogname.h" diff --git a/fuse/test-fuse.c b/fuse/test-fuse.c index 546c08a0b..e5faa0be7 100644 --- a/fuse/test-fuse.c +++ b/fuse/test-fuse.c @@ -50,7 +50,7 @@ #endif #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" diff --git a/fuse/test-guestmount-fd.c b/fuse/test-guestmount-fd.c index abef7b293..ae18b43fe 100644 --- a/fuse/test-guestmount-fd.c +++ b/fuse/test-guestmount-fd.c @@ -33,7 +33,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #define GUESTMOUNT_BINARY "guestmount" #define GUESTUNMOUNT_BINARY "guestunmount" diff --git a/fuse/test-guestunmount-fd.c b/fuse/test-guestunmount-fd.c index 6756f18cb..16508d082 100644 --- a/fuse/test-guestunmount-fd.c +++ b/fuse/test-guestunmount-fd.c @@ -36,7 +36,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/generator/OCaml.ml b/generator/OCaml.ml index f6a4292b9..53f105198 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -425,7 +425,7 @@ and generate_ocaml_c () #include <caml/signals.h> #include <guestfs.h> -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"guestfs-c.h\" diff --git a/generator/erlang.ml b/generator/erlang.ml index 03cca3368..a7627cff9 100644 --- a/generator/erlang.ml +++ b/generator/erlang.ml @@ -254,7 +254,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"actions.h\" "; @@ -344,7 +344,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"actions.h\" "; @@ -535,7 +535,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"actions.h\" diff --git a/generator/fish.ml b/generator/fish.ml index 3d99c9081..546cd8ed6 100644 --- a/generator/fish.ml +++ b/generator/fish.ml @@ -99,7 +99,7 @@ let generate_fish_run_cmds actions () pr "#include \"getprogname.h\"\n"; pr "\n"; pr "#include \"guestfs.h\"\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"guestfs-utils.h\"\n"; pr "#include \"structs-print.h\"\n"; pr "\n"; pr "#include \"fish.h\"\n"; @@ -607,7 +607,7 @@ let generate_fish_cmds () pr "#include <errno.h>\n"; pr "\n"; pr "#include \"guestfs.h\"\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"guestfs-utils.h\"\n"; pr "#include \"structs-print.h\"\n"; pr "\n"; pr "#include \"fish.h\"\n"; diff --git a/generator/java.ml b/generator/java.ml index a7d0ed359..94d68c14a 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -585,7 +585,7 @@ and generate_java_c actions () #include \"com_redhat_et_libguestfs_GuestFS.h\" #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"structs-cleanups.h\" /* Note that this function returns. The exception is not thrown diff --git a/generator/lua.ml b/generator/lua.ml index b40c51753..c47938c8a 100644 --- a/generator/lua.ml +++ b/generator/lua.ml @@ -64,7 +64,7 @@ let generate_lua_c () #endif #include <guestfs.h> -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #define GUESTFS_LUA_HANDLE \"guestfs handle\" diff --git a/generator/php.ml b/generator/php.ml index 0721e431a..3c0ace53a 100644 --- a/generator/php.ml +++ b/generator/php.ml @@ -90,7 +90,7 @@ and generate_php_c () #include <php_guestfs_php.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" /* Only for POINTER_NOT_IMPLEMENTED */ +#include \"guestfs-utils.h\" /* Only for POINTER_NOT_IMPLEMENTED */ static int res_guestfs_h; diff --git a/generator/python.ml b/generator/python.ml index c6c237241..331367696 100644 --- a/generator/python.ml +++ b/generator/python.ml @@ -42,7 +42,7 @@ let rec generate_python_actions_h () #define GUESTFS_PYTHON_ACTIONS_H_ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; diff --git a/generator/ruby.ml b/generator/ruby.ml index 825cab32a..f209109ee 100644 --- a/generator/ruby.ml +++ b/generator/ruby.ml @@ -55,7 +55,7 @@ let rec generate_ruby_h () #endif #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" /* Only for POINTER_NOT_IMPLEMENTED */ +#include \"guestfs-utils.h\" /* Only for POINTER_NOT_IMPLEMENTED */ #include \"extconf.h\" diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index c3cb62c4d..b1681cd09 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -47,7 +47,7 @@ let rec generate_c_api_tests () #include <errno.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"guestfs-utils.h\" #include \"structs-cleanups.h\" #include \"tests.h\" diff --git a/java/handle.c b/java/handle.c index 0993f33ed..bae3be474 100644 --- a/java/handle.c +++ b/java/handle.c @@ -25,7 +25,7 @@ #include "com_redhat_et_libguestfs_GuestFS.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" /* This is the opaque data passed between _set_event_callback and * the C wrapper which calls the Java event callback. diff --git a/lib/Makefile.am b/lib/Makefile.am index 3e71f3084..a80dc49a9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -57,7 +57,7 @@ libguestfs_la_SOURCES = \ ../common/errnostring/errnostring.h \ ../common/protocol/guestfs_protocol.h \ ../common/qemuopts/qemuopts.h \ - ../common/utils/guestfs-internal-frontend.h \ + ../common/utils/guestfs-utils.h \ ../common/structs/structs-cleanups.h \ guestfs.h \ guestfs-internal.h \ diff --git a/lib/guestfs-internal-all.h b/lib/guestfs-internal-all.h index bf87b8295..e7ffd1826 100644 --- a/lib/guestfs-internal-all.h +++ b/lib/guestfs-internal-all.h @@ -22,11 +22,10 @@ * tools (ie. I<all> C code). * * If you need a definition used by only the library, put it in - * F<lib/guestfs-internal.h> instead. If you need a definition used - * by only the frontend (non-daemon) parts of libguestfs, try - * F<lib/guestfs-internal-frontend.h>. If a definition is used by - * only a single tool, it should not be in any shared header file at - * all. + * F<lib/guestfs-internal.h> instead. + * + * If a definition is used by only a single tool, it should not be in + * any shared header file at all. */ #ifndef GUESTFS_INTERNAL_ALL_H_ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index 190b5cdd2..589968b29 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -20,8 +20,7 @@ * This header file is included in the libguestfs library (F<lib/>) * only. * - * See also F<lib/guestfs-internal-frontend.h> and - * F<lib/guestfs-internal-all.h> + * See also F<lib/guestfs-internal-all.h>. */ #ifndef GUESTFS_INTERNAL_H_ @@ -55,7 +54,7 @@ #include "hash.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #if ENABLE_PROBES #include <sys/sdt.h> diff --git a/lib/unit-tests.c b/lib/unit-tests.c index d44bc41e0..67a174f51 100644 --- a/lib/unit-tests.c +++ b/lib/unit-tests.c @@ -38,7 +38,7 @@ #include "guestfs.h" #include "guestfs-internal.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" /** * Test C<guestfs_int_split_string>. diff --git a/make-fs/make-fs.c b/make-fs/make-fs.c index e30745a0f..7b99280d9 100644 --- a/make-fs/make-fs.c +++ b/make-fs/make-fs.c @@ -35,7 +35,7 @@ #include <sys/wait.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "xstrtol.h" #include "getprogname.h" diff --git a/mllib/getopt-c.c b/mllib/getopt-c.c index d2d98768c..7b7e39be2 100644 --- a/mllib/getopt-c.c +++ b/mllib/getopt-c.c @@ -40,7 +40,7 @@ #include <caml/callback.h> #include <caml/printexc.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" extern value guestfs_int_mllib_getopt_parse (value argsv, value specsv, value anon_funv, value usage_msgv); diff --git a/mllib/uri-c.c b/mllib/uri-c.c index ffb55306b..3e539c50e 100644 --- a/mllib/uri-c.c +++ b/mllib/uri-c.c @@ -31,7 +31,7 @@ #include <caml/mlvalues.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "uri.h" extern value guestfs_int_mllib_parse_uri (value argv); diff --git a/ocaml/guestfs-c.c b/ocaml/guestfs-c.c index 0df57758d..9602338da 100644 --- a/ocaml/guestfs-c.c +++ b/ocaml/guestfs-c.c @@ -23,7 +23,7 @@ #include <errno.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include <caml/config.h> #include <caml/alloc.h> diff --git a/p2v/p2v.h b/p2v/p2v.h index be913c024..5996a01d8 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -32,10 +32,10 @@ /* We don't use libguestfs directly here, and we don't link to it * either (in fact, we don't want libguestfs on the ISO). However * we include this just so that we can use the convenience macros in - * guestfs-internal-frontend.h. + * utils.h. */ #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" /* Ensure we don't use libguestfs. */ #define guestfs_h DO_NOT_USE diff --git a/python/MANIFEST.in b/python/MANIFEST.in index 116367d16..619e4b6c1 100644 --- a/python/MANIFEST.in +++ b/python/MANIFEST.in @@ -17,8 +17,8 @@ include actions.h include c-ctype.h +include cleanups.h include config.h include guestfs-internal-all.h -include guestfs-internal-frontend-cleanups.h -include guestfs-internal-frontend.h +include guestfs-utils.h include ignore-value.h diff --git a/python/Makefile.am b/python/Makefile.am index 7e91bbb7e..dba45a74d 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -101,9 +101,9 @@ stamp-extra-files: \ cleanups.h \ config.h \ guestfs-internal-all.h \ - guestfs-internal-frontend.h \ + guestfs-utils.h ignore-value.h \ - utils.c + utils.c \ touch $@ config.h: @@ -124,8 +124,8 @@ ignore-value.h: guestfs-internal-all.h: ln $(top_srcdir)/lib/guestfs-internal-all.h $@ -guestfs-internal-frontend.h: - ln $(top_srcdir)/common/utils/guestfs-internal-frontend.h $@ +guestfs-utils.h: + ln $(top_srcdir)/common/utils/guestfs-utils.h $@ utils.c: ln $(top_srcdir)/common/utils/utils.c $@ @@ -152,7 +152,7 @@ CLEANFILES += \ cleanups.c \ cleanups.h \ guestfs-internal-all.h \ - guestfs-internal-frontend.h \ + guestfs-utils.h \ ignore-value.h \ stamp-extra-files \ utils.c diff --git a/rescue/escape.c b/rescue/escape.c index f7f7d84c4..a7949972a 100644 --- a/rescue/escape.c +++ b/rescue/escape.c @@ -30,7 +30,7 @@ #include "c-ctype.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "rescue.h" diff --git a/rescue/rescue.c b/rescue/rescue.c index 25bd55077..a41eaa6b1 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -40,7 +40,7 @@ #include "xvasprintf.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "windows.h" #include "options.h" diff --git a/rescue/suggest.c b/rescue/suggest.c index f32bb83c7..43e020cc0 100644 --- a/rescue/suggest.c +++ b/rescue/suggest.c @@ -25,7 +25,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "options.h" diff --git a/test-tool/test-tool.c b/test-tool/test-tool.c index 2d63b4f4e..18c544fa3 100644 --- a/test-tool/test-tool.c +++ b/test-tool/test-tool.c @@ -36,7 +36,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" diff --git a/tests/c-api/test-add-drive-opts.c b/tests/c-api/test-add-drive-opts.c index 4f39e2690..462bc54e3 100644 --- a/tests/c-api/test-add-drive-opts.c +++ b/tests/c-api/test-add-drive-opts.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-add-libvirt-dom.c b/tests/c-api/test-add-libvirt-dom.c index 672e8612f..076ea071d 100644 --- a/tests/c-api/test-add-libvirt-dom.c +++ b/tests/c-api/test-add-libvirt-dom.c @@ -31,7 +31,7 @@ #include "xgetcwd.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" static void make_test_xml (FILE *fp, const char *cwd) diff --git a/tests/c-api/test-backend-settings.c b/tests/c-api/test-backend-settings.c index 371a24776..17e0f0b76 100644 --- a/tests/c-api/test-backend-settings.c +++ b/tests/c-api/test-backend-settings.c @@ -29,7 +29,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-config.c b/tests/c-api/test-config.c index fef6c64e5..bffae5254 100644 --- a/tests/c-api/test-config.c +++ b/tests/c-api/test-config.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-create-handle.c b/tests/c-api/test-create-handle.c index 170649926..442ec6b73 100644 --- a/tests/c-api/test-create-handle.c +++ b/tests/c-api/test-create-handle.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-debug-to-file.c b/tests/c-api/test-debug-to-file.c index 10e36c63b..5e6b8ac29 100644 --- a/tests/c-api/test-debug-to-file.c +++ b/tests/c-api/test-debug-to-file.c @@ -30,7 +30,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" diff --git a/tests/c-api/test-environment.c b/tests/c-api/test-environment.c index d5a03d4fb..fade0720b 100644 --- a/tests/c-api/test-environment.c +++ b/tests/c-api/test-environment.c @@ -31,7 +31,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-event-string.c b/tests/c-api/test-event-string.c index e94000d0a..839f3b9cf 100644 --- a/tests/c-api/test-event-string.c +++ b/tests/c-api/test-event-string.c @@ -27,7 +27,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-last-errno.c b/tests/c-api/test-last-errno.c index acd5cd97d..9c5b2cf3a 100644 --- a/tests/c-api/test-last-errno.c +++ b/tests/c-api/test-last-errno.c @@ -31,7 +31,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-private-data.c b/tests/c-api/test-private-data.c index 9ebe7a892..5107ec8a5 100644 --- a/tests/c-api/test-private-data.c +++ b/tests/c-api/test-private-data.c @@ -29,7 +29,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #define PREFIX "test_" diff --git a/tests/c-api/test-user-cancel.c b/tests/c-api/test-user-cancel.c index d429a0fe3..3216ef53a 100644 --- a/tests/c-api/test-user-cancel.c +++ b/tests/c-api/test-user-cancel.c @@ -48,7 +48,7 @@ #include "cloexec.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" static const off_t filesize = 1024*1024*1024; diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c index f202a70ff..dc0ff8802 100644 --- a/tests/c-api/tests-main.c +++ b/tests/c-api/tests-main.c @@ -37,7 +37,7 @@ #define GUESTFS_WARN_DEPRECATED 1 #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "structs-cleanups.h" #include "tests.h" diff --git a/tests/charsets/test-charset-fidelity.c b/tests/charsets/test-charset-fidelity.c index 952256edd..967afbc88 100644 --- a/tests/charsets/test-charset-fidelity.c +++ b/tests/charsets/test-charset-fidelity.c @@ -31,7 +31,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "getprogname.h" diff --git a/tests/disks/test-add-disks.c b/tests/disks/test-add-disks.c index b968bd352..163f41844 100644 --- a/tests/disks/test-add-disks.c +++ b/tests/disks/test-add-disks.c @@ -34,7 +34,7 @@ #include <assert.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "getprogname.h" diff --git a/tests/events/test-libvirt-auth-callbacks.c b/tests/events/test-libvirt-auth-callbacks.c index a18ab2429..d8f71ae4d 100644 --- a/tests/events/test-libvirt-auth-callbacks.c +++ b/tests/events/test-libvirt-auth-callbacks.c @@ -28,7 +28,7 @@ #include <libvirt/libvirt.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #define EXPECT_OK 1 #define EXPECT_FAIL -1 diff --git a/tests/mount-local/test-parallel-mount-local.c b/tests/mount-local/test-parallel-mount-local.c index 542f1ed3d..f50bff8ca 100644 --- a/tests/mount-local/test-parallel-mount-local.c +++ b/tests/mount-local/test-parallel-mount-local.c @@ -38,7 +38,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "estimate-max-threads.h" #include "ignore-value.h" diff --git a/tests/parallel/test-parallel.c b/tests/parallel/test-parallel.c index 276340e4b..234274080 100644 --- a/tests/parallel/test-parallel.c +++ b/tests/parallel/test-parallel.c @@ -36,7 +36,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "ignore-value.h" #include "getprogname.h" diff --git a/tests/regressions/rhbz1055452.c b/tests/regressions/rhbz1055452.c index 79cd54033..b18a88529 100644 --- a/tests/regressions/rhbz1055452.c +++ b/tests/regressions/rhbz1055452.c @@ -33,7 +33,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/regressions/rhbz501893.c b/tests/regressions/rhbz501893.c index b763a107b..398e27ea5 100644 --- a/tests/regressions/rhbz501893.c +++ b/tests/regressions/rhbz501893.c @@ -24,7 +24,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/tests/regressions/rhbz790721.c b/tests/regressions/rhbz790721.c index 2dfcd2768..bc69269fe 100644 --- a/tests/regressions/rhbz790721.c +++ b/tests/regressions/rhbz790721.c @@ -43,7 +43,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "getprogname.h" diff --git a/tests/regressions/rhbz914931.c b/tests/regressions/rhbz914931.c index bfc8f83ea..dc44b54c8 100644 --- a/tests/regressions/rhbz914931.c +++ b/tests/regressions/rhbz914931.c @@ -32,7 +32,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "getprogname.h" diff --git a/tests/regressions/test-big-heap.c b/tests/regressions/test-big-heap.c index 1cbf8d23c..b841fba70 100644 --- a/tests/regressions/test-big-heap.c +++ b/tests/regressions/test-big-heap.c @@ -33,7 +33,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" int main (int argc, char *argv[]) diff --git a/utils/boot-analysis/boot-analysis-timeline.c b/utils/boot-analysis/boot-analysis-timeline.c index 8198c5677..3ff031487 100644 --- a/utils/boot-analysis/boot-analysis-timeline.c +++ b/utils/boot-analysis/boot-analysis-timeline.c @@ -33,7 +33,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "boot-analysis.h" diff --git a/utils/boot-analysis/boot-analysis-utils.c b/utils/boot-analysis/boot-analysis-utils.c index 4308bead3..98e7725f3 100644 --- a/utils/boot-analysis/boot-analysis-utils.c +++ b/utils/boot-analysis/boot-analysis-utils.c @@ -27,7 +27,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "boot-analysis-utils.h" diff --git a/utils/boot-analysis/boot-analysis.c b/utils/boot-analysis/boot-analysis.c index 1bec9a57b..346305d1d 100644 --- a/utils/boot-analysis/boot-analysis.c +++ b/utils/boot-analysis/boot-analysis.c @@ -40,7 +40,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "boot-analysis.h" #include "boot-analysis-utils.h" diff --git a/utils/boot-benchmark/boot-benchmark.c b/utils/boot-benchmark/boot-benchmark.c index 4af394387..c448769d2 100644 --- a/utils/boot-benchmark/boot-benchmark.c +++ b/utils/boot-benchmark/boot-benchmark.c @@ -34,7 +34,7 @@ #include <math.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "boot-analysis-utils.h" diff --git a/utils/qemu-boot/qemu-boot.c b/utils/qemu-boot/qemu-boot.c index 68818358b..660f90c57 100644 --- a/utils/qemu-boot/qemu-boot.c +++ b/utils/qemu-boot/qemu-boot.c @@ -36,7 +36,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "estimate-max-threads.h" #include "getprogname.h" diff --git a/utils/qemu-speed-test/qemu-speed-test.c b/utils/qemu-speed-test/qemu-speed-test.c index 5aa663c14..71f181ad9 100644 --- a/utils/qemu-speed-test/qemu-speed-test.c +++ b/utils/qemu-speed-test/qemu-speed-test.c @@ -39,7 +39,7 @@ #include <sys/time.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #include "getprogname.h" diff --git a/v2v/libvirt_utils-c.c b/v2v/libvirt_utils-c.c index 98ed30e06..93ee5208e 100644 --- a/v2v/libvirt_utils-c.c +++ b/v2v/libvirt_utils-c.c @@ -41,7 +41,7 @@ #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "guestfs-utils.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 13/42] daemon: Link guestfsd with libutils.
After the previous refactoring, we are able to link the daemon to common/utils, and also remove some of the "duplicate" functions that the daemon carried ("duplicate" in quotes because they were often not exact duplicates). Also this removes the duplicate reimplementation of (most) cleanup functions in the daemon, since those are provided by libutils now. It also allows us in future (but not in this commit) to move utility functions from the daemon into libutils. --- daemon/Makefile.am | 8 +++++-- daemon/augeas.c | 2 +- daemon/btrfs.c | 18 +++++++-------- daemon/cleanups.c | 49 +--------------------------------------- daemon/cleanups.h | 51 ----------------------------------------- daemon/daemon.h | 31 ++++++++++++++++--------- daemon/debug.c | 4 ++-- daemon/echo-daemon.c | 2 +- daemon/guestfsd.c | 64 ---------------------------------------------------- daemon/ldm.c | 2 +- daemon/lvm.c | 4 ++-- daemon/md.c | 8 ++++--- daemon/stat.c | 2 +- docs/C_SOURCE_FILES | 1 - generator/daemon.ml | 8 +++---- 15 files changed, 53 insertions(+), 201 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0d3dde516..eedf09d52 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -49,6 +49,8 @@ endif guestfsd_SOURCES = \ ../common/errnostring/errnostring.h \ ../common/protocol/guestfs_protocol.h \ + ../common/utils/cleanups.h \ + ../common/utils/guestfs-utils.h \ 9p.c \ acl.c \ actions.h \ @@ -62,7 +64,6 @@ guestfsd_SOURCES = \ cap.c \ checksum.c \ cleanups.c \ - cleanups.h \ cmp.c \ command.c \ command.h \ @@ -178,6 +179,7 @@ guestfsd_SOURCES = \ guestfsd_LDADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ + ../common/utils/libutils.la \ $(ACL_LIBS) \ $(CAP_LIBS) \ $(YAJL_LIBS) \ @@ -206,7 +208,9 @@ guestfsd_CPPFLAGS = \ -I$(top_srcdir)/common/errnostring \ -I$(top_builddir)/common/errnostring \ -I$(top_srcdir)/common/protocol \ - -I$(top_builddir)/common/protocol + -I$(top_builddir)/common/protocol \ + -I$(top_srcdir)/common/utils \ + -I$(top_builddir)/common/utils guestfsd_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(RPC_CFLAGS) \ diff --git a/daemon/augeas.c b/daemon/augeas.c index 5adc959a5..bd54c4849 100644 --- a/daemon/augeas.c +++ b/daemon/augeas.c @@ -436,7 +436,7 @@ do_aug_ls (const char *path) if (matches == NULL) return NULL; /* do_aug_match has already sent the error */ - sort_strings (matches, count_strings ((void *) matches)); + sort_strings (matches, guestfs_int_count_strings ((void *) matches)); return matches; /* Caller frees. */ } diff --git a/daemon/btrfs.c b/daemon/btrfs.c index ae2310b53..5f1e5d1d0 100644 --- a/daemon/btrfs.c +++ b/daemon/btrfs.c @@ -152,7 +152,7 @@ do_mkfs_btrfs (char *const *devices, int leafsize, const char *label, const char *metadata, int nodesize, int sectorsize) { - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); const size_t MAX_ARGS = nr_devices + 64; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -500,7 +500,7 @@ do_btrfs_subvolume_list (const mountable_t *fs) guestfs_int_btrfssubvolume_list *ret = NULL; - const size_t nr_subvolumes = count_strings (lines); + const size_t nr_subvolumes = guestfs_int_count_strings (lines); ret = malloc (sizeof *ret); if (!ret) { @@ -733,7 +733,7 @@ int do_btrfs_device_add (char *const *devices, const char *fs) { static int btrfs_device_add_needs_force = -1; - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); const size_t MAX_ARGS = nr_devices + 8; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -781,7 +781,7 @@ do_btrfs_device_add (char *const *devices, const char *fs) int do_btrfs_device_delete (char *const *devices, const char *fs) { - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); if (nr_devices == 0) return 0; @@ -1391,7 +1391,7 @@ do_btrfs_qgroup_show (const char *path) * 0/5 9249849344 9249849344 * */ - const size_t nr_qgroups = count_strings (lines) - 2; + const size_t nr_qgroups = guestfs_int_count_strings (lines) - 2; guestfs_int_btrfsqgroup_list *ret = NULL; ret = malloc (sizeof *ret); if (!ret) { @@ -1821,7 +1821,7 @@ do_btrfs_balance_status (const char *path) if (!lines) return NULL; - nlines = count_strings (lines); + nlines = guestfs_int_count_strings (lines); ret = calloc (1, sizeof *ret); if (ret == NULL) { @@ -1938,7 +1938,7 @@ do_btrfs_scrub_status (const char *path) if (!lines) return NULL; - if (count_strings (lines) < 2) { + if (guestfs_int_count_strings (lines) < 2) { reply_with_error ("truncated output from 'btrfs scrub status -R' command"); return NULL; } @@ -2124,7 +2124,7 @@ int do_btrfs_image (char *const *sources, const char *image, int compresslevel) { - const size_t nr_sources = count_strings (sources); + const size_t nr_sources = guestfs_int_count_strings (sources); const size_t MAX_ARGS = 64 + nr_sources; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -2229,7 +2229,7 @@ do_btrfs_filesystem_show (const char *device) if (!lines) return NULL; - if (count_strings (lines) < 3) { + if (guestfs_int_count_strings (lines) < 3) { reply_with_error ("truncated output from 'btrfs filesystem show' command"); return NULL; } diff --git a/daemon/cleanups.c b/daemon/cleanups.c index 3102cf94b..b4767178a 100644 --- a/daemon/cleanups.c +++ b/daemon/cleanups.c @@ -24,51 +24,7 @@ #include <augeas.h> -#include "cleanups.h" - -/* Use by the CLEANUP_* macros. Do not call these directly. */ -void -cleanup_free (void *ptr) -{ - free (* (void **) ptr); -} - -extern void free_strings (char **argv); - -void -cleanup_free_string_list (void *ptr) -{ - free_strings (* (char ***) ptr); -} - -void -cleanup_unlink_free (void *ptr) -{ - char *filename = * (char **) ptr; - - if (filename) { - unlink (filename); - free (filename); - } -} - -void -cleanup_close (void *ptr) -{ - const int fd = * (int *) ptr; - - if (fd >= 0) - close (fd); -} - -void -cleanup_fclose (void *ptr) -{ - FILE *f = * (FILE **) ptr; - - if (f) - fclose (f); -} +#include "daemon.h" void cleanup_aug_close (void *ptr) @@ -79,9 +35,6 @@ cleanup_aug_close (void *ptr) aug_close (aug); } -struct stringsbuf; -extern void free_stringsbuf (struct stringsbuf *sb); - void cleanup_free_stringsbuf (void *ptr) { diff --git a/daemon/cleanups.h b/daemon/cleanups.h deleted file mode 100644 index a791244cb..000000000 --- a/daemon/cleanups.h +++ /dev/null @@ -1,51 +0,0 @@ -/* libguestfs - the guestfsd daemon - * Copyright (C) 2009-2015 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. - */ - -#ifndef GUESTFSD_CLEANUPS_H -#define GUESTFSD_CLEANUPS_H - -/* These functions are used internally by the CLEANUP_* macros. - * Don't call them directly. - */ -extern void cleanup_free (void *ptr); -extern void cleanup_free_string_list (void *ptr); -extern void cleanup_unlink_free (void *ptr); -extern void cleanup_close (void *ptr); -extern void cleanup_fclose (void *ptr); -extern void cleanup_aug_close (void *ptr); -extern void cleanup_free_stringsbuf (void *ptr); - -#ifdef HAVE_ATTRIBUTE_CLEANUP -#define CLEANUP_FREE __attribute__((cleanup(cleanup_free))) -#define CLEANUP_FREE_STRING_LIST \ - __attribute__((cleanup(cleanup_free_string_list))) -#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free))) -#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close))) -#define CLEANUP_FCLOSE __attribute__((cleanup(cleanup_fclose))) -#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close))) -#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf))) -#else -#define CLEANUP_FREE -#define CLEANUP_FREE_STRING_LIST -#define CLEANUP_UNLINK_FREE -#define CLEANUP_CLOSE -#define CLEANUP_AUG_CLOSE -#define CLEANUP_FREE_STRINGSBUF -#endif - -#endif /* GUESTFSD_CLEANUPS_H */ diff --git a/daemon/daemon.h b/daemon/daemon.h index 400116514..be7a3bedc 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -30,9 +30,11 @@ #include "guestfs_protocol.h" -#include "guestfs-internal-all.h" - #include "cleanups.h" +#include "guestfs-utils.h" + +#include "guestfs-internal-all.h" + #include "structs-cleanups.h" #include "command.h" @@ -76,6 +78,22 @@ extern int xread (int sock, void *buf, size_t len) extern char *mountable_to_string (const mountable_t *mountable); +/*-- in cleanups.c --*/ + +/* These functions are used internally by the CLEANUP_* macros. + * Don't call them directly. + */ +extern void cleanup_aug_close (void *ptr); +extern void cleanup_free_stringsbuf (void *ptr); + +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close))) +#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf))) +#else +#define CLEANUP_AUG_CLOSE +#define CLEANUP_FREE_STRINGSBUF +#endif + /*-- in mount.c --*/ extern int mount_vfs_nochroot (const char *options, const char *vfstype, @@ -109,21 +127,12 @@ extern int end_stringsbuf (struct stringsbuf *sb); extern char **take_stringsbuf (struct stringsbuf *sb); extern void free_stringsbuf (struct stringsbuf *sb); -extern size_t count_strings (char *const *argv); extern void sort_strings (char **argv, size_t len); -extern void free_strings (char **argv); 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); -/* Concatenate strings, optionally with a separator string between - * each. On error, these return NULL but do NOT call reply_with_* nor - * free anything. - */ -extern char *concat_strings (char *const *argv); -extern char *join_strings (const char *separator, char *const *argv); - extern struct stringsbuf split_lines_sb (char *str); extern char **split_lines (char *str); diff --git a/daemon/debug.c b/daemon/debug.c index b18d87c26..e2d43a7ca 100644 --- a/daemon/debug.c +++ b/daemon/debug.c @@ -444,7 +444,7 @@ debug_ldd (const char *subcmd, size_t argc, char *const *const argv) static char * debug_ls (const char *subcmd, size_t argc, char *const *const argv) { - const size_t len = count_strings (argv); + const size_t len = guestfs_int_count_strings (argv); CLEANUP_FREE const char **cargv = NULL; size_t i; int r; @@ -477,7 +477,7 @@ debug_ls (const char *subcmd, size_t argc, char *const *const argv) static char * debug_ll (const char *subcmd, size_t argc, char *const *const argv) { - const size_t len = count_strings (argv); + const size_t len = guestfs_int_count_strings (argv); CLEANUP_FREE const char **cargv = NULL; size_t i; int r; diff --git a/daemon/echo-daemon.c b/daemon/echo-daemon.c index d566a9bb2..15429f072 100644 --- a/daemon/echo-daemon.c +++ b/daemon/echo-daemon.c @@ -28,7 +28,7 @@ do_echo_daemon (char *const *argv) { char *out; - out = join_strings (" ", argv); + out = guestfs_int_join_strings (" ", argv); if (out == NULL) { reply_with_perror ("malloc"); return NULL; diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index db2bb702f..b3f40628b 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -583,16 +583,6 @@ take_stringsbuf (struct stringsbuf *sb) return ret; } -size_t -count_strings (char *const *argv) -{ - size_t argc; - - for (argc = 0; argv[argc] != NULL; ++argc) - ; - return argc; -} - /** * Returns true if C<v> is a power of 2. * @@ -620,19 +610,6 @@ sort_strings (char **argv, size_t len) } void -free_strings (char **argv) -{ - size_t argc; - - if (!argv) - return; - - for (argc = 0; argv[argc] != NULL; ++argc) - free (argv[argc]); - free (argv); -} - -void free_stringslen (char **argv, size_t len) { size_t i; @@ -720,47 +697,6 @@ sort_device_names (char **argv, size_t len) qsort (argv, len, sizeof (char *), compare_device_names_vp); } -char * -concat_strings (char *const *argv) -{ - return join_strings ("", argv); -} - -char * -join_strings (const char *separator, char *const *argv) -{ - size_t i, len, seplen, rlen; - char *r; - - seplen = strlen (separator); - - len = 0; - for (i = 0; argv[i] != NULL; ++i) { - if (i > 0) - len += seplen; - len += strlen (argv[i]); - } - len++; /* for final \0 */ - - r = malloc (len); - if (r == NULL) - return NULL; - - rlen = 0; - for (i = 0; argv[i] != NULL; ++i) { - if (i > 0) { - memcpy (&r[rlen], separator, seplen); - rlen += seplen; - } - len = strlen (argv[i]); - memcpy (&r[rlen], argv[i], len); - rlen += len; - } - r[rlen] = '\0'; - - return r; -} - /** * Split an output string into a NULL-terminated list of lines, * wrapped into a stringsbuf. diff --git a/daemon/ldm.c b/daemon/ldm.c index 7753b0d82..75418e8d3 100644 --- a/daemon/ldm.c +++ b/daemon/ldm.c @@ -320,7 +320,7 @@ do_ldmtool_scan_devices (char * const * devices) int r; CLEANUP_FREE char *out = NULL, *err = NULL; - nr_devices = count_strings (devices); + nr_devices = guestfs_int_count_strings (devices); argv = malloc ((3 + nr_devices) * sizeof (char *)); if (argv == NULL) { reply_with_perror ("malloc"); diff --git a/daemon/lvm.c b/daemon/lvm.c index 6c57046ff..5d12b009f 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -337,7 +337,7 @@ do_vgcreate (const char *volgroup, char *const *physvols) CLEANUP_FREE char *err = NULL; CLEANUP_FREE const char **argv = NULL; - argc = count_strings (physvols) + 3; + argc = guestfs_int_count_strings (physvols) + 3; argv = malloc (sizeof (char *) * (argc + 1)); if (argv == NULL) { reply_with_perror ("malloc"); @@ -643,7 +643,7 @@ do_vg_activate (int activate, char *const *volgroups) CLEANUP_FREE char *err = NULL; CLEANUP_FREE const char **argv = NULL; - argc = count_strings (volgroups) + 4; + argc = guestfs_int_count_strings (volgroups) + 4; argv = malloc (sizeof (char *) * (argc+1)); if (argv == NULL) { reply_with_perror ("malloc"); diff --git a/daemon/md.c b/daemon/md.c index 3f31529e2..64d98fae5 100644 --- a/daemon/md.c +++ b/daemon/md.c @@ -97,7 +97,8 @@ do_md_create (const char *name, char *const *devices, } } else - nrdevices = count_strings (devices) + count_bits (umissingbitmap); + nrdevices + guestfs_int_count_strings (devices) + count_bits (umissingbitmap); if (optargs_bitmask & GUESTFS_MD_CREATE_LEVEL_BITMASK) { if (STRNEQ (level, "linear") && STRNEQ (level, "raid0") && @@ -124,10 +125,11 @@ do_md_create (const char *name, char *const *devices, } /* Check invariant. */ - if (count_strings (devices) + count_bits (umissingbitmap) !+ if (guestfs_int_count_strings (devices) + count_bits (umissingbitmap) ! (size_t) (nrdevices + spare)) { reply_with_error ("devices (%zu) + bits set in missingbitmap (%zu) is not equal to nrdevices (%d) + spare (%d)", - count_strings (devices), count_bits (umissingbitmap), + guestfs_int_count_strings (devices), + count_bits (umissingbitmap), nrdevices, spare); return -1; } diff --git a/daemon/stat.c b/daemon/stat.c index 73f19226b..a1cd49245 100644 --- a/daemon/stat.c +++ b/daemon/stat.c @@ -127,7 +127,7 @@ do_internal_lstatnslist (const char *path, char *const *names) guestfs_int_statns_list *ret; size_t i, nr_names; - nr_names = count_strings (names); + nr_names = guestfs_int_count_strings (names); ret = malloc (sizeof *ret); if (!ret) { diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index bfb837178..8ce6b5865 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -74,7 +74,6 @@ daemon/btrfs.c daemon/cap.c daemon/checksum.c daemon/cleanups.c -daemon/cleanups.h daemon/cmp.c daemon/command.c daemon/command.h diff --git a/generator/daemon.ml b/generator/daemon.ml index 0300dc54b..2ae462864 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -397,7 +397,7 @@ let generate_daemon_stubs actions () | RStringList (RPlainString, n) | RHashtable (RPlainString, RPlainString, n) -> pr " struct guestfs_%s_ret ret;\n" name; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -413,7 +413,7 @@ let generate_daemon_stubs actions () pr " free (r[i]);\n"; pr " r[i] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -428,7 +428,7 @@ let generate_daemon_stubs actions () pr " free (r[i]);\n"; pr " r[i] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -443,7 +443,7 @@ let generate_daemon_stubs actions () pr " free (r[i+1]);\n"; pr " r[i+1] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name -- 2.13.0
Richard W.M. Jones
2017-Jun-21 17:29 UTC
[Libguestfs] [PATCH v8 14/42] 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 a80dc49a9..f166d8942 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-21 17:29 UTC
[Libguestfs] [PATCH v8 15/42] 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 | 50 +++++++++++++ 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, 871 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 eedf09d52..40b770762 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..6b8b452a5 --- /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 = "<unnamed>") 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..1d35991b6 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,46 @@ 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 + */ + if (Tag_val (Field (exn, 0)) == String_tag) + /* For End_of_file and a few other constant exceptions. */ + exn_name = String_val (Field (exn, 0)); + else + /* For most exceptions. */ + 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: unhandled 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-21 17:29 UTC
[Libguestfs] [PATCH v8 16/42] 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 40b770762..6fb1c5384 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-21 17:29 UTC
[Libguestfs] [PATCH v8 17/42] 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 6fb1c5384..62a009dc5 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-21 17:29 UTC
[Libguestfs] [PATCH v8 18/42] 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 62a009dc5..5bb47d08b 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 1d35991b6..dbc8fef45 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-21 17:29 UTC
[Libguestfs] [PATCH v8 19/42] 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 5bb47d08b..3b49ae3bb 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-21 17:29 UTC
[Libguestfs] [PATCH v8 20/42] 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 3b49ae3bb..32c8d93c8 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-21 17:29 UTC
[Libguestfs] [PATCH v8 21/42] 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 32c8d93c8..fab82ebbe 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-21 17:29 UTC
[Libguestfs] [PATCH v8 22/42] 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 fab82ebbe..5fd0d77c6 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-21 17:29 UTC
[Libguestfs] [PATCH v8 23/42] 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 5fd0d77c6..4ee62e564 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-21 17:29 UTC
[Libguestfs] [PATCH v8 24/42] 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 4ee62e564..9baf13422 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-21 17:29 UTC
[Libguestfs] [PATCH v8 25/42] 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 9baf13422..7c50e1346 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..987006a4d --- /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 f166d8942..164d7743f 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-21 17:29 UTC
[Libguestfs] [PATCH v8 26/42] 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 7c50e1346..22a3036f8 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-21 17:29 UTC
[Libguestfs] [PATCH v8 27/42] 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 22a3036f8..e9c71ba3c 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-21 17:29 UTC
[Libguestfs] [PATCH v8 28/42] 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 e9c71ba3c..62ce49498 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-21 17:29 UTC
[Libguestfs] [PATCH v8 29/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 30/42] 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 62ce49498..b49b7d907 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-21 17:29 UTC
[Libguestfs] [PATCH v8 31/42] 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 b49b7d907..87e608688 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-21 17:29 UTC
[Libguestfs] [PATCH v8 32/42] 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 87e608688..d239f38ef 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 164d7743f..c7b6be955 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-21 17:29 UTC
[Libguestfs] [PATCH v8 33/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 34/42] 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 d239f38ef..a864b6996 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-21 17:29 UTC
[Libguestfs] [PATCH v8 35/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 36/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 37/42] 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 a864b6996..8921f6239 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-21 17:29 UTC
[Libguestfs] [PATCH v8 38/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 39/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 40/42] 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 c7b6be955..83b33ac8d 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-21 17:29 UTC
[Libguestfs] [PATCH v8 41/42] 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-21 17:29 UTC
[Libguestfs] [PATCH v8 42/42] 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/guestfsd.c | 2 + daemon/inspect.ml | 398 +++++ daemon/inspect.mli | 41 + daemon/inspect_fs.ml | 366 +++++ daemon/inspect_fs.mli | 23 + daemon/inspect_fs_unix.ml | 789 ++++++++++ daemon/inspect_fs_unix.mli | 44 + daemon/inspect_fs_unix_fstab.ml | 537 +++++++ daemon/inspect_fs_unix_fstab.mli | 34 + daemon/inspect_fs_windows.ml | 493 ++++++ daemon/inspect_fs_windows.mli | 24 + daemon/inspect_types.ml | 313 ++++ daemon/inspect_types.mli | 180 +++ daemon/inspect_utils.ml | 193 +++ daemon/inspect_utils.mli | 57 + 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 + 45 files changed, 4661 insertions(+), 4976 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 8921f6239..1ea046383 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 6b8b452a5..8dcdf9ab2 100644 --- a/daemon/chroot.ml +++ b/daemon/chroot.ml @@ -32,7 +32,7 @@ let create ?(name = "<unnamed>") 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/guestfsd.c b/daemon/guestfsd.c index dbc8fef45..e8f797868 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -1176,6 +1176,8 @@ ocaml_exn_to_reply_with_error (const char *func, value exn) 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 if (STREQ (exn_name, "Augeas.Error")) + reply_with_error ("augeas error: %s", String_val (Field (exn, 1))); else reply_with_error ("internal error: %s: unhandled exception thrown: %s", func, exn_name); diff --git a/daemon/inspect.ml b/daemon/inspect.ml new file mode 100644 index 000000000..91c0322c9 --- /dev/null +++ b/daemon/inspect.ml @@ -0,0 +1,398 @@ +(* 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 + + merge usr root; + 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; + 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..3f57c93b5 --- /dev/null +++ b/daemon/inspect_fs.ml @@ -0,0 +1,366 @@ +(* 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 + (* The following struct is mutated in place by callees: *) + let data = null_inspection_data in + + let debug_matching what + if verbose () then + eprintf "check_filesystem: %s matched %s\n%!" + (Mountable.to_string mountable) what + in + + (* Grub /boot? *) + if Is.is_file "/grub/menu.lst" || + Is.is_file "/grub/grub.conf" || + Is.is_file "/grub2/grub.cfg" then ( + debug_matching "Grub /boot"; + () + ) + (* 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 ( + debug_matching "FreeBSD root"; + role := `Root; + 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 ( + debug_matching "NetBSD root"; + role := `Root; + 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 ( + debug_matching "OpenBSD root"; + role := `Root; + 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 ( + debug_matching "Hurd root"; + role := `Root; + 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 ( + debug_matching "Minix root"; + role := `Root; + 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 ( + debug_matching "Linux root"; + role := `Root; + 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 ( + debug_matching "CoreOS root"; + role := `Root; + 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 ( + debug_matching "Linux /usr/local"; + () + ) + (* 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 ( + debug_matching "Linux /usr"; + role := `Usr; + 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 ( + debug_matching "CoreOS /usr"; + role := `Usr; + 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 ( + debug_matching "Linux /var"; + () + ) + (* Windows root? *) + else if Inspect_fs_windows.is_windows_systemroot () then ( + debug_matching "Windows root"; + role := `Root; + 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 ( + debug_matching "Windows volume with installed applications"; + () + ) + (* Windows volume (but not root)? *) + else if is_dir_nocase "/System Volume Information" then ( + debug_matching "Windows volume without installed applications"; + () + ) + (* FreeDOS? *) + else if is_dir_nocase "/FDOS" && + is_file_nocase "/FDOS/FREEDOS.BSS" then ( + debug_matching "FreeDOS"; + role := `Root; + data.os_type <- Some OS_TYPE_DOS; + data.distro <- Some DISTRO_FREEDOS; + (* FreeDOS is a mix of 16 and 32 bit, but + * assume it requires a 32 bit i386 processor. + *) + data.arch <- Some "i386" + ) + (* None of the above. *) + else ( + debug_matching "no known OS partition" + ); + + (* The above code should have set [data.os_type] and [data.distro] + * fields, so we can now guess the package management system. + *) + data.package_format <- check_package_format data; + data.package_management <- check_package_management data; + + 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..0c171b76a --- /dev/null +++ b/daemon/inspect_fs_unix.ml @@ -0,0 +1,789 @@ +(* 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" ] + +(* 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 + *) +let rec 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; + None + ) + else + Some (read_whole_file release_file) + ) () in + + match lines with + | None -> false + | Some lines -> + let lines = String.nsplit "\n" lines in + + List.iter ( + fun line -> + let line = String.trim line in + if line = "" || line.[0] = '#' then + () + 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.distro <- distro + | None -> () + ) + else if key = "PRETTY_NAME" then + data.product_name <- Some value + else if key = "VERSION_ID" then + parse_version_from_major_minor value data + ) + ) lines; + + (* If we haven't got all the fields, exit right away. *) + if data.distro = None || data.product_name = None then + false + else ( + (* 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) } -> + false + | _ -> true + ) + +(* 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; + None + ) + else + Some (read_whole_file release_file) + ) () in + + match lines with + | None -> false + | Some lines -> + let lines = String.nsplit "\n" lines in + + (* Some distros (eg. RHEL 3) have a bare lsb-release file that might + * just contain the LSB_VERSION field and nothing else. In that case + * we must bail out (return false). + *) + let ok = ref false in + + List.iter ( + fun line -> + if verbose () then + eprintf "parse_lsb_release: parsing: %s\n%!" line; + + if data.distro = None && line = "DISTRIB_ID=Ubuntu" then ( + ok := true; + data. distro <- Some DISTRO_UBUNTU + ) + else if data.distro = None && line = "DISTRIB_ID=LinuxMint" then ( + ok := true; + data.distro <- Some DISTRO_LINUX_MINT + ) + else if data.distro = None && line = "DISTRIB_ID=\"Mageia\"" then ( + ok := true; + data.distro <- Some DISTRO_MAGEIA + ) + else if data.distro = None && line = "DISTRIB_ID=CoreOS" then ( + ok := true; + data.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 ( + ok := true; + let n = String.length line in + let product_name = String.sub line 21 (n-22) in + data.product_name <- Some product_name + ) + else if String.is_prefix line "DISTRIB_DESCRIPTION=" then ( + ok := true; + let n = String.length line in + let product_name = String.sub line 20 (n-20) in + data.product_name <- Some product_name + ) + ) lines; + + !ok + +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; + None + ) + else + Some (read_whole_file release_file) + ) () in + + match lines with + | None -> false + | Some lines -> + let lines = String.nsplit "\n" lines in + + if lines = [] then false + else ( + (* First line is dist release name. *) + let product_name = List.hd lines in + data.product_name <- Some product_name; + + (* 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.distro <- Some DISTRO_SLES; + data.version <- version + ) + else if Str.string_match re_opensuse product_name 0 then ( + (* Second line contains version string. *) + if List.length lines >= 2 then ( + let line = List.nth lines 1 in + parse_version_from_major_minor line data + ); + + data.distro <- Some DISTRO_OPENSUSE + ); + + true + ) + +(* 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; + "" + ) + else + read_first_line_from_file release_file + ) () in + if product_name = "" then + false + else ( + if verbose () then + eprintf "parse_generic: product_name = %s\n%!" product_name; + + data.product_name <- Some product_name; + data.distro <- Some distro; + + (match rex with + | Some rex -> + (* If ~rex was supplied, then it must match the release file, + * else the parsing fails. + *) + if Str.string_match rex product_name 0 then ( + (* 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 -> () + | None, Some _ -> () + | Some major, None -> data.version <- Some (major, 0) + | Some major, Some minor -> data.version <- Some (major, minor) + ); + true + ) + else + false (* ... else the parsing fails. *) + + | 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; + true + ) + ) + +(* The list of release file tests that we run for Linux root filesystems. + * This is processed in order. + * + * For each test, first we check if the named release file exists. + * If so, the parse function is called. If not, we go on to the next + * test. + * + * Each parse function should return true or false. If a parse function + * returns true, then we finish, else if it returns false then we continue + * to the next test. + *) +type parse_function = string -> inspection_data -> bool + +let linux_root_tests : (string * parse_function) list = [ + (* 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.distro <- Some DISTRO_REDHAT_BASED; true); + + "/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.distro <- Some DISTRO_ARCHLINUX; true); + + "/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.distro <- Some DISTRO_SUSE_BASED; true); + + "/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; +] + +let rec check_linux_root mountable data + let os_type = OS_TYPE_LINUX in + data.os_type <- Some os_type; + + let rec loop = function + | (release_file, parse_fun) :: tests -> + if verbose () then + eprintf "check_linux_root: checking %s\n%!" release_file; + if Is.is_file ~followsymlinks:true release_file then ( + if parse_fun release_file data then () (* true => finished *) + else loop tests + ) else loop tests + | [] -> () + in + loop linux_root_tests; + + data.arch <- check_architecture (); + data.fstab <- + Inspect_fs_unix_fstab.check_fstab ~mdadm_conf:true mountable os_type; + data.hostname <- check_hostname_linux () + +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 _) as hostname -> hostname + | None -> + if Is.is_file "/etc/sysconfig/network" then + with_augeas ~name:"check_hostname_from_sysconfig_network" + ["/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_from_file: %s" filename in + Chroot.create ~name (Sysroot.sysroot ()) in + + let hostname + 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) + ) () in + + match hostname with + | None | Some "" -> None + | (Some _) as hostname -> hostname + +(* 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. + *) + aug_get_noerrors aug "/files/etc/sysconfig/network/HOSTNAME" + +(* The currently mounted device looks like a Linux /usr. *) +let check_linux_usr data + data.os_type <- Some OS_TYPE_LINUX; + + if Is.is_file "/lib/os-release" ~followsymlinks:true then + ignore (parse_os_release "/lib/os-release" data); + + (match check_architecture () with + | None -> () + | (Some _) as arch -> data.arch <- arch + ) + +(* 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.os_type <- Some OS_TYPE_LINUX; + data.distro <- Some DISTRO_COREOS; + + (* Determine hostname. *) + data.hostname <- check_hostname_linux (); + + (* CoreOS does not contain /etc/fstab to determine the mount points. + * Associate this filesystem with the "/" mount point. + *) + data.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 + data.os_type <- Some OS_TYPE_LINUX; + data.distro <- Some DISTRO_COREOS; + + if Is.is_file "/lib/os-release" ~followsymlinks:true then + ignore (parse_os_release "/lib/os-release" data) + else if Is.is_file "/share/coreos/lsb-release" ~followsymlinks:true then + ignore (parse_lsb_release "/share/coreos/lsb-release" data); + + (* Determine the architecture. *) + (match check_architecture () with + | None -> () + | (Some _) as arch -> data.arch <- arch + ); + + (* CoreOS does not contain /etc/fstab to determine the mount points. + * Associate this filesystem with the "/usr" mount point. + *) + data.fstab <- [ mountable, "/usr" ] + +let rec check_freebsd_root mountable data + let os_type = OS_TYPE_FREEBSD and distro = DISTRO_FREEBSD in + data.os_type <- Some os_type; + data.distro <- Some distro; + + (* 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 Is.is_file "/etc/motd" ~followsymlinks:true then + ignore (parse_generic distro "/etc/motd" data); + + (* Determine the architecture. *) + data.arch <- check_architecture (); + (* We already know /etc/fstab exists because it's part of the test + * in the caller. + *) + data.fstab <- Inspect_fs_unix_fstab.check_fstab mountable os_type; + data.hostname <- check_hostname_freebsd () + +(* 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 + data.os_type <- Some os_type; + data.distro <- Some distro; + + if Is.is_file "/etc/release" ~followsymlinks:true then + ignore (parse_generic ~rex:re_netbsd distro "/etc/release" data); + + (* Determine the architecture. *) + data.arch <- check_architecture (); + (* We already know /etc/fstab exists because it's part of the test + * in the caller. + *) + data.fstab <- Inspect_fs_unix_fstab.check_fstab mountable os_type; + data.hostname <- check_hostname_freebsd () + +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 + data.os_type <- Some os_type; + data.distro <- Some distro; + + (* The first line of /etc/motd gets automatically updated at boot. *) + if Is.is_file "/etc/motd" ~followsymlinks:true then + ignore (parse_generic distro "/etc/motd" data); + + (* 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]. + *) + + (* Determine the architecture. *) + data.arch <- check_architecture (); + (* We already know /etc/fstab exists because it's part of the test + * in the caller. + *) + data.fstab <- Inspect_fs_unix_fstab.check_fstab mountable os_type; + data.hostname <- check_hostname_freebsd () + +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 + data.os_type <- Some os_type; + + if Is.is_file "/etc/debian_version" ~followsymlinks:true then ( + let distro = DISTRO_DEBIAN in + ignore (parse_generic distro "/etc/debian_version" data) + ); + (* Arch Hurd also exists, but inconveniently it doesn't have + * the normal /etc/arch-release file. XXX + *) + + (* Determine the architecture. *) + data.arch <- check_architecture (); + (* We already know /etc/fstab exists because it's part of the test + * in the caller. + *) + data.fstab <- Inspect_fs_unix_fstab.check_fstab mountable os_type; + data.hostname <- check_hostname_hurd () + +and check_hostname_hurd () = check_hostname_linux () + +let rec check_minix_root data + let os_type = OS_TYPE_MINIX in + data.os_type <- Some os_type; + + if Is.is_file "/etc/version" ~followsymlinks:true then ( + ignore (parse_generic ~rex:re_minix DISTRO_MEEGO (* XXX unset below *) + "/etc/version" data); + data.distro <- None + ); + + (* Determine the architecture. *) + data.arch <- check_architecture (); + (* TODO: enable fstab inspection once resolve_fstab_device + * implements the proper mapping from the Minix device names + * to the appliance names. + *) + data.hostname <- check_hostname_minix () + +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..af58e5dcc --- /dev/null +++ b/daemon/inspect_fs_unix.mli @@ -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. + *) + +val check_coreos_usr : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the CoreOS [/usr] filesystem mounted on sysroot. *) + +val check_coreos_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the CoreOS filesystem mounted on sysroot. *) + +val check_freebsd_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the FreeBSD filesystem mounted on sysroot. *) + +val check_hurd_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the Hurd filesystem mounted on sysroot. *) + +val check_linux_usr : Inspect_types.inspection_data -> unit +(** Inspect the Linux [/usr] filesystem mounted on sysroot. *) + +val check_linux_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the Linux filesystem mounted on sysroot. *) + +val check_minix_root : Inspect_types.inspection_data -> unit +(** Inspect the Minix filesystem mounted on sysroot. *) + +val check_netbsd_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** Inspect the NetBSD filesystem mounted on sysroot. *) + +val check_openbsd_root : Mountable.t -> Inspect_types.inspection_data -> unit +(** 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..f103eb304 --- /dev/null +++ b/daemon/inspect_fs_unix_fstab.ml @@ -0,0 +1,537 @@ +(* 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 ~name:"check_fstab_aug" + 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 = aug_matches_noerrors 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 = aug_get_noerrors aug (entry ^ "/spec") in + let mp = aug_get_noerrors aug (entry ^ "/file") in + let vfstype = aug_get_noerrors 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 = aug_matches_noerrors aug (entry ^ "/opt") in + let rec loop = function + | [] -> mountable (* no subvol, return whole device *) + | opt :: opts -> + let optname = aug_get_noerrors aug opt in + match optname with + | None -> loop opts + | Some "subvol" -> + let subvol = aug_get_noerrors 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 = aug_matches_noerrors 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 = aug_get_noerrors aug (entry ^ "/devicename") in + let uuid = aug_get_noerrors 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 + + let debug_matching what + if verbose () then + eprintf "resolve_fstab_device: %s matched %s\n%!" spec what + in + + if String.is_prefix spec "/dev/mapper" then ( + debug_matching "/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. + *) + 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 ( + debug_matching "xdev"; + 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 ( + debug_matching "cciss"; + 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 ( + debug_matching "md<N>"; + try + Mountable.of_device (StringMap.find spec md_map) + with + | Not_found -> default + ) + + else if Str.string_match re_diskbyid spec 0 then ( + debug_matching "diskbyid"; + 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 ( + debug_matching "FreeBSD GPT"; + (* 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 ( + debug_matching "FreeBSD MBR"; + (* 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 ( + debug_matching "NetBSD"; + (* 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 ( + debug_matching "OpenBSD"; + (* 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 ( + debug_matching "Hurd"; + 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 ( + debug_matching "no known device scheme"; + 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..313babd0f --- /dev/null +++ b/daemon/inspect_fs_windows.ml @@ -0,0 +1,493 @@ +(* 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 + + data.os_type <- Some OS_TYPE_WINDOWS; + data.distro <- Some DISTRO_WINDOWS; + data.windows_systemroot <- Some systemroot; + data.arch <- Some (check_windows_arch systemroot); + + (* 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 + data.windows_software_hive <- software_hive; + + 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 + data.windows_system_hive <- system_hive; + + match software_hive, system_hive with + | None, _ | Some _, None -> () + | Some software_hive, Some system_hive -> + (* Check software hive. *) + check_windows_software_registry software_hive data; + + (* Check system hive. *) + check_windows_system_registry system_hive 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. *) + (try + let v = List.assoc "ProductName" values in + data.product_name <- Some (hivex_value_as_utf8 h v) + with + Not_found -> () + ); + + (* Version is complicated. Use CurrentMajorVersionNumber and + * CurrentMinorVersionNumber if present. If they are not + * found, fall back on CurrentVersion. + *) + (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.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 + ); + + (* InstallationType (product_variant). *) + (try + let v = List.assoc "InstallationType" values in + data.product_variant <- Some (hivex_value_as_utf8 h v) + with + Not_found -> () + ); + with + | Not_found -> + if verbose () then + eprintf "check_windows_software_registry: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\n%!" + ) (* with_hive *) + +and check_windows_system_registry system_hive data + with_hive (Sysroot.sysroot () // system_hive) ( + fun h root -> + get_drive_mappings h root data; + + let current_control_set = get_current_control_set h root in + data.windows_current_control_set <- current_control_set; + + match current_control_set with + | None -> () + | Some current_control_set -> + let hostname = get_hostname h root current_control_set in + data.hostname <- hostname + ) (* 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.drive_mappings <- values + + with + | Not_found -> + if verbose () then + eprintf "check_windows_system_registry: cannot find drive mappings\n%!" + +(* 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 + (* The blob_guid is returned as a lowercase hex string. *) + let blob_guid = extract_guid_from_registry_blob blob in + + if verbose () then + eprintf "map_registry_disk_blob_gpt: searching for GUID %s\n%!" + blob_guid; + + 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 + + (* Must be lowercase hex. *) + sprintf "%08Lx-%04Lx-%04Lx-%04Lx-%012Lx" + data1 data2 data3 + (Int64.shift_right_logical 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..a7dcd76ba --- /dev/null +++ b/daemon/inspect_fs_windows.mli @@ -0,0 +1,24 @@ +(* 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 -> unit +(** 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..a774ea272 --- /dev/null +++ b/daemon/inspect_types.ml @@ -0,0 +1,313 @@ +(* 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 = { + mutable os_type : os_type option; + mutable distro : distro option; + mutable package_format : package_format option; + mutable package_management : package_management option; + mutable product_name : string option; + mutable product_variant : string option; + mutable version : version option; + mutable arch : string option; + mutable hostname : string option; + mutable fstab : fstab_entry list; + mutable windows_systemroot : string option; + mutable windows_software_hive : string option; + mutable windows_system_hive : string option; + mutable windows_current_control_set : string option; + mutable 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 " type: %s\n" (string_of_os_type v)) + data.os_type; + may (fun v -> bpf " distro: %s\n" (string_of_distro v)) + data.distro; + may (fun v -> bpf " package_format: %s\n" (string_of_package_format v)) + data.package_format; + may (fun v -> bpf " package_management: %s\n" (string_of_package_management v)) + data.package_management; + may (fun v -> bpf " product_name: %s\n" v) + data.product_name; + may (fun v -> bpf " product_variant: %s\n" v) + data.product_variant; + may (fun (major, minor) -> bpf " version: %d.%d\n" major minor) + data.version; + may (fun v -> bpf " arch: %s\n" v) + data.arch; + may (fun v -> bpf " hostname: %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 " fstab: [%s]\n" (String.concat ", " v) + ); + may (fun v -> bpf " windows_systemroot: %s\n" v) + data.windows_systemroot; + may (fun v -> bpf " windows_software_hive: %s\n" v) + data.windows_software_hive; + may (fun v -> bpf " windows_system_hive: %s\n" v) + data.windows_system_hive; + may (fun v -> bpf " windows_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 " drive_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 + + parent.os_type <- merge child.os_type parent.os_type; + parent.distro <- merge child.distro parent.distro; + parent.package_format <- merge child.package_format parent.package_format; + parent.package_management <- + merge child.package_management parent.package_management; + parent.product_name <- merge child.product_name parent.product_name; + parent.product_variant <- merge child.product_variant parent.product_variant; + parent.version <- merge child.version parent.version; + parent.arch <- merge child.arch parent.arch; + parent.hostname <- merge child.hostname parent.hostname; + parent.fstab <- child.fstab @ parent.fstab; + parent.windows_systemroot <- + merge child.windows_systemroot parent.windows_systemroot; + parent.windows_software_hive <- + merge child.windows_software_hive parent.windows_software_hive; + parent.windows_system_hive <- + merge child.windows_system_hive parent.windows_system_hive; + parent.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. *) + parent.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) } -> null_inspection_data + in + + match parent_fs with + | { role = RoleRoot parent_data } -> + merge_inspection_data (inspection_data_of_fs child_fs) parent_data + | { role = RoleUsr parent_data } -> + merge_inspection_data (inspection_data_of_fs child_fs) parent_data + | { role = (RoleSwap|RoleOther) } -> + () + +let inspect_fses = ref [] diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli new file mode 100644 index 000000000..aae266845 --- /dev/null +++ b/daemon/inspect_types.mli @@ -0,0 +1,180 @@ +(* 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 *) +} +(** A single filesystem. *) + +and root = { + root_location : location; + inspection_data : inspection_data; +} +(** A root (as in "inspect_get_roots"). *) + +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 +(** During inspection, single filesystems are assigned a role which is + one of root, /usr, swap or other. *) + +and inspection_data = { + mutable os_type : os_type option; + mutable distro : distro option; + mutable package_format : package_format option; + mutable package_management : package_management option; + mutable product_name : string option; + mutable product_variant : string option; + mutable version : version option; + mutable arch : string option; + mutable hostname : string option; + mutable fstab : fstab_entry list; + mutable windows_systemroot : string option; + mutable windows_software_hive : string option; + mutable windows_system_hive : string option; + mutable windows_current_control_set : string option; + mutable drive_mappings : drive_mapping list; +} +(** During inspection, this data is collected incrementally for each + filesystem. At the end of inspection, inspection data is merged + into the root. *) + +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 -> unit +(** [merge_inspection_data child parent] merges two sets of inspection + data into the parent. 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 -> unit +(** [merge child_fs parent_fs] merges two filesystems, + using [merge_inspection_data] to merge the inspection data of + the child into the parent. (Nothing else is merged, only + the inspection data). *) + +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..9f66bbfea --- /dev/null +++ b/daemon/inspect_utils.ml @@ -0,0 +1,193 @@ +(* 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 ?name configfiles f + let sysroot = Sysroot.sysroot () in + let name + match name with + | None -> sprintf "with_augeas: %s" (String.concat " " configfiles) + | Some name -> name in + let chroot = Chroot.create ~name 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 (aug_rm_noerrors 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 = aug_matches_noerrors 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 aug_get_noerrors 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 + +and aug_get_noerrors aug path + try Augeas.get aug path + with Augeas.Error _ -> None + +and aug_matches_noerrors aug path + try Augeas.matches aug path + with Augeas.Error _ -> [] + +and aug_rm_noerrors aug path + try Augeas.rm aug path + with Augeas.Error _ -> 0 + +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. XXX fix function and callers + *) +let is_partition partition + try + let device = Devsparts.part_to_dev partition in + ignore (Devsparts.device_index device); + 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 verbose () then + eprintf "parse_version_from_major_minor: parsing '%s'\n%!" str; + + 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 + | None, Some _ -> () + | Some major, None -> data.version <- Some (major, 0) + | Some major, Some minor -> data.version <- Some (major, minor) + ) + else ( + eprintf "parse_version_from_major_minor: cannot parse version from '%s'\n" + str + ) + +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..74e196b52 --- /dev/null +++ b/daemon/inspect_utils.mli @@ -0,0 +1,57 @@ +(* 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 : ?name:string -> 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 aug_get_noerrors : Augeas.t -> string -> string option +val aug_matches_noerrors : Augeas.t -> string -> string list +val aug_rm_noerrors : Augeas.t -> string -> int +(** When inspecting a guest, we don't particularly care if Augeas + calls fail. These functions wrap {!Augeas.get}, {!Augeas.matches} + and {!Augeas.rm} returning null content if there is an error. *) + +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 -> unit +(** 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 83b33ac8d..185828cc7 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
Pino Toscano
2017-Jul-10 12:13 UTC
Re: [Libguestfs] [PATCH v8 07/42] common/utils: Move ‘uefi.c’ to ‘lib/’.
On Wednesday, 21 June 2017 19:29:14 CEST Richard W.M. Jones wrote:> This was only used inside the library, so move it there. > ---Not a problem with this, just noting down that theoretically the C uefi.c does not need the i386, and x86_64 arrays (so making the library slightly smaller).> diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h > index ec70336e2..190b5cdd2 100644 > --- a/lib/guestfs-internal.h > +++ b/lib/guestfs-internal.h > @@ -1011,4 +1011,16 @@ extern bool guestfs_int_version_cmp_ge (const struct version *a, const struct ve > #define version_init_null(v) guestfs_int_version_from_values (v, 0, 0, 0) > #define version_is_null(v) ((v)->v_major == 0 && (v)->v_minor == 0 && (v)->v_micro == 0) > > +/* uefi.c */ > +struct uefi_firmware { > + const char *code; /* code file (NULL = end of list) */ > + const char *code_debug; /* code file with debugging msgs (may be NULL)*/ > + const char *vars; /* vars template file */ > + int flags; /* various flags, see below */While you are moving this code, can you please fix the indentation of the above lines? The first three are indented using tabs, and the last using spaces. (Any of the two is fine, as long as all the lines use the same.) Thanks, -- Pino Toscano
Pino Toscano
2017-Jul-10 12:40 UTC
Re: [Libguestfs] [PATCH v8 00/42] Refactor utilities and reimplement inspection.
On Wednesday, 21 June 2017 19:29:07 CEST Richard W.M. Jones wrote:> v7 was: > https://www.redhat.com/archives/libguestfs/2017-June/msg00169.html > https://www.redhat.com/archives/libguestfs/2017-June/msg00184.html > > I believe this addresses all comments received so far.Patches #1-13 LGTM (just with a small fixup needed in patch #7).> Also it now passes a test where I compared about 100 disk images > processed with old and new virt-inspector binaries. The output is > identical in all cases except one which is caused by a bug in blkid > (https://bugzilla.redhat.com/show_bug.cgi?id=1463615).This happens also with the current code, right? -- Pino Toscano
Richard W.M. Jones
2017-Jul-10 15:47 UTC
Re: [Libguestfs] [PATCH v8 00/42] Refactor utilities and reimplement inspection.
On Mon, Jul 10, 2017 at 02:40:27PM +0200, Pino Toscano wrote:> On Wednesday, 21 June 2017 19:29:07 CEST Richard W.M. Jones wrote: > > Also it now passes a test where I compared about 100 disk images > > processed with old and new virt-inspector binaries. The output is > > identical in all cases except one which is caused by a bug in blkid > > (https://bugzilla.redhat.com/show_bug.cgi?id=1463615). > > This happens also with the current code, right?In theory it would, yes. In practice it doesn't happen, but that's apparently because the bug is non-deterministic in a way that it doesn't fail in the current exact test case. But if we changed the test in some undetermined but innocuous way then it might appear at any time. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Reasonably Related Threads
- [PATCH v6 00/41] Refactor utilities, reimplement inspection in the daemon.
- [PATCH v5 00/32] Refactor utilities, implement some APIs in OCaml.
- [PATCH 0/5] Miscellaneous refactoring of common/utils, create common/mltools
- [PATCH v7 00/13] Refactor utilities
- [PATCH 00/12] Refactor utility functions.