X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/b2e465d6d32d2dc884f58b94acb7e35f671a87fe..4bec02c237e8e218bc7d4bcf7142e0450480138b:/ftparchive/cachedb.cc diff --git a/ftparchive/cachedb.cc b/ftparchive/cachedb.cc index dd63f215a..b04244347 100644 --- a/ftparchive/cachedb.cc +++ b/ftparchive/cachedb.cc @@ -1,6 +1,6 @@ // -*- mode: cpp; mode: fold -*- // Description /*{{{*/ -// $Id: cachedb.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $ +// $Id: cachedb.cc,v 1.7 2004/05/08 19:41:01 mdz Exp $ /* ###################################################################### CacheDB @@ -10,14 +10,13 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "cachedb.h" -#endif - #include "cachedb.h" +#include #include #include +#include +#include #include #include @@ -27,8 +26,10 @@ // CacheDB::ReadyDB - Ready the DB2 /*{{{*/ // --------------------------------------------------------------------- /* This opens the DB2 file for caching package information */ -bool CacheDB::ReadyDB(string DB) +bool CacheDB::ReadyDB(string const &DB) { + int err; + ReadOnly = _config->FindB("APT::FTPArchive::ReadOnlyDB",false); // Close the old DB @@ -39,7 +40,7 @@ bool CacheDB::ReadyDB(string DB) corrupted DB */ if (DBFailed() == true) { - _error->Warning("DB was corrupted, file renamed to %s.old",DBFile.c_str()); + _error->Warning(_("DB was corrupted, file renamed to %s.old"),DBFile.c_str()); rename(DBFile.c_str(),(DBFile+".old").c_str()); } @@ -49,13 +50,32 @@ bool CacheDB::ReadyDB(string DB) if (DB.empty()) return true; - - if ((errno = db_open(DB.c_str(),DB_HASH, + + db_create(&Dbp, NULL, 0); + if ((err = Dbp->open(Dbp, NULL, DB.c_str(), NULL, DB_BTREE, (ReadOnly?DB_RDONLY:DB_CREATE), - 0644,0,0,&Dbp)) != 0) + 0644)) != 0) { - Dbp = 0; - return _error->Errno("db_open","Unable to open DB2 file %s",DB.c_str()); + if (err == DB_OLD_VERSION) + { + _error->Warning(_("DB is old, attempting to upgrade %s"),DBFile.c_str()); + err = Dbp->upgrade(Dbp, DB.c_str(), 0); + if (!err) + err = Dbp->open(Dbp, NULL, DB.c_str(), NULL, DB_HASH, + (ReadOnly?DB_RDONLY:DB_CREATE), 0644); + + } + // the database format has changed from DB_HASH to DB_BTREE in + // apt 0.6.44 + if (err == EINVAL) + { + _error->Error(_("DB format is invalid. If you upgraded from an older version of apt, please remove and re-create the database.")); + } + if (err) + { + Dbp = 0; + return _error->Error(_("Unable to open DB file %s: %s"),DB.c_str(), db_strerror(err)); + } } DBFile = DB; @@ -63,48 +83,127 @@ bool CacheDB::ReadyDB(string DB) return true; } /*}}}*/ -// CacheDB::SetFile - Select a file to be working with /*{{{*/ +// CacheDB::OpenFile - Open the file /*{{{*/ // --------------------------------------------------------------------- -/* All future actions will be performed against this file */ -bool CacheDB::SetFile(string FileName,struct stat St,FileFd *Fd) +/* */ +bool CacheDB::OpenFile() +{ + Fd = new FileFd(FileName,FileFd::ReadOnly); + if (_error->PendingError() == true) + { + delete Fd; + Fd = NULL; + return false; + } + return true; +} + /*}}}*/ +// CacheDB::GetFileStat - Get stats from the file /*{{{*/ +// --------------------------------------------------------------------- +/* This gets the size from the database if it's there. If we need + * to look at the file, also get the mtime from the file. */ +bool CacheDB::GetFileStat(bool const &doStat) +{ + if ((CurStat.Flags & FlSize) == FlSize && doStat == false) + { + /* Already worked out the file size */ + } + else + { + /* Get it from the file. */ + if (Fd == NULL && OpenFile() == false) + { + return false; + } + // Stat the file + struct stat St; + if (fstat(Fd->Fd(),&St) != 0) + { + return _error->Errno("fstat", + _("Failed to stat %s"),FileName.c_str()); + } + CurStat.FileSize = St.st_size; + CurStat.mtime = htonl(St.st_mtime); + CurStat.Flags |= FlSize; + } + return true; +} + /*}}}*/ +// CacheDB::GetCurStat - Set the CurStat variable. /*{{{*/ +// --------------------------------------------------------------------- +/* Sets the CurStat variable. Either to 0 if no database is used + * or to the value in the database if one is used */ +bool CacheDB::GetCurStat() { - delete DebFile; - DebFile = 0; - this->FileName = FileName; - this->Fd = Fd; - this->FileStat = St; - FileStat = St; memset(&CurStat,0,sizeof(CurStat)); - Stats.Bytes += St.st_size; - Stats.Packages++; - - if (DBLoaded == false) - return true; + if (DBLoaded) + { + /* First see if there is anything about it + in the database */ + /* Get the flags (and mtime) */ InitQuery("st"); - // Ensure alignment of the returned structure Data.data = &CurStat; Data.ulen = sizeof(CurStat); Data.flags = DB_DBT_USERMEM; - // Lookup the stat info and confirm the file is unchanged - if (Get() == true) - { - if (CurStat.st_mtime != htonl(St.st_mtime)) + if (Get() == false) { - CurStat.st_mtime = htonl(St.st_mtime); CurStat.Flags = 0; - _error->Warning("File date has changed %s",FileName.c_str()); } + CurStat.Flags = ntohl(CurStat.Flags); + CurStat.FileSize = ntohl(CurStat.FileSize); } - else + return true; +} + /*}}}*/ +// CacheDB::GetFileInfo - Get all the info about the file /*{{{*/ +// --------------------------------------------------------------------- +bool CacheDB::GetFileInfo(string const &FileName, bool const &DoControl, bool const &DoContents, + bool const &GenContentsOnly, bool const &DoMD5, bool const &DoSHA1, + bool const &DoSHA256, bool const &checkMtime) +{ + this->FileName = FileName; + + if (GetCurStat() == false) { - CurStat.st_mtime = htonl(St.st_mtime); - CurStat.Flags = 0; + return false; } - CurStat.Flags = ntohl(CurStat.Flags); OldStat = CurStat; + + if (GetFileStat(checkMtime) == false) + { + delete Fd; + Fd = NULL; + return false; + } + + /* if mtime changed, update CurStat from disk */ + if (checkMtime == true && OldStat.mtime != CurStat.mtime) + CurStat.Flags = FlSize; + + Stats.Bytes += CurStat.FileSize; + Stats.Packages++; + + if ((DoControl && LoadControl() == false) + || (DoContents && LoadContents(GenContentsOnly) == false) + || (DoMD5 && GetMD5(false) == false) + || (DoSHA1 && GetSHA1(false) == false) + || (DoSHA256 && GetSHA256(false) == false)) + { + delete Fd; + Fd = NULL; + delete DebFile; + DebFile = NULL; + return false; + } + + delete Fd; + Fd = NULL; + delete DebFile; + DebFile = NULL; + return true; } /*}}}*/ @@ -123,6 +222,10 @@ bool CacheDB::LoadControl() CurStat.Flags &= ~FlControl; } + if (Fd == NULL && OpenFile() == false) + { + return false; + } // Create a deb instance to read the archive if (DebFile == 0) { @@ -136,7 +239,7 @@ bool CacheDB::LoadControl() return false; if (Control.Control == 0) - return _error->Error("Archive has no control record"); + return _error->Error(_("Archive has no control record")); // Write back the control information InitQuery("cl"); @@ -148,7 +251,7 @@ bool CacheDB::LoadControl() // CacheDB::LoadContents - Load the File Listing /*{{{*/ // --------------------------------------------------------------------- /* */ -bool CacheDB::LoadContents(bool GenOnly) +bool CacheDB::LoadContents(bool const &GenOnly) { // Try to read the control information out of the DB. if ((CurStat.Flags & FlContents) == FlContents) @@ -167,6 +270,10 @@ bool CacheDB::LoadContents(bool GenOnly) CurStat.Flags &= ~FlContents; } + if (Fd == NULL && OpenFile() == false) + { + return false; + } // Create a deb instance to read the archive if (DebFile == 0) { @@ -185,10 +292,37 @@ bool CacheDB::LoadContents(bool GenOnly) return true; } /*}}}*/ + +static string bytes2hex(uint8_t *bytes, size_t length) { + char space[65]; + if (length * 2 > sizeof(space) - 1) length = (sizeof(space) - 1) / 2; + for (size_t i = 0; i < length; i++) + snprintf(&space[i*2], 3, "%02x", bytes[i]); + return string(space); +} + +static inline unsigned char xdig2num(char const &dig) { + if (isdigit(dig)) return dig - '0'; + if ('a' <= dig && dig <= 'f') return dig - 'a' + 10; + if ('A' <= dig && dig <= 'F') return dig - 'A' + 10; + return 0; +} + +static void hex2bytes(uint8_t *bytes, const char *hex, int length) { + while (length-- > 0) { + *bytes = 0; + if (isxdigit(hex[0]) && isxdigit(hex[1])) { + *bytes = xdig2num(hex[0]) * 16 + xdig2num(hex[1]); + hex += 2; + } + bytes++; + } +} + // CacheDB::GetMD5 - Get the MD5 hash /*{{{*/ // --------------------------------------------------------------------- /* */ -bool CacheDB::GetMD5(string &MD5Res,bool GenOnly) +bool CacheDB::GetMD5(bool const &GenOnly) { // Try to read the control information out of the DB. if ((CurStat.Flags & FlMD5) == FlMD5) @@ -196,28 +330,88 @@ bool CacheDB::GetMD5(string &MD5Res,bool GenOnly) if (GenOnly == true) return true; - InitQuery("m5"); - if (Get() == true) - { - MD5Res = string((char *)Data.data,Data.size); + MD5Res = bytes2hex(CurStat.MD5, sizeof(CurStat.MD5)); return true; } - CurStat.Flags &= ~FlMD5; - } - Stats.MD5Bytes += FileStat.st_size; + Stats.MD5Bytes += CurStat.FileSize; + if (Fd == NULL && OpenFile() == false) + { + return false; + } MD5Summation MD5; - if (Fd->Seek(0) == false || MD5.AddFD(Fd->Fd(),FileStat.st_size) == false) + if (Fd->Seek(0) == false || MD5.AddFD(Fd->Fd(),CurStat.FileSize) == false) return false; MD5Res = MD5.Result(); - InitQuery("m5"); - if (Put(MD5Res.begin(),MD5Res.length()) == true) + hex2bytes(CurStat.MD5, MD5Res.data(), sizeof(CurStat.MD5)); CurStat.Flags |= FlMD5; return true; } /*}}}*/ +// CacheDB::GetSHA1 - Get the SHA1 hash /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool CacheDB::GetSHA1(bool const &GenOnly) +{ + // Try to read the control information out of the DB. + if ((CurStat.Flags & FlSHA1) == FlSHA1) + { + if (GenOnly == true) + return true; + + SHA1Res = bytes2hex(CurStat.SHA1, sizeof(CurStat.SHA1)); + return true; + } + + Stats.SHA1Bytes += CurStat.FileSize; + + if (Fd == NULL && OpenFile() == false) + { + return false; + } + SHA1Summation SHA1; + if (Fd->Seek(0) == false || SHA1.AddFD(Fd->Fd(),CurStat.FileSize) == false) + return false; + + SHA1Res = SHA1.Result(); + hex2bytes(CurStat.SHA1, SHA1Res.data(), sizeof(CurStat.SHA1)); + CurStat.Flags |= FlSHA1; + return true; +} + /*}}}*/ +// CacheDB::GetSHA256 - Get the SHA256 hash /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool CacheDB::GetSHA256(bool const &GenOnly) +{ + // Try to read the control information out of the DB. + if ((CurStat.Flags & FlSHA256) == FlSHA256) + { + if (GenOnly == true) + return true; + + SHA256Res = bytes2hex(CurStat.SHA256, sizeof(CurStat.SHA256)); + return true; + } + + Stats.SHA256Bytes += CurStat.FileSize; + + if (Fd == NULL && OpenFile() == false) + { + return false; + } + SHA256Summation SHA256; + if (Fd->Seek(0) == false || SHA256.AddFD(Fd->Fd(),CurStat.FileSize) == false) + return false; + + SHA256Res = SHA256.Result(); + hex2bytes(CurStat.SHA256, SHA256Res.data(), sizeof(CurStat.SHA256)); + CurStat.Flags |= FlSHA256; + return true; +} + /*}}}*/ // CacheDB::Finish - Write back the cache structure /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -225,14 +419,17 @@ bool CacheDB::Finish() { // Optimize away some writes. if (CurStat.Flags == OldStat.Flags && - CurStat.st_mtime == OldStat.st_mtime) + CurStat.mtime == OldStat.mtime) return true; // Write the stat information CurStat.Flags = htonl(CurStat.Flags); + CurStat.FileSize = htonl(CurStat.FileSize); InitQuery("st"); Put(&CurStat,sizeof(CurStat)); CurStat.Flags = ntohl(CurStat.Flags); + CurStat.FileSize = ntohl(CurStat.FileSize); + return true; } /*}}}*/ @@ -246,15 +443,9 @@ bool CacheDB::Clean() /* I'm not sure what VERSION_MINOR should be here.. 2.4.14 certainly needs the lower one and 2.7.7 needs the upper.. */ -#if DB_VERSION_MAJOR >= 2 && DB_VERSION_MINOR >= 7 - DBC *Cursor; - if ((errno = Dbp->cursor(Dbp,0,&Cursor,0)) != 0) - return _error->Error("Unable to get a cursor"); -#else DBC *Cursor; - if ((errno = Dbp->cursor(Dbp,0,&Cursor)) != 0) - return _error->Error("Unable to get a cursor"); -#endif + if ((errno = Dbp->cursor(Dbp, NULL, &Cursor, 0)) != 0) + return _error->Error(_("Unable to get a cursor")); DBT Key; DBT Data; @@ -262,22 +453,21 @@ bool CacheDB::Clean() memset(&Data,0,sizeof(Data)); while ((errno = Cursor->c_get(Cursor,&Key,&Data,DB_NEXT)) == 0) { - const char *Colon = (char *)Key.data; - for (; Colon != (char *)Key.data+Key.size && *Colon != ':'; Colon++); - if ((char *)Key.data+Key.size - Colon > 2) + const char *Colon = (char*)memrchr(Key.data, ':', Key.size); + if (Colon) { - if (stringcmp((char *)Key.data,Colon,"st") == 0 || - stringcmp((char *)Key.data,Colon,"cn") == 0 || - stringcmp((char *)Key.data,Colon,"m5") == 0 || - stringcmp((char *)Key.data,Colon,"cl") == 0) + if (stringcmp(Colon + 1, (char *)Key.data+Key.size,"st") == 0 || + stringcmp(Colon + 1, (char *)Key.data+Key.size,"cl") == 0 || + stringcmp(Colon + 1, (char *)Key.data+Key.size,"cn") == 0) { - if (FileExists(string(Colon+1,(const char *)Key.data+Key.size)) == true) + if (FileExists(string((const char *)Key.data,Colon)) == true) continue; } } Cursor->c_del(Cursor,0); } + Dbp->compact(Dbp, NULL, NULL, NULL, NULL, DB_FREE_SPACE, NULL); return true; }