X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/578bfd0aed2ec993f4ad85fa6a7094a852261422..3a4477a4241bcfbc9db541cf34cc75c1f1111b78:/apt-pkg/contrib/mmap.cc diff --git a/apt-pkg/contrib/mmap.cc b/apt-pkg/contrib/mmap.cc index 85cac1cca..b3f29032c 100644 --- a/apt-pkg/contrib/mmap.cc +++ b/apt-pkg/contrib/mmap.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: mmap.cc,v 1.1 1998/07/02 02:58:13 jgg Exp $ +// $Id: mmap.cc,v 1.22 2001/05/27 05:19:30 jgg Exp $ /* ###################################################################### MMap Class - Provides 'real' mmap or a faked mmap using read(). @@ -13,33 +13,41 @@ libc6 generates warnings -- which should be errors, g++ isn't properly strict. - The configure test notes that some OS's have broken private mmap's - so on those OS's we can't use mmap. This means we have to use - configure to test mmap and can't rely on the POSIX - _POSIX_MAPPED_FILES test. - ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ #define _BSD_SOURCE -#include -#include +#include +#include + +#include #include #include -#include #include #include +#include +#include + +#include /*}}}*/ // MMap::MMap - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -MMap::MMap(File &F,unsigned long Flags) : Fd(F), Flags(Flags), iSize(0), - Base(0) +MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0), + Base(0), SyncToFd(NULL) { if ((Flags & NoImmMap) != NoImmMap) - Map(); + Map(F); +} + /*}}}*/ +// MMap::MMap - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0), + Base(0), SyncToFd(NULL) +{ } /*}}}*/ // MMap::~MMap - Destructor /*{{{*/ @@ -47,13 +55,13 @@ MMap::MMap(File &F,unsigned long Flags) : Fd(F), Flags(Flags), iSize(0), /* */ MMap::~MMap() { - Close(true); + Close(); } /*}}}*/ // MMap::Map - Perform the mapping /*{{{*/ // --------------------------------------------------------------------- /* */ -bool MMap::Map() +bool MMap::Map(FileFd &Fd) { iSize = Fd.Size(); @@ -65,10 +73,30 @@ bool MMap::Map() if ((Flags & Public) != Public) Map = MAP_PRIVATE; + if (iSize == 0) + return _error->Error(_("Can't mmap an empty file")); + // Map it. Base = mmap(0,iSize,Prot,Map,Fd.Fd(),0); if (Base == (void *)-1) - return _error->Errno("mmap","Couldn't make mmap of %u bytes",iSize); + { + if (errno == ENODEV || errno == EINVAL) + { + // The filesystem doesn't support this particular kind of mmap. + // So we allocate a buffer and read the whole file into it. + int const dupped_fd = dup(Fd.Fd()); + if (dupped_fd == -1) + return _error->Errno("mmap", _("Couldn't duplicate file descriptor %i"), Fd.Fd()); + + Base = new unsigned char[iSize]; + SyncToFd = new FileFd (dupped_fd); + if (!SyncToFd->Seek(0L) || !SyncToFd->Read(Base, iSize)) + return false; + } + else + return _error->Errno("mmap",_("Couldn't make mmap of %lu bytes"), + iSize); + } return true; } @@ -76,30 +104,55 @@ bool MMap::Map() // MMap::Close - Close the map /*{{{*/ // --------------------------------------------------------------------- /* */ -bool MMap::Close(bool DoClose) +bool MMap::Close(bool DoSync) { - if (Fd.IsOpen() == false) + if ((Flags & UnMapped) == UnMapped || Base == 0 || iSize == 0) return true; - - Sync(); - - if (munmap((char *)Base,iSize) != 0) - _error->Warning("Unable to munmap"); + if (DoSync == true) + Sync(); + + if (SyncToFd != NULL) + { + delete[] (char *)Base; + delete SyncToFd; + SyncToFd = NULL; + } + else + { + if (munmap((char *)Base, iSize) != 0) + _error->WarningE("mmap", _("Unable to close mmap")); + } + iSize = 0; - if (DoClose == true) - Fd.Close(); + Base = 0; return true; } /*}}}*/ // MMap::Sync - Syncronize the map with the disk /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* This is done in syncronous mode - the docs indicate that this will + not return till all IO is complete */ bool MMap::Sync() { - if ((Flags & ReadOnly) == ReadOnly) - if (msync((char *)Base,iSize,MS_SYNC) != 0) - return _error->Error("msync","Unable to write mmap"); + if ((Flags & UnMapped) == UnMapped) + return true; + +#ifdef _POSIX_SYNCHRONIZED_IO + if ((Flags & ReadOnly) != ReadOnly) + { + if (SyncToFd != NULL) + { + if (!SyncToFd->Seek(0) || !SyncToFd->Write(Base, iSize)) + return false; + } + else + { + if (msync((char *)Base, iSize, MS_SYNC) < 0) + return _error->Errno("msync", _("Unable to synchronize mmap")); + } + } +#endif return true; } /*}}}*/ @@ -108,9 +161,26 @@ bool MMap::Sync() /* */ bool MMap::Sync(unsigned long Start,unsigned long Stop) { - if ((Flags & ReadOnly) == ReadOnly) - if (msync((char *)Base+(int)(Start/PAGE_SIZE)*PAGE_SIZE,Stop - Start,MS_SYNC) != 0) - return _error->Error("msync","Unable to write mmap"); + if ((Flags & UnMapped) == UnMapped) + return true; + +#ifdef _POSIX_SYNCHRONIZED_IO + unsigned long PSize = sysconf(_SC_PAGESIZE); + if ((Flags & ReadOnly) != ReadOnly) + { + if (SyncToFd != 0) + { + if (!SyncToFd->Seek(0) || + !SyncToFd->Write (((char *)Base)+Start, Stop-Start)) + return false; + } + else + { + if (msync((char *)Base+(int)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0) + return _error->Errno("msync", _("Unable to synchronize mmap")); + } + } +#endif return true; } /*}}}*/ @@ -118,43 +188,120 @@ bool MMap::Sync(unsigned long Start,unsigned long Stop) // DynamicMMap::DynamicMMap - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -DynamicMMap::DynamicMMap(File &F,unsigned long Flags,unsigned long WorkSpace) : - MMap(F,Flags | NoImmMap), WorkSpace(WorkSpace) +DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long const &Workspace, + unsigned long const &Grow, unsigned long const &Limit) : + MMap(F,Flags | NoImmMap), Fd(&F), WorkSpace(Workspace), + GrowFactor(Grow), Limit(Limit) { - unsigned long EndOfFile = Fd.Size(); - Fd.Seek(WorkSpace); - char C = 0; - Fd.Write(&C,sizeof(C)); - Map(); + if (_error->PendingError() == true) + return; + + unsigned long EndOfFile = Fd->Size(); + if (EndOfFile > WorkSpace) + WorkSpace = EndOfFile; + else if(WorkSpace > 0) + { + Fd->Seek(WorkSpace - 1); + char C = 0; + Fd->Write(&C,sizeof(C)); + } + + Map(F); iSize = EndOfFile; } /*}}}*/ +// DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/ +// --------------------------------------------------------------------- +/* We try here to use mmap to reserve some space - this is much more + cooler than the fallback solution to simply allocate a char array + and could come in handy later than we are able to grow such an mmap */ +DynamicMMap::DynamicMMap(unsigned long Flags,unsigned long const &WorkSpace, + unsigned long const &Grow, unsigned long const &Limit) : + MMap(Flags | NoImmMap | UnMapped), Fd(0), WorkSpace(WorkSpace), + GrowFactor(Grow), Limit(Limit) +{ + if (_error->PendingError() == true) + return; + + // disable Moveable if we don't grow + if (Grow == 0) + Flags &= ~Moveable; + +#ifndef __linux__ + // kfreebsd doesn't have mremap, so we use the fallback + if ((Flags & Moveable) == Moveable) + Flags |= Fallback; +#endif + +#ifdef _POSIX_MAPPED_FILES + if ((Flags & Fallback) != Fallback) { + // Set the permissions. + int Prot = PROT_READ; + int Map = MAP_PRIVATE | MAP_ANONYMOUS; + if ((Flags & ReadOnly) != ReadOnly) + Prot |= PROT_WRITE; + if ((Flags & Public) == Public) + Map = MAP_SHARED | MAP_ANONYMOUS; + + // use anonymous mmap() to get the memory + Base = (unsigned char*) mmap(0, WorkSpace, Prot, Map, -1, 0); + + if(Base == MAP_FAILED) + _error->Errno("DynamicMMap",_("Couldn't make mmap of %lu bytes"),WorkSpace); + + iSize = 0; + return; + } +#endif + // fallback to a static allocated space + Base = new unsigned char[WorkSpace]; + memset(Base,0,WorkSpace); + iSize = 0; +} + /*}}}*/ // DynamicMMap::~DynamicMMap - Destructor /*{{{*/ // --------------------------------------------------------------------- /* We truncate the file to the size of the memory data set */ DynamicMMap::~DynamicMMap() { + if (Fd == 0) + { +#ifdef _POSIX_MAPPED_FILES + munmap(Base, WorkSpace); +#else + delete [] (unsigned char *)Base; +#endif + return; + } + unsigned long EndOfFile = iSize; + iSize = WorkSpace; Close(false); - ftruncate(Fd.Fd(),EndOfFile); - Fd.Close(); + if(ftruncate(Fd->Fd(),EndOfFile) < 0) + _error->Errno("ftruncate", _("Failed to truncate file")); } /*}}}*/ // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/ // --------------------------------------------------------------------- -/* */ -unsigned long DynamicMMap::RawAllocate(unsigned long Size) +/* This allocates a block of memory aligned to the given size */ +unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln) { unsigned long Result = iSize; - iSize += Size; - - // Just in case error check - if (Result > WorkSpace) + if (Aln != 0) + Result += Aln - (iSize%Aln); + + iSize = Result + Size; + + // try to grow the buffer + while(Result + Size > WorkSpace) { - _error->Error("Dynamic MMap ran out of room"); - return 0; + if(!Grow()) + { + _error->Error(_("Dynamic MMap ran out of room. Please increase the size " + "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace); + return 0; + } } - return Result; } /*}}}*/ @@ -163,7 +310,7 @@ unsigned long DynamicMMap::RawAllocate(unsigned long Size) /* This allocates an Item of size ItemSize so that it is aligned to its size in the file. */ unsigned long DynamicMMap::Allocate(unsigned long ItemSize) -{ +{ // Look for a matching pool entry Pool *I; Pool *Empty = 0; @@ -174,7 +321,6 @@ unsigned long DynamicMMap::Allocate(unsigned long ItemSize) if (I->ItemSize == ItemSize) break; } - // No pool is allocated, use an unallocated one if (I == Pools + PoolCount) { @@ -189,16 +335,23 @@ unsigned long DynamicMMap::Allocate(unsigned long ItemSize) I->ItemSize = ItemSize; I->Count = 0; } - + + unsigned long Result = 0; // Out of space, allocate some more if (I->Count == 0) { - I->Count = 20*1024/ItemSize; - I->Start = RawAllocate(I->Count*ItemSize); - } - + const unsigned long size = 20*1024; + I->Count = size/ItemSize; + Result = RawAllocate(size,ItemSize); + // Does the allocation failed ? + if (Result == 0 && _error->PendingError()) + return 0; + I->Start = Result; + } + else + Result = I->Start; + I->Count--; - unsigned long Result = I->Start; I->Start += ItemSize; return Result/ItemSize; } @@ -209,19 +362,70 @@ unsigned long DynamicMMap::Allocate(unsigned long ItemSize) unsigned long DynamicMMap::WriteString(const char *String, unsigned long Len) { - unsigned long Result = iSize; - // Just in case error check - if (Result > WorkSpace) - { - _error->Error("Dynamic MMap ran out of room"); - return 0; - } - - if (Len == 0) + if (Len == (unsigned long)-1) Len = strlen(String); - iSize += Len + 1; + + unsigned long Result = RawAllocate(Len+1,0); + + if (Result == 0 && _error->PendingError()) + return 0; + memcpy((char *)Base + Result,String,Len); ((char *)Base)[Result + Len] = 0; return Result; } /*}}}*/ +// DynamicMMap::Grow - Grow the mmap /*{{{*/ +// --------------------------------------------------------------------- +/* This method is a wrapper around different methods to (try to) grow + a mmap (or our char[]-fallback). Encounterable environments: + 1. Moveable + !Fallback + linux -> mremap with MREMAP_MAYMOVE + 2. Moveable + !Fallback + !linux -> not possible (forbidden by constructor) + 3. Moveable + Fallback -> realloc + 4. !Moveable + !Fallback + linux -> mremap alone - which will fail in 99,9% + 5. !Moveable + !Fallback + !linux -> not possible (forbidden by constructor) + 6. !Moveable + Fallback -> not possible + [ While Moveable and Fallback stands for the equally named flags and + "linux" indicates a linux kernel instead of a freebsd kernel. ] + So what you can see here is, that a MMAP which want to be growable need + to be moveable to have a real chance but that this method will at least try + the nearly impossible 4 to grow it before it finally give up: Never say never. */ +bool DynamicMMap::Grow() { + if (Limit != 0 && WorkSpace >= Limit) + return _error->Error(_("The size of a MMap has already reached the defined limit of %lu bytes," + "abort the try to grow the MMap."), Limit); + + unsigned long const newSize = WorkSpace + 1024*1024; + + if(Fd != 0) { + Fd->Seek(newSize - 1); + char C = 0; + Fd->Write(&C,sizeof(C)); + } + if ((Flags & Fallback) != Fallback) { +#if defined(_POSIX_MAPPED_FILES) && defined(__linux__) + #ifdef MREMAP_MAYMOVE + if ((Flags & Moveable) == Moveable) + Base = mremap(Base, WorkSpace, newSize, MREMAP_MAYMOVE); + else + #endif + Base = mremap(Base, WorkSpace, newSize, 0); + + if(Base == MAP_FAILED) + return false; +#else + return false; +#endif + } else { + if ((Flags & Moveable) != Moveable) + return false; + + Base = realloc(Base, newSize); + if (Base == NULL) + return false; + } + + WorkSpace = newSize; + return true; +} + /*}}}*/