]>
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 /*{{{*/
19 #define _DEFAULT_SOURCE
22 #include <apt-pkg/mmap.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/macros.h>
37 // MMap::MMap - Constructor /*{{{*/
38 // ---------------------------------------------------------------------
40 MMap::MMap(FileFd
&F
,unsigned long Flags
) : Flags(Flags
), iSize(0),
41 Base(nullptr), SyncToFd(nullptr)
46 // MMap::MMap - Constructor /*{{{*/
47 // ---------------------------------------------------------------------
49 MMap::MMap(unsigned long Flags
) : Flags(Flags
), iSize(0),
50 Base(nullptr), SyncToFd(nullptr)
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"));
80 // We can't mmap compressed fd's directly, so we need to read it completely
81 if (Fd
.IsCompressed() == true)
83 if ((Flags
& ReadOnly
) != ReadOnly
)
84 return _error
->Error("Compressed file %s can only be mapped readonly", Fd
.Name().c_str());
86 if (unlikely(Base
== nullptr))
87 return _error
->Errno("MMap-compressed-malloc", _("Couldn't make mmap of %llu bytes"), iSize
);
88 SyncToFd
= new FileFd();
89 if (Fd
.Seek(0L) == false || Fd
.Read(Base
, iSize
) == false)
90 return _error
->Error("Compressed file %s can't be read into mmap", Fd
.Name().c_str());
95 Base
= (Flags
& Fallback
) ? MAP_FAILED
: mmap(0,iSize
,Prot
,Map
,Fd
.Fd(),0);
96 if (Base
== MAP_FAILED
)
98 if (errno
== ENODEV
|| errno
== EINVAL
|| (Flags
& Fallback
))
100 // The filesystem doesn't support this particular kind of mmap.
101 // So we allocate a buffer and read the whole file into it.
102 if ((Flags
& ReadOnly
) == ReadOnly
)
104 // for readonly, we don't need sync, so make it simple
105 Base
= malloc(iSize
);
106 if (unlikely(Base
== nullptr))
107 return _error
->Errno("MMap-malloc", _("Couldn't make mmap of %llu bytes"), iSize
);
108 SyncToFd
= new FileFd();
109 return Fd
.Seek(0L) && Fd
.Read(Base
, iSize
);
111 // FIXME: Writing to compressed fd's ?
112 int const dupped_fd
= dup(Fd
.Fd());
114 return _error
->Errno("mmap", _("Couldn't duplicate file descriptor %i"), Fd
.Fd());
116 Base
= malloc(iSize
);
117 if (unlikely(Base
== nullptr))
118 return _error
->Errno("MMap-calloc", _("Couldn't make mmap of %llu bytes"), iSize
);
119 SyncToFd
= new FileFd (dupped_fd
);
120 if (!SyncToFd
->Seek(0L) || !SyncToFd
->Read(Base
, iSize
))
124 return _error
->Errno("MMap-mmap", _("Couldn't make mmap of %llu bytes"), iSize
);
130 // MMap::Close - Close the map /*{{{*/
131 // ---------------------------------------------------------------------
133 bool MMap::Close(bool DoSync
)
135 if ((Flags
& UnMapped
) == UnMapped
|| validData() == false || iSize
== 0)
141 if (SyncToFd
!= NULL
)
149 if (munmap((char *)Base
, iSize
) != 0)
150 _error
->WarningE("mmap", _("Unable to close mmap"));
158 // MMap::Sync - Syncronize the map with the disk /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This is done in syncronous mode - the docs indicate that this will
161 not return till all IO is complete */
164 if ((Flags
& UnMapped
) == UnMapped
)
167 if ((Flags
& ReadOnly
) != ReadOnly
)
169 if (SyncToFd
!= NULL
)
171 if (!SyncToFd
->Seek(0) || !SyncToFd
->Write(Base
, iSize
))
176 #ifdef _POSIX_SYNCHRONIZED_IO
177 if (msync((char *)Base
, iSize
, MS_SYNC
) < 0)
178 return _error
->Errno("msync", _("Unable to synchronize mmap"));
185 // MMap::Sync - Syncronize a section of the file to disk /*{{{*/
186 // ---------------------------------------------------------------------
188 bool MMap::Sync(unsigned long Start
,unsigned long Stop
)
190 if ((Flags
& UnMapped
) == UnMapped
)
193 if ((Flags
& ReadOnly
) != ReadOnly
)
197 if (!SyncToFd
->Seek(Start
) ||
198 !SyncToFd
->Write (((char *)Base
)+Start
, Stop
-Start
))
203 #ifdef _POSIX_SYNCHRONIZED_IO
204 unsigned long long const PSize
= sysconf(_SC_PAGESIZE
);
205 Start
= (Start
/PSize
)*PSize
;
206 if (msync((char *)Base
+Start
, Stop
- Start
, MS_SYNC
) < 0)
207 return _error
->Errno("msync", _("Unable to synchronize mmap"));
215 // DynamicMMap::DynamicMMap - Constructor /*{{{*/
216 // ---------------------------------------------------------------------
218 DynamicMMap::DynamicMMap(FileFd
&F
,unsigned long Flags
,unsigned long const &Workspace
,
219 unsigned long const &Grow
, unsigned long const &Limit
) :
220 MMap(Flags
), Fd(&F
), WorkSpace(Workspace
),
221 GrowFactor(Grow
), Limit(Limit
)
223 // disable Moveable if we don't grow
225 this->Flags
&= ~Moveable
;
228 // kfreebsd doesn't have mremap, so we use the fallback
229 if ((this->Flags
& Moveable
) == Moveable
)
230 this->Flags
|= Fallback
;
233 unsigned long long EndOfFile
= Fd
->Size();
234 if (EndOfFile
> WorkSpace
)
235 WorkSpace
= EndOfFile
;
236 else if(WorkSpace
> 0)
238 Fd
->Seek(WorkSpace
- 1);
240 Fd
->Write(&C
,sizeof(C
));
247 // DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
248 // ---------------------------------------------------------------------
249 /* We try here to use mmap to reserve some space - this is much more
250 cooler than the fallback solution to simply allocate a char array
251 and could come in handy later than we are able to grow such an mmap */
252 DynamicMMap::DynamicMMap(unsigned long Flags
,unsigned long const &WorkSpace
,
253 unsigned long const &Grow
, unsigned long const &Limit
) :
254 MMap(Flags
| UnMapped
), Fd(0), WorkSpace(WorkSpace
),
255 GrowFactor(Grow
), Limit(Limit
)
257 // disable Moveable if we don't grow
259 this->Flags
&= ~Moveable
;
262 // kfreebsd doesn't have mremap, so we use the fallback
263 if ((this->Flags
& Moveable
) == Moveable
)
264 this->Flags
|= Fallback
;
267 #ifdef _POSIX_MAPPED_FILES
268 if ((this->Flags
& Fallback
) != Fallback
) {
269 // Set the permissions.
270 int Prot
= PROT_READ
;
272 int Map
= MAP_PRIVATE
| MAP_ANONYMOUS
;
274 int Map
= MAP_PRIVATE
| MAP_ANON
;
276 if ((this->Flags
& ReadOnly
) != ReadOnly
)
278 if ((this->Flags
& Public
) == Public
)
280 Map
= MAP_SHARED
| MAP_ANONYMOUS
;
282 Map
= MAP_SHARED
| MAP_ANON
;
285 // use anonymous mmap() to get the memory
286 Base
= (unsigned char*) mmap(0, WorkSpace
, Prot
, Map
, -1, 0);
288 if(Base
== MAP_FAILED
)
289 _error
->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace
);
295 // fallback to a static allocated space
296 Base
= calloc(WorkSpace
, 1);
300 // DynamicMMap::~DynamicMMap - Destructor /*{{{*/
301 // ---------------------------------------------------------------------
302 /* We truncate the file to the size of the memory data set */
303 DynamicMMap::~DynamicMMap()
307 if (validData() == false)
309 #ifdef _POSIX_MAPPED_FILES
310 if ((Flags
& Fallback
) != Fallback
) {
311 munmap(Base
, WorkSpace
);
318 unsigned long long EndOfFile
= iSize
;
321 if(ftruncate(Fd
->Fd(),EndOfFile
) < 0)
322 _error
->Errno("ftruncate", _("Failed to truncate file"));
325 // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
326 // ---------------------------------------------------------------------
327 /* This allocates a block of memory aligned to the given size */
328 unsigned long DynamicMMap::RawAllocate(unsigned long long Size
,unsigned long Aln
)
330 unsigned long long Result
= iSize
;
332 Result
+= Aln
- (iSize%Aln
);
334 iSize
= Result
+ Size
;
336 // try to grow the buffer
337 while(Result
+ Size
> WorkSpace
)
341 _error
->Fatal(_("Dynamic MMap ran out of room. Please increase the size "
342 "of APT::Cache-Start. Current value: %lu. (man 5 apt.conf)"), WorkSpace
);
349 // DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
350 // ---------------------------------------------------------------------
351 /* This allocates an Item of size ItemSize so that it is aligned to its
353 unsigned long DynamicMMap::Allocate(unsigned long ItemSize
)
355 if (unlikely(ItemSize
== 0))
357 _error
->Fatal("Can't allocate an item of size zero");
361 // Look for a matching pool entry
364 for (I
= Pools
; I
!= Pools
+ PoolCount
; ++I
)
366 if (I
->ItemSize
== 0)
368 if (I
->ItemSize
== ItemSize
)
371 // No pool is allocated, use an unallocated one
372 if (I
== Pools
+ PoolCount
)
374 // Woops, we ran out, the calling code should allocate more.
377 _error
->Error("Ran out of allocation pools");
382 I
->ItemSize
= ItemSize
;
386 unsigned long Result
= 0;
387 // Out of space, allocate some more
390 const unsigned long size
= 20*1024;
391 I
->Count
= size
/ItemSize
;
392 Pool
* oldPools
= Pools
;
393 _error
->PushToStack();
394 Result
= RawAllocate(size
,ItemSize
);
395 bool const newError
= _error
->PendingError();
396 _error
->MergeWithStack();
397 if (Pools
!= oldPools
)
398 I
+= Pools
- oldPools
;
400 // Does the allocation failed ?
401 if (Result
== 0 && newError
)
409 I
->Start
+= ItemSize
;
410 return Result
/ItemSize
;
413 // DynamicMMap::WriteString - Write a string to the file /*{{{*/
414 // ---------------------------------------------------------------------
415 /* Strings are aligned to 16 bytes */
416 unsigned long DynamicMMap::WriteString(const char *String
,
419 if (Len
== (unsigned long)-1)
420 Len
= strlen(String
);
422 _error
->PushToStack();
423 unsigned long Result
= RawAllocate(Len
+1+sizeof(uint16_t),sizeof(uint16_t));
424 bool const newError
= _error
->PendingError();
425 _error
->MergeWithStack();
427 if (Base
== NULL
|| (Result
== 0 && newError
))
430 if (Len
>= std::numeric_limits
<uint16_t>::max())
433 uint16_t LenToWrite
= Len
;
434 memcpy((char *)Base
+ Result
, &LenToWrite
, sizeof(LenToWrite
));
435 Result
+= + sizeof(LenToWrite
);
437 memcpy((char *)Base
+ Result
,String
,Len
);
438 ((char *)Base
)[Result
+ Len
] = 0;
442 // DynamicMMap::Grow - Grow the mmap /*{{{*/
443 // ---------------------------------------------------------------------
444 /* This method is a wrapper around different methods to (try to) grow
445 a mmap (or our char[]-fallback). Encounterable environments:
446 1. Moveable + !Fallback + linux -> mremap with MREMAP_MAYMOVE
447 2. Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
448 3. Moveable + Fallback -> realloc
449 4. !Moveable + !Fallback + linux -> mremap alone - which will fail in 99,9%
450 5. !Moveable + !Fallback + !linux -> not possible (forbidden by constructor)
451 6. !Moveable + Fallback -> not possible
452 [ While Moveable and Fallback stands for the equally named flags and
453 "linux" indicates a linux kernel instead of a freebsd kernel. ]
454 So what you can see here is, that a MMAP which want to be growable need
455 to be moveable to have a real chance but that this method will at least try
456 the nearly impossible 4 to grow it before it finally give up: Never say never. */
457 bool DynamicMMap::Grow() {
458 if (Limit
!= 0 && WorkSpace
>= Limit
)
459 return _error
->Error(_("Unable to increase the size of the MMap as the "
460 "limit of %lu bytes is already reached."), Limit
);
462 return _error
->Error(_("Unable to increase size of the MMap as automatic growing is disabled by user."));
464 unsigned long long const newSize
= WorkSpace
+ GrowFactor
;
467 Fd
->Seek(newSize
- 1);
469 Fd
->Write(&C
,sizeof(C
));
472 unsigned long const poolOffset
= Pools
- ((Pool
*) Base
);
474 if ((Flags
& Fallback
) != Fallback
) {
475 #if defined(_POSIX_MAPPED_FILES) && defined(__linux__)
476 #ifdef MREMAP_MAYMOVE
478 if ((Flags
& Moveable
) == Moveable
)
479 Base
= mremap(Base
, WorkSpace
, newSize
, MREMAP_MAYMOVE
);
482 Base
= mremap(Base
, WorkSpace
, newSize
, 0);
484 if(Base
== MAP_FAILED
)
490 if ((Flags
& Moveable
) != Moveable
)
493 auto Temp
= realloc(Base
, newSize
);
498 /* Set new memory to 0 */
499 memset((char*)Base
+ WorkSpace
, 0, newSize
- WorkSpace
);
503 Pools
=(Pool
*) Base
+ poolOffset
;