"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-input-memory.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-input-memory.c:
4  *
5  * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf-config.h>
23 #include <string.h>
24 #include <gsf/gsf-input-memory.h>
25 #include <gsf/gsf-input-impl.h>
26 #include <gsf/gsf-impl-utils.h>
27 #include <gsf/gsf-utils.h>
28 #include <gsf/gsf-shared-memory.h>
29 #include <glib/gstdio.h>
30
31 #ifdef HAVE_MMAP
32
33 #if defined(FREEBSD) || defined(__FreeBSD__)
34 /* We must keep the file open while pages are mapped.  */
35 /* http://www.freebsd.org/cgi/query-pr.cgi?pr=48291 */
36 #define HAVE_BROKEN_MMAP
37 #endif /* defined(FREEBSD) || defined(__FreeBSD__) */
38
39 #elif defined(G_OS_WIN32)
40
41 #include <windows.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <io.h>
46 #include <fcntl.h>
47
48 #define MAP_FAILED NULL
49 #endif /* HAVE_MMAP */
50
51 #ifndef O_BINARY
52 #define O_BINARY 0
53 #endif
54
55 static GObjectClass *parent_class;
56
57 struct _GsfInputMemory {
58         GsfInput parent;
59         GsfSharedMemory *shared;
60 #ifdef HAVE_BROKEN_MMAP
61         int fd;
62 #endif
63 };
64 typedef GsfInputClass GsfInputMemoryClass;
65
66 /**
67  * gsf_input_memory_new:
68  * @buf: The input bytes
69  * @length: The length of @buf
70  * @needs_free: Whether you want this memory to be free'd at object destruction
71  *
72  * Returns: A new #GsfInputMemory
73  */
74 GsfInput *
75 gsf_input_memory_new (guint8 const *buf, gsf_off_t length, gboolean needs_free)
76 {
77         GsfInputMemory *mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
78         if (G_UNLIKELY (NULL == mem)) return NULL;
79
80         mem->shared = gsf_shared_memory_new ((void *)buf, length, needs_free);
81         gsf_input_set_size (GSF_INPUT (mem), length);
82         return GSF_INPUT (mem);
83 }
84
85 /**
86  * gsf_input_memory_new_clone:
87  * @buf: The input bytes
88  * @length: The length of @buf
89  *
90  * Returns: A new #GsfInputMemory
91  **/
92 GsfInput *
93 gsf_input_memory_new_clone (guint8 const *buf, gsf_off_t length)
94 {
95         GsfInputMemory *mem = NULL;
96         void *cpy;
97
98         g_return_val_if_fail (buf != NULL || length == 0, NULL);
99         g_return_val_if_fail (length >= 0, NULL);
100
101         mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
102         if (G_UNLIKELY (NULL == mem)) return NULL;
103
104         cpy = g_try_malloc (MAX (1, length) * sizeof (guint8));
105         if (cpy == NULL) {
106                 g_object_unref (mem);
107                 return NULL;
108         }
109         memcpy (cpy, buf, length);
110         mem->shared = gsf_shared_memory_new (cpy, length, TRUE);
111         gsf_input_set_size (GSF_INPUT (mem), length);
112         return GSF_INPUT (mem);
113 }
114
115 static void
116 gsf_input_memory_finalize (GObject *obj)
117 {
118         GsfInputMemory *mem = (GsfInputMemory *) (obj);
119
120         if (mem->shared)
121                 g_object_unref (G_OBJECT (mem->shared));
122
123 #ifdef HAVE_BROKEN_MMAP
124         if (mem->fd != -1)
125                 close (mem->fd);
126 #endif
127
128         parent_class->finalize (obj);
129 }
130
131 static GsfInput *
132 gsf_input_memory_dup (GsfInput *src_input, G_GNUC_UNUSED GError **err)
133 {
134         GsfInputMemory const *src = (GsfInputMemory *) (src_input);
135         GsfInputMemory *dst = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
136         if (G_UNLIKELY (NULL == dst)) return NULL;
137
138         dst->shared = src->shared;
139         g_object_ref (G_OBJECT (dst->shared));
140         gsf_input_set_size (GSF_INPUT (dst), src->shared->size);
141
142 #ifdef HAVE_BROKEN_MMAP
143         if (src->fd != -1)
144                 dst->fd = dup (src->fd);
145 #endif
146
147         return GSF_INPUT (dst);
148 }
149
150 static guint8 const *
151 gsf_input_memory_read (GsfInput *input, size_t num_bytes, guint8 *optional_buffer)
152 {
153         GsfInputMemory *mem = (GsfInputMemory *) (input);
154         guchar const *src = mem->shared->buf;
155
156         if (src == NULL)
157                 return NULL;
158         if (optional_buffer) {
159                 memcpy (optional_buffer, src + input->cur_offset, num_bytes);
160                 return optional_buffer;
161         } else
162                 return src + input->cur_offset;
163 }
164
165 static gboolean
166 gsf_input_memory_seek (G_GNUC_UNUSED GsfInput *input,
167                        G_GNUC_UNUSED gsf_off_t offset,
168                        G_GNUC_UNUSED GSeekType whence)
169 {
170         return FALSE;
171 }
172
173 static void
174 gsf_input_memory_init (GObject *obj)
175 {
176         GsfInputMemory *mem = (GsfInputMemory *) (obj);
177         mem->shared = NULL;
178 #ifdef HAVE_BROKEN_MMAP
179         mem->fd = -1;
180 #endif
181 }
182
183 static void
184 gsf_input_memory_class_init (GObjectClass *gobject_class)
185 {
186         GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
187
188         gobject_class->finalize = gsf_input_memory_finalize;
189         input_class->Dup        = gsf_input_memory_dup;
190         input_class->Read       = gsf_input_memory_read;
191         input_class->Seek       = gsf_input_memory_seek;
192
193         parent_class = g_type_class_peek_parent (gobject_class);
194 }
195
196 GSF_CLASS (GsfInputMemory, gsf_input_memory,
197            gsf_input_memory_class_init, gsf_input_memory_init,
198            GSF_INPUT_TYPE)
199
200 /***************************************************************************/
201
202 #ifdef HAVE_MMAP
203
204 #include <unistd.h>
205 #include <sys/mman.h>
206 #include <sys/types.h>
207 #include <sys/stat.h>
208 #include <fcntl.h>
209 #include <errno.h>
210
211 #ifndef PROT_READ
212 #define PROT_READ 0x1
213 #endif /* PROT_READ */
214
215 #if !defined(MAP_FAILED) || defined(__osf__)
216 /* Someone needs their head examined - BSD ? */
217 #       define MAP_FAILED ((void *)-1)
218 #endif /* !defined(MAP_FAILED) || defined(__osf__) */
219
220 #endif /* HAVE_MMAP */
221
222 /**
223  * gsf_input_mmap_new:
224  * @filename: The file on disk that you want to mmap
225  * @err: A #GError, or optionally %null
226  *
227  * Returns: A new #GsfInputMemory
228  */
229 GsfInput *
230 gsf_input_mmap_new (char const *filename, GError **err)
231 {
232 #if defined(HAVE_MMAP) || defined(G_OS_WIN32)
233         GsfInputMemory *mem;
234         guint8 *buf = NULL;
235         struct stat st;
236         int fd;
237         size_t size;
238
239         mem = g_object_new (GSF_INPUT_MEMORY_TYPE, NULL);
240         if (G_UNLIKELY (NULL == mem)) return NULL;
241
242         fd = g_open (filename, O_RDONLY | O_BINARY, 0);
243         if (fd < 0 || fstat (fd, &st) < 0) {
244                 if (err != NULL) {
245                         int save_errno = errno;
246                         char *utf8name = g_filename_display_name (filename);
247                         *err = g_error_new (gsf_input_error_id (), 0,
248                                             "%s: %s",
249                                             utf8name, g_strerror (save_errno));
250                         g_free (utf8name);
251                 }
252                 if (fd >= 0) close (fd);
253                 return NULL;
254         }
255
256         if (!S_ISREG (st.st_mode)) {
257                 if (err != NULL) {
258                         char *utf8name = g_filename_display_name (filename);
259                         *err = g_error_new (gsf_input_error_id (), 0,
260                                             "%s: Is not a regular file",
261                                             utf8name);
262                         g_free (utf8name);
263                 }
264                 close (fd);
265                 return NULL;
266         }
267
268         size = (size_t) st.st_size;
269         if ((off_t) size != st.st_size) { /* Check for overflow */
270                 if (err != NULL) {
271                         char *utf8name = g_filename_display_name (filename);
272                         *err = g_error_new (gsf_input_error_id (), 0,
273                                             "%s: File too large to be memory mapped",
274                                             utf8name);
275                         g_free (utf8name);
276                 }
277                 close (fd);
278                 return NULL;
279         }
280
281 #ifdef G_OS_WIN32
282         {
283                 HANDLE handle = CreateFileMapping ((HANDLE)_get_osfhandle (fd), NULL, PAGE_READONLY, 0, 0, NULL);
284                 buf = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, 0);
285                 CloseHandle (handle);
286         }
287 #else
288         buf = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
289 #endif
290
291         if (buf == MAP_FAILED) {
292                 if (err != NULL) {
293                         int save_errno = errno;
294                         char *utf8name = g_filename_display_name (filename);
295                         *err = g_error_new (gsf_input_error_id (), 0,
296                                             "%s: %s",
297                                             utf8name, g_strerror (save_errno));
298                         g_free (utf8name);
299                 }
300                 close (fd);
301                 return NULL;
302         }
303
304         mem->shared = gsf_shared_memory_mmapped_new (buf, (gsf_off_t) size);
305         gsf_input_set_size (GSF_INPUT (mem), (gsf_off_t) size);
306         gsf_input_set_name (GSF_INPUT (mem), filename);
307
308 #ifdef HAVE_BROKEN_MMAP
309         mem->fd = fd;
310 #else
311         close (fd);
312 #endif
313
314         return GSF_INPUT (mem);
315 #else
316         (void)filename;
317         if (err != NULL)
318                 *err = g_error_new (gsf_input_error_id (), 0,
319                                     "mmap not supported");
320         return NULL;
321 #endif
322 }