changeset 39313:6b0fda728c9b

sha1sum: use AF_ALG when available Linux supports accessing kernel crypto API via AF_ALG since version 2.6.38. Coreutils uses libcrypto when available and fallbacks to generic C implementation of various hashing functions. Add a generic afalg_stream() function which uses AF_ALG to calculate the hash of a stream and use sendfile() when possible (regular file with size less or equal than 0x7ffff000 (2,147,479,552) bytes, AKA MAX_RW_COUNT). Use afalg_stream() only in sha1sum for now, but other hashes are possible. The speed gain really depends on the CPU type, on systems which doesn't use libcrypto ranges from ~10% to 320%. This is a test on a Intel(R) Xeon(R) CPU E3-1265L V2 and Debian stretch: $ truncate -s 2GB 2g.bin $ time sha1sum 2g.bin 752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin real 0m4.829s user 0m4.437s sys 0m0.391s $ time ./sha1sum-afalg 2g.bin 752ef2367f479e79e4f0cded9c270c2890506ab0 2g.bin real 0m3.164s user 0m0.000s sys 0m3.162s Signed-off-by: Matteo Croce <mcroce@redhat.com>
author Matteo Croce <mcroce@redhat.com>
date Sat, 28 Apr 2018 15:32:55 +0200
parents 82f26115dbb8
children 63b59e9dcbd7
files ChangeLog lib/af_alg.c lib/af_alg.h lib/sha1.c m4/linux-if-alg.m4 modules/crypto/sha1
diffstat 6 files changed, 226 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat May 05 12:23:02 2018 +0200
+++ b/ChangeLog	Sat Apr 28 15:32:55 2018 +0200
@@ -1,3 +1,15 @@
+2018-04-28  Matteo Croce  <mcroce@redhat.com>
+
+	sha1sum: Use AF_ALG when available.
+	* lib/af_alg.h: New file.
+	* lib/af_alg.c: New file.
+	* lib/sha1.c: Include af_alg.h.
+	(sha1_stream): Use afalg_stream when available.
+	* m4/linux-if-alg.m4: New file.
+	* modules/crypto/sha1 (Files): Add the new files.
+	(configure.ac): Invoke gl_LINUX_IF_ALG_H.
+	(Makefile.am): Add af_alg.c.
+
 2018-05-05  Bruno Haible  <bruno@clisp.org>
 
 	all: Replace more http URLs by https URLs.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/af_alg.c	Sat Apr 28 15:32:55 2018 +0200
@@ -0,0 +1,115 @@
+/* af_alg.c - Functions to compute message digest from file streams using
+   Linux kernel crypto API.
+   Copyright (C) 2018 Free Software Foundation, 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, 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Matteo Croce <mcroce@redhat.com>, 2018.  */
+
+#include <config.h>
+
+#ifdef HAVE_LINUX_IF_ALG_H
+
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_alg.h>
+#include <sys/stat.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+
+#include "af_alg.h"
+
+/* from linux/include/linux/fs.h: (INT_MAX & PAGE_MASK).  */
+#define MAX_RW_COUNT 0x7FFFF000
+#define BLOCKSIZE 32768
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+  struct sockaddr_alg salg = {
+    .salg_family = AF_ALG,
+    .salg_type = "hash",
+  };
+  int ret, cfd, ofd;
+  struct stat st;
+
+  cfd = socket (AF_ALG, SOCK_SEQPACKET, 0);
+  if (cfd < 0)
+    return -EAFNOSUPPORT;
+
+  /* avoid calling both strcpy and strlen.  */
+  for (int i = 0; (salg.salg_name[i] = alg[i]); i++)
+    if (i == sizeof salg.salg_name - 1)
+      return -EINVAL;
+
+  ret = bind (cfd, (struct sockaddr *) &salg, sizeof salg);
+  if (ret < 0)
+    {
+      ret = -EAFNOSUPPORT;
+      goto out_cfd;
+    }
+
+  ofd = accept (cfd, NULL, 0);
+  if (ofd < 0)
+    {
+      ret = -EAFNOSUPPORT;
+      goto out_cfd;
+    }
+
+  /* if file is a regular file, attempt sendfile to pipe the data.  */
+  if (!fstat (fileno (stream), &st)
+      && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
+      && st.st_size && st.st_size <= MAX_RW_COUNT)
+    {
+      if (sendfile (ofd, fileno (stream), NULL, st.st_size) != st.st_size)
+        {
+          ret = -EIO;
+          goto out_ofd;
+        }
+      else
+        ret = 0;
+    }
+  else
+    {
+      /* sendfile not possible, do a classic read-write loop.  */
+      ssize_t size;
+      char buf[BLOCKSIZE];
+      while ((size = fread (buf, 1, sizeof buf, stream)))
+        {
+          if (send (ofd, buf, size, MSG_MORE) != size)
+            {
+              ret = -EIO;
+              goto out_ofd;
+            }
+        }
+      if (ferror (stream))
+        {
+          ret = -EIO;
+          goto out_ofd;
+        }
+    }
+
+  if (read (ofd, resblock, hashlen) != hashlen)
+    ret = -EIO;
+  else
+    ret = 0;
+
+out_ofd:
+  close (ofd);
+out_cfd:
+  close (cfd);
+  return ret;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/af_alg.h	Sat Apr 28 15:32:55 2018 +0200
@@ -0,0 +1,49 @@
+/* af_alg.h - Declaration of functions to compute message digest from
+   file streams using Linux kernel crypto API.
+   Copyright (C) 2018 Free Software Foundation, 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, 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Matteo Croce <mcroce@redhat.com>, 2018. */
+
+#ifndef AF_ALG_H
+# define AF_ALG_H 1
+
+# include <stdio.h>
+# include <errno.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+# ifdef HAVE_LINUX_IF_ALG_H
+
+int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen);
+
+# else
+
+static int
+afalg_stream (FILE * stream, void *resblock, const char *alg, ssize_t hashlen)
+{
+  return -EAFNOSUPPORT;
+}
+
+# endif
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
--- a/lib/sha1.c	Sat May 05 12:23:02 2018 +0200
+++ b/lib/sha1.c	Sat Apr 28 15:32:55 2018 +0200
@@ -33,6 +33,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef HAVE_LINUX_IF_ALG_H
+# include "af_alg.h"
+#endif
+
 #if USE_UNLOCKED_IO
 # include "unlocked-io.h"
 #endif
@@ -130,8 +134,19 @@
 {
   struct sha1_ctx ctx;
   size_t sum;
+  char *buffer;
+#ifdef HAVE_LINUX_IF_ALG_H
+  int ret;
 
-  char *buffer = malloc (BLOCKSIZE + 72);
+  ret = afalg_stream(stream, resblock, "sha1", SHA1_DIGEST_SIZE);
+  if (!ret)
+      return 0;
+
+  if (ret == -EIO)
+      return 1;
+#endif
+
+  buffer = malloc (BLOCKSIZE + 72);
   if (!buffer)
     return 1;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m4/linux-if-alg.m4	Sat Apr 28 15:32:55 2018 +0200
@@ -0,0 +1,29 @@
+dnl Check whether linux/if_alg.h has needed features.
+
+dnl Copyright 2018 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Matteo Croce.
+
+AC_DEFUN_ONCE([gl_LINUX_IF_ALG_H],
+[
+  AC_REQUIRE([gl_HEADER_SYS_SOCKET])
+  AC_CACHE_CHECK([whether linux/if_alg.h has struct sockaddr_alg.],
+    [gl_cv_header_linux_if_alg_salg],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM([[#include <sys/socket.h>
+                          #include <linux/if_alg.h>
+                          struct sockaddr_alg salg = {
+                            .salg_family = AF_ALG,
+                            .salg_type = "hash",
+                            .salg_name = "sha1",
+                          };]])],
+       [gl_cv_header_linux_if_alg_salg=yes],
+       [gl_cv_header_linux_if_alg_salg=no])])
+  if test "$gl_cv_header_linux_if_alg_salg" = yes; then
+    AC_DEFINE([HAVE_LINUX_IF_ALG_H], [1],
+      [Define to 1 if you have `struct sockaddr_alg' defined.])
+  fi
+])
--- a/modules/crypto/sha1	Sat May 05 12:23:02 2018 +0200
+++ b/modules/crypto/sha1	Sat Apr 28 15:32:55 2018 +0200
@@ -5,8 +5,11 @@
 lib/gl_openssl.h
 lib/sha1.h
 lib/sha1.c
+lib/af_alg.h
+lib/af_alg.c
 m4/gl-openssl.m4
 m4/sha1.m4
+m4/linux-if-alg.m4
 
 Depends-on:
 extern-inline
@@ -15,9 +18,10 @@
 
 configure.ac:
 gl_SHA1
+gl_LINUX_IF_ALG_H
 
 Makefile.am:
-lib_SOURCES += sha1.c
+lib_SOURCES += sha1.c af_alg.c
 
 Include:
 "sha1.h"