summaryrefslogtreecommitdiff
path: root/lib/crc32_file.c
blob: e6d468b424fd32190ee41ea7e63ba0761b438d56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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;
}