]>
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> 
  24 #include <apt-pkg/fileutl.h> 
  37 // MMap::MMap - Constructor                                             /*{{{*/ 
  38 // --------------------------------------------------------------------- 
  40 MMap::MMap(FileFd 
&F
,unsigned long Flags
) : Flags(Flags
), iSize(0), 
  41                      Base(0), SyncToFd(NULL
) 
  43    if ((Flags 
& NoImmMap
) != NoImmMap
) 
  47 // MMap::MMap - Constructor                                             /*{{{*/ 
  48 // --------------------------------------------------------------------- 
  50 MMap::MMap(unsigned long Flags
) : Flags(Flags
), iSize(0), 
  51                      Base(0), SyncToFd(NULL
) 
  55 // MMap::~MMap - Destructor                                             /*{{{*/ 
  56 // --------------------------------------------------------------------- 
  63 // MMap::Map - Perform the mapping                                      /*{{{*/ 
  64 // --------------------------------------------------------------------- 
  66 bool MMap::Map(FileFd 
&Fd
) 
  70    // Set the permissions. 
  73    if ((Flags 
& ReadOnly
) != ReadOnly
) 
  75    if ((Flags 
& Public
) != Public
) 
  79       return _error
->Error(_("Can't mmap an empty file")); 
  81    // We can't mmap compressed fd's directly, so we need to read it completely 
  82    if (Fd
.IsCompressed() == true) 
  84       if ((Flags 
& ReadOnly
) != ReadOnly
) 
  85          return _error
->Error("Compressed file %s can only be mapped readonly", Fd
.Name().c_str()); 
  86       Base 
= new unsigned char[iSize
]; 
  87       if (Fd
.Seek(0L) == false || Fd
.Read(Base
, iSize
) == false) 
  93    Base 
= mmap(0,iSize
,Prot
,Map
,Fd
.Fd(),0); 
  94    if (Base 
== (void *)-1) 
  96       if (errno 
== ENODEV 
|| errno 
== EINVAL
) 
  98          // The filesystem doesn't support this particular kind of mmap. 
  99          // So we allocate a buffer and read the whole file into it. 
 100          if ((Flags 
& ReadOnly
) == ReadOnly
) 
 102             // for readonly, we don't need sync, so make it simple 
 103             Base 
= new unsigned char[iSize
]; 
 104             return Fd
.Read(Base
, iSize
); 
 106          // FIXME: Writing to compressed fd's ? 
 107          int const dupped_fd 
= dup(Fd
.Fd()); 
 109             return _error
->Errno("mmap", _("Couldn't duplicate file descriptor %i"), Fd
.Fd()); 
 111          Base 
= new unsigned char[iSize
]; 
 112          SyncToFd 
= new FileFd (dupped_fd
); 
 113          if (!SyncToFd
->Seek(0L) || !SyncToFd
->Read(Base
, iSize
)) 
 117          return _error
->Errno("mmap",_("Couldn't make mmap of %llu bytes"), 
 124 // MMap::Close - Close the map                                          /*{{{*/ 
 125 // --------------------------------------------------------------------- 
 127 bool MMap::Close(bool DoSync
) 
 129    if ((Flags 
& UnMapped
) == UnMapped 
|| validData() == false || iSize 
== 0) 
 135    if (SyncToFd 
!= NULL
) 
 137       delete[] (char *)Base
; 
 143       if (munmap((char *)Base
, iSize
) != 0) 
 144          _error
->WarningE("mmap", _("Unable to close mmap")); 
 152 // MMap::Sync - Syncronize the map with the disk                        /*{{{*/ 
 153 // --------------------------------------------------------------------- 
 154 /* This is done in syncronous mode - the docs indicate that this will  
 155    not return till all IO is complete */ 
 158    if ((Flags 
& UnMapped
) == UnMapped
) 
 161 #ifdef _POSIX_SYNCHRONIZED_IO    
 162    if ((Flags 
& ReadOnly
) != ReadOnly
) 
 164       if (SyncToFd 
!= NULL
) 
 166          if (!SyncToFd
->Seek(0) || !SyncToFd
->Write(Base
, iSize
)) 
 171          if (msync((char *)Base
, iSize
, MS_SYNC
) < 0) 
 172             return _error
->Errno("msync", _("Unable to synchronize mmap")); 
 179 // MMap::Sync - Syncronize a section of the file to disk                /*{{{*/ 
 180 // --------------------------------------------------------------------- 
 182 bool MMap::Sync(unsigned long Start
,unsigned long Stop
) 
 184    if ((Flags 
& UnMapped
) == UnMapped
) 
 187 #ifdef _POSIX_SYNCHRONIZED_IO 
 188    unsigned long long PSize 
= sysconf(_SC_PAGESIZE
); 
 189    if ((Flags 
& ReadOnly
) != ReadOnly
) 
 193          if (!SyncToFd
->Seek(0) || 
 194              !SyncToFd
->Write (((char *)Base
)+Start
, Stop
-Start
)) 
 199          if (msync((char *)Base
+(unsigned long long)(Start
/PSize
)*PSize
,Stop 
- Start
,MS_SYNC
) < 0) 
 200             return _error
->Errno("msync", _("Unable to synchronize mmap")); 
 208 // DynamicMMap::DynamicMMap - Constructor                               /*{{{*/ 
 209 // --------------------------------------------------------------------- 
 211 DynamicMMap::DynamicMMap(FileFd 
&F
,unsigned long Flags
,unsigned long const &Workspace
, 
 212                          unsigned long const &Grow
, unsigned long const &Limit
) : 
 213                 MMap(F
,Flags 
| NoImmMap
), Fd(&F
), WorkSpace(Workspace
), 
 214                 GrowFactor(Grow
), Limit(Limit
) 
 216    if (_error
->PendingError() == true) 
 219    unsigned long long EndOfFile 
= Fd
->Size(); 
 220    if (EndOfFile 
> WorkSpace
) 
 221       WorkSpace 
= EndOfFile
; 
 222    else if(WorkSpace 
> 0) 
 224       Fd
->Seek(WorkSpace 
- 1); 
 226       Fd
->Write(&C
,sizeof(C
)); 
 233 // DynamicMMap::DynamicMMap - Constructor for a non-file backed map     /*{{{*/ 
 234 // --------------------------------------------------------------------- 
 235 /* We try here to use mmap to reserve some space - this is much more 
 236    cooler than the fallback solution to simply allocate a char array 
 237    and could come in handy later than we are able to grow such an mmap */ 
 238 DynamicMMap::DynamicMMap(unsigned long Flags
,unsigned long const &WorkSpace
, 
 239                          unsigned long const &Grow
, unsigned long const &Limit
) : 
 240                 MMap(Flags 
| NoImmMap 
| UnMapped
), Fd(0), WorkSpace(WorkSpace
), 
 241                 GrowFactor(Grow
), Limit(Limit
) 
 243         if (_error
->PendingError() == true) 
 246         // disable Moveable if we don't grow 
 248                 this->Flags 
&= ~Moveable
; 
 251         // kfreebsd doesn't have mremap, so we use the fallback 
 252         if ((this->Flags 
& Moveable
) == Moveable
) 
 253                 this->Flags 
|= Fallback
; 
 256 #ifdef _POSIX_MAPPED_FILES 
 257         if ((this->Flags 
& Fallback
) != Fallback
) { 
 258                 // Set the permissions. 
 259                 int Prot 
= PROT_READ
; 
 261                 int Map 
= MAP_PRIVATE 
| MAP_ANONYMOUS
; 
 263                 int Map 
= MAP_PRIVATE 
| MAP_ANON
; 
 265                 if ((this->Flags 
& ReadOnly
) != ReadOnly
) 
 267                 if ((this->Flags 
& Public
) == Public
) 
 269                         Map 
= MAP_SHARED 
| MAP_ANONYMOUS
; 
 271                         Map 
= MAP_SHARED 
| MAP_ANON
; 
 274                 // use anonymous mmap() to get the memory 
 275                 Base 
= (unsigned char*) mmap(0, WorkSpace
, Prot
, Map
, -1, 0); 
 277                 if(Base 
== MAP_FAILED
) 
 278                         _error
->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace
); 
 284         // fallback to a static allocated space 
 285         Base 
= new unsigned char[WorkSpace
]; 
 286         memset(Base
,0,WorkSpace
); 
 290 // DynamicMMap::~DynamicMMap - Destructor                               /*{{{*/ 
 291 // --------------------------------------------------------------------- 
 292 /* We truncate the file to the size of the memory data set */ 
 293 DynamicMMap::~DynamicMMap() 
 297       if (validData() == false) 
 299 #ifdef _POSIX_MAPPED_FILES 
 300       munmap(Base
, WorkSpace
); 
 302       delete [] (unsigned char *)Base
; 
 307    unsigned long long EndOfFile 
= iSize
; 
 310    if(ftruncate(Fd
->Fd(),EndOfFile
) < 0) 
 311       _error
->Errno("ftruncate", _("Failed to truncate file")); 
 314 // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space   /*{{{*/ 
 315 // --------------------------------------------------------------------- 
 316 /* This allocates a block of memory aligned to the given size */ 
 317 unsigned long DynamicMMap::RawAllocate(unsigned long long Size
,unsigned long Aln
) 
 319    unsigned long long Result 
= iSize
; 
 321       Result 
+= Aln 
- (iSize%Aln
); 
 323    iSize 
= Result 
+ Size
; 
 325    // try to grow the buffer 
 326    while(Result 
+ Size 
> WorkSpace
) 
 330          _error
->Fatal(_("Dynamic MMap ran out of room. Please increase the size " 
 331                          "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace
); 
 338 // DynamicMMap::Allocate - Pooled aligned allocation                    /*{{{*/ 
 339 // --------------------------------------------------------------------- 
 340 /* This allocates an Item of size ItemSize so that it is aligned to its 
 342 unsigned long DynamicMMap::Allocate(unsigned long ItemSize
) 
 344    // Look for a matching pool entry 
 347    for (I 
= Pools
; I 
!= Pools 
+ PoolCount
; ++I
) 
 349       if (I
->ItemSize 
== 0) 
 351       if (I
->ItemSize 
== ItemSize
) 
 354    // No pool is allocated, use an unallocated one 
 355    if (I 
== Pools 
+ PoolCount
) 
 357       // Woops, we ran out, the calling code should allocate more. 
 360          _error
->Error("Ran out of allocation pools"); 
 365       I
->ItemSize 
= ItemSize
; 
 369    unsigned long Result 
= 0; 
 370    // Out of space, allocate some more 
 373       const unsigned long size 
= 20*1024; 
 374       I
->Count 
= size
/ItemSize
; 
 375       Pool
* oldPools 
= Pools
; 
 376       Result 
= RawAllocate(size
,ItemSize
); 
 377       if (Pools 
!= oldPools
) 
 378          I 
+= Pools 
- oldPools
; 
 380       // Does the allocation failed ? 
 381       if (Result 
== 0 && _error
->PendingError()) 
 389    I
->Start 
+= ItemSize
; 
 390    return Result
/ItemSize
; 
 393 // DynamicMMap::WriteString - Write a string to the file                /*{{{*/ 
 394 // --------------------------------------------------------------------- 
 395 /* Strings are not aligned to anything */ 
 396 unsigned long DynamicMMap::WriteString(const char *String
, 
 399    if (Len 
== (unsigned long)-1) 
 400       Len 
= strlen(String
); 
 402    unsigned long const Result 
= RawAllocate(Len
+1,0); 
 404    if (Result 
== 0 && _error
->PendingError()) 
 407    memcpy((char *)Base 
+ Result
,String
,Len
); 
 408    ((char *)Base
)[Result 
+ Len
] = 0; 
 412 // DynamicMMap::Grow - Grow the mmap                                    /*{{{*/ 
 413 // --------------------------------------------------------------------- 
 414 /* This method is a wrapper around different methods to (try to) grow 
 415    a mmap (or our char[]-fallback). Encounterable environments: 
 416    1. Moveable + !Fallback + linux -> mremap with MREMAP_MAYMOVE 
 417    2. Moveable + !Fallback + !linux -> not possible (forbidden by constructor) 
 418    3. Moveable + Fallback -> realloc 
 419    4. !Moveable + !Fallback + linux -> mremap alone - which will fail in 99,9% 
 420    5. !Moveable + !Fallback + !linux -> not possible (forbidden by constructor) 
 421    6. !Moveable + Fallback -> not possible 
 422    [ While Moveable and Fallback stands for the equally named flags and 
 423      "linux" indicates a linux kernel instead of a freebsd kernel. ] 
 424    So what you can see here is, that a MMAP which want to be growable need 
 425    to be moveable to have a real chance but that this method will at least try 
 426    the nearly impossible 4 to grow it before it finally give up: Never say never. */ 
 427 bool DynamicMMap::Grow() { 
 428         if (Limit 
!= 0 && WorkSpace 
>= Limit
) 
 429                 return _error
->Error(_("Unable to increase the size of the MMap as the " 
 430                                        "limit of %lu bytes is already reached."), Limit
); 
 432                 return _error
->Error(_("Unable to increase size of the MMap as automatic growing is disabled by user.")); 
 434         unsigned long long const newSize 
= WorkSpace 
+ GrowFactor
; 
 437                 Fd
->Seek(newSize 
- 1); 
 439                 Fd
->Write(&C
,sizeof(C
)); 
 442         unsigned long const poolOffset 
= Pools 
- ((Pool
*) Base
); 
 444         if ((Flags 
& Fallback
) != Fallback
) { 
 445 #if defined(_POSIX_MAPPED_FILES) && defined(__linux__) 
 446    #ifdef MREMAP_MAYMOVE 
 448                 if ((Flags 
& Moveable
) == Moveable
) 
 449                         Base 
= mremap(Base
, WorkSpace
, newSize
, MREMAP_MAYMOVE
); 
 452                         Base 
= mremap(Base
, WorkSpace
, newSize
, 0); 
 454                 if(Base 
== MAP_FAILED
) 
 460                 if ((Flags 
& Moveable
) != Moveable
) 
 463                 Base 
= realloc(Base
, newSize
); 
 468         Pools 
=(Pool
*) Base 
+ poolOffset
;