LCOV - code coverage report
Current view: top level - glib - gmappedfile.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 91.5 % 71 65
Test Date: 2025-01-07 05:22:06 Functions: 100.0 % 10 10
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GLIB - Library of useful routines for C programming
       2                 :             :  * gmappedfile.c: Simplified wrapper around the mmap() function.
       3                 :             :  *
       4                 :             :  * Copyright 2005 Matthias Clasen
       5                 :             :  *
       6                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       7                 :             :  *
       8                 :             :  * This library is free software; you can redistribute it and/or
       9                 :             :  * modify it under the terms of the GNU Lesser General Public
      10                 :             :  * License as published by the Free Software Foundation; either
      11                 :             :  * version 2.1 of the License, or (at your option) any later version.
      12                 :             :  *
      13                 :             :  * This library is distributed in the hope that it will be useful,
      14                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16                 :             :  * Lesser General Public License for more details.
      17                 :             :  *
      18                 :             :  * You should have received a copy of the GNU Lesser General Public
      19                 :             :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : 
      24                 :             : #include <errno.h>
      25                 :             : #include <sys/types.h> 
      26                 :             : #include <sys/stat.h> 
      27                 :             : #include <fcntl.h>
      28                 :             : #ifdef HAVE_MMAP
      29                 :             : #include <sys/mman.h>
      30                 :             : #endif
      31                 :             : 
      32                 :             : #include "glibconfig.h"
      33                 :             : 
      34                 :             : #ifdef G_OS_UNIX
      35                 :             : #include <unistd.h>
      36                 :             : #endif
      37                 :             : 
      38                 :             : #ifdef G_OS_WIN32
      39                 :             : #include <windows.h>
      40                 :             : #include <io.h>
      41                 :             : 
      42                 :             : #undef fstat
      43                 :             : #define fstat(a,b) _fstati64(a,b)
      44                 :             : #undef stat
      45                 :             : #define stat _stati64
      46                 :             : 
      47                 :             : #ifndef S_ISREG
      48                 :             : #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
      49                 :             : #endif
      50                 :             : 
      51                 :             : #endif
      52                 :             : 
      53                 :             : #ifndef O_CLOEXEC
      54                 :             : #define O_CLOEXEC 0
      55                 :             : #endif
      56                 :             : 
      57                 :             : #include "gconvert.h"
      58                 :             : #include "gerror.h"
      59                 :             : #include "gfileutils.h"
      60                 :             : #include "gmappedfile.h"
      61                 :             : #include "gmem.h"
      62                 :             : #include "gmessages.h"
      63                 :             : #include "gstdio.h"
      64                 :             : #include "gstrfuncs.h"
      65                 :             : #include "gatomic.h"
      66                 :             : 
      67                 :             : #include "glibintl.h"
      68                 :             : 
      69                 :             : 
      70                 :             : #ifndef _O_BINARY
      71                 :             : #define _O_BINARY 0
      72                 :             : #endif
      73                 :             : 
      74                 :             : #ifndef MAP_FAILED
      75                 :             : #define MAP_FAILED ((void *) -1)
      76                 :             : #endif
      77                 :             : 
      78                 :             : /**
      79                 :             :  * GMappedFile:
      80                 :             :  *
      81                 :             :  * The #GMappedFile represents a file mapping created with
      82                 :             :  * g_mapped_file_new(). It has only private members and should
      83                 :             :  * not be accessed directly.
      84                 :             :  */
      85                 :             : 
      86                 :             : struct _GMappedFile
      87                 :             : {
      88                 :             :   gchar *contents;
      89                 :             :   gsize  length;
      90                 :             :   gpointer free_func;
      91                 :             :   int    ref_count;
      92                 :             : #ifdef G_OS_WIN32
      93                 :             :   HANDLE mapping;
      94                 :             : #endif
      95                 :             : };
      96                 :             : 
      97                 :             : static void
      98                 :         336 : g_mapped_file_destroy (GMappedFile *file)
      99                 :             : {
     100                 :         336 :   if (file->length)
     101                 :             :     {
     102                 :             : #ifdef HAVE_MMAP
     103                 :         332 :       munmap (file->contents, file->length);
     104                 :             : #endif
     105                 :             : #ifdef G_OS_WIN32
     106                 :             :       UnmapViewOfFile (file->contents);
     107                 :             :       CloseHandle (file->mapping);
     108                 :             : #endif
     109                 :             :     }
     110                 :             : 
     111                 :         336 :   g_slice_free (GMappedFile, file);
     112                 :         336 : }
     113                 :             : 
     114                 :             : static GMappedFile*
     115                 :         341 : mapped_file_new_from_fd (int           fd,
     116                 :             :                          gboolean      writable,
     117                 :             :                          const gchar  *filename,
     118                 :             :                          GError      **error)
     119                 :             : {
     120                 :             :   GMappedFile *file;
     121                 :             :   struct stat st;
     122                 :             : 
     123                 :         341 :   file = g_slice_new0 (GMappedFile);
     124                 :         341 :   file->ref_count = 1;
     125                 :         341 :   file->free_func = g_mapped_file_destroy;
     126                 :             : 
     127                 :         341 :   if (fstat (fd, &st) == -1)
     128                 :             :     {
     129                 :           0 :       int save_errno = errno;
     130                 :           0 :       gchar *display_filename = filename ? g_filename_display_name (filename) : NULL;
     131                 :             : 
     132                 :           0 :       g_set_error (error,
     133                 :             :                    G_FILE_ERROR,
     134                 :           0 :                    g_file_error_from_errno (save_errno),
     135                 :             :                    _("Failed to get attributes of file ā€œ%s%s%s%sā€: fstat() failed: %s"),
     136                 :             :                    display_filename ? display_filename : "fd",
     137                 :             :                    display_filename ? "' " : "",
     138                 :             :                    display_filename ? display_filename : "",
     139                 :             :                    display_filename ? "'" : "",
     140                 :             :                    g_strerror (save_errno));
     141                 :           0 :       g_free (display_filename);
     142                 :           0 :       goto out;
     143                 :             :     }
     144                 :             : 
     145                 :             :   /* mmap() on size 0 will fail with EINVAL, so we avoid calling mmap()
     146                 :             :    * in that case -- but only if we have a regular file; we still want
     147                 :             :    * attempts to mmap a character device to fail, for example.
     148                 :             :    */
     149                 :         341 :   if (st.st_size == 0 && S_ISREG (st.st_mode))
     150                 :             :     {
     151                 :           4 :       file->length = 0;
     152                 :           4 :       file->contents = NULL;
     153                 :           4 :       return file;
     154                 :             :     }
     155                 :             : 
     156                 :         337 :   file->contents = MAP_FAILED;
     157                 :             : 
     158                 :             : #ifdef HAVE_MMAP
     159                 :             :   if (sizeof (st.st_size) > sizeof (gsize) && st.st_size > (off_t) G_MAXSIZE)
     160                 :             :     {
     161                 :             :       errno = EINVAL;
     162                 :             :     }
     163                 :             :   else
     164                 :             :     {      
     165                 :         337 :       file->length = (gsize) st.st_size;
     166                 :         337 :       file->contents = (gchar *) mmap (NULL,  file->length,
     167                 :             :                                        writable ? PROT_READ|PROT_WRITE : PROT_READ,
     168                 :             :                                        MAP_PRIVATE, fd, 0);
     169                 :             :     }
     170                 :             : #endif
     171                 :             : #ifdef G_OS_WIN32
     172                 :             :   file->length = st.st_size;
     173                 :             :   file->mapping = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL,
     174                 :             :                                      writable ? PAGE_WRITECOPY : PAGE_READONLY,
     175                 :             :                                      0, 0,
     176                 :             :                                      NULL);
     177                 :             :   if (file->mapping != NULL)
     178                 :             :     {
     179                 :             :       file->contents = MapViewOfFile (file->mapping,
     180                 :             :                                       writable ? FILE_MAP_COPY : FILE_MAP_READ,
     181                 :             :                                       0, 0,
     182                 :             :                                       0);
     183                 :             :       if (file->contents == NULL)
     184                 :             :         {
     185                 :             :           file->contents = MAP_FAILED;
     186                 :             :           CloseHandle (file->mapping);
     187                 :             :           file->mapping = NULL;
     188                 :             :         }
     189                 :             :     }
     190                 :             : #endif
     191                 :             : 
     192                 :             :   
     193                 :         337 :   if (file->contents == MAP_FAILED)
     194                 :             :     {
     195                 :           1 :       int save_errno = errno;
     196                 :           1 :       gchar *display_filename = filename ? g_filename_display_name (filename) : NULL;
     197                 :             : 
     198                 :           2 :       g_set_error (error,
     199                 :             :                    G_FILE_ERROR,
     200                 :           1 :                    g_file_error_from_errno (save_errno),
     201                 :             :                    _("Failed to map %s%s%s%s: mmap() failed: %s"),
     202                 :             :                    display_filename ? display_filename : "fd",
     203                 :             :                    display_filename ? "' " : "",
     204                 :             :                    display_filename ? display_filename : "",
     205                 :             :                    display_filename ? "'" : "",
     206                 :             :                    g_strerror (save_errno));
     207                 :           1 :       g_free (display_filename);
     208                 :           1 :       goto out;
     209                 :             :     }
     210                 :             : 
     211                 :         336 :   return file;
     212                 :             : 
     213                 :           1 :  out:
     214                 :           1 :   g_slice_free (GMappedFile, file);
     215                 :             : 
     216                 :           1 :   return NULL;
     217                 :             : }
     218                 :             : 
     219                 :             : /**
     220                 :             :  * g_mapped_file_new:
     221                 :             :  * @filename: (type filename): The path of the file to load, in the GLib
     222                 :             :  *     filename encoding
     223                 :             :  * @writable: whether the mapping should be writable
     224                 :             :  * @error: return location for a #GError, or %NULL
     225                 :             :  *
     226                 :             :  * Maps a file into memory. On UNIX, this is using the mmap() function.
     227                 :             :  *
     228                 :             :  * If @writable is %TRUE, the mapped buffer may be modified, otherwise
     229                 :             :  * it is an error to modify the mapped buffer. Modifications to the buffer
     230                 :             :  * are not visible to other processes mapping the same file, and are not
     231                 :             :  * written back to the file.
     232                 :             :  *
     233                 :             :  * Note that modifications of the underlying file might affect the contents
     234                 :             :  * of the #GMappedFile. Therefore, mapping should only be used if the file
     235                 :             :  * will not be modified, or if all modifications of the file are done
     236                 :             :  * atomically (e.g. using g_file_set_contents()).
     237                 :             :  *
     238                 :             :  * If @filename is the name of an empty, regular file, the function
     239                 :             :  * will successfully return an empty #GMappedFile. In other cases of
     240                 :             :  * size 0 (e.g. device files such as /dev/null), @error will be set
     241                 :             :  * to the #GFileError value %G_FILE_ERROR_INVAL.
     242                 :             :  *
     243                 :             :  * Returns: a newly allocated #GMappedFile which must be unref'd
     244                 :             :  *    with g_mapped_file_unref(), or %NULL if the mapping failed.
     245                 :             :  *
     246                 :             :  * Since: 2.8
     247                 :             :  */
     248                 :             : GMappedFile *
     249                 :         361 : g_mapped_file_new (const gchar  *filename,
     250                 :             :                    gboolean      writable,
     251                 :             :                    GError      **error)
     252                 :             : {
     253                 :             :   GMappedFile *file;
     254                 :             :   int fd;
     255                 :             : 
     256                 :         361 :   g_return_val_if_fail (filename != NULL, NULL);
     257                 :         361 :   g_return_val_if_fail (!error || *error == NULL, NULL);
     258                 :             : 
     259                 :         361 :   fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY | O_CLOEXEC, 0);
     260                 :         361 :   if (fd == -1)
     261                 :             :     {
     262                 :          22 :       int save_errno = errno;
     263                 :          22 :       gchar *display_filename = g_filename_display_name (filename);
     264                 :             : 
     265                 :          44 :       g_set_error (error,
     266                 :             :                    G_FILE_ERROR,
     267                 :          22 :                    g_file_error_from_errno (save_errno),
     268                 :             :                    _("Failed to open file ā€œ%sā€: open() failed: %s"),
     269                 :             :                    display_filename,
     270                 :             :                    g_strerror (save_errno));
     271                 :          22 :       g_free (display_filename);
     272                 :          22 :       return NULL;
     273                 :             :     }
     274                 :             : 
     275                 :         339 :   file = mapped_file_new_from_fd (fd, writable, filename, error);
     276                 :             : 
     277                 :         339 :   close (fd);
     278                 :             : 
     279                 :         339 :   return file;
     280                 :             : }
     281                 :             : 
     282                 :             : 
     283                 :             : /**
     284                 :             :  * g_mapped_file_new_from_fd:
     285                 :             :  * @fd: The file descriptor of the file to load
     286                 :             :  * @writable: whether the mapping should be writable
     287                 :             :  * @error: return location for a #GError, or %NULL
     288                 :             :  *
     289                 :             :  * Maps a file into memory. On UNIX, this is using the mmap() function.
     290                 :             :  *
     291                 :             :  * If @writable is %TRUE, the mapped buffer may be modified, otherwise
     292                 :             :  * it is an error to modify the mapped buffer. Modifications to the buffer
     293                 :             :  * are not visible to other processes mapping the same file, and are not
     294                 :             :  * written back to the file.
     295                 :             :  *
     296                 :             :  * Note that modifications of the underlying file might affect the contents
     297                 :             :  * of the #GMappedFile. Therefore, mapping should only be used if the file
     298                 :             :  * will not be modified, or if all modifications of the file are done
     299                 :             :  * atomically (e.g. using g_file_set_contents()).
     300                 :             :  *
     301                 :             :  * Returns: a newly allocated #GMappedFile which must be unref'd
     302                 :             :  *    with g_mapped_file_unref(), or %NULL if the mapping failed.
     303                 :             :  *
     304                 :             :  * Since: 2.32
     305                 :             :  */
     306                 :             : GMappedFile *
     307                 :           2 : g_mapped_file_new_from_fd (gint          fd,
     308                 :             :                            gboolean      writable,
     309                 :             :                            GError      **error)
     310                 :             : {
     311                 :           2 :   return mapped_file_new_from_fd (fd, writable, NULL, error);
     312                 :             : }
     313                 :             : 
     314                 :             : /**
     315                 :             :  * g_mapped_file_get_length:
     316                 :             :  * @file: a #GMappedFile
     317                 :             :  *
     318                 :             :  * Returns the length of the contents of a #GMappedFile.
     319                 :             :  *
     320                 :             :  * Returns: the length of the contents of @file.
     321                 :             :  *
     322                 :             :  * Since: 2.8
     323                 :             :  */
     324                 :             : gsize
     325                 :         123 : g_mapped_file_get_length (GMappedFile *file)
     326                 :             : {
     327                 :         123 :   g_return_val_if_fail (file != NULL, 0);
     328                 :             : 
     329                 :         123 :   return file->length;
     330                 :             : }
     331                 :             : 
     332                 :             : /**
     333                 :             :  * g_mapped_file_get_contents:
     334                 :             :  * @file: a #GMappedFile
     335                 :             :  *
     336                 :             :  * Returns the contents of a #GMappedFile. 
     337                 :             :  *
     338                 :             :  * Note that the contents may not be zero-terminated,
     339                 :             :  * even if the #GMappedFile is backed by a text file.
     340                 :             :  *
     341                 :             :  * If the file is empty then %NULL is returned.
     342                 :             :  *
     343                 :             :  * Returns: (transfer none) (nullable): the contents of @file, or %NULL.
     344                 :             :  *
     345                 :             :  * Since: 2.8
     346                 :             :  */
     347                 :             : gchar *
     348                 :         128 : g_mapped_file_get_contents (GMappedFile *file)
     349                 :             : {
     350                 :         128 :   g_return_val_if_fail (file != NULL, NULL);
     351                 :             : 
     352                 :         128 :   return file->contents;
     353                 :             : }
     354                 :             : 
     355                 :             : /**
     356                 :             :  * g_mapped_file_free:
     357                 :             :  * @file: a #GMappedFile
     358                 :             :  *
     359                 :             :  * This call existed before #GMappedFile had refcounting and is currently
     360                 :             :  * exactly the same as g_mapped_file_unref().
     361                 :             :  *
     362                 :             :  * Since: 2.8
     363                 :             :  * Deprecated:2.22: Use g_mapped_file_unref() instead.
     364                 :             :  */
     365                 :             : void
     366                 :           5 : g_mapped_file_free (GMappedFile *file)
     367                 :             : {
     368                 :           5 :   g_mapped_file_unref (file);
     369                 :           5 : }
     370                 :             : 
     371                 :             : /**
     372                 :             :  * g_mapped_file_ref:
     373                 :             :  * @file: a #GMappedFile
     374                 :             :  *
     375                 :             :  * Increments the reference count of @file by one.  It is safe to call
     376                 :             :  * this function from any thread.
     377                 :             :  *
     378                 :             :  * Returns: the passed in #GMappedFile.
     379                 :             :  *
     380                 :             :  * Since: 2.22
     381                 :             :  **/
     382                 :             : GMappedFile *
     383                 :         221 : g_mapped_file_ref (GMappedFile *file)
     384                 :             : {
     385                 :         221 :   g_return_val_if_fail (file != NULL, NULL);
     386                 :             : 
     387                 :         221 :   g_atomic_int_inc (&file->ref_count);
     388                 :             : 
     389                 :         221 :   return file;
     390                 :             : }
     391                 :             : 
     392                 :             : /**
     393                 :             :  * g_mapped_file_unref:
     394                 :             :  * @file: a #GMappedFile
     395                 :             :  *
     396                 :             :  * Decrements the reference count of @file by one.  If the reference count
     397                 :             :  * drops to 0, unmaps the buffer of @file and frees it.
     398                 :             :  *
     399                 :             :  * It is safe to call this function from any thread.
     400                 :             :  *
     401                 :             :  * Since 2.22
     402                 :             :  **/
     403                 :             : void
     404                 :         557 : g_mapped_file_unref (GMappedFile *file)
     405                 :             : {
     406                 :         557 :   g_return_if_fail (file != NULL);
     407                 :             : 
     408                 :         557 :   if (g_atomic_int_dec_and_test (&file->ref_count))
     409                 :         336 :     g_mapped_file_destroy (file);
     410                 :             : }
     411                 :             : 
     412                 :             : /**
     413                 :             :  * g_mapped_file_get_bytes:
     414                 :             :  * @file: a #GMappedFile
     415                 :             :  *
     416                 :             :  * Creates a new #GBytes which references the data mapped from @file.
     417                 :             :  * The mapped contents of the file must not be modified after creating this
     418                 :             :  * bytes object, because a #GBytes should be immutable.
     419                 :             :  *
     420                 :             :  * Returns: (transfer full): A newly allocated #GBytes referencing data
     421                 :             :  *     from @file
     422                 :             :  *
     423                 :             :  * Since: 2.34
     424                 :             :  **/
     425                 :             : GBytes *
     426                 :         207 : g_mapped_file_get_bytes (GMappedFile *file)
     427                 :             : {
     428                 :         207 :   g_return_val_if_fail (file != NULL, NULL);
     429                 :             : 
     430                 :         207 :   return g_bytes_new_with_free_func (file->contents,
     431                 :             :                                      file->length,
     432                 :             :                                      (GDestroyNotify) g_mapped_file_unref,
     433                 :         207 :                                      g_mapped_file_ref (file));
     434                 :             : }
        

Generated by: LCOV version 2.0-1