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

source code of gtk/subprojects/glib/glib/gmappedfile.c