summaryrefslogtreecommitdiff
path: root/elfutils/lib/crc32_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'elfutils/lib/crc32_file.c')
-rw-r--r--elfutils/lib/crc32_file.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/elfutils/lib/crc32_file.c b/elfutils/lib/crc32_file.c
new file mode 100644
index 00000000..e6d468b4
--- /dev/null
+++ b/elfutils/lib/crc32_file.c
@@ -0,0 +1,59 @@
+#include "system.h"
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+int
+crc32_file (int fd, uint32_t *resp)
+{
+ unsigned char buffer[1024 * 8];
+ uint32_t crc = 0;
+ off_t off = 0;
+ ssize_t count;
+
+ struct stat st;
+ if (fstat (fd, &st) == 0)
+ {
+ /* Try mapping in the file data. */
+ size_t mapsize = st.st_size;
+ void *mapped = mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mapped == MAP_FAILED && errno == ENOMEM)
+ {
+ const size_t pagesize = sysconf (_SC_PAGE_SIZE);
+ mapsize = ((mapsize / 2) + pagesize - 1) & -pagesize;
+ while (mapsize >= pagesize
+ && (mapped = mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE,
+ fd, 0)) == MAP_FAILED && errno == ENOMEM)
+ mapsize /= 2;
+ }
+ if (mapped != MAP_FAILED)
+ {
+ do
+ {
+ if (st.st_size <= (off_t) mapsize)
+ {
+ *resp = crc32 (crc, mapped, st.st_size);
+ munmap (mapped, mapsize);
+ return 0;
+ }
+ crc = crc32 (crc, mapped, mapsize);
+ off += mapsize;
+ st.st_size -= mapsize;
+ } while (mmap (mapped, mapsize, PROT_READ, MAP_FIXED|MAP_PRIVATE,
+ fd, off) == mapped);
+ munmap (mapped, mapsize);
+ }
+ }
+
+ while ((count = TEMP_FAILURE_RETRY (pread (fd, buffer, sizeof buffer,
+ off))) > 0)
+ {
+ off += count;
+ crc = crc32 (crc, buffer, count);
+ }
+
+ *resp = crc;
+
+ return count == 0 ? 0 : -1;
+}