Coverage Report

Created: 2026-02-26 06:23

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