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