]>
git.saurik.com Git - apt.git/blob - apt-pkg/contrib/mmap.cc
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 /*{{{*/
22 #include <apt-pkg/mmap.h>
23 #include <apt-pkg/error.h>
36 // MMap::MMap - Constructor /*{{{*/
37 // ---------------------------------------------------------------------
39 MMap::MMap(FileFd
&F
,unsigned long Flags
) : Flags(Flags
), iSize(0),
40 Base(0), SyncToFd(NULL
)
42 if ((Flags
& NoImmMap
) != NoImmMap
)
46 // MMap::MMap - Constructor /*{{{*/
47 // ---------------------------------------------------------------------
49 MMap::MMap(unsigned long Flags
) : Flags(Flags
), iSize(0),
50 Base(0), SyncToFd(NULL
)
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)
84 if (errno
== ENODEV
|| errno
== EINVAL
)
86 // The filesystem doesn't support this particular kind of mmap.
87 // So we allocate a buffer and read the whole file into it.
88 int const dupped_fd
= dup(Fd
.Fd());
90 return _error
->Errno("mmap", _("Couldn't duplicate file descriptor %i"), Fd
.Fd());
92 Base
= new unsigned char[iSize
];
93 SyncToFd
= new FileFd (dupped_fd
);
94 if (!SyncToFd
->Seek(0L) || !SyncToFd
->Read(Base
, iSize
))
98 return _error
->Errno("mmap",_("Couldn't make mmap of %lu bytes"),
105 // MMap::Close - Close the map /*{{{*/
106 // ---------------------------------------------------------------------
108 bool MMap::Close(bool DoSync
)
110 if ((Flags
& UnMapped
) == UnMapped
|| validData() == false || iSize
== 0)
116 if (SyncToFd
!= NULL
)
118 delete[] (char *)Base
;
124 if (munmap((char *)Base
, iSize
) != 0)
125 _error
->WarningE("mmap", _("Unable to close mmap"));
133 // MMap::Sync - Syncronize the map with the disk /*{{{*/
134 // ---------------------------------------------------------------------
135 /* This is done in syncronous mode - the docs indicate that this will
136 not return till all IO is complete */
139 if ((Flags
& UnMapped
) == UnMapped
)
142 #ifdef _POSIX_SYNCHRONIZED_IO
143 if ((Flags
& ReadOnly
) != ReadOnly
)
145 if (SyncToFd
!= NULL
)
147 if (!SyncToFd
->Seek(0) || !SyncToFd
->Write(Base
, iSize
))
152 if (msync((char *)Base
, iSize
, MS_SYNC
) < 0)
153 return _error
->Errno("msync", _("Unable to synchronize mmap"));
160 // MMap::Sync - Syncronize a section of the file to disk /*{{{*/
161 // ---------------------------------------------------------------------
163 bool MMap::Sync(unsigned long Start
,unsigned long Stop
)
165 if ((Flags
& UnMapped
) == UnMapped
)
168 #ifdef _POSIX_SYNCHRONIZED_IO
169 unsigned long PSize
= sysconf(_SC_PAGESIZE
);
170 if ((Flags
& ReadOnly
) != ReadOnly
)
174 if (!SyncToFd
->Seek(0) ||
175 !SyncToFd
->Write (((char *)Base
)+Start
, Stop
-Start
))
180 if (msync((char *)Base
+(int)(Start
/PSize
)*PSize
,Stop
- Start
,MS_SYNC
) < 0)
181 return _error
->Errno("msync", _("Unable to synchronize mmap"));
189 // DynamicMMap::DynamicMMap - Constructor /*{{{*/
190 // ---------------------------------------------------------------------
192 DynamicMMap::DynamicMMap(FileFd
&F
,unsigned long Flags
,unsigned long const &Workspace
,
193 unsigned long const &Grow
, unsigned long const &Limit
) :
194 MMap(F
,Flags
| NoImmMap
), Fd(&F
), WorkSpace(Workspace
),
195 GrowFactor(Grow
), Limit(Limit
)
197 if (_error
->PendingError() == true)
200 unsigned long EndOfFile
= Fd
->Size();
201 if (EndOfFile
> WorkSpace
)
202 WorkSpace
= EndOfFile
;
203 else if(WorkSpace
> 0)
205 Fd
->Seek(WorkSpace
- 1);
207 Fd
->Write(&C
,sizeof(C
));
214 // DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
215 // ---------------------------------------------------------------------
216 /* We try here to use mmap to reserve some space - this is much more
217 cooler than the fallback solution to simply allocate a char array
218 and could come in handy later than we are able to grow such an mmap */
219 DynamicMMap::DynamicMMap(unsigned long Flags
,unsigned long const &WorkSpace
,
220 unsigned long const &Grow
, unsigned long const &Limit
) :
221 MMap(Flags
| NoImmMap
| UnMapped
), Fd(0), WorkSpace(WorkSpace
),
222 GrowFactor(Grow
), Limit(Limit
)
224 if (_error
->PendingError() == true)
227 // disable Moveable if we don't grow
229 this->Flags
&= ~Moveable
;
232 // kfreebsd doesn't have mremap, so we use the fallback
233 if ((this->Flags
& Moveable
) == Moveable
)
234 this->Flags
|= Fallback
;
237 #ifdef _POSIX_MAPPED_FILES
238 if ((this->Flags
& Fallback
) != Fallback
) {
239 // Set the permissions.
240 int Prot
= PROT_READ
;
242 int Map
= MAP_PRIVATE
| MAP_ANONYMOUS
;
244 int Map
= MAP_PRIVATE
| MAP_ANON
;
246 if ((this->Flags
& ReadOnly
) != ReadOnly
)
248 if ((this->Flags
& Public
) == Public
)
250 Map
= MAP_SHARED
| MAP_ANONYMOUS
;
252 Map
= MAP_SHARED
| MAP_ANON
;
255 // use anonymous mmap() to get the memory
256 Base
= (unsigned char*) mmap(0, WorkSpace
, Prot
, Map
, -1, 0);
258 if(Base
== MAP_FAILED
)
259 _error
->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace
);
265 // fallback to a static allocated space
266 Base
= new unsigned char[WorkSpace
];
267 memset(Base
,0,WorkSpace
);
271 // DynamicMMap::~DynamicMMap - Destructor /*{{{*/
272 // ---------------------------------------------------------------------
273 /* We truncate the file to the size of the memory data set */
274 DynamicMMap::~DynamicMMap()
278 if (validData() == false)
280 #ifdef _POSIX_MAPPED_FILES
281 munmap(Base
, WorkSpace
);
283 delete [] (unsigned char *)Base
;
288 unsigned long EndOfFile
= iSize
;
291 if(ftruncate(Fd
->Fd(),EndOfFile
) < 0)
292 _error
->Errno("ftruncate", _("Failed to truncate file"));
295 // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
296 // ---------------------------------------------------------------------
297 /* This allocates a block of memory aligned to the given size */
298 unsigned long DynamicMMap::RawAllocate(unsigned long Size
,unsigned long Aln
)
300 unsigned long Result
= iSize
;
302 Result
+= Aln
- (iSize%Aln
);
304 iSize
= Result
+ Size
;
306 // try to grow the buffer
307 while(Result
+ Size
> WorkSpace
)
311 _error
->Fatal(_("Dynamic MMap ran out of room. Please increase the size "
312 "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace
);
319 // DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
320 // ---------------------------------------------------------------------
321 /* This allocates an Item of size ItemSize so that it is aligned to its
323 unsigned long DynamicMMap::Allocate(unsigned long ItemSize
)
325 // Look for a matching pool entry
328 for (I
= Pools
; I
!= Pools
+ PoolCount
; ++I
)
330 if (I
->ItemSize
== 0)
332 if (I
->ItemSize
== ItemSize
)
335 // No pool is allocated, use an unallocated one
336 if (I
== Pools
+ PoolCount
)
338 // Woops, we ran out, the calling code should allocate more.
341 _error
->Error("Ran out of allocation pools");
346 I
->ItemSize
= ItemSize
;
350 unsigned long Result
= 0;
351 // Out of space, allocate some more
354 const unsigned long size
= 20*1024;
355 I
->Count
= size
/ItemSize
;
356 Pool
* oldPools
= Pools
;
357 Result
= RawAllocate(size
,ItemSize
);
358 if (Pools
!= oldPools
)
359 I
+= Pools
- oldPools
;
361 // Does the allocation failed ?
362 if (Result
== 0 && _error
->PendingError())
370 I
->Start
+= ItemSize
;
371 return Result
/ItemSize
;
374 // DynamicMMap::WriteString - Write a string to the file /*{{{*/
375 // ---------------------------------------------------------------------
376 /* Strings are not aligned to anything */
377 unsigned long DynamicMMap::WriteString(const char *String
,
380 if (Len
== (unsigned long)-1)
381 Len
= strlen(String
);
383 unsigned long const Result
= RawAllocate(Len
+1,0);
385 if (Result
== 0 && _error
->PendingError())
388 memcpy((char *)Base
+ Result
,String
,Len
);
389 ((char *)Base
)[Result
+ Len
] = 0;
393 // DynamicMMap::Grow - Grow the mmap /*{{{*/
394 // ---------------------------------------------------------------------
395 /* This method is a wrapper around different methods to (try to) grow
396 a mmap (or our char[]-fallback). Encounterable environments:
397 1. Moveable + !Fallback + linux -> mremap with MREMAP_MAYMOVE
398 2. Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
399 3. Moveable + Fallback -> realloc
400 4. !Moveable + !Fallback + linux -> mremap alone - which will fail in 99,9%
401 5. !Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
402 6. !Moveable + Fallback -> not possible
403 [ While Moveable and Fallback stands for the equally named flags and
404 "linux" indicates a linux kernel instead of a freebsd kernel. ]
405 So what you can see here is, that a MMAP which want to be growable need
406 to be moveable to have a real chance but that this method will at least try
407 the nearly impossible 4 to grow it before it finally give up: Never say never. */
408 bool DynamicMMap::Grow() {
409 if (Limit
!= 0 && WorkSpace
>= Limit
)
410 return _error
->Error(_("Unable to increase the size of the MMap as the "
411 "limit of %lu bytes is already reached."), Limit
);
413 return _error
->Error(_("Unable to increase size of the MMap as automatic growing is disabled by user."));
415 unsigned long const newSize
= WorkSpace
+ GrowFactor
;
418 Fd
->Seek(newSize
- 1);
420 Fd
->Write(&C
,sizeof(C
));
423 unsigned long const poolOffset
= Pools
- ((Pool
*) Base
);
425 if ((Flags
& Fallback
) != Fallback
) {
426 #if defined(_POSIX_MAPPED_FILES) && defined(__linux__)
427 #ifdef MREMAP_MAYMOVE
429 if ((Flags
& Moveable
) == Moveable
)
430 Base
= mremap(Base
, WorkSpace
, newSize
, MREMAP_MAYMOVE
);
433 Base
= mremap(Base
, WorkSpace
, newSize
, 0);
435 if(Base
== MAP_FAILED
)
441 if ((Flags
& Moveable
) != Moveable
)
444 Base
= realloc(Base
, newSize
);
449 Pools
=(Pool
*) Base
+ poolOffset
;