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