X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/13eb93fcb6a03afd8fc05fa2b4c60046473e8507..b711c01e777977a4f9e2b78d7ab91f09f3fdf03f:/apt-pkg/contrib/mmap.cc diff --git a/apt-pkg/contrib/mmap.cc b/apt-pkg/contrib/mmap.cc index 5f56178f4..160718ea5 100644 --- a/apt-pkg/contrib/mmap.cc +++ b/apt-pkg/contrib/mmap.cc @@ -17,27 +17,28 @@ /*}}}*/ // Include Files /*{{{*/ #define _BSD_SOURCE +#include + #include #include -#include - -#include +#include #include #include #include #include #include -#include - +#include #include - /*}}}*/ + +#include + /*}}}*/ // MMap::MMap - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0), - Base(0) + Base(0), SyncToFd(NULL) { if ((Flags & NoImmMap) != NoImmMap) Map(F); @@ -47,7 +48,7 @@ MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0), // --------------------------------------------------------------------- /* */ MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0), - Base(0) + Base(0), SyncToFd(NULL) { } /*}}}*/ @@ -65,7 +66,7 @@ MMap::~MMap() bool MMap::Map(FileFd &Fd) { iSize = Fd.Size(); - + // Set the permissions. int Prot = PROT_READ; int Map = MAP_SHARED; @@ -76,11 +77,46 @@ bool MMap::Map(FileFd &Fd) if (iSize == 0) return _error->Error(_("Can't mmap an empty file")); - + + // We can't mmap compressed fd's directly, so we need to read it completely + if (Fd.IsCompressed() == true) + { + if ((Flags & ReadOnly) != ReadOnly) + return _error->Error("Compressed file %s can only be mapped readonly", Fd.Name().c_str()); + Base = new unsigned char[iSize]; + if (Fd.Seek(0L) == false || Fd.Read(Base, iSize) == false) + return _error->Error("Compressed file %s can't be read into mmap", Fd.Name().c_str()); + return true; + } + // Map it. Base = mmap(0,iSize,Prot,Map,Fd.Fd(),0); if (Base == (void *)-1) - return _error->Errno("mmap",_("Couldn't make mmap of %lu 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. + if ((Flags & ReadOnly) == ReadOnly) + { + // for readonly, we don't need sync, so make it simple + Base = new unsigned char[iSize]; + return Fd.Read(Base, iSize); + } + // FIXME: Writing to compressed fd's ? + 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 %llu bytes"), + iSize); + } return true; } @@ -90,15 +126,24 @@ bool MMap::Map(FileFd &Fd) /* */ bool MMap::Close(bool DoSync) { - if ((Flags & UnMapped) == UnMapped || Base == 0 || iSize == 0) + if ((Flags & UnMapped) == UnMapped || validData() == false || iSize == 0) return true; if (DoSync == true) Sync(); - - if (munmap((char *)Base,iSize) != 0) - _error->Warning("Unable to munmap"); - + + 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; Base = 0; return true; @@ -115,8 +160,18 @@ bool MMap::Sync() #ifdef _POSIX_SYNCHRONIZED_IO if ((Flags & ReadOnly) != ReadOnly) - if (msync((char *)Base,iSize,MS_SYNC) < 0) - return _error->Errno("msync","Unable to write mmap"); + { + 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; } @@ -130,39 +185,38 @@ bool MMap::Sync(unsigned long Start,unsigned long Stop) return true; #ifdef _POSIX_SYNCHRONIZED_IO - unsigned long PSize = sysconf(_SC_PAGESIZE); + unsigned long long PSize = sysconf(_SC_PAGESIZE); if ((Flags & ReadOnly) != ReadOnly) - if (msync((char *)Base+(int)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0) - return _error->Errno("msync","Unable to write mmap"); + { + if (SyncToFd != 0) + { + if (!SyncToFd->Seek(0) || + !SyncToFd->Write (((char *)Base)+Start, Stop-Start)) + return false; + } + else + { + if (msync((char *)Base+(unsigned long long)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0) + return _error->Errno("msync", _("Unable to synchronize mmap")); + } + } #endif return true; } /*}}}*/ -// DynamicMMapSegfaultHandler /*{{{*/ -// --------------------------------------------------------------------- -/* In theory, the mmap should never segfault because we check the available - size of our mmap before we use it, but there are a few reports out there - which state that the mmap segfaults without further notice. So this handler - will take care of all these segfaults which should never happen... */ -void DynamicMMapSegfaultHandler(int) -{ - _error->Error(_("Dynamic MMap segfaults, most likely because it ran out of room. " - "Please increase the size of APT::Cache-Limit. (man 5 apt.conf)")); - _error->DumpErrors(); - exit(EXIT_FAILURE); -} - /*}}}*/ // DynamicMMap::DynamicMMap - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long WorkSpace) : - MMap(F,Flags | NoImmMap), Fd(&F), 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) { if (_error->PendingError() == true) return; - unsigned long EndOfFile = Fd->Size(); + unsigned long long EndOfFile = Fd->Size(); if (EndOfFile > WorkSpace) WorkSpace = EndOfFile; else if(WorkSpace > 0) @@ -181,30 +235,56 @@ DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long WorkSpace) /* 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 WorkSpace) : - MMap(Flags | NoImmMap | UnMapped), Fd(0), WorkSpace(WorkSpace) +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; + if (_error->PendingError() == true) + return; - if (_config->FindB("MMap::SegfaultHandler",true) == true) - { - struct sigaction sa; - sa.sa_handler = DynamicMMapSegfaultHandler; - sigaction(SIGSEGV, &sa, NULL); - } + // disable Moveable if we don't grow + if (Grow == 0) + this->Flags &= ~Moveable; + +#ifndef __linux__ + // kfreebsd doesn't have mremap, so we use the fallback + if ((this->Flags & Moveable) == Moveable) + this->Flags |= Fallback; +#endif #ifdef _POSIX_MAPPED_FILES - // use anonymous mmap() to get the memory - Base = (unsigned char*) mmap(0, WorkSpace, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - if(Base != MAP_FAILED) - return; + if ((this->Flags & Fallback) != Fallback) { + // Set the permissions. + int Prot = PROT_READ; +#ifdef MAP_ANONYMOUS + int Map = MAP_PRIVATE | MAP_ANONYMOUS; +#else + int Map = MAP_PRIVATE | MAP_ANON; #endif - // fallback to a static allocated space - Base = new unsigned char[WorkSpace]; - memset(Base,0,WorkSpace); - iSize = 0; + if ((this->Flags & ReadOnly) != ReadOnly) + Prot |= PROT_WRITE; + if ((this->Flags & Public) == Public) +#ifdef MAP_ANONYMOUS + Map = MAP_SHARED | MAP_ANONYMOUS; +#else + Map = MAP_SHARED | MAP_ANON; +#endif + + // 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 /*{{{*/ @@ -214,6 +294,8 @@ DynamicMMap::~DynamicMMap() { if (Fd == 0) { + if (validData() == false) + return; #ifdef _POSIX_MAPPED_FILES munmap(Base, WorkSpace); #else @@ -222,7 +304,7 @@ DynamicMMap::~DynamicMMap() return; } - unsigned long EndOfFile = iSize; + unsigned long long EndOfFile = iSize; iSize = WorkSpace; Close(false); if(ftruncate(Fd->Fd(),EndOfFile) < 0) @@ -232,20 +314,20 @@ DynamicMMap::~DynamicMMap() // DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/ // --------------------------------------------------------------------- /* This allocates a block of memory aligned to the given size */ -unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln) +unsigned long DynamicMMap::RawAllocate(unsigned long long Size,unsigned long Aln) { - unsigned long Result = iSize; + unsigned long long Result = iSize; if (Aln != 0) Result += Aln - (iSize%Aln); - + iSize = Result + Size; - + // try to grow the buffer while(Result + Size > WorkSpace) { if(!Grow()) { - _error->Error(_("Dynamic MMap ran out of room. Please increase the size " + _error->Fatal(_("Dynamic MMap ran out of room. Please increase the size " "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace); return 0; } @@ -258,11 +340,11 @@ unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln) /* 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; - for (I = Pools; I != Pools + PoolCount; I++) + for (I = Pools; I != Pools + PoolCount; ++I) { if (I->ItemSize == 0) Empty = I; @@ -283,17 +365,28 @@ 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,ItemSize); - } + const unsigned long size = 20*1024; + I->Count = size/ItemSize; + Pool* oldPools = Pools; + Result = RawAllocate(size,ItemSize); + if (Pools != oldPools) + I += Pools - oldPools; + + // 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; + I->Start += ItemSize; return Result/ItemSize; } /*}}}*/ @@ -303,21 +396,14 @@ unsigned long DynamicMMap::Allocate(unsigned long ItemSize) unsigned long DynamicMMap::WriteString(const char *String, unsigned long Len) { - unsigned long Result = iSize; - // try to grow the buffer - while(Result + Len > WorkSpace) - { - 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; - } - } - if (Len == (unsigned long)-1) Len = strlen(String); - iSize += Len + 1; + + unsigned long const 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; @@ -325,30 +411,62 @@ unsigned long DynamicMMap::WriteString(const char *String, /*}}}*/ // DynamicMMap::Grow - Grow the mmap /*{{{*/ // --------------------------------------------------------------------- -/* This method will try to grow the mmap we currently use. This doesn't - work most of the time because we can't move the mmap around in the - memory for now as this would require to adjust quite a lot of pointers - but why we should not at least try to grow it before we give up? */ -bool DynamicMMap::Grow() -{ -#ifdef _POSIX_MAPPED_FILES - unsigned long newSize = WorkSpace + 1024*1024; +/* 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(_("Unable to increase the size of the MMap as the " + "limit of %lu bytes is already reached."), Limit); + if (GrowFactor <= 0) + return _error->Error(_("Unable to increase size of the MMap as automatic growing is disabled by user.")); - if(Fd != 0) - { - Fd->Seek(newSize - 1); - char C = 0; - Fd->Write(&C,sizeof(C)); - } + unsigned long long const newSize = WorkSpace + GrowFactor; - Base = mremap(Base, WorkSpace, newSize, 0); - if(Base == MAP_FAILED) - return false; + if(Fd != 0) { + Fd->Seek(newSize - 1); + char C = 0; + Fd->Write(&C,sizeof(C)); + } - WorkSpace = newSize; - return true; + unsigned long const poolOffset = Pools - ((Pool*) Base); + + 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; + return false; #endif + } else { + if ((Flags & Moveable) != Moveable) + return false; + + Base = realloc(Base, newSize); + if (Base == NULL) + return false; + } + + Pools =(Pool*) Base + poolOffset; + WorkSpace = newSize; + return true; } /*}}}*/