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