]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
added adler32 functions
[libucw.git] / lib / lizard-safe.c
1 /*
2  *      LiZaRd -- Fast compression method based on Lempel-Ziv 77
3  *
4  *      (c) 2004, Robert Spalek <robert@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "lib/lizard.h"
12
13 #include <stdlib.h>
14 #include <sys/mman.h>
15 #include <sys/user.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <setjmp.h>
19 #include <errno.h>
20
21 struct lizard_buffer {
22   uns len;
23   void *ptr;
24   struct sigaction old_sigsegv_handler;
25 };
26
27 struct lizard_buffer *
28 lizard_alloc(void)
29 {
30   struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
31   buf->len = 0;
32   buf->ptr = NULL;
33   handle_signal(SIGSEGV, &buf->old_sigsegv_handler);
34   return buf;
35 }
36
37 void
38 lizard_free(struct lizard_buffer *buf)
39 {
40   if (buf->ptr)
41     munmap(buf->ptr, buf->len + PAGE_SIZE);
42   unhandle_signal(SIGSEGV, &buf->old_sigsegv_handler);
43   xfree(buf);
44 }
45
46 static void
47 lizard_realloc(struct lizard_buffer *buf, uns max_len)
48   /* max_len needs to be aligned to PAGE_SIZE */
49 {
50   if (max_len <= buf->len)
51     return;
52   if (max_len < 2*buf->len)                             // to ensure logarithmic cost
53     max_len = 2*buf->len;
54
55   if (buf->ptr)
56     munmap(buf->ptr, buf->len + PAGE_SIZE);
57   buf->len = max_len;
58   buf->ptr = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
59   if (buf->ptr == MAP_FAILED)
60     die("mmap(anonymous): %m");
61   if (mprotect(buf->ptr + buf->len, PAGE_SIZE, PROT_NONE) < 0)
62     die("mprotect: %m");
63 }
64
65 static jmp_buf safe_decompress_jump;
66 static int
67 sigsegv_handler(int signal UNUSED)
68 {
69   log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
70   longjmp(safe_decompress_jump, 1);
71   return 1;
72 }
73
74 byte *
75 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
76   /* Decompresses in into buf, sets *ptr to the data, and returns the
77    * uncompressed length.  If an error has occured, -1 is returned and errno is
78    * set.  The buffer buf is automatically reallocated.  SIGSEGV is caught in
79    * case of buffer-overflow.  The function is not re-entrant because of a
80    * static longjmp handler.  */
81 {
82   uns lock_offset = ALIGN(expected_length + 3, PAGE_SIZE);      // +3 due to the unaligned access
83   if (lock_offset > buf->len)
84     lizard_realloc(buf, lock_offset);
85   volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
86   signal_handler[SIGSEGV] = sigsegv_handler;
87   byte *ptr;
88   if (!setjmp(safe_decompress_jump))
89   {
90     ptr = buf->ptr + buf->len - lock_offset;
91     int len = lizard_decompress(in, ptr);
92     if (len != (int) expected_length)
93     {
94       ptr = NULL;
95       errno = EINVAL;
96     }
97   }
98   else
99   {
100     ptr = NULL;
101     errno = EFAULT;
102   }
103   signal_handler[SIGSEGV] = old_handler;
104   return ptr;
105 }
106
107 #define BASE 65521 /* largest prime smaller than 65536 */
108
109 inline uns
110 update_adler32(uns adler, byte *ptr, uns len)
111   /* taken from RFC1950 */
112 {
113   uns s1 = adler & 0xffff;
114   uns s2 = (adler >> 16) & 0xffff;
115   for (uns n = 0; n < len; n++) {
116     s1 = (s1 + ptr[n]) % BASE;
117     s2 = (s2 + s1)     % BASE;
118   }
119   return (s2 << 16) + s1;
120 }
121
122 uns
123 adler32(byte *ptr, uns len)
124 {
125   return update_adler32(1, ptr, len);
126 }