[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v3 1/4] sha1sum: use AF_ALG when available
From: |
Matteo Croce |
Subject: |
[PATCH v3 1/4] sha1sum: use AF_ALG when available |
Date: |
Sat, 28 Apr 2018 15:32:55 +0200 |
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 <address@hidden>
---
lib/af_alg.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/af_alg.h | 49 ++++++++++++++++++++++
lib/sha1.c | 17 +++++++-
m4/linux-if-alg.m4 | 29 +++++++++++++
modules/crypto/sha1 | 6 ++-
5 files changed, 214 insertions(+), 2 deletions(-)
create mode 100644 lib/af_alg.c
create mode 100644 lib/af_alg.h
create mode 100644 m4/linux-if-alg.m4
diff --git a/lib/af_alg.c b/lib/af_alg.c
new file mode 100644
index 000000000..3af099e43
--- /dev/null
+++ b/lib/af_alg.c
@@ -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 <address@hidden>, 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
diff --git a/lib/af_alg.h b/lib/af_alg.h
new file mode 100644
index 000000000..978e21d36
--- /dev/null
+++ b/lib/af_alg.h
@@ -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 <address@hidden>, 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
diff --git a/lib/sha1.c b/lib/sha1.c
index 37d46b68e..a84bf211a 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -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 @@ sha1_stream (FILE *stream, void *resblock)
{
struct sha1_ctx ctx;
size_t sum;
+ char *buffer;
+#ifdef HAVE_LINUX_IF_ALG_H
+ int ret;
+
+ ret = afalg_stream(stream, resblock, "sha1", SHA1_DIGEST_SIZE);
+ if (!ret)
+ return 0;
+
+ if (ret == -EIO)
+ return 1;
+#endif
- char *buffer = malloc (BLOCKSIZE + 72);
+ buffer = malloc (BLOCKSIZE + 72);
if (!buffer)
return 1;
diff --git a/m4/linux-if-alg.m4 b/m4/linux-if-alg.m4
new file mode 100644
index 000000000..3ed18ae33
--- /dev/null
+++ b/m4/linux-if-alg.m4
@@ -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
+])
diff --git a/modules/crypto/sha1 b/modules/crypto/sha1
index d65f99418..dbd41d21b 100644
--- a/modules/crypto/sha1
+++ b/modules/crypto/sha1
@@ -5,8 +5,11 @@ Files:
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 @@ stdint
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"
--
2.14.3