Mercurial > gnulib
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"