Mercurial > gnulib
view lib/af_alg.c @ 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 | |
children | dc3b666f1382 |
line wrap: on
line source
/* 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