GMappedFile: add refcounting, switch to GSlice
[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 = "";
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  * Returns: the contents of @file.
246  *
247  * Since: 2.8
248  */
249 gchar *
250 g_mapped_file_get_contents (GMappedFile *file)
251 {
252   g_return_val_if_fail (file != NULL, NULL);
253
254   return file->contents;
255 }
256
257 /**
258  * g_mapped_file_free:
259  * @file: a #GMappedFile
260  *
261  * This call existed before #GMappedFile had refcounting and is currently
262  * exactly the same as g_mapped_file_unref().
263  *
264  * Since: 2.8
265  * Deprecated:2.22: Use g_mapped_file_unref() instead.
266  */
267 void
268 g_mapped_file_free (GMappedFile *file)
269 {
270   g_mapped_file_unref (file);
271 }
272
273 /**
274  * g_mapped_file_ref:
275  * @file: a #GMappedFile
276  *
277  * Increments the reference count of @file by one.  It is safe to call
278  * this function from any thread.
279  *
280  * Return value: the passed in #GMappedFile.
281  *
282  * Since: 2.22
283  **/
284 GMappedFile *
285 g_mapped_file_ref (GMappedFile *file)
286 {
287   g_return_val_if_fail (file != NULL, NULL);
288   g_return_val_if_fail (file->ref_count > 0, file);
289
290   g_atomic_int_inc (&file->ref_count);
291
292   return file;
293 }
294
295 /**
296  * g_mapped_file_unref:
297  * @file: a #GMappedFile
298  *
299  * Decrements the reference count of @file by one.  If the reference count
300  * drops to 0, unmaps the buffer of @file and frees it.
301  *
302  * It is safe to call this function from any thread.
303  *
304  * Since 2.22
305  **/
306 void
307 g_mapped_file_unref (GMappedFile *file)
308 {
309   g_return_if_fail (file != NULL);
310   g_return_if_fail (file->ref_count > 0);
311
312   if (g_atomic_int_dec_and_test (&file->ref_count))
313     {
314       if (file->length)
315         {
316 #ifdef HAVE_MMAP
317           munmap (file->contents, file->length);
318 #endif
319 #ifdef G_OS_WIN32
320           UnmapViewOfFile (file->contents);
321           CloseHandle (file->mapping);
322 #endif
323         }
324
325       g_slice_free (GMappedFile, file);
326     }
327 }
328
329 #define __G_MAPPED_FILE_C__
330 #include "galiasdef.c"