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