1/*
2 * Copyright © 2009  Red Hat, Inc.
3 *
4 *  This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Red Hat Author(s): Behdad Esfahbod
25 */
26
27/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
28#if defined(_POSIX_C_SOURCE)
29#undef _POSIX_C_SOURCE
30#endif
31#define _POSIX_C_SOURCE 199309L
32
33#include "hb-private.hh"
34
35#include "hb-blob.h"
36#include "hb-object-private.hh"
37
38#ifdef HAVE_SYS_MMAN_H
39#ifdef HAVE_UNISTD_H
40#include <unistd.h>
41#endif /* HAVE_UNISTD_H */
42#include <sys/mman.h>
43#endif /* HAVE_SYS_MMAN_H */
44
45#include <stdio.h>
46#include <errno.h>
47
48
49
50#ifndef HB_DEBUG_BLOB
51#define HB_DEBUG_BLOB (HB_DEBUG+0)
52#endif
53
54
55struct hb_blob_t {
56  hb_object_header_t header;
57  ASSERT_POD ();
58
59  bool immutable;
60
61  const char *data;
62  unsigned int length;
63  hb_memory_mode_t mode;
64
65  void *user_data;
66  hb_destroy_func_t destroy;
67};
68
69
70static bool _try_writable (hb_blob_t *blob);
71
72static void
73_hb_blob_destroy_user_data (hb_blob_t *blob)
74{
75  if (blob->destroy) {
76    blob->destroy (blob->user_data);
77    blob->user_data = NULL;
78    blob->destroy = NULL;
79  }
80}
81
82hb_blob_t *
83hb_blob_create (const char        *data,
84		unsigned int       length,
85		hb_memory_mode_t   mode,
86		void              *user_data,
87		hb_destroy_func_t  destroy)
88{
89  hb_blob_t *blob;
90
91  if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
92    if (destroy)
93      destroy (user_data);
94    return hb_blob_get_empty ();
95  }
96
97  blob->data = data;
98  blob->length = length;
99  blob->mode = mode;
100
101  blob->user_data = user_data;
102  blob->destroy = destroy;
103
104  if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
105    blob->mode = HB_MEMORY_MODE_READONLY;
106    if (!_try_writable (blob)) {
107      hb_blob_destroy (blob);
108      return hb_blob_get_empty ();
109    }
110  }
111
112  return blob;
113}
114
115hb_blob_t *
116hb_blob_create_sub_blob (hb_blob_t    *parent,
117			 unsigned int  offset,
118			 unsigned int  length)
119{
120  hb_blob_t *blob;
121
122  if (!length || offset >= parent->length)
123    return hb_blob_get_empty ();
124
125  hb_blob_make_immutable (parent);
126
127  blob = hb_blob_create (parent->data + offset,
128			 MIN (length, parent->length - offset),
129			 HB_MEMORY_MODE_READONLY,
130			 hb_blob_reference (parent),
131			 (hb_destroy_func_t) hb_blob_destroy);
132
133  return blob;
134}
135
136hb_blob_t *
137hb_blob_get_empty (void)
138{
139  static const hb_blob_t _hb_blob_nil = {
140    HB_OBJECT_HEADER_STATIC,
141
142    true, /* immutable */
143
144    NULL, /* data */
145    0, /* length */
146    HB_MEMORY_MODE_READONLY, /* mode */
147
148    NULL, /* user_data */
149    NULL  /* destroy */
150  };
151
152  return const_cast<hb_blob_t *> (&_hb_blob_nil);
153}
154
155hb_blob_t *
156hb_blob_reference (hb_blob_t *blob)
157{
158  return hb_object_reference (blob);
159}
160
161void
162hb_blob_destroy (hb_blob_t *blob)
163{
164  if (!hb_object_destroy (blob)) return;
165
166  _hb_blob_destroy_user_data (blob);
167
168  free (blob);
169}
170
171hb_bool_t
172hb_blob_set_user_data (hb_blob_t          *blob,
173		       hb_user_data_key_t *key,
174		       void *              data,
175		       hb_destroy_func_t   destroy,
176		       hb_bool_t           replace)
177{
178  return hb_object_set_user_data (blob, key, data, destroy, replace);
179}
180
181void *
182hb_blob_get_user_data (hb_blob_t          *blob,
183		       hb_user_data_key_t *key)
184{
185  return hb_object_get_user_data (blob, key);
186}
187
188
189void
190hb_blob_make_immutable (hb_blob_t *blob)
191{
192  if (hb_object_is_inert (blob))
193    return;
194
195  blob->immutable = true;
196}
197
198hb_bool_t
199hb_blob_is_immutable (hb_blob_t *blob)
200{
201  return blob->immutable;
202}
203
204
205unsigned int
206hb_blob_get_length (hb_blob_t *blob)
207{
208  return blob->length;
209}
210
211const char *
212hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
213{
214  if (length)
215    *length = blob->length;
216
217  return blob->data;
218}
219
220char *
221hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
222{
223  if (!_try_writable (blob)) {
224    if (length)
225      *length = 0;
226
227    return NULL;
228  }
229
230  if (length)
231    *length = blob->length;
232
233  return const_cast<char *> (blob->data);
234}
235
236
237static hb_bool_t
238_try_make_writable_inplace_unix (hb_blob_t *blob)
239{
240#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
241  uintptr_t pagesize = -1, mask, length;
242  const char *addr;
243
244#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
245  pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
246#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
247  pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
248#elif defined(HAVE_GETPAGESIZE)
249  pagesize = (uintptr_t) getpagesize ();
250#endif
251
252  if ((uintptr_t) -1L == pagesize) {
253    DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
254    return false;
255  }
256  DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
257
258  mask = ~(pagesize-1);
259  addr = (const char *) (((uintptr_t) blob->data) & mask);
260  length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
261  DEBUG_MSG_FUNC (BLOB, blob,
262		  "calling mprotect on [%p..%p] (%lu bytes)",
263		  addr, addr+length, (unsigned long) length);
264  if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
265    DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
266    return false;
267  }
268
269  blob->mode = HB_MEMORY_MODE_WRITABLE;
270
271  DEBUG_MSG_FUNC (BLOB, blob,
272		  "successfully made [%p..%p] (%lu bytes) writable\n",
273		  addr, addr+length, (unsigned long) length);
274  return true;
275#else
276  return false;
277#endif
278}
279
280static bool
281_try_writable_inplace (hb_blob_t *blob)
282{
283  DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
284
285  if (_try_make_writable_inplace_unix (blob))
286    return true;
287
288  DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
289
290  /* Failed to make writable inplace, mark that */
291  blob->mode = HB_MEMORY_MODE_READONLY;
292  return false;
293}
294
295static bool
296_try_writable (hb_blob_t *blob)
297{
298  if (blob->immutable)
299    return false;
300
301  if (blob->mode == HB_MEMORY_MODE_WRITABLE)
302    return true;
303
304  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
305    return true;
306
307  if (blob->mode == HB_MEMORY_MODE_WRITABLE)
308    return true;
309
310
311  DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
312
313  char *new_data;
314
315  new_data = (char *) malloc (blob->length);
316  if (unlikely (!new_data))
317    return false;
318
319  DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
320
321  memcpy (new_data, blob->data, blob->length);
322  _hb_blob_destroy_user_data (blob);
323  blob->mode = HB_MEMORY_MODE_WRITABLE;
324  blob->data = new_data;
325  blob->user_data = new_data;
326  blob->destroy = free;
327
328  return true;
329}
330
331
332