merge from the donkult branch
[ntk/apt.git] / apt-pkg / contrib / mmap.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
0db4a45b 3// $Id: mmap.cc,v 1.22 2001/05/27 05:19:30 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 MMap Class - Provides 'real' mmap or a faked mmap using read().
7
8 MMap cover class.
9
10 Some broken versions of glibc2 (libc6) have a broken definition
11 of mmap that accepts a char * -- all other systems (and libc5) use
12 void *. We can't safely do anything here that would be portable, so
13 libc6 generates warnings -- which should be errors, g++ isn't properly
14 strict.
15
578bfd0a
AL
16 ##################################################################### */
17 /*}}}*/
18// Include Files /*{{{*/
19#define _BSD_SOURCE
094a497d
AL
20#include <apt-pkg/mmap.h>
21#include <apt-pkg/error.h>
578bfd0a 22
b2e465d6
AL
23#include <apti18n.h>
24
578bfd0a
AL
25#include <sys/mman.h>
26#include <sys/stat.h>
578bfd0a
AL
27#include <unistd.h>
28#include <fcntl.h>
13eb93fc 29#include <stdlib.h>
4f333a8b
MV
30
31#include <cstring>
578bfd0a
AL
32 /*}}}*/
33
34// MMap::MMap - Constructor /*{{{*/
35// ---------------------------------------------------------------------
36/* */
2d11135a 37MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0),
578bfd0a
AL
38 Base(0)
39{
40 if ((Flags & NoImmMap) != NoImmMap)
2d11135a
AL
41 Map(F);
42}
43 /*}}}*/
44// MMap::MMap - Constructor /*{{{*/
45// ---------------------------------------------------------------------
46/* */
47MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0),
48 Base(0)
49{
578bfd0a
AL
50}
51 /*}}}*/
52// MMap::~MMap - Destructor /*{{{*/
53// ---------------------------------------------------------------------
54/* */
55MMap::~MMap()
56{
2d11135a 57 Close();
578bfd0a
AL
58}
59 /*}}}*/
60// MMap::Map - Perform the mapping /*{{{*/
61// ---------------------------------------------------------------------
62/* */
2d11135a 63bool MMap::Map(FileFd &Fd)
578bfd0a
AL
64{
65 iSize = Fd.Size();
66
67 // Set the permissions.
68 int Prot = PROT_READ;
69 int Map = MAP_SHARED;
70 if ((Flags & ReadOnly) != ReadOnly)
71 Prot |= PROT_WRITE;
72 if ((Flags & Public) != Public)
73 Map = MAP_PRIVATE;
74
b35d2f5f 75 if (iSize == 0)
b2e465d6 76 return _error->Error(_("Can't mmap an empty file"));
b35d2f5f 77
578bfd0a
AL
78 // Map it.
79 Base = mmap(0,iSize,Prot,Map,Fd.Fd(),0);
80 if (Base == (void *)-1)
b2e465d6 81 return _error->Errno("mmap",_("Couldn't make mmap of %lu bytes"),iSize);
578bfd0a
AL
82
83 return true;
84}
85 /*}}}*/
86// MMap::Close - Close the map /*{{{*/
87// ---------------------------------------------------------------------
88/* */
2d11135a 89bool MMap::Close(bool DoSync)
578bfd0a 90{
2d11135a 91 if ((Flags & UnMapped) == UnMapped || Base == 0 || iSize == 0)
578bfd0a 92 return true;
2d11135a 93
1164783d
AL
94 if (DoSync == true)
95 Sync();
578bfd0a
AL
96
97 if (munmap((char *)Base,iSize) != 0)
98 _error->Warning("Unable to munmap");
99
100 iSize = 0;
b2e465d6 101 Base = 0;
578bfd0a
AL
102 return true;
103}
104 /*}}}*/
105// MMap::Sync - Syncronize the map with the disk /*{{{*/
106// ---------------------------------------------------------------------
0149949b
AL
107/* This is done in syncronous mode - the docs indicate that this will
108 not return till all IO is complete */
578bfd0a
AL
109bool MMap::Sync()
110{
2d11135a
AL
111 if ((Flags & UnMapped) == UnMapped)
112 return true;
113
de3c15ea 114#ifdef _POSIX_SYNCHRONIZED_IO
1164783d 115 if ((Flags & ReadOnly) != ReadOnly)
e9fce64b 116 if (msync((char *)Base,iSize,MS_SYNC) < 0)
4b1b89c5 117 return _error->Errno("msync","Unable to write mmap");
de3c15ea 118#endif
578bfd0a
AL
119 return true;
120}
121 /*}}}*/
122// MMap::Sync - Syncronize a section of the file to disk /*{{{*/
123// ---------------------------------------------------------------------
124/* */
125bool MMap::Sync(unsigned long Start,unsigned long Stop)
126{
2d11135a
AL
127 if ((Flags & UnMapped) == UnMapped)
128 return true;
129
35c22def
AL
130#ifdef _POSIX_SYNCHRONIZED_IO
131 unsigned long PSize = sysconf(_SC_PAGESIZE);
1164783d 132 if ((Flags & ReadOnly) != ReadOnly)
e9fce64b 133 if (msync((char *)Base+(int)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0)
4b1b89c5 134 return _error->Errno("msync","Unable to write mmap");
de3c15ea 135#endif
578bfd0a
AL
136 return true;
137}
138 /*}}}*/
139
140// DynamicMMap::DynamicMMap - Constructor /*{{{*/
141// ---------------------------------------------------------------------
142/* */
c5f44afc 143DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long WorkSpace) :
2d11135a 144 MMap(F,Flags | NoImmMap), Fd(&F), WorkSpace(WorkSpace)
578bfd0a 145{
d38b7b3d
AL
146 if (_error->PendingError() == true)
147 return;
148
2d11135a 149 unsigned long EndOfFile = Fd->Size();
b2e465d6
AL
150 if (EndOfFile > WorkSpace)
151 WorkSpace = EndOfFile;
ea92d036 152 else if(WorkSpace > 0)
b2e465d6 153 {
ea92d036 154 Fd->Seek(WorkSpace - 1);
b2e465d6
AL
155 char C = 0;
156 Fd->Write(&C,sizeof(C));
157 }
158
2d11135a 159 Map(F);
578bfd0a
AL
160 iSize = EndOfFile;
161}
162 /*}}}*/
2d11135a
AL
163// DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
164// ---------------------------------------------------------------------
f1c6a8ca
DK
165/* We try here to use mmap to reserve some space - this is much more
166 cooler than the fallback solution to simply allocate a char array
167 and could come in handy later than we are able to grow such an mmap */
2d11135a
AL
168DynamicMMap::DynamicMMap(unsigned long Flags,unsigned long WorkSpace) :
169 MMap(Flags | NoImmMap | UnMapped), Fd(0), WorkSpace(WorkSpace)
170{
171 if (_error->PendingError() == true)
172 return;
f1c6a8ca
DK
173
174#ifdef _POSIX_MAPPED_FILES
eb162ff7
DK
175 // Set the permissions.
176 int Prot = PROT_READ;
177 int Map = MAP_PRIVATE | MAP_ANONYMOUS;
178 if ((Flags & ReadOnly) != ReadOnly)
179 Prot |= PROT_WRITE;
180 if ((Flags & Public) == Public)
181 Map = MAP_SHARED | MAP_ANONYMOUS;
182
f1c6a8ca 183 // use anonymous mmap() to get the memory
eb162ff7
DK
184 Base = (unsigned char*) mmap(0, WorkSpace, Prot, Map, -1, 0);
185
d5972534 186 if(Base == MAP_FAILED)
eb162ff7 187 _error->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace);
d5972534 188#else
f1c6a8ca 189 // fallback to a static allocated space
2d11135a 190 Base = new unsigned char[WorkSpace];
0db4a45b 191 memset(Base,0,WorkSpace);
d5972534 192#endif
2d11135a
AL
193 iSize = 0;
194}
195 /*}}}*/
578bfd0a
AL
196// DynamicMMap::~DynamicMMap - Destructor /*{{{*/
197// ---------------------------------------------------------------------
198/* We truncate the file to the size of the memory data set */
199DynamicMMap::~DynamicMMap()
200{
2d11135a
AL
201 if (Fd == 0)
202 {
f1c6a8ca 203#ifdef _POSIX_MAPPED_FILES
d5972534 204 if(munmap(Base, WorkSpace) < 0)
f1c6a8ca 205#else
2d11135a 206 delete [] (unsigned char *)Base;
f1c6a8ca 207#endif
2d11135a
AL
208 return;
209 }
210
578bfd0a 211 unsigned long EndOfFile = iSize;
1164783d 212 iSize = WorkSpace;
2d11135a 213 Close(false);
3c8cda8b
MV
214 if(ftruncate(Fd->Fd(),EndOfFile) < 0)
215 _error->Errno("ftruncate", _("Failed to truncate file"));
578bfd0a
AL
216}
217 /*}}}*/
218// DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
219// ---------------------------------------------------------------------
f55a958f
AL
220/* This allocates a block of memory aligned to the given size */
221unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln)
578bfd0a
AL
222{
223 unsigned long Result = iSize;
f55a958f
AL
224 if (Aln != 0)
225 Result += Aln - (iSize%Aln);
c5f44afc 226
f55a958f 227 iSize = Result + Size;
c5f44afc 228
f1c6a8ca
DK
229 // try to grow the buffer
230 while(Result + Size > WorkSpace)
578bfd0a 231 {
f1c6a8ca
DK
232 if(!Grow())
233 {
234 _error->Error(_("Dynamic MMap ran out of room. Please increase the size "
235 "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace);
236 return 0;
237 }
578bfd0a 238 }
578bfd0a
AL
239 return Result;
240}
241 /*}}}*/
242// DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
243// ---------------------------------------------------------------------
244/* This allocates an Item of size ItemSize so that it is aligned to its
245 size in the file. */
246unsigned long DynamicMMap::Allocate(unsigned long ItemSize)
c5f44afc 247{
578bfd0a
AL
248 // Look for a matching pool entry
249 Pool *I;
250 Pool *Empty = 0;
251 for (I = Pools; I != Pools + PoolCount; I++)
252 {
253 if (I->ItemSize == 0)
254 Empty = I;
255 if (I->ItemSize == ItemSize)
256 break;
257 }
578bfd0a
AL
258 // No pool is allocated, use an unallocated one
259 if (I == Pools + PoolCount)
260 {
261 // Woops, we ran out, the calling code should allocate more.
262 if (Empty == 0)
263 {
264 _error->Error("Ran out of allocation pools");
265 return 0;
266 }
267
268 I = Empty;
269 I->ItemSize = ItemSize;
270 I->Count = 0;
271 }
c5f44afc
DK
272
273 unsigned long Result = 0;
578bfd0a
AL
274 // Out of space, allocate some more
275 if (I->Count == 0)
276 {
c5f44afc
DK
277 const unsigned long size = 20*1024;
278 I->Count = size/ItemSize;
279 Result = RawAllocate(size,ItemSize);
280 // Does the allocation failed ?
281 if (Result == 0 && _error->PendingError())
282 return 0;
283 I->Start = Result;
284 }
285 else
286 Result = I->Start;
f55a958f 287
578bfd0a 288 I->Count--;
c5f44afc 289 I->Start += ItemSize;
578bfd0a
AL
290 return Result/ItemSize;
291}
292 /*}}}*/
293// DynamicMMap::WriteString - Write a string to the file /*{{{*/
294// ---------------------------------------------------------------------
295/* Strings are not aligned to anything */
296unsigned long DynamicMMap::WriteString(const char *String,
297 unsigned long Len)
298{
6e52073f 299 if (Len == (unsigned long)-1)
578bfd0a 300 Len = strlen(String);
c5f44afc
DK
301
302 unsigned long Result = RawAllocate(Len+1,0);
303
304 if (Result == 0 && _error->PendingError())
305 return 0;
306
578bfd0a
AL
307 memcpy((char *)Base + Result,String,Len);
308 ((char *)Base)[Result + Len] = 0;
309 return Result;
310}
311 /*}}}*/
f1c6a8ca
DK
312// DynamicMMap::Grow - Grow the mmap /*{{{*/
313// ---------------------------------------------------------------------
314/* This method will try to grow the mmap we currently use. This doesn't
315 work most of the time because we can't move the mmap around in the
316 memory for now as this would require to adjust quite a lot of pointers
317 but why we should not at least try to grow it before we give up? */
318bool DynamicMMap::Grow()
319{
320#ifdef _POSIX_MAPPED_FILES
321 unsigned long newSize = WorkSpace + 1024*1024;
322
323 if(Fd != 0)
324 {
325 Fd->Seek(newSize - 1);
326 char C = 0;
327 Fd->Write(&C,sizeof(C));
328 }
329
330 Base = mremap(Base, WorkSpace, newSize, 0);
331 if(Base == MAP_FAILED)
332 return false;
333
334 WorkSpace = newSize;
335 return true;
336#else
337 return false;
338#endif
339}
340 /*}}}*/