1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2002 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * $Id$
34  */
35 
36 #include "mmapstring.h"
37 
38 #include "chash.h"
39 
40 #include <glib.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <sys/mman.h>
44 #include <string.h>
45 #include <pthread.h>
46 #include <limits.h>
47 
48 #include "utils.h"
49 
50 #define MMAP_STRING_DEFAULT_CEIL (8 * 1024 * 1024)
51 
52 #define DEFAULT_TMP_PATH "/tmp"
53 
54 static char tmpdir[PATH_MAX] = DEFAULT_TMP_PATH;
55 
56 static size_t mmap_string_ceil = MMAP_STRING_DEFAULT_CEIL;
57 
58 /* MMAPString references */
59 
60 static pthread_mutex_t mmapstring_lock = PTHREAD_MUTEX_INITIALIZER;
61 static chash * mmapstring_hashtable = NULL;
62 
mmapstring_hashtable_init()63 static void mmapstring_hashtable_init()
64 {
65   mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
66 }
67 
mmap_string_set_tmpdir(char * directory)68 void mmap_string_set_tmpdir(char * directory)
69 {
70   strncpy(tmpdir, directory, PATH_MAX);
71   tmpdir[PATH_MAX - 1] = 0;
72 }
73 
74 
mmap_string_ref(MMAPString * string)75 int mmap_string_ref(MMAPString * string)
76 {
77   chash * ht;
78   int r;
79   chashdatum key;
80   chashdatum data;
81 
82   pthread_mutex_lock(&mmapstring_lock);
83   if (mmapstring_hashtable == NULL) {
84     mmapstring_hashtable_init();
85   }
86   ht = mmapstring_hashtable;
87 
88   if (ht == NULL) {
89     pthread_mutex_unlock(&mmapstring_lock);
90     return -1;
91   }
92 
93   key.data = &string->str;
94   key.len = sizeof(string->str);
95   data.data = string;
96   data.len = 0;
97 
98   r = chash_set(mmapstring_hashtable, &key, &data, NULL);
99   pthread_mutex_unlock(&mmapstring_lock);
100 
101   if (r < 0)
102     return r;
103 
104   return 0;
105 }
106 
mmap_string_unref(char * str)107 int mmap_string_unref(char * str)
108 {
109   MMAPString * string;
110   chash * ht;
111   chashdatum key;
112   chashdatum data;
113   int r;
114 
115   pthread_mutex_lock(&mmapstring_lock);
116   ht = mmapstring_hashtable;
117 
118   if (ht == NULL) {
119     pthread_mutex_unlock(&mmapstring_lock);
120     return -1;
121   }
122 
123   key.data = &str;
124   key.len = sizeof(str);
125 
126   r = chash_get(ht, &key, &data);
127   if (r < 0)
128     string = NULL;
129   else
130     string = data.data;
131 
132   if (string != NULL) {
133     chash_delete(ht, &key, NULL);
134     if (chash_count(ht) == 0) {
135       chash_free(ht);
136       mmapstring_hashtable = NULL;
137     }
138   }
139 
140   pthread_mutex_unlock(&mmapstring_lock);
141 
142   if (string != NULL) {
143     mmap_string_free(string);
144     return 0;
145   }
146   else
147     return -1;
148 }
149 
150 
151 
152 /* MMAPString */
153 
154 #define MY_MAXSIZE ((size_t) -1)
155 
156 static inline size_t
nearest_power(size_t base,size_t num)157 nearest_power (size_t base, size_t num)
158 {
159   if (num > MY_MAXSIZE / 2) {
160     return MY_MAXSIZE;
161   }
162   else {
163     size_t n = base;
164 
165     while (n < num)
166       n <<= 1;
167 
168     return n;
169   }
170 }
171 
mmap_string_set_ceil(size_t ceil)172 void mmap_string_set_ceil(size_t ceil)
173 {
174   mmap_string_ceil = ceil;
175 }
176 
177 /* Strings.
178  */
179 
mmap_string_realloc_file(MMAPString * string)180 static MMAPString * mmap_string_realloc_file(MMAPString * string)
181 {
182   char * data;
183 
184   if (string->fd == -1) {
185     char tmpfilename[PATH_MAX];
186     int fd;
187 
188     * tmpfilename = 0;
189     strcat(tmpfilename, tmpdir);
190     strcat(tmpfilename, "/libetpan-mmapstring-XXXXXX");
191 
192     fd = g_mkstemp(tmpfilename);
193     if (fd == -1)
194       return NULL;
195 
196     if (unlink(tmpfilename) == -1) {
197       close(fd);
198       return NULL;
199     }
200 
201     if (ftruncate(fd, string->allocated_len) == -1) {
202       close(fd);
203       return NULL;
204     }
205 
206     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
207 		MAP_SHARED, fd, 0);
208 
209     if (data == MAP_FAILED) {
210       close(fd);
211       return NULL;
212     }
213 
214     if (string->str != NULL)
215       memcpy(data, string->str, string->len + 1);
216 
217     string->fd = fd;
218     string->mmapped_size = string->allocated_len;
219     free(string->str);
220     string->str = data;
221   }
222   else {
223     if (munmap(string->str, string->mmapped_size) == -1)
224       return NULL;
225 
226     if (ftruncate(string->fd, string->allocated_len) == -1)
227       return NULL;
228 
229     data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
230 		MAP_SHARED, string->fd, 0);
231 
232     if (data == MAP_FAILED)
233       return NULL;
234 
235     string->mmapped_size = string->allocated_len;
236     string->str = data;
237   }
238 
239   return string;
240 }
241 
mmap_string_realloc_memory(MMAPString * string)242 static MMAPString * mmap_string_realloc_memory(MMAPString * string)
243 {
244   char * tmp;
245 
246   tmp =  realloc (string->str, string->allocated_len);
247 
248   if (tmp == NULL)
249     string = NULL;
250   else
251     string->str = tmp;
252 
253   return string;
254 }
255 
256 static MMAPString *
mmap_string_maybe_expand(MMAPString * string,size_t len)257 mmap_string_maybe_expand (MMAPString* string,
258 			  size_t len)
259 {
260   if (string->len + len >= string->allocated_len)
261     {
262       size_t old_size;
263       MMAPString * newstring;
264 
265       old_size = string->allocated_len;
266 
267       string->allocated_len = nearest_power (1, string->len + len + 1);
268 
269 #ifndef MMAP_UNAVAILABLE
270       if (string->allocated_len > mmap_string_ceil)
271 	newstring = mmap_string_realloc_file(string);
272       else {
273 #endif
274 	newstring = mmap_string_realloc_memory(string);
275 #ifndef MMAP_UNAVAILABLE
276 	if (newstring == NULL)
277 	  newstring = mmap_string_realloc_file(string);
278       }
279 #endif
280 
281       if (newstring == NULL)
282 	string->allocated_len = old_size;
283     }
284 
285   return string;
286 }
287 
288 MMAPString*
mmap_string_sized_new(size_t dfl_size)289 mmap_string_sized_new (size_t dfl_size)
290 {
291   MMAPString *string;
292 
293   string = malloc(sizeof(* string));
294   if (string == NULL)
295     return NULL;
296 
297   string->allocated_len = 0;
298   string->len   = 0;
299   string->str   = NULL;
300   string->fd    = -1;
301   string->mmapped_size = 0;
302 
303   if (mmap_string_maybe_expand (string, MAX (dfl_size, 2)) == NULL) {
304     free(string);
305     return NULL;
306   }
307 
308   if (string->str == NULL) {
309     free(string);
310     return NULL;
311   }
312 
313   string->str[0] = '\0';
314 
315   return string;
316 }
317 
318 MMAPString*
mmap_string_new(const char * init)319 mmap_string_new (const char *init)
320 {
321   MMAPString *string;
322 
323   string = mmap_string_sized_new (init ? strlen (init) + 2 : 2);
324   if (string == NULL)
325     return NULL;
326 
327   if (init)
328     mmap_string_append (string, init);
329 
330   return string;
331 }
332 
333 MMAPString*
mmap_string_new_len(const char * init,size_t len)334 mmap_string_new_len (const char *init,
335 		     size_t len)
336 {
337   MMAPString *string;
338 
339   if (len <= 0)
340     return mmap_string_new (init);
341   else
342     {
343       string = mmap_string_sized_new (len);
344 
345       if (init)
346         mmap_string_append_len (string, init, len);
347 
348       return string;
349     }
350 }
351 
352 void
mmap_string_free(MMAPString * string)353 mmap_string_free (MMAPString *string)
354 {
355   if (string == NULL)
356     return;
357 
358   if (string->fd != -1) {
359     munmap(string->str, string->mmapped_size);
360     close(string->fd);
361   }
362   else {
363     free (string->str);
364   }
365   free(string);
366 }
367 
368 MMAPString*
mmap_string_assign(MMAPString * string,const char * rval)369 mmap_string_assign (MMAPString     *string,
370 		    const char *rval)
371 {
372   mmap_string_truncate (string, 0);
373   if (mmap_string_append (string, rval) == NULL)
374     return NULL;
375 
376   return string;
377 }
378 
379 MMAPString*
mmap_string_truncate(MMAPString * string,size_t len)380 mmap_string_truncate (MMAPString *string,
381 		      size_t    len)
382 {
383   string->len = MIN (len, string->len);
384   string->str[string->len] = 0;
385 
386   return string;
387 }
388 
389 /**
390  * mmap_string_set_size:
391  * @string: a #MMAPString
392  * @len: the new length
393  *
394  * Sets the length of a #MMAPString. If the length is less than
395  * the current length, the string will be truncated. If the
396  * length is greater than the current length, the contents
397  * of the newly added area are undefined. (However, as
398  * always, string->str[string->len] will be a nul byte.)
399  *
400  * Return value: @string
401  **/
402 MMAPString*
mmap_string_set_size(MMAPString * string,size_t len)403 mmap_string_set_size (MMAPString *string,
404 		      size_t    len)
405 {
406   if (len >= string->allocated_len)
407     if (mmap_string_maybe_expand (string, len - string->len) == NULL)
408       return NULL;
409 
410   string->len = len;
411   string->str[len] = 0;
412 
413   return string;
414 }
415 
416 /*
417 static int in_mapped_zone(MMAPString * string, char * val)
418 {
419   return (val >= string->str) && (val < string->str + string->mmapped_size);
420 }
421 */
422 
423 MMAPString*
mmap_string_insert_len(MMAPString * string,size_t pos,const char * val,size_t len)424 mmap_string_insert_len (MMAPString     *string,
425 			size_t       pos,
426 			const char *val,
427 			size_t       len)
428 {
429   if (mmap_string_maybe_expand (string, len) == NULL)
430     return NULL;
431 
432   if (pos < string->len)
433     memmove (string->str + pos + len, string->str + pos, string->len - pos);
434 
435   /* insert the new string */
436   memmove (string->str + pos, val, len);
437 
438   string->len += len;
439 
440   string->str[string->len] = 0;
441 
442   return string;
443 }
444 
445 MMAPString*
mmap_string_append(MMAPString * string,const char * val)446 mmap_string_append (MMAPString     *string,
447 		    const char *val)
448 {
449   return mmap_string_insert_len (string, string->len, val, strlen(val));
450 }
451 
452 MMAPString*
mmap_string_append_len(MMAPString * string,const char * val,size_t len)453 mmap_string_append_len (MMAPString	 *string,
454 			const char *val,
455 			size_t       len)
456 {
457   return mmap_string_insert_len (string, string->len, val, len);
458 }
459 
460 MMAPString*
mmap_string_append_c(MMAPString * string,char c)461 mmap_string_append_c (MMAPString *string,
462 		      char    c)
463 {
464   return mmap_string_insert_c (string, string->len, c);
465 }
466 
467 MMAPString*
mmap_string_prepend(MMAPString * string,const char * val)468 mmap_string_prepend (MMAPString     *string,
469 		     const char *val)
470 {
471   return mmap_string_insert_len (string, 0, val, strlen(val));
472 }
473 
474 MMAPString*
mmap_string_prepend_len(MMAPString * string,const char * val,size_t len)475 mmap_string_prepend_len (MMAPString	  *string,
476 			 const char *val,
477 			 size_t       len)
478 {
479   return mmap_string_insert_len (string, 0, val, len);
480 }
481 
482 MMAPString*
mmap_string_prepend_c(MMAPString * string,char c)483 mmap_string_prepend_c (MMAPString *string,
484 		       char    c)
485 {
486   return mmap_string_insert_c (string, 0, c);
487 }
488 
489 MMAPString*
mmap_string_insert(MMAPString * string,size_t pos,const char * val)490 mmap_string_insert (MMAPString     *string,
491 		    size_t       pos,
492 		    const char *val)
493 {
494   return mmap_string_insert_len (string, pos, val, strlen(val));
495 }
496 
497 MMAPString*
mmap_string_insert_c(MMAPString * string,size_t pos,char c)498 mmap_string_insert_c (MMAPString *string,
499 		      size_t   pos,
500 		      char    c)
501 {
502   if (mmap_string_maybe_expand (string, 1) == NULL)
503     return NULL;
504 
505   /* If not just an append, move the old stuff */
506   if (pos < string->len)
507     memmove (string->str + pos + 1, string->str + pos, string->len - pos);
508 
509   string->str[pos] = c;
510 
511   string->len += 1;
512 
513   string->str[string->len] = 0;
514 
515   return string;
516 }
517 
518 MMAPString*
mmap_string_erase(MMAPString * string,size_t pos,size_t len)519 mmap_string_erase (MMAPString *string,
520 		   size_t    pos,
521 		   size_t    len)
522 {
523   if ((pos + len) < string->len)
524     memmove (string->str + pos, string->str + pos + len,
525 	     string->len - (pos + len));
526 
527   string->len -= len;
528 
529   string->str[string->len] = 0;
530 
531   return string;
532 }
533