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 }