1 /* 2 * Copyright © 2009 Red Hat, Inc. 3 * Copyright © 2018 Ebrahim Byagowi 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 */ 27 28 29 /* https://github.com/harfbuzz/harfbuzz/issues/1308 30 * http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html 31 * https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html 32 */ 33 #ifndef _POSIX_C_SOURCE 34 #pragma GCC diagnostic push 35 #pragma GCC diagnostic ignored "-Wunused-macros" 36 #define _POSIX_C_SOURCE 200809L 37 #pragma GCC diagnostic pop 38 #endif 39 40 #include "hb.hh" 41 #include "hb-blob.hh" 42 43 #ifdef HAVE_SYS_MMAN_H 44 #ifdef HAVE_UNISTD_H 45 #include <unistd.h> 46 #endif /* HAVE_UNISTD_H */ 47 #include <sys/mman.h> 48 #endif /* HAVE_SYS_MMAN_H */ 49 50 #include <stdio.h> 51 #include <errno.h> 52 #include <stdlib.h> 53 54 55 /** 56 * SECTION: hb-blob 57 * @title: hb-blob 58 * @short_description: Binary data containers 59 * @include: hb.h 60 * 61 * Blobs wrap a chunk of binary data to handle lifecycle management of data 62 * while it is passed between client and HarfBuzz. Blobs are primarily used 63 * to create font faces, but also to access font face tables, as well as 64 * pass around other binary data. 65 **/ 66 67 68 /** 69 * hb_blob_create: (skip) 70 * @data: Pointer to blob data. 71 * @length: Length of @data in bytes. 72 * @mode: Memory mode for @data. 73 * @user_data: Data parameter to pass to @destroy. 74 * @destroy: Callback to call when @data is not needed anymore. 75 * 76 * Creates a new "blob" object wrapping @data. The @mode parameter is used 77 * to negotiate ownership and lifecycle of @data. 78 * 79 * Return value: New blob, or the empty blob if something failed or if @length is 80 * zero. Destroy with hb_blob_destroy(). 81 * 82 * Since: 0.9.2 83 **/ 84 hb_blob_t * 85 hb_blob_create (const char *data, 86 unsigned int length, 87 hb_memory_mode_t mode, 88 void *user_data, 89 hb_destroy_func_t destroy) 90 { 91 hb_blob_t *blob; 92 93 if (!length || 94 length >= 1u << 31 || 95 !(blob = hb_object_create<hb_blob_t> ())) { 96 if (destroy) 97 destroy (user_data); 98 return hb_blob_get_empty (); 99 } 100 101 blob->data = data; 102 blob->length = length; 103 blob->mode = mode; 104 105 blob->user_data = user_data; 106 blob->destroy = destroy; 107 108 if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { 109 blob->mode = HB_MEMORY_MODE_READONLY; 110 if (!blob->try_make_writable ()) { 111 hb_blob_destroy (blob); 112 return hb_blob_get_empty (); 113 } 114 } 115 116 return blob; 117 } 118 119 static void 120 _hb_blob_destroy (void *data) 121 { 122 hb_blob_destroy ((hb_blob_t *) data); 123 } 124 125 /** 126 * hb_blob_create_sub_blob: 127 * @parent: Parent blob. 128 * @offset: Start offset of sub-blob within @parent, in bytes. 129 * @length: Length of sub-blob. 130 * 131 * Returns a blob that represents a range of bytes in @parent. The new 132 * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it 133 * will never modify data in the parent blob. The parent data is not 134 * expected to be modified, and will result in undefined behavior if it 135 * is. 136 * 137 * Makes @parent immutable. 138 * 139 * Return value: New blob, or the empty blob if something failed or if 140 * @length is zero or @offset is beyond the end of @parent's data. Destroy 141 * with hb_blob_destroy(). 142 * 143 * Since: 0.9.2 144 **/ 145 hb_blob_t * 146 hb_blob_create_sub_blob (hb_blob_t *parent, 147 unsigned int offset, 148 unsigned int length) 149 { 150 hb_blob_t *blob; 151 152 if (!length || !parent || offset >= parent->length) 153 return hb_blob_get_empty (); 154 155 hb_blob_make_immutable (parent); 156 157 blob = hb_blob_create (parent->data + offset, 158 MIN (length, parent->length - offset), 159 HB_MEMORY_MODE_READONLY, 160 hb_blob_reference (parent), 161 _hb_blob_destroy); 162 163 return blob; 164 } 165 166 /** 167 * hb_blob_copy_writable_or_fail: 168 * @blob: A blob. 169 * 170 * Makes a writable copy of @blob. 171 * 172 * Return value: New blob, or nullptr if allocation failed. 173 * 174 * Since: 1.8.0 175 **/ 176 hb_blob_t * 177 hb_blob_copy_writable_or_fail (hb_blob_t *blob) 178 { 179 blob = hb_blob_create (blob->data, 180 blob->length, 181 HB_MEMORY_MODE_DUPLICATE, 182 nullptr, 183 nullptr); 184 185 if (unlikely (blob == hb_blob_get_empty ())) 186 blob = nullptr; 187 188 return blob; 189 } 190 191 /** 192 * hb_blob_get_empty: 193 * 194 * Returns the singleton empty blob. 195 * 196 * See TODO:link object types for more information. 197 * 198 * Return value: (transfer full): the empty blob. 199 * 200 * Since: 0.9.2 201 **/ 202 hb_blob_t * 203 hb_blob_get_empty () 204 { 205 return const_cast<hb_blob_t *> (&Null(hb_blob_t)); 206 } 207 208 /** 209 * hb_blob_reference: (skip) 210 * @blob: a blob. 211 * 212 * Increases the reference count on @blob. 213 * 214 * See TODO:link object types for more information. 215 * 216 * Return value: @blob. 217 * 218 * Since: 0.9.2 219 **/ 220 hb_blob_t * 221 hb_blob_reference (hb_blob_t *blob) 222 { 223 return hb_object_reference (blob); 224 } 225 226 /** 227 * hb_blob_destroy: (skip) 228 * @blob: a blob. 229 * 230 * Decreases the reference count on @blob, and if it reaches zero, destroys 231 * @blob, freeing all memory, possibly calling the destroy-callback the blob 232 * was created for if it has not been called already. 233 * 234 * See TODO:link object types for more information. 235 * 236 * Since: 0.9.2 237 **/ 238 void 239 hb_blob_destroy (hb_blob_t *blob) 240 { 241 if (!hb_object_destroy (blob)) return; 242 243 blob->fini_shallow (); 244 245 free (blob); 246 } 247 248 /** 249 * hb_blob_set_user_data: (skip) 250 * @blob: a blob. 251 * @key: key for data to set. 252 * @data: data to set. 253 * @destroy: callback to call when @data is not needed anymore. 254 * @replace: whether to replace an existing data with the same key. 255 * 256 * Return value: 257 * 258 * Since: 0.9.2 259 **/ 260 hb_bool_t 261 hb_blob_set_user_data (hb_blob_t *blob, 262 hb_user_data_key_t *key, 263 void * data, 264 hb_destroy_func_t destroy, 265 hb_bool_t replace) 266 { 267 return hb_object_set_user_data (blob, key, data, destroy, replace); 268 } 269 270 /** 271 * hb_blob_get_user_data: (skip) 272 * @blob: a blob. 273 * @key: key for data to get. 274 * 275 * 276 * 277 * Return value: (transfer none): 278 * 279 * Since: 0.9.2 280 **/ 281 void * 282 hb_blob_get_user_data (hb_blob_t *blob, 283 hb_user_data_key_t *key) 284 { 285 return hb_object_get_user_data (blob, key); 286 } 287 288 289 /** 290 * hb_blob_make_immutable: 291 * @blob: a blob. 292 * 293 * 294 * 295 * Since: 0.9.2 296 **/ 297 void 298 hb_blob_make_immutable (hb_blob_t *blob) 299 { 300 if (hb_object_is_immutable (blob)) 301 return; 302 303 hb_object_make_immutable (blob); 304 } 305 306 /** 307 * hb_blob_is_immutable: 308 * @blob: a blob. 309 * 310 * 311 * 312 * Return value: TODO 313 * 314 * Since: 0.9.2 315 **/ 316 hb_bool_t 317 hb_blob_is_immutable (hb_blob_t *blob) 318 { 319 return hb_object_is_immutable (blob); 320 } 321 322 323 /** 324 * hb_blob_get_length: 325 * @blob: a blob. 326 * 327 * 328 * 329 * Return value: the length of blob data in bytes. 330 * 331 * Since: 0.9.2 332 **/ 333 unsigned int 334 hb_blob_get_length (hb_blob_t *blob) 335 { 336 return blob->length; 337 } 338 339 /** 340 * hb_blob_get_data: 341 * @blob: a blob. 342 * @length: (out): 343 * 344 * 345 * 346 * Returns: (transfer none) (array length=length): 347 * 348 * Since: 0.9.2 349 **/ 350 const char * 351 hb_blob_get_data (hb_blob_t *blob, unsigned int *length) 352 { 353 if (length) 354 *length = blob->length; 355 356 return blob->data; 357 } 358 359 /** 360 * hb_blob_get_data_writable: 361 * @blob: a blob. 362 * @length: (out): output length of the writable data. 363 * 364 * Tries to make blob data writable (possibly copying it) and 365 * return pointer to data. 366 * 367 * Fails if blob has been made immutable, or if memory allocation 368 * fails. 369 * 370 * Returns: (transfer none) (array length=length): Writable blob data, 371 * or %NULL if failed. 372 * 373 * Since: 0.9.2 374 **/ 375 char * 376 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) 377 { 378 if (!blob->try_make_writable ()) { 379 if (length) 380 *length = 0; 381 382 return nullptr; 383 } 384 385 if (length) 386 *length = blob->length; 387 388 return const_cast<char *> (blob->data); 389 } 390 391 392 bool 393 hb_blob_t::try_make_writable_inplace_unix () 394 { 395 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) 396 uintptr_t pagesize = -1, mask, length; 397 const char *addr; 398 399 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) 400 pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); 401 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) 402 pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); 403 #elif defined(HAVE_GETPAGESIZE) 404 pagesize = (uintptr_t) getpagesize (); 405 #endif 406 407 if ((uintptr_t) -1L == pagesize) { 408 DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); 409 return false; 410 } 411 DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); 412 413 mask = ~(pagesize-1); 414 addr = (const char *) (((uintptr_t) this->data) & mask); 415 length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; 416 DEBUG_MSG_FUNC (BLOB, this, 417 "calling mprotect on [%p..%p] (%lu bytes)", 418 addr, addr+length, (unsigned long) length); 419 if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { 420 DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); 421 return false; 422 } 423 424 this->mode = HB_MEMORY_MODE_WRITABLE; 425 426 DEBUG_MSG_FUNC (BLOB, this, 427 "successfully made [%p..%p] (%lu bytes) writable\n", 428 addr, addr+length, (unsigned long) length); 429 return true; 430 #else 431 return false; 432 #endif 433 } 434 435 bool 436 hb_blob_t::try_make_writable_inplace () 437 { 438 DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); 439 440 if (this->try_make_writable_inplace_unix ()) 441 return true; 442 443 DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); 444 445 /* Failed to make writable inplace, mark that */ 446 this->mode = HB_MEMORY_MODE_READONLY; 447 return false; 448 } 449 450 bool 451 hb_blob_t::try_make_writable () 452 { 453 if (hb_object_is_immutable (this)) 454 return false; 455 456 if (this->mode == HB_MEMORY_MODE_WRITABLE) 457 return true; 458 459 if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) 460 return true; 461 462 if (this->mode == HB_MEMORY_MODE_WRITABLE) 463 return true; 464 465 466 DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); 467 468 char *new_data; 469 470 new_data = (char *) malloc (this->length); 471 if (unlikely (!new_data)) 472 return false; 473 474 DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); 475 476 memcpy (new_data, this->data, this->length); 477 this->destroy_user_data (); 478 this->mode = HB_MEMORY_MODE_WRITABLE; 479 this->data = new_data; 480 this->user_data = new_data; 481 this->destroy = free; 482 483 return true; 484 } 485 486 /* 487 * Mmap 488 */ 489 490 #ifdef HAVE_MMAP 491 # include <sys/types.h> 492 # include <sys/stat.h> 493 # include <fcntl.h> 494 #endif 495 496 #ifdef _WIN32 497 # include <windows.h> 498 #else 499 # ifndef O_BINARY 500 # define O_BINARY 0 501 # endif 502 #endif 503 504 #ifndef MAP_NORESERVE 505 # define MAP_NORESERVE 0 506 #endif 507 508 struct hb_mapped_file_t 509 { 510 char *contents; 511 unsigned long length; 512 #ifdef _WIN32 513 HANDLE mapping; 514 #endif 515 }; 516 517 #if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) 518 static void 519 _hb_mapped_file_destroy (void *file_) 520 { 521 hb_mapped_file_t *file = (hb_mapped_file_t *) file_; 522 #ifdef HAVE_MMAP 523 munmap (file->contents, file->length); 524 #elif defined(_WIN32) 525 UnmapViewOfFile (file->contents); 526 CloseHandle (file->mapping); 527 #else 528 assert (0); // If we don't have mmap we shouldn't reach here 529 #endif 530 531 free (file); 532 } 533 #endif 534 535 /** 536 * hb_blob_create_from_file: 537 * @file_name: font filename. 538 * 539 * Returns: A hb_blob_t pointer with the content of the file 540 * 541 * Since: 1.7.7 542 **/ 543 hb_blob_t * 544 hb_blob_create_from_file (const char *file_name) 545 { 546 /* Adopted from glib's gmappedfile.c with Matthias Clasen and 547 Allison Lortie permission but changed a lot to suit our need. */ 548 #if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) 549 hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t)); 550 if (unlikely (!file)) return hb_blob_get_empty (); 551 552 int fd = open (file_name, O_RDONLY | O_BINARY, 0); 553 if (unlikely (fd == -1)) goto fail_without_close; 554 555 struct stat st; 556 if (unlikely (fstat (fd, &st) == -1)) goto fail; 557 558 file->length = (unsigned long) st.st_size; 559 file->contents = (char *) mmap (nullptr, file->length, PROT_READ, 560 MAP_PRIVATE | MAP_NORESERVE, fd, 0); 561 562 if (unlikely (file->contents == MAP_FAILED)) goto fail; 563 564 close (fd); 565 566 return hb_blob_create (file->contents, file->length, 567 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, 568 (hb_destroy_func_t) _hb_mapped_file_destroy); 569 570 fail: 571 close (fd); 572 fail_without_close: 573 free (file); 574 575 #elif defined(_WIN32) && !defined(HB_NO_MMAP) 576 hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t)); 577 if (unlikely (!file)) return hb_blob_get_empty (); 578 579 HANDLE fd; 580 unsigned int size = strlen (file_name) + 1; 581 wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size); 582 if (unlikely (wchar_file_name == nullptr)) goto fail_without_close; 583 mbstowcs (wchar_file_name, file_name, size); 584 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) 585 { 586 CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; 587 ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); 588 ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; 589 ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; 590 ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; 591 ceparams.lpSecurityAttributes = nullptr; 592 ceparams.hTemplateFile = nullptr; 593 fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, 594 OPEN_EXISTING, &ceparams); 595 } 596 #else 597 fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, 598 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 599 nullptr); 600 #endif 601 free (wchar_file_name); 602 603 if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; 604 605 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) 606 { 607 LARGE_INTEGER length; 608 GetFileSizeEx (fd, &length); 609 file->length = length.LowPart; 610 file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); 611 } 612 #else 613 file->length = (unsigned long) GetFileSize (fd, nullptr); 614 file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); 615 #endif 616 if (unlikely (file->mapping == nullptr)) goto fail; 617 618 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) 619 file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); 620 #else 621 file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); 622 #endif 623 if (unlikely (file->contents == nullptr)) goto fail; 624 625 CloseHandle (fd); 626 return hb_blob_create (file->contents, file->length, 627 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, 628 (hb_destroy_func_t) _hb_mapped_file_destroy); 629 630 fail: 631 CloseHandle (fd); 632 fail_without_close: 633 free (file); 634 635 #endif 636 637 /* The following tries to read a file without knowing its size beforehand 638 It's used as a fallback for systems without mmap or to read from pipes */ 639 unsigned long len = 0, allocated = BUFSIZ * 16; 640 char *data = (char *) malloc (allocated); 641 if (unlikely (data == nullptr)) return hb_blob_get_empty (); 642 643 FILE *fp = fopen (file_name, "rb"); 644 if (unlikely (fp == nullptr)) goto fread_fail_without_close; 645 646 while (!feof (fp)) 647 { 648 if (allocated - len < BUFSIZ) 649 { 650 allocated *= 2; 651 /* Don't allocate and go more than ~536MB, our mmap reader still 652 can cover files like that but lets limit our fallback reader */ 653 if (unlikely (allocated > (2 << 28))) goto fread_fail; 654 char *new_data = (char *) realloc (data, allocated); 655 if (unlikely (new_data == nullptr)) goto fread_fail; 656 data = new_data; 657 } 658 659 unsigned long addition = fread (data + len, 1, allocated - len, fp); 660 661 int err = ferror (fp); 662 #ifdef EINTR // armcc doesn't have it 663 if (unlikely (err == EINTR)) continue; 664 #endif 665 if (unlikely (err)) goto fread_fail; 666 667 len += addition; 668 } 669 670 return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data, 671 (hb_destroy_func_t) free); 672 673 fread_fail: 674 fclose (fp); 675 fread_fail_without_close: 676 free (data); 677 return hb_blob_get_empty (); 678 }