[Libguestfs] [PATCH v6 16/41] daemon: Reimplement ‘file’ API in OCaml.

Richard W.M. Jones
Thu, 15 Jun 2017 17:06:06 -0000

‘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 3fb70fe52..f354a0d6f 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -259,6 +259,7 @@ guestfsd_CFLAGS = \
 SOURCES_MLI = \
 	chroot.mli \
 	sysroot.mli \
+	file.mli \
 	utils.mli

 SOURCES_ML = \
@@ -266,6 +267,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