Richard W.M. Jones
2016-Dec-14 13:11 UTC
[Libguestfs] [PATCH v2 0/4] sysprep: Remove various backup files.
In v2: - The backup-files operation now operates on a conservative whitelist of filesystems, so it won't touch anything in /usr. Consequently it also runs much more quickly, about 4 seconds on the barebones virt-builder fedora-25 image. - Call Gc.compact () in visit_tests. - Added documentation to fnmatch.mli.
Richard W.M. Jones
2016-Dec-14 13:11 UTC
[Libguestfs] [PATCH v2 1/4] mllib: Add a binding for visit function in cat/visit.c.
--- .gitignore | 1 + mllib/Makefile.am | 32 ++++++- mllib/visit-c.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++ mllib/visit.ml | 36 ++++++++ mllib/visit.mli | 71 +++++++++++++++ mllib/visit_tests.ml | 150 ++++++++++++++++++++++++++++++ 6 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 mllib/visit-c.c create mode 100644 mllib/visit.ml create mode 100644 mllib/visit.mli create mode 100644 mllib/visit_tests.ml diff --git a/.gitignore b/.gitignore index da59e44..76a16c5 100644 --- a/.gitignore +++ b/.gitignore @@ -332,6 +332,7 @@ 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/mllib/Makefile.am b/mllib/Makefile.am index 001b5e3..3949b1e 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -24,6 +24,7 @@ EXTRA_DIST = \ common_utils_tests.ml \ getopt_tests.ml \ JSON_tests.ml \ + visit_tests.ml \ test-getopt.sh SOURCES_MLI = \ @@ -41,7 +42,8 @@ SOURCES_MLI = \ regedit.mli \ StatVFS.mli \ stringMap.mli \ - URI.mli + URI.mli \ + visit.mli SOURCES_ML = \ guestfs_config.ml \ @@ -56,6 +58,7 @@ SOURCES_ML = \ progress.ml \ URI.ml \ mkdtemp.ml \ + visit.ml \ planner.ml \ regedit.ml \ StatVFS.ml \ @@ -65,6 +68,7 @@ SOURCES_ML = \ checksums.ml SOURCES_C = \ + ../cat/visit.c \ ../fish/decrypt.c \ ../fish/keys.c \ ../fish/progress.c \ @@ -77,7 +81,8 @@ SOURCES_C = \ mkdtemp-c.c \ progress-c.c \ statvfs-c.c \ - uri-c.c + uri-c.c \ + visit-c.c if HAVE_OCAML @@ -103,6 +108,7 @@ libmllib_a_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/src \ + -I$(top_srcdir)/cat \ -I$(top_srcdir)/fish libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ @@ -183,6 +189,10 @@ 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) @@ -202,6 +212,9 @@ 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) @@ -236,6 +249,16 @@ 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 = \ @@ -248,6 +271,11 @@ 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/mllib/visit-c.c b/mllib/visit-c.c new file mode 100644 index 0000000..00a622e --- /dev/null +++ b/mllib/visit-c.c @@ -0,0 +1,253 @@ +/* Bindings for visitor function. + * Copyright (C) 2016 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 <errno.h> +#include <assert.h> + +#include <caml/alloc.h> +#include <caml/callback.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "visit.h" + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +struct visitor_function_wrapper_args { + /* In both case we are pointing to local roots, hence why these are + * value* not value. + */ + value *exnp; /* Safe place to store any exception + raised by visitor_function. */ + value *fvp; /* visitor_function. */ +}; + +static int visitor_function_wrapper (const char *dir, const char *name, const struct guestfs_statns *stat, const struct guestfs_xattr_list *xattrs, void *opaque); +static value copy_statns (const struct guestfs_statns *statns); +static value copy_xattr (const struct guestfs_xattr *xattr); +static value copy_xattr_list (const struct guestfs_xattr_list *xattrs); + +value +guestfs_int_mllib_visit (value gv, value dirv, value fv) +{ + CAMLparam3 (gv, dirv, fv); + guestfs_h *g = (guestfs_h *) Int64_val (gv); + struct visitor_function_wrapper_args args; + /* 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)); + /* This stack address is used to point to the exception, if one is + * raised in the visitor_function. Note that the macro initializes + * this to Val_unit, which is how we know if an exception was set. + */ + CAMLlocal1 (exn); + + args.exnp = &exn; + args.fvp = &fv; + + if (visit (g, dir, visitor_function_wrapper, &args) == -1) { + if (exn != Val_unit) { + /* The failure was caused by visitor_function raising an + * exception. Re-raise it here. + */ + caml_raise (exn); + } + + /* Otherwise it's some other failure. The visit function has + * already printed the error to stderr (XXX - fix), so we raise a + * generic Failure. + */ + caml_failwith ("visit"); + } + + CAMLreturn (Val_unit); +} + +static int +visitor_function_wrapper (const char *dir, + const char *filename, + const struct guestfs_statns *stat, + const struct guestfs_xattr_list *xattrs, + void *opaque) +{ + CAMLparam0 (); + CAMLlocal5 (dirv, filenamev, statv, xattrsv, v); + struct visitor_function_wrapper_args *args = opaque; + + assert (dir != NULL); + assert (stat != NULL); + assert (xattrs != NULL); + assert (args != NULL); + + dirv = caml_copy_string (dir); + if (filename == NULL) + filenamev = Val_int (0); /* None */ + else { + filenamev = caml_alloc (1, 0); + v = caml_copy_string (filename); + Store_field (filenamev, 0, v); + } + statv = copy_statns (stat); + xattrsv = copy_xattr_list (xattrs); + + /* Call the visitor_function. */ + value argsv[4] = { dirv, filenamev, statv, xattrsv }; + v = caml_callbackN_exn (*args->fvp, 4, argsv); + if (Is_exception_result (v)) { + /* The visitor_function raised an exception. Store the exception + * in the 'exn' field on the stack of guestfs_int_mllib_visit, and + * return an error. + */ + *args->exnp = Extract_exception (v); + return -1; + } + + /* No error, return normally. */ + CAMLreturnT (int, 0); +} + +value +guestfs_int_mllib_full_path (value dirv, value namev) +{ + CAMLparam2 (dirv, namev); + CAMLlocal1 (rv); + const char *name = NULL; + char *ret; + + if (namev != Val_int (0)) + name = String_val (Field (namev, 0)); + + ret = full_path (String_val (dirv), name); + rv = caml_copy_string (ret); + free (ret); + + CAMLreturn (rv); +} + +#define is(t) \ + value \ + guestfs_int_mllib_is_##t (value iv) \ + { \ + return Val_bool (is_##t (Int64_val (iv))); \ + } +is(reg) +is(dir) +is(chr) +is(blk) +is(fifo) +is(lnk) +is(sock) + +/* The functions below are copied from ocaml/guestfs-c-actions.c. */ + +static value +copy_statns (const struct guestfs_statns *statns) +{ + CAMLparam0 (); + CAMLlocal2 (rv, v); + + rv = caml_alloc (22, 0); + v = caml_copy_int64 (statns->st_dev); + Store_field (rv, 0, v); + v = caml_copy_int64 (statns->st_ino); + Store_field (rv, 1, v); + v = caml_copy_int64 (statns->st_mode); + Store_field (rv, 2, v); + v = caml_copy_int64 (statns->st_nlink); + Store_field (rv, 3, v); + v = caml_copy_int64 (statns->st_uid); + Store_field (rv, 4, v); + v = caml_copy_int64 (statns->st_gid); + Store_field (rv, 5, v); + v = caml_copy_int64 (statns->st_rdev); + Store_field (rv, 6, v); + v = caml_copy_int64 (statns->st_size); + Store_field (rv, 7, v); + v = caml_copy_int64 (statns->st_blksize); + Store_field (rv, 8, v); + v = caml_copy_int64 (statns->st_blocks); + Store_field (rv, 9, v); + v = caml_copy_int64 (statns->st_atime_sec); + Store_field (rv, 10, v); + v = caml_copy_int64 (statns->st_atime_nsec); + Store_field (rv, 11, v); + v = caml_copy_int64 (statns->st_mtime_sec); + Store_field (rv, 12, v); + v = caml_copy_int64 (statns->st_mtime_nsec); + Store_field (rv, 13, v); + v = caml_copy_int64 (statns->st_ctime_sec); + Store_field (rv, 14, v); + v = caml_copy_int64 (statns->st_ctime_nsec); + Store_field (rv, 15, v); + v = caml_copy_int64 (statns->st_spare1); + Store_field (rv, 16, v); + v = caml_copy_int64 (statns->st_spare2); + Store_field (rv, 17, v); + v = caml_copy_int64 (statns->st_spare3); + Store_field (rv, 18, v); + v = caml_copy_int64 (statns->st_spare4); + Store_field (rv, 19, v); + v = caml_copy_int64 (statns->st_spare5); + Store_field (rv, 20, v); + v = caml_copy_int64 (statns->st_spare6); + Store_field (rv, 21, v); + CAMLreturn (rv); +} + +static value +copy_xattr (const struct guestfs_xattr *xattr) +{ + CAMLparam0 (); + CAMLlocal2 (rv, v); + + rv = caml_alloc (2, 0); + v = caml_copy_string (xattr->attrname); + Store_field (rv, 0, v); + v = caml_alloc_string (xattr->attrval_len); + memcpy (String_val (v), xattr->attrval, xattr->attrval_len); + Store_field (rv, 1, v); + CAMLreturn (rv); +} + +static value +copy_xattr_list (const struct guestfs_xattr_list *xattrs) +{ + CAMLparam0 (); + CAMLlocal2 (rv, v); + unsigned int i; + + if (xattrs->len == 0) + CAMLreturn (Atom (0)); + else { + rv = caml_alloc (xattrs->len, 0); + for (i = 0; i < xattrs->len; ++i) { + v = copy_xattr (&xattrs->val[i]); + Store_field (rv, i, v); + } + CAMLreturn (rv); + } +} diff --git a/mllib/visit.ml b/mllib/visit.ml new file mode 100644 index 0000000..c979e3e --- /dev/null +++ b/mllib/visit.ml @@ -0,0 +1,36 @@ +(* Bindings for visitor function. + * Copyright (C) 2016 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 visitor_function = string -> string option -> Guestfs.statns -> Guestfs.xattr array -> unit + +external c_visit : int64 -> string -> visitor_function -> unit + "guestfs_int_mllib_visit" + +let visit g dir f + c_visit (Guestfs.c_pointer g) dir f + +external full_path : string -> string option -> string + "guestfs_int_mllib_full_path" + +external is_reg : int64 -> bool = "guestfs_int_mllib_is_reg" "noalloc" +external is_dir : int64 -> bool = "guestfs_int_mllib_is_dir" "noalloc" +external is_chr : int64 -> bool = "guestfs_int_mllib_is_chr" "noalloc" +external is_blk : int64 -> bool = "guestfs_int_mllib_is_blk" "noalloc" +external is_fifo : int64 -> bool = "guestfs_int_mllib_is_fifo" "noalloc" +external is_lnk : int64 -> bool = "guestfs_int_mllib_is_lnk" "noalloc" +external is_sock : int64 -> bool = "guestfs_int_mllib_is_sock" "noalloc" diff --git a/mllib/visit.mli b/mllib/visit.mli new file mode 100644 index 0000000..57ff85f --- /dev/null +++ b/mllib/visit.mli @@ -0,0 +1,71 @@ +(* Bindings for visitor function. + * Copyright (C) 2016 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. + *) + +(** Bindings for the virt-ls visitor function used to recursively + visit every file and directory in a filesystem. *) + +type visitor_function = string -> string option -> Guestfs.statns -> Guestfs.xattr array -> unit +(** The visitor function is a callback called once for every directory + and every file. + + For the root directory, [visitor_function dir None statns xattrs] is + called. [statns] is the stat of the root directory and the + array [xattrs] contains extended attributes. + + For all other directories and files, + [visitor_function dir (Some name) statns xattrs] is called, where + [dir] is the parent directory path and [name] is the filename + (which might also be a directory). [statns] is the stat of [name] + and the array [xattrs] contains extended attributes. + + The visitor callback may raise an exception, which will cause + the whole visit to fail with an error (raising the same exception). *) + +val visit : Guestfs.t -> string -> visitor_function -> unit +(** [visit g dir f] calls the [visitor_function f] once for + every directory and every file. + + If the visitor function raises an exception, then the whole visit + stops and raises the same exception. + + Also other errors can happen, and those will cause a [Failure + "visit"] exception to be raised. (Because of the implementation + of the underlying function, the real error is printed + unconditionally to stderr). + + If the visit function returns normally you can assume there + was no error. *) + +val full_path : string -> string option -> string +(** This can be called with the [dir] and [name] parameters from + [visitor_function] to return the full canonical path. *) + +val is_reg : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a regular file. *) +val is_dir : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a directory. *) +val is_chr : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a character device. *) +val is_blk : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a block device. *) +val is_fifo : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a FIFO. *) +val is_lnk : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a symbolic link. *) +val is_sock : int64 -> bool +(** Returns true if [G.statns.st_mode] represents a Unix domain socket. *) diff --git a/mllib/visit_tests.ml b/mllib/visit_tests.ml new file mode 100644 index 0000000..f5d2b57 --- /dev/null +++ b/mllib/visit_tests.ml @@ -0,0 +1,150 @@ +(* Bindings for visitor function. + * Copyright (C) 2016 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 [Visit] module. *) + +open Printf + +open Visit + +module G = Guestfs + +let rec main () + let g = new G.guestfs () in + g#add_drive_scratch (Int64.mul 1024L (Int64.mul 1024L 1024L)); + g#launch (); + + g#mkfs "ext4" "/dev/sda"; + g#mount_options "user_xattr" "/dev/sda" "/"; + + (* Create some files and directories. *) + g#mkdir "/dir1"; + g#touch "/dir1/file1"; + g#touch "/dir1/file2"; + g#mkdir "/dir2"; + g#mkdir "/dir3"; + g#mkdir "/dir3/dir4"; + g#touch "/dir3/dir4/file6"; + g#setxattr "user.name" "data" 4 "/dir3/dir4/file6"; + g#mkfifo 0o444 "/dir3/dir4/pipe"; + g#touch "/dir3/file3"; + g#touch "/dir3/file4"; + g#mknod_b 0o444 1 2 "/dir3/block"; + g#mknod_c 0o444 1 2 "/dir3/char"; + + (* Recurse over them using the visitor function, and check the + * results. + *) + let visited = ref [] in + visit g#ocaml_handle "/" ( + fun dir filename stat xattrs -> + if filename <> Some "lost+found" then + visited := (dir, filename, stat, xattrs) :: !visited + ); + let visited = List.sort compare !visited in + let str = string_of_visited visited in + let expected = "\ +/: directory +/dir1: directory +/dir2: directory +/dir3: directory +/dir1/file1: file +/dir1/file2: file +/dir3/block: block device +/dir3/char: char device +/dir3/dir4: directory +/dir3/file3: file +/dir3/file4: file +/dir3/dir4/file6: file user.name=data +/dir3/dir4/pipe: fifo +" in + if str <> expected then ( + printf "'visit' read these files:\n%s\nexpected these files:\n%s\n" + str expected; + exit 1 + ); + + (* Recurse over a subdirectory. *) + let visited = ref [] in + visit g#ocaml_handle "/dir3" ( + fun dir filename stat xattrs -> + if filename <> Some "lost+found" then + visited := (dir, filename, stat, xattrs) :: !visited + ); + let visited = List.sort compare !visited in + let str = string_of_visited visited in + let expected = "\ +/dir3: directory +/dir3/block: block device +/dir3/char: char device +/dir3/dir4: directory +/dir3/file3: file +/dir3/file4: file +/dir3/dir4/file6: file user.name=data +/dir3/dir4/pipe: fifo +" in + if str <> expected then ( + printf "'visit' read these files:\n%s\nexpected these files:\n%s\n" + str expected; + exit 1 + ); + + (* Raise an exception in the visitor_function. *) + printf "testing exception in visitor function\n%!"; + (try visit g#ocaml_handle "/" (fun _ _ _ _ -> invalid_arg "test"); + assert false + with Invalid_argument "test" -> () + (* any other exception escapes and kills the test *) + ); + + (* Force an error and check [Failure "visit"] is raised. *) + printf "testing general error in visit\n%!"; + (try visit g#ocaml_handle "/nosuchdir" (fun _ _ _ _ -> ()); + assert false + with Failure "visit" -> () + (* any other exception escapes and kills the test *) + ); + + Gc.compact () + +and string_of_visited visited + let buf = Buffer.create 1024 in + List.iter (_string_of_visited buf) visited; + Buffer.contents buf + +and _string_of_visited buf (dir, name, stat, xattrs) + let path = full_path dir name in + bprintf buf "%s: %s%s\n" path (string_of_stat stat) (string_of_xattrs xattrs) + +and string_of_stat { G.st_mode = mode } + if is_reg mode then "file" + else if is_dir mode then "directory" + else if is_chr mode then "char device" + else if is_blk mode then "block device" + else if is_fifo mode then "fifo" + else if is_lnk mode then "link" + else if is_sock mode then "socket" + else sprintf "unknown mode 0%Lo" mode + +and string_of_xattrs xattrs + String.concat "" (List.map string_of_xattr (Array.to_list xattrs)) + +and string_of_xattr { G.attrname = name; G.attrval = v } + sprintf " %s=%s" name v + +let () = main () -- 2.10.2
Richard W.M. Jones
2016-Dec-14 13:12 UTC
[Libguestfs] [PATCH v2 2/4] mllib: Add a binding for fnmatch(3) in glibc or gnulib.
This is taken from supermin. --- mllib/Makefile.am | 3 +++ mllib/fnmatch-c.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mllib/fnmatch.ml | 29 ++++++++++++++++++++++++ mllib/fnmatch.mli | 37 +++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 mllib/fnmatch-c.c create mode 100644 mllib/fnmatch.ml create mode 100644 mllib/fnmatch.mli diff --git a/mllib/Makefile.am b/mllib/Makefile.am index 3949b1e..4cf4955 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -33,6 +33,7 @@ SOURCES_MLI = \ curl.mli \ dev_t.mli \ exit.mli \ + fnmatch.mli \ fsync.mli \ getopt.mli \ JSON.mli \ @@ -59,6 +60,7 @@ SOURCES_ML = \ URI.ml \ mkdtemp.ml \ visit.ml \ + fnmatch.ml \ planner.ml \ regedit.ml \ StatVFS.ml \ @@ -76,6 +78,7 @@ SOURCES_C = \ common_utils-c.c \ dev_t-c.c \ exit-c.c \ + fnmatch-c.c \ fsync-c.c \ getopt-c.c \ mkdtemp-c.c \ diff --git a/mllib/fnmatch-c.c b/mllib/fnmatch-c.c new file mode 100644 index 0000000..dc2984e --- /dev/null +++ b/mllib/fnmatch-c.c @@ -0,0 +1,66 @@ +/* Binding for fnmatch. + * Copyright (C) 2009-2016 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 <stdlib.h> +#include <fnmatch.h> +#include <errno.h> + +#include <caml/alloc.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> +#include <caml/unixsupport.h> + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +/* NB: These flags must appear in the same order as fnmatch.ml */ +static int flags[] = { + FNM_NOESCAPE, + FNM_PATHNAME, + FNM_PERIOD, + FNM_FILE_NAME, + FNM_LEADING_DIR, + FNM_CASEFOLD, +}; + +value +guestfs_int_mllib_fnmatch (value patternv, value strv, value flagsv) +{ + CAMLparam3 (patternv, strv, flagsv); + int f = 0, r; + + /* Convert flags to bitmask. */ + while (flagsv != Val_int (0)) { + f |= flags[Int_val (Field (flagsv, 0))]; + flagsv = Field (flagsv, 1); + } + + r = fnmatch (String_val (patternv), String_val (strv), f); + + if (r == 0) + CAMLreturn (Val_true); + else if (r == FNM_NOMATCH) + CAMLreturn (Val_false); + else { + /* XXX The fnmatch specification doesn't mention what errors can + * be returned by fnmatch. Assume they are errnos for now. + */ + unix_error (errno, (char *) "fnmatch", patternv); + } +} diff --git a/mllib/fnmatch.ml b/mllib/fnmatch.ml new file mode 100644 index 0000000..7ea844f --- /dev/null +++ b/mllib/fnmatch.ml @@ -0,0 +1,29 @@ +(* Binding for fnmatch. + * Copyright (C) 2009-2016 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 + *) + +(* NB: These flags must appear in the same order as fnmatch-c.c *) +type flag +| FNM_NOESCAPE +| FNM_PATHNAME +| FNM_PERIOD +| FNM_FILE_NAME +| FNM_LEADING_DIR +| FNM_CASEFOLD + +external fnmatch : string -> string -> flag list -> bool + "guestfs_int_mllib_fnmatch" diff --git a/mllib/fnmatch.mli b/mllib/fnmatch.mli new file mode 100644 index 0000000..105e398 --- /dev/null +++ b/mllib/fnmatch.mli @@ -0,0 +1,37 @@ +(* Binding for fnmatch. + * Copyright (C) 2009-2016 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 + *) + +(** Binding for the fnmatch(3) function in glibc or gnulib. *) + +type flag +| FNM_NOESCAPE +| FNM_PATHNAME +| FNM_PERIOD +| FNM_FILE_NAME +| FNM_LEADING_DIR +| FNM_CASEFOLD +(** Flags passed to the fnmatch function. *) + +val fnmatch : string -> string -> flag list -> bool +(** The [fnmatch pattern filename flags] function checks whether + the [filename] argument matches the wildcard in the [pattern] + argument. The [flags] is a list of flags. Consult the + fnmatch(3) man page for details of the flags. + + The [filename] might be a filename element or a full path + (depending on the pattern and flags). *) -- 2.10.2
Richard W.M. Jones
2016-Dec-14 13:12 UTC
[Libguestfs] [PATCH v2 3/4] sysprep: Add a new operation to remove editor backup files (RHBZ#1401320).
Remove editor backup files such as *~ and *.bak wherever they occur within the guest filesystem. This also includes a test. --- sysprep/Makefile.am | 2 + sysprep/sysprep_operation_backup_files.ml | 98 +++++++++++++++++++++++++++++++ sysprep/test-virt-sysprep-backup-files.sh | 64 ++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 sysprep/sysprep_operation_backup_files.ml create mode 100755 sysprep/test-virt-sysprep-backup-files.sh diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index e52f0d3..c9ce3b0 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -29,6 +29,7 @@ EXTRA_DIST = \ # Filenames sysprep_operation_<name>.ml in alphabetical order. operations = \ abrt_data \ + backup_files \ bash_history \ blkid_tab \ ca_certificates \ @@ -179,6 +180,7 @@ TESTS = \ if ENABLE_APPLIANCE TESTS += \ test-virt-sysprep.sh \ + test-virt-sysprep-backup-files.sh \ test-virt-sysprep-passwords.sh if HAVE_FUSE diff --git a/sysprep/sysprep_operation_backup_files.ml b/sysprep/sysprep_operation_backup_files.ml new file mode 100644 index 0000000..4c002c8 --- /dev/null +++ b/sysprep/sysprep_operation_backup_files.ml @@ -0,0 +1,98 @@ +(* virt-sysprep + * Copyright (C) 2012-2016 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 Sysprep_operation +open Common_gettext.Gettext +open Common_utils +open Visit +open Fnmatch + +module G = Guestfs + +let unix_whitelist = List.sort compare [ + "/etc"; + "/root"; + "/srv"; + "/tmp"; + "/var"; +] +let unix_whitelist_as_pod + String.concat "\n" (List.map ((^) " ") unix_whitelist) + +let globs = List.sort compare [ + "*.bak"; + "*~"; +] +let globs_as_pod = String.concat "\n" (List.map ((^) " ") globs) + +let backup_files_perform (g : Guestfs.guestfs) root side_effects + (* Unix-like? If so that only operate on the unix_whitelist + * filesystems, else operate on everything. + *) + let fses + match g#inspect_get_type root with + | "hurd" + | "linux" + | "minix" -> unix_whitelist + | f when String.is_suffix f "bsd" -> unix_whitelist + | _ -> ["/"] in + + List.iter ( + fun fs -> + if g#is_dir ~followsymlinks:false fs then ( + visit g#ocaml_handle fs ( + fun dir filename { G.st_mode = mode } _ -> + match dir, filename, mode with + (* Ignore root directory and non-regular files. *) + | _, None, _ -> () + | _, Some _, mode when not (is_reg mode) -> () + | dir, Some filename, _ -> + (* Check the filename against all of the globs, and if it + * matches any then delete it. + *) + let matching glob = fnmatch glob filename [FNM_NOESCAPE] in + if List.exists matching globs then ( + let path = full_path dir (Some filename) in + g#rm_f path + ) + ) + ) + ) fses + +let op = { + defaults with + name = "backup-files"; + enabled_by_default = true; + heading = s_"Remove editor backup files from the guest"; + pod_description = Some ( + sprintf (f_"\ +The following files are removed from anywhere in the guest +filesystem: + +%s + +On Linux and Unix operating systems, only the following filesystems +will be examined: + +%s") globs_as_pod unix_whitelist_as_pod); + perform_on_filesystems = Some backup_files_perform; +} + +let () = register_operation op diff --git a/sysprep/test-virt-sysprep-backup-files.sh b/sysprep/test-virt-sysprep-backup-files.sh new file mode 100755 index 0000000..bd9a36c --- /dev/null +++ b/sysprep/test-virt-sysprep-backup-files.sh @@ -0,0 +1,64 @@ +#!/bin/bash - +# libguestfs virt-sysprep test script +# Copyright (C) 2016 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. + +export LANG=C +set -e + +# Test removal of editor backup files. + +if [ "$(guestfish get-backend)" = "uml" ]; then + echo "$0: skipping test because uml backend does not support qcow2" + exit 77 +fi + +if [ ! -s ../test-data/phony-guests/fedora.img ]; then + echo "$0: skipping test because there is no phony Fedora test image" + exit 77 +fi + +rm -f test-backup-files.qcow2 +rm -f test-backup-files-before +rm -f test-backup-files-after + +# Add some backup files to the Fedora image. +guestfish -- \ + disk-create test-backup-files.qcow2 qcow2 -1 \ + backingfile:../test-data/phony-guests/fedora.img \ + backingformat:raw +guestfish -a test-backup-files.qcow2 -i <<'EOF' +# /bin and /usr are not on the whitelist, so these file shouldn't be deleted. +touch /bin/test~ +touch /usr/share/test~ +find / | cat > test-backup-files-before +touch /etc/fstab.bak +touch /etc/resolv.conf~ +EOF + +# Run virt-sysprep backup-files operation only. + +virt-sysprep -x --format qcow2 -a test-backup-files.qcow2 \ + --enable backup-files + +# Check the file list is the same as above. +guestfish -a test-backup-files.qcow2 -i find / > test-backup-files-after + +diff -u test-backup-files-before test-backup-files-after + +rm test-backup-files.qcow2 +rm test-backup-files-before +rm test-backup-files-after -- 2.10.2
Richard W.M. Jones
2016-Dec-14 13:12 UTC
[Libguestfs] [PATCH v2 4/4] sysprep: Add new operation for removing /etc/passwd- and other backup files (RHBZ#1401320).
--- sysprep/Makefile.am | 1 + sysprep/sysprep_operation_passwd_backups.ml | 54 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 sysprep/sysprep_operation_passwd_backups.ml diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index c9ce3b0..4722568 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -52,6 +52,7 @@ operations = \ pacct_log \ package_manager_cache \ pam_data \ + passwd_backups \ puppet_data_log \ rh_subscription_manager \ rhn_systemid \ diff --git a/sysprep/sysprep_operation_passwd_backups.ml b/sysprep/sysprep_operation_passwd_backups.ml new file mode 100644 index 0000000..d1995a4 --- /dev/null +++ b/sysprep/sysprep_operation_passwd_backups.ml @@ -0,0 +1,54 @@ +(* virt-sysprep + * Copyright (C) 2016 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 Sysprep_operation +open Common_gettext.Gettext + +module G = Guestfs + +let files = List.sort compare [ + "/etc/group-"; + "/etc/gshadow-"; + "/etc/passwd-"; + "/etc/shadow-"; + "/etc/subuid-"; + "/etc/subgid-"; +] +let files_as_pod = String.concat "\n" (List.map ((^) " ") files) + +let passwd_backups_perform (g : Guestfs.guestfs) root side_effects + let typ = g#inspect_get_type root in + if typ = "linux" then + List.iter g#rm_f files + +let op = { + defaults with + name = "passwd-backups"; + enabled_by_default = true; + heading = s_"Remove /etc/passwd- and similar backup files"; + pod_description = Some ( + sprintf (f_"\ +On Linux the following files are removed: + +%s") files_as_pod); + perform_on_filesystems = Some passwd_backups_perform; +} + +let () = register_operation op -- 2.10.2
Pino Toscano
2016-Dec-14 16:32 UTC
Re: [Libguestfs] [PATCH v2 3/4] sysprep: Add a new operation to remove editor backup files (RHBZ#1401320).
On Wednesday, 14 December 2016 13:12:01 CET Richard W.M. Jones wrote:> Remove editor backup files such as *~ and *.bak wherever > they occur within the guest filesystem. > > This also includes a test. > ---Looks nice -- I have few notes below. Also, should this operation include also the swap files that editors can leave while editing?> + let fses > + match g#inspect_get_type root with > + | "hurd" > + | "linux" > + | "minix" -> unix_whitelist > + | f when String.is_suffix f "bsd" -> unix_whitelist > + | _ -> ["/"] inThere's a similar list also in customize/customize_run.ml for the `SSHInject operation -- I guess an helper function could be added to Common_utils.> + List.iter ( > + fun fs -> > + if g#is_dir ~followsymlinks:false fs then ( > + visit g#ocaml_handle fs ( > + fun dir filename { G.st_mode = mode } _ -> > + match dir, filename, mode with > + (* Ignore root directory and non-regular files. *) > + | _, None, _ -> () > + | _, Some _, mode when not (is_reg mode) -> () > + | dir, Some filename, _ -> > + (* Check the filename against all of the globs, and if it > + * matches any then delete it. > + *) > + let matching glob = fnmatch glob filename [FNM_NOESCAPE] in > + if List.exists matching globs then (Considering the current globs are basically filename suffixes, wouldn't it be easier (and possibly faster too) to just check for them? let suffixes = [ ".bak"; "~" ] in ... if List.exists (String.is_suffix filename) suffixes then ( ... This would be easier to extend also for filename prefixes, in case we need them, so for now the fnmatch usage can be avoided.> + let path = full_path dir (Some filename) in > + g#rm_f path > + ) > + ) > + ) > + ) fses > + > +let op = { > + defaults with > + name = "backup-files"; > + enabled_by_default = true; > + heading = s_"Remove editor backup files from the guest"; > + pod_description = Some ( > + sprintf (f_"\ > +The following files are removed from anywhere in the guest > +filesystem: > + > +%s > + > +On Linux and Unix operating systems, only the following filesystems > +will be examined: > + > +%s") globs_as_pod unix_whitelist_as_pod); > + perform_on_filesystems = Some backup_files_perform; > +} > + > +let () = register_operation op > diff --git a/sysprep/test-virt-sysprep-backup-files.sh b/sysprep/test-virt-sysprep-backup-files.sh > new file mode 100755 > index 0000000..bd9a36c > --- /dev/null > +++ b/sysprep/test-virt-sysprep-backup-files.sh > @@ -0,0 +1,64 @@ > [...] > +guestfish -a test-backup-files.qcow2 -i <<'EOF'Can you please specify --format qcow2 explicitly?> +# Check the file list is the same as above. > +guestfish -a test-backup-files.qcow2 -i find / > test-backup-files-afterDitto. Thanks, -- Pino Toscano
Pino Toscano
2016-Dec-14 16:59 UTC
Re: [Libguestfs] [PATCH v2 4/4] sysprep: Add new operation for removing /etc/passwd- and other backup files (RHBZ#1401320).
On Wednesday, 14 December 2016 13:12:02 CET Richard W.M. Jones wrote:> ---LGTM -- just one note below.> +let files_as_pod = String.concat "\n" (List.map ((^) " ") files)Other in this and the previously added operation (patch #3), I see this function also in the logfiles operation -- can you please move it shared in Sysprep_operation? Thanks, -- Pino Toscano
Maybe Matching Threads
- [PATCH] sysprep: remove the process accounting log files
- [PATCH 0/4] sysprep: Remove various backup files.
- [PATCH v2 0/4] sysprep: Remove various backup files.
- [PATCH v3 0/5] sysprep: Remove various backup files.
- [PATCH v2 3/4] sysprep: Add a new operation to remove editor backup files (RHBZ#1401320).