1 /* map and unmap files in various ways
2 *
3 * Copyright: Nicos Dessipris
4 * Wriiten on: 13/02/1990
5 * Updated on:
6 * 10/5/93 J.Cupitt
7 * - im_mapfilerw() added
8 * 13/12/94 JC
9 * - ANSIfied
10 * 5/7/99 JC
11 * - better error if unable to map rw
12 * 31/3/02 JC
13 * - better mmap() fails error
14 * 19/9/02 JC
15 * - added im__mmap()/im__munmap() with windows versions
16 * 5/1/04 Lev Serebryakov
17 * - patched for freebsd compatibility
18 * 5/2/04 JC
19 * - now records length as well as base, so we unmap the right amount of
20 * memory even if files change behind our back
21 * 1/1/10
22 * - set NOCACHE if we can ... helps OS X performance a lot
23 * 25/3/11
24 * - move to vips_ namespace
25 */
26
27 /*
28
29 This file is part of VIPS.
30
31 VIPS is free software; you can redistribute it and/or modify
32 it under the terms of the GNU Lesser General Public License as published by
33 the Free Software Foundation; either version 2 of the License, or
34 (at your option) any later version.
35
36 This program is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 GNU Lesser General Public License for more details.
40
41 You should have received a copy of the GNU Lesser General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
44 02110-1301 USA
45
46 */
47
48 /*
49
50 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
51
52 */
53
54 /*
55 #define DEBUG
56 */
57
58 #ifdef HAVE_CONFIG_H
59 #include <config.h>
60 #endif /*HAVE_CONFIG_H*/
61 #include <vips/intl.h>
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <assert.h>
68
69 #include <sys/types.h>
70 #ifdef HAVE_SYS_MMAN_H
71 #include <sys/mman.h>
72 #endif /*HAVE_SYS_MMAN_H*/
73 #ifdef HAVE_SYS_FILE_H
74 #include <sys/file.h>
75 #endif /*HAVE_SYS_FILE_H*/
76 #include <sys/stat.h>
77 #ifdef HAVE_UNISTD_H
78 #include <unistd.h>
79 #endif /*HAVE_UNISTD_H*/
80
81 #include <vips/vips.h>
82
83 #ifdef G_OS_WIN32
84 #ifndef S_ISREG
85 #define S_ISREG(m) (!!(m & _S_IFREG))
86 #endif
87 #include <windows.h>
88 #include <io.h>
89 #endif /*G_OS_WIN32*/
90
91 /* Does this fd support mmap. Pipes won't, for example.
92 */
93 gboolean
vips__mmap_supported(int fd)94 vips__mmap_supported( int fd )
95 {
96 void *baseaddr;
97 size_t length = 4096;
98 off_t offset = 0;
99
100 #ifdef G_OS_WIN32
101 {
102 HANDLE hFile = (HANDLE) _get_osfhandle( fd );
103
104 DWORD flProtect;
105 HANDLE hMMFile;
106
107 DWORD dwDesiredAccess;
108 ULARGE_INTEGER quad;
109 DWORD dwFileOffsetHigh;
110 DWORD dwFileOffsetLow;
111
112 flProtect = PAGE_READONLY;
113 if( !(hMMFile = CreateFileMapping( hFile,
114 NULL, flProtect, 0, 0, NULL )) )
115 return( FALSE );
116
117 dwDesiredAccess = FILE_MAP_READ;
118 quad.QuadPart = offset;
119 dwFileOffsetLow = quad.LowPart;
120 dwFileOffsetHigh = quad.HighPart;
121 if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess,
122 dwFileOffsetHigh, dwFileOffsetLow, length )) ) {
123 CloseHandle( hMMFile );
124 return( FALSE );
125 }
126 CloseHandle( hMMFile );
127 UnmapViewOfFile( baseaddr );
128 }
129 #else /*!G_OS_WIN32*/
130 {
131 int prot = PROT_READ;
132 int flags = MAP_SHARED;
133
134 baseaddr = mmap( 0, length, prot, flags, fd, (off_t) offset );
135 if( baseaddr == MAP_FAILED )
136 return( FALSE );
137 munmap( baseaddr, length );
138 }
139 #endif /*G_OS_WIN32*/
140
141 return( TRUE );
142 }
143
144 void *
vips__mmap(int fd,int writeable,size_t length,gint64 offset)145 vips__mmap( int fd, int writeable, size_t length, gint64 offset )
146 {
147 void *baseaddr;
148
149 #ifdef DEBUG
150 printf( "vips__mmap: length = 0x%zx, offset = 0x%lx\n",
151 length, offset );
152 #endif /*DEBUG*/
153
154 #ifdef G_OS_WIN32
155 {
156 HANDLE hFile = (HANDLE) _get_osfhandle( fd );
157
158 DWORD flProtect;
159 DWORD dwDesiredAccess;
160
161 HANDLE hMMFile;
162
163 ULARGE_INTEGER quad;
164 DWORD dwFileOffsetHigh;
165 DWORD dwFileOffsetLow;
166
167 if( writeable ) {
168 flProtect = PAGE_READWRITE;
169 dwDesiredAccess = FILE_MAP_WRITE;
170 }
171 else {
172 flProtect = PAGE_READONLY;
173 dwDesiredAccess = FILE_MAP_READ;
174 }
175
176 quad.QuadPart = offset;
177 dwFileOffsetLow = quad.LowPart;
178 dwFileOffsetHigh = quad.HighPart;
179
180 if( !(hMMFile = CreateFileMapping( hFile,
181 NULL, flProtect, 0, 0, NULL )) ) {
182 vips_error_system( GetLastError(), "vips_mapfile",
183 "%s", _( "unable to CreateFileMapping" ) );
184 printf( "CreateFileMapping failed: %s\n", vips_error_buffer() );
185 return( NULL );
186 }
187
188 if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess,
189 dwFileOffsetHigh, dwFileOffsetLow, length )) ) {
190 vips_error_system( GetLastError(), "vips_mapfile",
191 "%s", _( "unable to MapViewOfFile" ) );
192 printf( "MapViewOfFile failed: %s\n", vips_error_buffer() );
193 CloseHandle( hMMFile );
194 return( NULL );
195 }
196
197 /* Can close mapping now ... view stays until UnmapViewOfFile().
198
199 FIXME ... is this a performance problem?
200
201 */
202 CloseHandle( hMMFile );
203 }
204 #else /*!G_OS_WIN32*/
205 {
206 int prot;
207 int flags;
208
209 if( writeable )
210 prot = PROT_WRITE;
211 else
212 prot = PROT_READ;
213
214 flags = MAP_SHARED;
215
216 /* OS X caches mmapped files very aggressively if this flags is not
217 * set. Scanning a large file without this flag will cause every other
218 * process to get swapped out and kill performance.
219 */
220 #ifdef MAP_NOCACHE
221 flags |= MAP_NOCACHE;
222 #endif /*MAP_NOCACHE*/
223
224 /* Casting gint64 to off_t should be safe, even on *nixes without
225 * LARGEFILE.
226 */
227
228 baseaddr = mmap( 0, length, prot, flags, fd, (off_t) offset );
229 if( baseaddr == MAP_FAILED ) {
230 vips_error_system( errno, "vips_mapfile",
231 "%s", _( "unable to mmap" ) );
232 g_warning( _( "map failed (%s), "
233 "running very low on system resources, "
234 "expect a crash soon" ), strerror( errno ) );
235 return( NULL );
236 }
237 }
238 #endif /*G_OS_WIN32*/
239
240 return( baseaddr );
241 }
242
243 int
vips__munmap(const void * start,size_t length)244 vips__munmap( const void *start, size_t length )
245 {
246 #ifdef G_OS_WIN32
247 if( !UnmapViewOfFile( (void *) start ) ) {
248 vips_error_system( GetLastError(), "vips_mapfile",
249 "%s", _( "unable to UnmapViewOfFile" ) );
250 return( -1 );
251 }
252 #else /*!G_OS_WIN32*/
253 if( munmap( (void *) start, length ) < 0 ) {
254 vips_error_system( errno, "vips_mapfile",
255 "%s", _( "unable to munmap file" ) );
256 return( -1 );
257 }
258 #endif /*G_OS_WIN32*/
259
260 return( 0 );
261 }
262
263 int
vips_mapfile(VipsImage * im)264 vips_mapfile( VipsImage *im )
265 {
266 struct stat st;
267 mode_t m;
268
269 assert( !im->baseaddr );
270
271 /* Check the size of the file; if it is less than 64 bytes, then flag
272 * an error, we won't be able to read the vips header without a segv.
273 */
274 g_assert( im->file_length > 0 );
275 if( im->file_length < 64 ) {
276 vips_error( "vips_mapfile",
277 "%s", _( "file is less than 64 bytes" ) );
278 return( -1 );
279 }
280 if( fstat( im->fd, &st ) == -1 ) {
281 vips_error( "vips_mapfile",
282 "%s", _( "unable to get file status" ) );
283 return( -1 );
284 }
285 m = (mode_t) st.st_mode;
286 if( !S_ISREG( m ) ) {
287 vips_error( "vips_mapfile",
288 "%s", _( "not a regular file" ) );
289 return( -1 );
290 }
291
292 if( !(im->baseaddr = vips__mmap( im->fd, 0, im->file_length, 0 )) )
293 return( -1 );
294
295 im->length = im->file_length;
296
297 return( 0 );
298 }
299
300 /* As above, but map read/write.
301 */
302 int
vips_mapfilerw(VipsImage * im)303 vips_mapfilerw( VipsImage *im )
304 {
305 struct stat st;
306 mode_t m;
307
308 assert( !im->baseaddr );
309
310 /* Check the size of the file if it is less than 64 bytes return
311 * make also sure that it is a regular file
312 */
313 g_assert( im->file_length > 0 );
314 if( fstat( im->fd, &st ) == -1 ) {
315 vips_error( "vips_mapfilerw",
316 "%s", _( "unable to get file status" ) );
317 return( -1 );
318 }
319 m = (mode_t) st.st_mode;
320 if( im->file_length < 64 || !S_ISREG( m ) ) {
321 vips_error( "vips_mapfile",
322 "%s", _( "unable to read data" ) );
323 return( -1 );
324 }
325
326 if( !(im->baseaddr = vips__mmap( im->fd, 1, im->file_length, 0 )) )
327 return( -1 );
328
329 im->length = im->file_length;
330
331 return( 0 );
332 }
333
334 /* From im_rwcheck() ... image needs to be a completely mapped read-only file,
335 * we try to remap it read-write.
336 */
337 int
vips_remapfilerw(VipsImage * image)338 vips_remapfilerw( VipsImage *image )
339 {
340 void *baseaddr;
341
342 #ifdef DEBUG
343 printf( "vips_remapfilerw:\n" );
344 #endif /*DEBUG*/
345
346 #ifdef G_OS_WIN32
347 {
348 HANDLE hFile = (HANDLE) _get_osfhandle( image->fd );
349 HANDLE hMMFile;
350
351 if( !(hMMFile = CreateFileMapping( hFile,
352 NULL, PAGE_READWRITE, 0, 0, NULL )) ) {
353 vips_error_system( GetLastError(), "vips_mapfile",
354 "%s", _( "unable to CreateFileMapping" ) );
355 return( -1 );
356 }
357
358 if( !UnmapViewOfFile( image->baseaddr ) ) {
359 vips_error_system( GetLastError(), "vips_mapfile",
360 "%s", _( "unable to UnmapViewOfFile" ) );
361 return( -1 );
362 }
363 if( !(baseaddr = (char *)MapViewOfFileEx( hMMFile, FILE_MAP_WRITE,
364 0, 0, 0, image->baseaddr )) ) {
365 vips_error_system( GetLastError(), "vips_mapfile",
366 "%s", _( "unable to MapViewOfFile" ) );
367 CloseHandle( hMMFile );
368 return( -1 );
369 }
370
371 /* Can close mapping now ... view stays until UnmapViewOfFile().
372
373 FIXME ... is this a performance problem?
374
375 */
376 CloseHandle( hMMFile );
377 }
378 #else /*!G_OS_WIN32*/
379 {
380 assert( image->dtype == VIPS_IMAGE_MMAPIN );
381
382 baseaddr = mmap( image->baseaddr, image->length,
383 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
384 image->fd, 0 );
385 if( baseaddr == (void *)-1 ) {
386 vips_error( "vips_mapfile", _( "unable to mmap: \"%s\" - %s" ),
387 image->filename, strerror( errno ) );
388 return( -1 );
389 }
390 }
391 #endif /*G_OS_WIN32*/
392
393 image->dtype = VIPS_IMAGE_MMAPINRW;
394
395 if( baseaddr != image->baseaddr ) {
396 vips_error( "vips_mapfile", _( "unable to mmap \"%s\" to same "
397 "address" ), image->filename );
398 image->baseaddr = baseaddr;
399 return( -1 );
400 }
401
402 return( 0 );
403 }
404
405