1 // -*- mode: cpp; mode: fold -*-
3 // $Id: mmap.cc,v 1.22 2001/05/27 05:19:30 jgg Exp $
4 /* ######################################################################
6 MMap Class - Provides 'real' mmap or a faked mmap using read().
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
16 ##################################################################### */
18 // Include Files /*{{{*/
20 #include <apt-pkg/mmap.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/configuration.h>
36 // MMap::MMap - Constructor /*{{{*/
37 // ---------------------------------------------------------------------
39 MMap::MMap(FileFd
&F
,unsigned long Flags
) : Flags(Flags
), iSize(0),
42 if ((Flags
& NoImmMap
) != NoImmMap
)
46 // MMap::MMap - Constructor /*{{{*/
47 // ---------------------------------------------------------------------
49 MMap::MMap(unsigned long Flags
) : Flags(Flags
), iSize(0),
54 // MMap::~MMap - Destructor /*{{{*/
55 // ---------------------------------------------------------------------
62 // MMap::Map - Perform the mapping /*{{{*/
63 // ---------------------------------------------------------------------
65 bool MMap::Map(FileFd
&Fd
)
69 // Set the permissions.
72 if ((Flags
& ReadOnly
) != ReadOnly
)
74 if ((Flags
& Public
) != Public
)
78 return _error
->Error(_("Can't mmap an empty file"));
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
);
88 // MMap::Close - Close the map /*{{{*/
89 // ---------------------------------------------------------------------
91 bool MMap::Close(bool DoSync
)
93 if ((Flags
& UnMapped
) == UnMapped
|| Base
== 0 || iSize
== 0)
99 if (munmap((char *)Base
,iSize
) != 0)
100 _error
->Warning("Unable to munmap");
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 */
113 if ((Flags
& UnMapped
) == UnMapped
)
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");
124 // MMap::Sync - Syncronize a section of the file to disk /*{{{*/
125 // ---------------------------------------------------------------------
127 bool MMap::Sync(unsigned long Start
,unsigned long Stop
)
129 if ((Flags
& UnMapped
) == UnMapped
)
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");
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)
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();
156 // DynamicMMap::DynamicMMap - Constructor /*{{{*/
157 // ---------------------------------------------------------------------
159 DynamicMMap::DynamicMMap(FileFd
&F
,unsigned long Flags
,unsigned long WorkSpace
) :
160 MMap(F
,Flags
| NoImmMap
), Fd(&F
), WorkSpace(WorkSpace
)
162 if (_error
->PendingError() == true)
165 unsigned long EndOfFile
= Fd
->Size();
166 if (EndOfFile
> WorkSpace
)
167 WorkSpace
= EndOfFile
;
168 else if(WorkSpace
> 0)
170 Fd
->Seek(WorkSpace
- 1);
172 Fd
->Write(&C
,sizeof(C
));
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
)
187 if (_error
->PendingError() == true)
190 if (_config
->FindB("MMap::SegfaultHandler",true) == true)
193 sa
.sa_handler
= DynamicMMapSegfaultHandler
;
194 sigaction(SIGSEGV
, &sa
, NULL
);
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
)
204 // fallback to a static allocated space
205 Base
= new unsigned char[WorkSpace
];
206 memset(Base
,0,WorkSpace
);
210 // DynamicMMap::~DynamicMMap - Destructor /*{{{*/
211 // ---------------------------------------------------------------------
212 /* We truncate the file to the size of the memory data set */
213 DynamicMMap::~DynamicMMap()
217 #ifdef _POSIX_MAPPED_FILES
218 munmap(Base
, WorkSpace
);
220 delete [] (unsigned char *)Base
;
225 unsigned long EndOfFile
= iSize
;
228 if(ftruncate(Fd
->Fd(),EndOfFile
) < 0)
229 _error
->Errno("ftruncate", _("Failed to truncate file"));
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
)
237 unsigned long Result
= iSize
;
239 Result
+= Aln
- (iSize%Aln
);
241 iSize
= Result
+ Size
;
243 // try to grow the buffer
244 while(Result
+ Size
> WorkSpace
)
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
);
256 // DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
257 // ---------------------------------------------------------------------
258 /* This allocates an Item of size ItemSize so that it is aligned to its
260 unsigned long DynamicMMap::Allocate(unsigned long ItemSize
)
262 // Look for a matching pool entry
265 for (I
= Pools
; I
!= Pools
+ PoolCount
; I
++)
267 if (I
->ItemSize
== 0)
269 if (I
->ItemSize
== ItemSize
)
272 // No pool is allocated, use an unallocated one
273 if (I
== Pools
+ PoolCount
)
275 // Woops, we ran out, the calling code should allocate more.
278 _error
->Error("Ran out of allocation pools");
283 I
->ItemSize
= ItemSize
;
287 // Out of space, allocate some more
290 I
->Count
= 20*1024/ItemSize
;
291 I
->Start
= RawAllocate(I
->Count
*ItemSize
,ItemSize
);
295 unsigned long Result
= I
->Start
;
296 I
->Start
+= ItemSize
;
297 return Result
/ItemSize
;
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
,
306 unsigned long Result
= iSize
;
307 // try to grow the buffer
308 while(Result
+ Len
> WorkSpace
)
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
);
318 if (Len
== (unsigned long)-1)
319 Len
= strlen(String
);
321 memcpy((char *)Base
+ Result
,String
,Len
);
322 ((char *)Base
)[Result
+ Len
] = 0;
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()
334 #ifdef _POSIX_MAPPED_FILES
335 unsigned long newSize
= WorkSpace
+ 1024*1024;
339 Fd
->Seek(newSize
- 1);
341 Fd
->Write(&C
,sizeof(C
));
344 Base
= mremap(Base
, WorkSpace
, newSize
, 0);
345 if(Base
== MAP_FAILED
)