Coverage Report

Created: 2025-11-02 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/subprojects/glib-2.80.3/glib/gmappedfile.c
Line
Count
Source
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
1.04k
#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
g_mapped_file_destroy (GMappedFile *file)
99
4
{
100
4
  if (file->length)
101
4
    {
102
4
#ifdef HAVE_MMAP
103
4
      munmap (file->contents, file->length);
104
4
#endif
105
#ifdef G_OS_WIN32
106
      UnmapViewOfFile (file->contents);
107
      CloseHandle (file->mapping);
108
#endif
109
4
    }
110
111
4
  g_slice_free (GMappedFile, file);
112
4
}
113
114
static GMappedFile*
115
mapped_file_new_from_fd (int           fd,
116
       gboolean      writable,
117
                         const gchar  *filename,
118
                         GError      **error)
119
4
{
120
4
  GMappedFile *file;
121
4
  struct stat st;
122
123
4
  file = g_slice_new0 (GMappedFile);
124
4
  file->ref_count = 1;
125
4
  file->free_func = g_mapped_file_destroy;
126
127
4
  if (fstat (fd, &st) == -1)
128
0
    {
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
0
                   G_FILE_ERROR,
134
0
                   g_file_error_from_errno (save_errno),
135
0
                   _("Failed to get attributes of file ā€œ%s%s%s%sā€: fstat() failed: %s"),
136
0
       display_filename ? display_filename : "fd",
137
0
       display_filename ? "' " : "",
138
0
       display_filename ? display_filename : "",
139
0
       display_filename ? "'" : "",
140
0
       g_strerror (save_errno));
141
0
      g_free (display_filename);
142
0
      goto out;
143
0
    }
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
4
  if (st.st_size == 0 && S_ISREG (st.st_mode))
150
0
    {
151
0
      file->length = 0;
152
0
      file->contents = NULL;
153
0
      return file;
154
0
    }
155
156
4
  file->contents = MAP_FAILED;
157
158
4
#ifdef HAVE_MMAP
159
4
  if (sizeof (st.st_size) > sizeof (gsize) && st.st_size > (off_t) G_MAXSIZE)
160
0
    {
161
0
      errno = EINVAL;
162
0
    }
163
4
  else
164
4
    {      
165
4
      file->length = (gsize) st.st_size;
166
4
      file->contents = (gchar *) mmap (NULL,  file->length,
167
4
               writable ? PROT_READ|PROT_WRITE : PROT_READ,
168
4
               MAP_PRIVATE, fd, 0);
169
4
    }
170
4
#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
4
  if (file->contents == MAP_FAILED)
194
0
    {
195
0
      int save_errno = errno;
196
0
      gchar *display_filename = filename ? g_filename_display_name (filename) : NULL;
197
198
0
      g_set_error (error,
199
0
       G_FILE_ERROR,
200
0
       g_file_error_from_errno (save_errno),
201
0
       _("Failed to map %s%s%s%s: mmap() failed: %s"),
202
0
       display_filename ? display_filename : "fd",
203
0
       display_filename ? "' " : "",
204
0
       display_filename ? display_filename : "",
205
0
       display_filename ? "'" : "",
206
0
       g_strerror (save_errno));
207
0
      g_free (display_filename);
208
0
      goto out;
209
0
    }
210
211
4
  return file;
212
213
0
 out:
214
0
  g_slice_free (GMappedFile, file);
215
216
0
  return NULL;
217
4
}
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
g_mapped_file_new (const gchar  *filename,
250
       gboolean      writable,
251
       GError      **error)
252
1.04k
{
253
1.04k
  GMappedFile *file;
254
1.04k
  int fd;
255
256
1.04k
  g_return_val_if_fail (filename != NULL, NULL);
257
1.04k
  g_return_val_if_fail (!error || *error == NULL, NULL);
258
259
1.04k
  fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY | O_CLOEXEC, 0);
260
1.04k
  if (fd == -1)
261
1.03k
    {
262
1.03k
      int save_errno = errno;
263
1.03k
      gchar *display_filename = g_filename_display_name (filename);
264
265
1.03k
      g_set_error (error,
266
1.03k
                   G_FILE_ERROR,
267
1.03k
                   g_file_error_from_errno (save_errno),
268
1.03k
                   _("Failed to open file ā€œ%sā€: open() failed: %s"),
269
1.03k
                   display_filename,
270
1.03k
       g_strerror (save_errno));
271
1.03k
      g_free (display_filename);
272
1.03k
      return NULL;
273
1.03k
    }
274
275
4
  file = mapped_file_new_from_fd (fd, writable, filename, error);
276
277
4
  close (fd);
278
279
4
  return file;
280
1.04k
}
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
g_mapped_file_new_from_fd (gint          fd,
308
         gboolean      writable,
309
         GError      **error)
310
0
{
311
0
  return mapped_file_new_from_fd (fd, writable, NULL, error);
312
0
}
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
g_mapped_file_get_length (GMappedFile *file)
326
4
{
327
4
  g_return_val_if_fail (file != NULL, 0);
328
329
4
  return file->length;
330
4
}
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: the contents of @file, or %NULL.
344
 *
345
 * Since: 2.8
346
 */
347
gchar *
348
g_mapped_file_get_contents (GMappedFile *file)
349
4
{
350
4
  g_return_val_if_fail (file != NULL, NULL);
351
352
4
  return file->contents;
353
4
}
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
g_mapped_file_free (GMappedFile *file)
367
0
{
368
0
  g_mapped_file_unref (file);
369
0
}
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
g_mapped_file_ref (GMappedFile *file)
384
4
{
385
4
  g_return_val_if_fail (file != NULL, NULL);
386
387
4
  g_atomic_int_inc (&file->ref_count);
388
389
4
  return file;
390
4
}
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
g_mapped_file_unref (GMappedFile *file)
405
8
{
406
8
  g_return_if_fail (file != NULL);
407
408
8
  if (g_atomic_int_dec_and_test (&file->ref_count))
409
4
    g_mapped_file_destroy (file);
410
8
}
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
g_mapped_file_get_bytes (GMappedFile *file)
427
0
{
428
0
  g_return_val_if_fail (file != NULL, NULL);
429
430
0
  return g_bytes_new_with_free_func (file->contents,
431
0
             file->length,
432
0
             (GDestroyNotify) g_mapped_file_unref,
433
0
             g_mapped_file_ref (file));
434
0
}