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