Fix invalid memory reads. (#340816, Nick Treleaven)
[platform/upstream/glib.git] / glib / gmappedfile.c
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 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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
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_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_MMAP
32 #include <sys/mman.h>
33 #endif
34
35 #include "glibconfig.h"
36
37 #ifdef G_OS_WIN32
38 #include <windows.h>
39 #include <io.h>
40 #endif
41
42 #include "gconvert.h"
43 #include "gerror.h"
44 #include "gfileutils.h"
45 #include "gmappedfile.h"
46 #include "gmem.h"
47 #include "gmessages.h"
48 #include "gstdio.h"
49 #include "gstrfuncs.h"
50
51 #include "glibintl.h"
52
53 #include "galias.h"
54
55 #ifndef _O_BINARY
56 #define _O_BINARY 0
57 #endif
58
59 #ifndef MAP_FAILED
60 #define MAP_FAILED ((void *) -1)
61 #endif
62
63 struct _GMappedFile 
64 {
65   gsize  length;
66   gchar *contents;
67 #ifdef G_OS_WIN32
68   HANDLE mapping;
69 #endif
70 };
71
72 /**
73  * g_mapped_file_new:
74  * @filename: The path of the file to load, in the GLib filename encoding
75  * @writable: wether the mapping should be writable
76  * @error: return location for a #GError, or %NULL
77  *
78  * Maps a file into memory. On UNIX, this is using the mmap() function.
79  *
80  * If @writable is %TRUE, the mapped buffer may be modified, otherwise
81  * it is an error to modify the mapped buffer. Modifications to the buffer 
82  * are not visible to other processes mapping the same file, and are not 
83  * written back to the file.
84  *
85  * Note that modifications of the underlying file might affect the contents
86  * of the #GMappedFile. Therefore, mapping should only be used if the file 
87  * will not be modified, or if all modifications of the file are done
88  * atomically (e.g. using g_file_set_contents()). 
89  *
90  * Return value: a newly allocated #GMappedFile which must be freed
91  *    with g_mapped_file_free(), or %NULL if the mapping failed. 
92  *
93  * Since: 2.8
94  */
95 GMappedFile *
96 g_mapped_file_new (const gchar  *filename,
97                    gboolean      writable,
98                    GError      **error)
99 {
100   GMappedFile *file;
101   int fd;
102   struct stat st;
103
104   g_return_val_if_fail (filename != NULL, NULL);
105   g_return_val_if_fail (!error || *error == NULL, NULL);
106
107   fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
108   if (fd == -1)
109     {
110       int save_errno = errno;
111       gchar *display_filename = g_filename_display_name (filename);
112       
113       g_set_error (error,
114                    G_FILE_ERROR,
115                    g_file_error_from_errno (save_errno),
116                    _("Failed to open file '%s': open() failed: %s"),
117                    display_filename, 
118                    g_strerror (save_errno));
119       g_free (display_filename);
120       return NULL;
121     }
122
123   file = g_new0 (GMappedFile, 1);
124
125   if (fstat (fd, &st) == -1)
126     {
127       int save_errno = errno;
128       gchar *display_filename = g_filename_display_name (filename);
129
130       g_set_error (error,
131                    G_FILE_ERROR,
132                    g_file_error_from_errno (save_errno),
133                    _("Failed to get attributes of file '%s': fstat() failed: %s"),
134                    display_filename, 
135                    g_strerror (save_errno));
136       g_free (display_filename);
137       goto out;
138     }
139
140   if (st.st_size == 0)
141     {
142       file->length = 0;
143       file->contents = "";
144       close (fd);
145       return file;
146     }
147
148   file->contents = MAP_FAILED;
149
150 #ifdef HAVE_MMAP
151   if (st.st_size > G_MAXSIZE)
152     {
153       errno = EINVAL;
154     }
155   else
156     {      
157       file->length = (gsize) st.st_size;
158       file->contents = (gchar *) mmap (NULL,  file->length,
159                                        writable ? PROT_READ|PROT_WRITE : PROT_READ,
160                                        MAP_PRIVATE, fd, 0);
161     }
162 #endif
163 #ifdef G_OS_WIN32
164   file->length = st.st_size;
165   file->mapping = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL,
166                                      writable ? PAGE_WRITECOPY : PAGE_READONLY,
167                                      0, 0,
168                                      NULL);
169   if (file->mapping != NULL)
170     {
171       file->contents = MapViewOfFile (file->mapping,
172                                       writable ? FILE_MAP_COPY : FILE_MAP_READ,
173                                       0, 0,
174                                       0);
175       if (file->contents == NULL)
176         {
177           file->contents = MAP_FAILED;
178           CloseHandle (file->mapping);
179           file->mapping = NULL;
180         }
181     }
182 #endif
183
184   
185   if (file->contents == MAP_FAILED)
186     {
187       int save_errno = errno;
188       gchar *display_filename = g_filename_display_name (filename);
189       
190       g_set_error (error,
191                    G_FILE_ERROR,
192                    g_file_error_from_errno (save_errno),
193                    _("Failed to map file '%s': mmap() failed: %s"),
194                    display_filename,
195                    g_strerror (save_errno));
196       g_free (display_filename);
197       goto out;
198     }
199
200   close (fd);
201   return file;
202
203  out:
204   close (fd);
205   g_free (file);
206
207   return NULL;
208 }
209
210 /**
211  * g_mapped_file_get_length:
212  * @file: a #GMappedFile
213  *
214  * Returns the length of the contents of a #GMappedFile.
215  *
216  * Returns: the length of the contents of @file.
217  *
218  * Since: 2.8
219  */
220 gsize
221 g_mapped_file_get_length (GMappedFile *file)
222 {
223   g_return_val_if_fail (file != NULL, 0);
224
225   return file->length;
226 }
227
228 /**
229  * g_mapped_file_get_contents:
230  * @file: a #GMappedFile
231  *
232  * Returns the contents of a #GMappedFile. 
233  *
234  * Note that the contents may not be zero-terminated,
235  * even if the #GMappedFile is backed by a text file.
236  *
237  * Returns: the contents of @file.
238  *
239  * Since: 2.8
240  */
241 gchar *
242 g_mapped_file_get_contents (GMappedFile *file)
243 {
244   g_return_val_if_fail (file != NULL, NULL);
245
246   return file->contents;
247 }
248
249 /**
250  * g_mapped_file_free:
251  * @file: a #GMappedFile
252  *
253  * Unmaps the buffer of @file and frees it. 
254  *
255  * Since: 2.8
256  */
257 void
258 g_mapped_file_free (GMappedFile *file)
259 {
260   g_return_if_fail (file != NULL);
261
262   if (file->length)
263   {
264 #ifdef HAVE_MMAP
265     munmap (file->contents, file->length);
266 #endif
267 #ifdef G_OS_WIN32
268     UnmapViewOfFile (file->contents);
269     CloseHandle (file->mapping);
270 #endif
271   }
272
273   g_free (file);
274 }
275
276
277 #define __G_MAPPED_FILE_C__
278 #include "galiasdef.c"