]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/mmap.cc
The 'not dead yet' release
[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 #else
182 // fallback to a static allocated space
183 Base = new unsigned char[WorkSpace];
184 memset(Base,0,WorkSpace);
185 #endif
186 iSize = 0;
187 }
188 /*}}}*/
189 // DynamicMMap::~DynamicMMap - Destructor /*{{{*/
190 // ---------------------------------------------------------------------
191 /* We truncate the file to the size of the memory data set */
192 DynamicMMap::~DynamicMMap()
193 {
194 if (Fd == 0)
195 {
196 #ifdef _POSIX_MAPPED_FILES
197 if(munmap(Base, WorkSpace) < 0)
198 #else
199 delete [] (unsigned char *)Base;
200 #endif
201 return;
202 }
203
204 unsigned long EndOfFile = iSize;
205 iSize = WorkSpace;
206 Close(false);
207 if(ftruncate(Fd->Fd(),EndOfFile) < 0)
208 _error->Errno("ftruncate", _("Failed to truncate file"));
209 }
210 /*}}}*/
211 // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
212 // ---------------------------------------------------------------------
213 /* This allocates a block of memory aligned to the given size */
214 unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln)
215 {
216 unsigned long Result = iSize;
217 if (Aln != 0)
218 Result += Aln - (iSize%Aln);
219
220 iSize = Result + Size;
221
222 // try to grow the buffer
223 while(Result + Size > WorkSpace)
224 {
225 if(!Grow())
226 {
227 _error->Error(_("Dynamic MMap ran out of room. Please increase the size "
228 "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace);
229 return 0;
230 }
231 }
232 return Result;
233 }
234 /*}}}*/
235 // DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
236 // ---------------------------------------------------------------------
237 /* This allocates an Item of size ItemSize so that it is aligned to its
238 size in the file. */
239 unsigned long DynamicMMap::Allocate(unsigned long ItemSize)
240 {
241 // Look for a matching pool entry
242 Pool *I;
243 Pool *Empty = 0;
244 for (I = Pools; I != Pools + PoolCount; I++)
245 {
246 if (I->ItemSize == 0)
247 Empty = I;
248 if (I->ItemSize == ItemSize)
249 break;
250 }
251 // No pool is allocated, use an unallocated one
252 if (I == Pools + PoolCount)
253 {
254 // Woops, we ran out, the calling code should allocate more.
255 if (Empty == 0)
256 {
257 _error->Error("Ran out of allocation pools");
258 return 0;
259 }
260
261 I = Empty;
262 I->ItemSize = ItemSize;
263 I->Count = 0;
264 }
265
266 unsigned long Result = 0;
267 // Out of space, allocate some more
268 if (I->Count == 0)
269 {
270 const unsigned long size = 20*1024;
271 I->Count = size/ItemSize;
272 Result = RawAllocate(size,ItemSize);
273 // Does the allocation failed ?
274 if (Result == 0 && _error->PendingError())
275 return 0;
276 I->Start = Result;
277 }
278 else
279 Result = I->Start;
280
281 I->Count--;
282 I->Start += ItemSize;
283 return Result/ItemSize;
284 }
285 /*}}}*/
286 // DynamicMMap::WriteString - Write a string to the file /*{{{*/
287 // ---------------------------------------------------------------------
288 /* Strings are not aligned to anything */
289 unsigned long DynamicMMap::WriteString(const char *String,
290 unsigned long Len)
291 {
292 if (Len == (unsigned long)-1)
293 Len = strlen(String);
294
295 unsigned long Result = RawAllocate(Len+1,0);
296
297 if (Result == 0 && _error->PendingError())
298 return 0;
299
300 memcpy((char *)Base + Result,String,Len);
301 ((char *)Base)[Result + Len] = 0;
302 return Result;
303 }
304 /*}}}*/
305 // DynamicMMap::Grow - Grow the mmap /*{{{*/
306 // ---------------------------------------------------------------------
307 /* This method will try to grow the mmap we currently use. This doesn't
308 work most of the time because we can't move the mmap around in the
309 memory for now as this would require to adjust quite a lot of pointers
310 but why we should not at least try to grow it before we give up? */
311 bool DynamicMMap::Grow()
312 {
313 #ifdef _POSIX_MAPPED_FILES
314 unsigned long newSize = WorkSpace + 1024*1024;
315
316 if(Fd != 0)
317 {
318 Fd->Seek(newSize - 1);
319 char C = 0;
320 Fd->Write(&C,sizeof(C));
321 }
322
323 Base = mremap(Base, WorkSpace, newSize, 0);
324 if(Base == MAP_FAILED)
325 return false;
326
327 WorkSpace = newSize;
328 return true;
329 #else
330 return false;
331 #endif
332 }
333 /*}}}*/