1 /* This file is part of GDBM.
2 Copyright (C) 2007, 2011, 2013 Free Software Foundation, Inc.
4 GDBM is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
9 GDBM is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
21 # include "gdbmdefs.h"
23 # include <sys/types.h>
24 # include <sys/time.h>
25 # include <sys/file.h>
26 # include <sys/stat.h>
27 # include <sys/mman.h>
30 /* Some systems fail to define this */
32 # define MAP_FAILED ((void*)-1)
35 /* Translate current offset in the mapped region into the absolute position */
36 # define _GDBM_MMAPPED_POS(dbf) ((dbf)->mapped_off + (dbf)->mapped_pos)
37 /* Return true if the absolute offset OFF lies within the currentlty mmapped
39 # define _GDBM_IN_MAPPED_REGION_P(dbf, off) \
40 ((off) >= (dbf)->mapped_off \
41 && ((off) - (dbf)->mapped_off) < (dbf)->mapped_size)
42 /* Return true if the current region needs to be remapped */
43 # define _GDBM_NEED_REMAP(dbf) \
44 (!(dbf)->mapped_region || (dbf)->mapped_pos == (dbf)->mapped_size)
45 /* Return the sum of the currently mapped size and DELTA */
46 # define SUM_FILE_SIZE(dbf, delta) \
47 ((dbf)->mapped_off + (dbf)->mapped_size + (delta))
49 /* Store the size of the GDBM file DBF in *PSIZE.
50 Return 0 on success and -1 on failure. */
52 _gdbm_file_size (GDBM_FILE dbf, off_t *psize)
55 if (fstat (dbf->desc, &sb))
61 /* Unmap the region. Reset all mapped fields to initial values. */
63 _gdbm_mapped_unmap (GDBM_FILE dbf)
65 if (dbf->mapped_region)
67 munmap (dbf->mapped_region, dbf->mapped_size);
68 dbf->mapped_region = NULL;
75 /* Remap the DBF file according to dbf->{mapped_off,mapped_pos,mapped_size}.
76 Take care to recompute {mapped_off,mapped_pos} so that the former lies
77 on a page size boundary. */
79 _gdbm_internal_remap (GDBM_FILE dbf, size_t size)
82 int flags = PROT_READ;
83 size_t page_size = sysconf (_SC_PAGESIZE);
85 munmap (dbf->mapped_region, dbf->mapped_size);
86 dbf->mapped_size = size;
91 dbf->mapped_pos += dbf->mapped_off % page_size;
92 dbf->mapped_off = (dbf->mapped_off / page_size) * page_size;
97 p = mmap (NULL, dbf->mapped_size, flags, MAP_SHARED,
98 dbf->desc, dbf->mapped_off);
101 dbf->mapped_region = NULL;
102 gdbm_errno = GDBM_MALLOC_ERROR;
106 dbf->mapped_region = p;
110 # define _REMAP_DEFAULT 0
111 # define _REMAP_EXTEND 1
112 # define _REMAP_END 2
114 /* Remap the GDBM file so that its mapped region ends on SIZEth byte.
115 If the file is opened with write permissions, FLAG controls how
116 it is expanded. The value _REMAP_DEFAULT truncates SIZE to the
117 actual file size. The value _REMAP_EXTEND extends the file, if
118 necessary, to accomodate max(SIZE,dbf->header->next_block) bytes.
119 Finally, the value _REMAP_END instructs the function to use
120 max(SIZE, file_size) as the upper bound of the mapped region.
122 If the file is opened read-only, FLAG is ignored and SIZE is
123 truncated to the actual file size.
125 The upper bound obtained that way is used as a *hint* to select
126 the actual size of the mapped region. which can never exceed
127 dbf->mapped_size_max.
129 The function returns 0 on success, -1 on failure. */
131 _gdbm_mapped_remap (GDBM_FILE dbf, off_t size, int flag)
133 off_t file_size, pos;
135 if (_gdbm_file_size (dbf, &file_size))
137 SAVE_ERRNO (_gdbm_mapped_unmap (dbf));
138 gdbm_errno = GDBM_FILE_STAT_ERROR;
142 if (flag == _REMAP_END && size < file_size)
147 if (size > file_size)
149 if (flag != _REMAP_DEFAULT)
153 if (size < dbf->header->next_block)
154 size = dbf->header->next_block;
155 lseek (dbf->desc, size - 1, SEEK_SET);
156 write (dbf->desc, &c, 1);
168 if (size > file_size)
171 if (size == SUM_FILE_SIZE (dbf, 0))
175 pos = _GDBM_MMAPPED_POS (dbf);
176 if (size > dbf->mapped_size_max)
178 dbf->mapped_off = pos;
180 size = dbf->mapped_size_max;
181 if (dbf->mapped_off + size > file_size)
182 size = file_size - dbf->mapped_off;
186 dbf->mapped_pos += dbf->mapped_off;
190 return _gdbm_internal_remap (dbf, size);
193 /* Initialize mapping system. If the file size is less than MAPPED_SIZE_MAX,
194 map the entire file into the memory. Otherwise, map first MAPPED_SIZE_MAX
197 _gdbm_mapped_init (GDBM_FILE dbf)
199 if (dbf->mapped_size_max == 0)
200 dbf->mapped_size_max = SIZE_T_MAX;
201 return _gdbm_mapped_remap (dbf, 0, _REMAP_END);
204 /* Read LEN bytes from the GDBM file DBF into BUFFER. If mmapping is
205 not initialized or if it fails, fall back to the classical read(1).
206 Return number of bytes read or -1 on failure. */
208 _gdbm_mapped_read (GDBM_FILE dbf, void *buffer, size_t len)
210 if (dbf->memory_mapping)
219 if (_GDBM_NEED_REMAP (dbf))
221 off_t pos = _GDBM_MMAPPED_POS (dbf);
222 if (_gdbm_mapped_remap (dbf, SUM_FILE_SIZE (dbf, len),
227 dbf->memory_mapping = FALSE;
228 if (lseek (dbf->desc, pos, SEEK_SET) != pos)
229 return total > 0 ? total : -1;
230 rc = read (dbf->desc, cbuf, len);
232 return total > 0 ? total : -1;
237 nbytes = dbf->mapped_size - dbf->mapped_pos;
243 memcpy (cbuf, (char*) dbf->mapped_region + dbf->mapped_pos, nbytes);
245 dbf->mapped_pos += nbytes;
251 return read (dbf->desc, buffer, len);
254 /* Write LEN bytes from BUFFER to the GDBM file DBF. If mmapping is
255 not initialized or if it fails, fall back to the classical write(1).
256 Return number of bytes written or -1 on failure. */
258 _gdbm_mapped_write (GDBM_FILE dbf, void *buffer, size_t len)
260 if (dbf->memory_mapping)
269 if (_GDBM_NEED_REMAP (dbf))
271 off_t pos = _GDBM_MMAPPED_POS (dbf);
272 if (_gdbm_mapped_remap (dbf, SUM_FILE_SIZE (dbf, len),
277 dbf->memory_mapping = FALSE;
278 if (lseek (dbf->desc, pos, SEEK_SET) != pos)
279 return total > 0 ? total : -1;
280 rc = write (dbf->desc, cbuf, len);
282 return total > 0 ? total : -1;
287 nbytes = dbf->mapped_size - dbf->mapped_pos;
293 memcpy ((char*) dbf->mapped_region + dbf->mapped_pos, cbuf, nbytes);
295 dbf->mapped_pos += nbytes;
301 return write (dbf->desc, buffer, len);
304 /* Seek to the offset OFFSET in the GDBM file DBF, according to the
305 lseek(1)-style directive WHENCE. Return the resulting absolute
306 offset or -1 in case of failure. If mmapping is not initialized or
307 if it fails, fall back to the classical lseek(1).
309 Return number of bytes written or -1 on failure. */
312 _gdbm_mapped_lseek (GDBM_FILE dbf, off_t offset, int whence)
314 if (dbf->memory_mapping)
325 needle = offset + _GDBM_MMAPPED_POS (dbf);
331 if (_gdbm_file_size (dbf, &file_size))
333 gdbm_errno = GDBM_FILE_STAT_ERROR;
336 needle = file_size - offset;
341 if (!_GDBM_IN_MAPPED_REGION_P (dbf, needle))
343 _gdbm_mapped_unmap (dbf);
344 dbf->mapped_off = needle;
348 dbf->mapped_pos = needle - dbf->mapped_off;
351 return lseek (dbf->desc, offset, whence);
354 /* Sync the mapped region to disk. */
356 _gdbm_mapped_sync (GDBM_FILE dbf)
358 if (dbf->mapped_region)
360 return msync (dbf->mapped_region, dbf->mapped_size,
361 MS_SYNC | MS_INVALIDATE);
363 return fsync (dbf->desc);