X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/f55a958ff2251f0061ab907157c99a350e56025f..49e1672884bf8d50713812bdd4eca26ab2076711:/apt-pkg/contrib/mmap.cc diff --git a/apt-pkg/contrib/mmap.cc b/apt-pkg/contrib/mmap.cc index 9febc5cdd..a176da636 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.2 1998/07/04 05:57:42 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,43 @@ 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 +#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,16 +57,16 @@ 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(); - + // Set the permissions. int Prot = PROT_READ; int Map = MAP_SHARED; @@ -65,10 +75,50 @@ bool MMap::Map() if ((Flags & Public) != Public) Map = MAP_PRIVATE; + 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 = malloc(iSize); + SyncToFd = new FileFd(); + 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); + Base = (Flags & Fallback) ? MAP_FAILED : 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 || (Flags & Fallback)) + { + // 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 = malloc(iSize); + SyncToFd = new FileFd(); + 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 = calloc(iSize, 1); + 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; } @@ -76,30 +126,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 || validData() == false || iSize == 0) return true; - - Sync(); - - if (munmap((char *)Base,iSize) != 0) - _error->Warning("Unable to munmap"); + if (DoSync == true) + Sync(); + + if (SyncToFd != NULL) + { + free(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; + + if ((Flags & ReadOnly) != ReadOnly) + { + if (SyncToFd != NULL) + { + if (!SyncToFd->Seek(0) || !SyncToFd->Write(Base, iSize)) + return false; + } + else + { +#ifdef _POSIX_SYNCHRONIZED_IO + if (msync((char *)Base, iSize, MS_SYNC) < 0) + return _error->Errno("msync", _("Unable to synchronize mmap")); +#endif + } + } return true; } /*}}}*/ @@ -108,9 +183,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; + + if ((Flags & ReadOnly) != ReadOnly) + { + if (SyncToFd != 0) + { + if (!SyncToFd->Seek(0) || + !SyncToFd->Write (((char *)Base)+Start, Stop-Start)) + return false; + } + else + { +#ifdef _POSIX_SYNCHRONIZED_IO + unsigned long long const PSize = sysconf(_SC_PAGESIZE); + 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; } /*}}}*/ @@ -118,44 +210,138 @@ 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; + + // 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 + + unsigned long 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) + 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 + 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 + 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 = calloc(WorkSpace, 1); + iSize = 0; +} + /*}}}*/ // DynamicMMap::~DynamicMMap - Destructor /*{{{*/ // --------------------------------------------------------------------- /* We truncate the file to the size of the memory data set */ DynamicMMap::~DynamicMMap() { - unsigned long EndOfFile = iSize; + if (Fd == 0) + { + if (validData() == false) + return; +#ifdef _POSIX_MAPPED_FILES + munmap(Base, WorkSpace); +#else + free(Base); +#endif + return; + } + + unsigned long 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 /*{{{*/ // --------------------------------------------------------------------- /* 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; - - // Just in case error check - if (Result > WorkSpace) + + // try to grow the buffer + while(Result + Size > WorkSpace) { - _error->Error("Dynamic MMap ran out of room"); - return 0; + if(!Grow()) + { + _error->Fatal(_("Dynamic MMap ran out of room. Please increase the size " + "of APT::Cache-Start. Current value: %lu. (man 5 apt.conf)"), WorkSpace); + return 0; + } } return Result; } @@ -165,18 +351,17 @@ 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; if (I->ItemSize == ItemSize) break; } - // No pool is allocated, use an unallocated one if (I == Pools + PoolCount) { @@ -191,16 +376,27 @@ 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; return Result/ItemSize; } @@ -211,19 +407,80 @@ 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 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; } /*}}}*/ +// 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(_("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.")); + + unsigned long long const newSize = WorkSpace + GrowFactor; + + if(Fd != 0) { + Fd->Seek(newSize - 1); + char C = 0; + Fd->Write(&C,sizeof(C)); + } + + 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; +#endif + } else { + if ((Flags & Moveable) != Moveable) + return false; + + Base = realloc(Base, newSize); + if (Base == NULL) + return false; + else + /* Set new memory to 0 */ + memset((char*)Base + WorkSpace, 0, newSize - WorkSpace); + } + + Pools =(Pool*) Base + poolOffset; + WorkSpace = newSize; + return true; +} + /*}}}*/