Return NULL from g_mapped_file_get_contents()
[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
41 #ifdef _MSC_VER
42 #define fstat(a,b) _fstat(a,b)
43 #endif
44
45 #endif
46
47 #include "gconvert.h"
48 #include "gerror.h"
49 #include "gfileutils.h"
50 #include "gmappedfile.h"
51 #include "gmem.h"
52 #include "gmessages.h"
53 #include "gstdio.h"
54 #include "gstrfuncs.h"
55 #include "gatomic.h"
56
57 #include "glibintl.h"
58
59 #include "galias.h"
60
61 #ifndef _O_BINARY
62 #define _O_BINARY 0
63 #endif
64
65 #ifndef MAP_FAILED
66 #define MAP_FAILED ((void *) -1)
67 #endif
68
69 struct _GMappedFile 
70 {
71   gsize  length;
72   gchar *contents;
73   int    ref_count;
74 #ifdef G_OS_WIN32
75   HANDLE mapping;
76 #endif
77 };
78
79 /**
80  * g_mapped_file_new:
81  * @filename: The path of the file to load, in the GLib filename encoding
82  * @writable: whether the mapping should be writable
83  * @error: return location for a #GError, or %NULL
84  *
85  * Maps a file into memory. On UNIX, this is using the mmap() function.
86  *
87  * If @writable is %TRUE, the mapped buffer may be modified, otherwise
88  * it is an error to modify the mapped buffer. Modifications to the buffer 
89  * are not visible to other processes mapping the same file, and are not 
90  * written back to the file.
91  *
92  * Note that modifications of the underlying file might affect the contents
93  * of the #GMappedFile. Therefore, mapping should only be used if the file 
94  * will not be modified, or if all modifications of the file are done
95  * atomically (e.g. using g_file_set_contents()). 
96  *
97  * Return value: a newly allocated #GMappedFile which must be unref'd
98  *    with g_mapped_file_unref(), or %NULL if the mapping failed.
99  *
100  * Since: 2.8
101  */
102 GMappedFile *
103 g_mapped_file_new (const gchar  *filename,
104                    gboolean      writable,
105                    GError      **error)
106 {
107   GMappedFile *file;
108   int fd;
109   struct stat st;
110
111   g_return_val_if_fail (filename != NULL, NULL);
112   g_return_val_if_fail (!error || *error == NULL, NULL);
113
114   fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
115   if (fd == -1)
116     {
117       int save_errno = errno;
118       gchar *display_filename = g_filename_display_name (filename);
119       
120       g_set_error (error,
121                    G_FILE_ERROR,
122                    g_file_error_from_errno (save_errno),
123                    _("Failed to open file '%s': open() failed: %s"),
124                    display_filename, 
125                    g_strerror (save_errno));
126       g_free (display_filename);
127       return NULL;
128     }
129
130   file = g_slice_new0 (GMappedFile);
131   file->ref_count = 1;
132
133   if (fstat (fd, &st) == -1)
134     {
135       int save_errno = errno;
136       gchar *display_filename = g_filename_display_name (filename);
137
138       g_set_error (error,
139                    G_FILE_ERROR,
140                    g_file_error_from_errno (save_errno),
141                    _("Failed to get attributes of file '%s': fstat() failed: %s"),
142                    display_filename, 
143                    g_strerror (save_errno));
144       g_free (display_filename);
145       goto out;
146     }
147
148   if (st.st_size == 0)
149     {
150       file->length = 0;
151       file->contents = NULL;
152       close (fd);
153       return file;
154     }
155
156   file->contents = MAP_FAILED;
157
158 #ifdef HAVE_MMAP
159   if (st.st_size > G_MAXSIZE)
160     {
161       errno = EINVAL;
162     }
163   else
164     {      
165       file->length = (gsize) st.st_size;
166       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   if (file->contents == MAP_FAILED)
194     {
195       int save_errno = errno;
196       gchar *display_filename = g_filename_display_name (filename);
197       
198       g_set_error (error,
199                    G_FILE_ERROR,
200                    g_file_error_from_errno (save_errno),
201                    _("Failed to map file '%s': mmap() failed: %s"),
202                    display_filename,
203                    g_strerror (save_errno));
204       g_free (display_filename);
205       goto out;
206     }
207
208   close (fd);
209   return file;
210
211  out:
212   close (fd);
213   g_slice_free (GMappedFile, file);
214
215   return NULL;
216 }
217
218 /**
219  * g_mapped_file_get_length:
220  * @file: a #GMappedFile
221  *
222  * Returns the length of the contents of a #GMappedFile.
223  *
224  * Returns: the length of the contents of @file.
225  *
226  * Since: 2.8
227  */
228 gsize
229 g_mapped_file_get_length (GMappedFile *file)
230 {
231   g_return_val_if_fail (file != NULL, 0);
232
233   return file->length;
234 }
235
236 /**
237  * g_mapped_file_get_contents:
238  * @file: a #GMappedFile
239  *
240  * Returns the contents of a #GMappedFile. 
241  *
242  * Note that the contents may not be zero-terminated,
243  * even if the #GMappedFile is backed by a text file.
244  *
245  * If the file is empty then %NULL is returned.
246  *
247  * Returns: the contents of @file, or %NULL.
248  *
249  * Since: 2.8
250  */
251 gchar *
252 g_mapped_file_get_contents (GMappedFile *file)
253 {
254   g_return_val_if_fail (file != NULL, NULL);
255
256   return file->contents;
257 }
258
259 /**
260  * g_mapped_file_free:
261  * @file: a #GMappedFile
262  *
263  * This call existed before #GMappedFile had refcounting and is currently
264  * exactly the same as g_mapped_file_unref().
265  *
266  * Since: 2.8
267  * Deprecated:2.22: Use g_mapped_file_unref() instead.
268  */
269 void
270 g_mapped_file_free (GMappedFile *file)
271 {
272   g_mapped_file_unref (file);
273 }
274
275 /**
276  * g_mapped_file_ref:
277  * @file: a #GMappedFile
278  *
279  * Increments the reference count of @file by one.  It is safe to call
280  * this function from any thread.
281  *
282  * Return value: the passed in #GMappedFile.
283  *
284  * Since: 2.22
285  **/
286 GMappedFile *
287 g_mapped_file_ref (GMappedFile *file)
288 {
289   g_return_val_if_fail (file != NULL, NULL);
290   g_return_val_if_fail (file->ref_count > 0, file);
291
292   g_atomic_int_inc (&file->ref_count);
293
294   return file;
295 }
296
297 /**
298  * g_mapped_file_unref:
299  * @file: a #GMappedFile
300  *
301  * Decrements the reference count of @file by one.  If the reference count
302  * drops to 0, unmaps the buffer of @file and frees it.
303  *
304  * It is safe to call this function from any thread.
305  *
306  * Since 2.22
307  **/
308 void
309 g_mapped_file_unref (GMappedFile *file)
310 {
311   g_return_if_fail (file != NULL);
312   g_return_if_fail (file->ref_count > 0);
313
314   if (g_atomic_int_dec_and_test (&file->ref_count))
315     {
316       if (file->length)
317         {
318 #ifdef HAVE_MMAP
319           munmap (file->contents, file->length);
320 #endif
321 #ifdef G_OS_WIN32
322           UnmapViewOfFile (file->contents);
323           CloseHandle (file->mapping);
324 #endif
325         }
326
327       g_slice_free (GMappedFile, file);
328     }
329 }
330
331 #define __G_MAPPED_FILE_C__
332 #include "galiasdef.c"