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 #ifndef _POSIX_C_SOURCE 29 #define _POSIX_C_SOURCE 199309L 30 #endif 31 32 #include "hb-private.hh" 33 #include "hb-debug.hh" 34 35 #include "hb-object-private.hh" 36 37 #ifdef HAVE_SYS_MMAN_H 38 #ifdef HAVE_UNISTD_H 39 #include <unistd.h> 40 #endif /* HAVE_UNISTD_H */ 41 #include <sys/mman.h> 42 #endif /* HAVE_SYS_MMAN_H */ 43 44 #include <stdio.h> 45 #include <errno.h> 46 47 48 struct hb_blob_t { 49 hb_object_header_t header; 50 ASSERT_POD (); 51 52 bool immutable; 53 54 const char *data; 55 unsigned int length; 56 hb_memory_mode_t mode; 57 58 void *user_data; 59 hb_destroy_func_t destroy; 60 }; 61 62 63 static bool _try_writable (hb_blob_t *blob); 64 65 static void 66 _hb_blob_destroy_user_data (hb_blob_t *blob) 67 { 68 if (blob->destroy) { 69 blob->destroy (blob->user_data); 70 blob->user_data = nullptr; 71 blob->destroy = nullptr; 72 } 73 } 74 75 /** 76 * hb_blob_create: (skip) 77 * @data: Pointer to blob data. 78 * @length: Length of @data in bytes. 79 * @mode: Memory mode for @data. 80 * @user_data: Data parameter to pass to @destroy. 81 * @destroy: Callback to call when @data is not needed anymore. 82 * 83 * Creates a new "blob" object wrapping @data. The @mode parameter is used 84 * to negotiate ownership and lifecycle of @data. 85 * 86 * Return value: New blob, or the empty blob if something failed or if @length is 87 * zero. Destroy with hb_blob_destroy(). 88 * 89 * Since: 0.9.2 90 **/ 91 hb_blob_t * 92 hb_blob_create (const char *data, 93 unsigned int length, 94 hb_memory_mode_t mode, 95 void *user_data, 96 hb_destroy_func_t destroy) 97 { 98 hb_blob_t *blob; 99 100 if (!length || 101 length >= 1u << 31 || 102 !(blob = hb_object_create<hb_blob_t> ())) { 103 if (destroy) 104 destroy (user_data); 105 return hb_blob_get_empty (); 106 } 107 108 blob->data = data; 109 blob->length = length; 110 blob->mode = mode; 111 112 blob->user_data = user_data; 113 blob->destroy = destroy; 114 115 if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { 116 blob->mode = HB_MEMORY_MODE_READONLY; 117 if (!_try_writable (blob)) { 118 hb_blob_destroy (blob); 119 return hb_blob_get_empty (); 120 } 121 } 122 123 return blob; 124 } 125 126 static void 127 _hb_blob_destroy (void *data) 128 { 129 hb_blob_destroy ((hb_blob_t *) data); 130 } 131 132 /** 133 * hb_blob_create_sub_blob: 134 * @parent: Parent blob. 135 * @offset: Start offset of sub-blob within @parent, in bytes. 136 * @length: Length of sub-blob. 137 * 138 * Returns a blob that represents a range of bytes in @parent. The new 139 * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it 140 * will never modify data in the parent blob. The parent data is not 141 * expected to be modified, and will result in undefined behavior if it 142 * is. 143 * 144 * Makes @parent immutable. 145 * 146 * Return value: New blob, or the empty blob if something failed or if 147 * @length is zero or @offset is beyond the end of @parent's data. Destroy 148 * with hb_blob_destroy(). 149 * 150 * Since: 0.9.2 151 **/ 152 hb_blob_t * 153 hb_blob_create_sub_blob (hb_blob_t *parent, 154 unsigned int offset, 155 unsigned int length) 156 { 157 hb_blob_t *blob; 158 159 if (!length || offset >= parent->length) 160 return hb_blob_get_empty (); 161 162 hb_blob_make_immutable (parent); 163 164 blob = hb_blob_create (parent->data + offset, 165 MIN (length, parent->length - offset), 166 HB_MEMORY_MODE_READONLY, 167 hb_blob_reference (parent), 168 _hb_blob_destroy); 169 170 return blob; 171 } 172 173 /** 174 * hb_blob_get_empty: 175 * 176 * Returns the singleton empty blob. 177 * 178 * See TODO:link object types for more information. 179 * 180 * Return value: (transfer full): the empty blob. 181 * 182 * Since: 0.9.2 183 **/ 184 hb_blob_t * 185 hb_blob_get_empty (void) 186 { 187 static const hb_blob_t _hb_blob_nil = { 188 HB_OBJECT_HEADER_STATIC, 189 190 true, /* immutable */ 191 192 nullptr, /* data */ 193 0, /* length */ 194 HB_MEMORY_MODE_READONLY, /* mode */ 195 196 nullptr, /* user_data */ 197 nullptr /* destroy */ 198 }; 199 200 return const_cast<hb_blob_t *> (&_hb_blob_nil); 201 } 202 203 /** 204 * hb_blob_reference: (skip) 205 * @blob: a blob. 206 * 207 * Increases the reference count on @blob. 208 * 209 * See TODO:link object types for more information. 210 * 211 * Return value: @blob. 212 * 213 * Since: 0.9.2 214 **/ 215 hb_blob_t * 216 hb_blob_reference (hb_blob_t *blob) 217 { 218 return hb_object_reference (blob); 219 } 220 221 /** 222 * hb_blob_destroy: (skip) 223 * @blob: a blob. 224 * 225 * Descreases the reference count on @blob, and if it reaches zero, destroys 226 * @blob, freeing all memory, possibly calling the destroy-callback the blob 227 * was created for if it has not been called already. 228 * 229 * See TODO:link object types for more information. 230 * 231 * Since: 0.9.2 232 **/ 233 void 234 hb_blob_destroy (hb_blob_t *blob) 235 { 236 if (!hb_object_destroy (blob)) return; 237 238 _hb_blob_destroy_user_data (blob); 239 240 free (blob); 241 } 242 243 /** 244 * hb_blob_set_user_data: (skip) 245 * @blob: a blob. 246 * @key: key for data to set. 247 * @data: data to set. 248 * @destroy: callback to call when @data is not needed anymore. 249 * @replace: whether to replace an existing data with the same key. 250 * 251 * Return value: 252 * 253 * Since: 0.9.2 254 **/ 255 hb_bool_t 256 hb_blob_set_user_data (hb_blob_t *blob, 257 hb_user_data_key_t *key, 258 void * data, 259 hb_destroy_func_t destroy, 260 hb_bool_t replace) 261 { 262 return hb_object_set_user_data (blob, key, data, destroy, replace); 263 } 264 265 /** 266 * hb_blob_get_user_data: (skip) 267 * @blob: a blob. 268 * @key: key for data to get. 269 * 270 * 271 * 272 * Return value: (transfer none): 273 * 274 * Since: 0.9.2 275 **/ 276 void * 277 hb_blob_get_user_data (hb_blob_t *blob, 278 hb_user_data_key_t *key) 279 { 280 return hb_object_get_user_data (blob, key); 281 } 282 283 284 /** 285 * hb_blob_make_immutable: 286 * @blob: a blob. 287 * 288 * 289 * 290 * Since: 0.9.2 291 **/ 292 void 293 hb_blob_make_immutable (hb_blob_t *blob) 294 { 295 if (hb_object_is_inert (blob)) 296 return; 297 298 blob->immutable = true; 299 } 300 301 /** 302 * hb_blob_is_immutable: 303 * @blob: a blob. 304 * 305 * 306 * 307 * Return value: TODO 308 * 309 * Since: 0.9.2 310 **/ 311 hb_bool_t 312 hb_blob_is_immutable (hb_blob_t *blob) 313 { 314 return blob->immutable; 315 } 316 317 318 /** 319 * hb_blob_get_length: 320 * @blob: a blob. 321 * 322 * 323 * 324 * Return value: the length of blob data in bytes. 325 * 326 * Since: 0.9.2 327 **/ 328 unsigned int 329 hb_blob_get_length (hb_blob_t *blob) 330 { 331 return blob->length; 332 } 333 334 /** 335 * hb_blob_get_data: 336 * @blob: a blob. 337 * @length: (out): 338 * 339 * 340 * 341 * Returns: (transfer none) (array length=length): 342 * 343 * Since: 0.9.2 344 **/ 345 const char * 346 hb_blob_get_data (hb_blob_t *blob, unsigned int *length) 347 { 348 if (length) 349 *length = blob->length; 350 351 return blob->data; 352 } 353 354 /** 355 * hb_blob_get_data_writable: 356 * @blob: a blob. 357 * @length: (out): output length of the writable data. 358 * 359 * Tries to make blob data writable (possibly copying it) and 360 * return pointer to data. 361 * 362 * Fails if blob has been made immutable, or if memory allocation 363 * fails. 364 * 365 * Returns: (transfer none) (array length=length): Writable blob data, 366 * or %NULL if failed. 367 * 368 * Since: 0.9.2 369 **/ 370 char * 371 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) 372 { 373 if (!_try_writable (blob)) { 374 if (length) 375 *length = 0; 376 377 return nullptr; 378 } 379 380 if (length) 381 *length = blob->length; 382 383 return const_cast<char *> (blob->data); 384 } 385 386 387 static hb_bool_t 388 _try_make_writable_inplace_unix (hb_blob_t *blob) 389 { 390 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) 391 uintptr_t pagesize = -1, mask, length; 392 const char *addr; 393 394 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) 395 pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); 396 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) 397 pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); 398 #elif defined(HAVE_GETPAGESIZE) 399 pagesize = (uintptr_t) getpagesize (); 400 #endif 401 402 if ((uintptr_t) -1L == pagesize) { 403 DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); 404 return false; 405 } 406 DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); 407 408 mask = ~(pagesize-1); 409 addr = (const char *) (((uintptr_t) blob->data) & mask); 410 length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr; 411 DEBUG_MSG_FUNC (BLOB, blob, 412 "calling mprotect on [%p..%p] (%lu bytes)", 413 addr, addr+length, (unsigned long) length); 414 if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { 415 DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno)); 416 return false; 417 } 418 419 blob->mode = HB_MEMORY_MODE_WRITABLE; 420 421 DEBUG_MSG_FUNC (BLOB, blob, 422 "successfully made [%p..%p] (%lu bytes) writable\n", 423 addr, addr+length, (unsigned long) length); 424 return true; 425 #else 426 return false; 427 #endif 428 } 429 430 static bool 431 _try_writable_inplace (hb_blob_t *blob) 432 { 433 DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n"); 434 435 if (_try_make_writable_inplace_unix (blob)) 436 return true; 437 438 DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); 439 440 /* Failed to make writable inplace, mark that */ 441 blob->mode = HB_MEMORY_MODE_READONLY; 442 return false; 443 } 444 445 static bool 446 _try_writable (hb_blob_t *blob) 447 { 448 if (blob->immutable) 449 return false; 450 451 if (blob->mode == HB_MEMORY_MODE_WRITABLE) 452 return true; 453 454 if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) 455 return true; 456 457 if (blob->mode == HB_MEMORY_MODE_WRITABLE) 458 return true; 459 460 461 DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data); 462 463 char *new_data; 464 465 new_data = (char *) malloc (blob->length); 466 if (unlikely (!new_data)) 467 return false; 468 469 DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); 470 471 memcpy (new_data, blob->data, blob->length); 472 _hb_blob_destroy_user_data (blob); 473 blob->mode = HB_MEMORY_MODE_WRITABLE; 474 blob->data = new_data; 475 blob->user_data = new_data; 476 blob->destroy = free; 477 478 return true; 479 } 480