diff options
Diffstat (limited to 'methods')
-rw-r--r-- | methods/connect.cc | 2 | ||||
-rw-r--r-- | methods/ftp.cc | 7 | ||||
-rw-r--r-- | methods/gpgv.cc | 36 | ||||
-rw-r--r-- | methods/http.cc | 63 | ||||
-rw-r--r-- | methods/http.h | 5 | ||||
-rw-r--r-- | methods/makefile | 9 | ||||
-rw-r--r-- | methods/rred.cc | 262 |
7 files changed, 355 insertions, 29 deletions
diff --git a/methods/connect.cc b/methods/connect.cc index 4e48927ed..8c2ac6d56 100644 --- a/methods/connect.cc +++ b/methods/connect.cc @@ -103,6 +103,8 @@ static bool DoConnect(struct addrinfo *Addr,string Host, if (Err != 0) { errno = Err; + if(errno == ECONNREFUSED) + Owner->SetFailExtraMsg("\nFailReason: ConnectionRefused"); return _error->Errno("connect",_("Could not connect to %s:%s (%s)."),Host.c_str(), Service,Name); } diff --git a/methods/ftp.cc b/methods/ftp.cc index f595e0ca4..0c2aa00a7 100644 --- a/methods/ftp.cc +++ b/methods/ftp.cc @@ -1055,9 +1055,12 @@ bool FtpMethod::Fetch(FetchItem *Itm) UBuf.modtime = FailTime; utime(FailFile.c_str(),&UBuf); - // If the file is missing we hard fail otherwise transient fail - if (Missing == true) + // If the file is missing we hard fail and delete the destfile + // otherwise transient fail + if (Missing == true) { + unlink(FailFile.c_str()); return false; + } Fail(true); return true; } diff --git a/methods/gpgv.cc b/methods/gpgv.cc index 5cb154f66..ba7389cba 100644 --- a/methods/gpgv.cc +++ b/methods/gpgv.cc @@ -11,6 +11,7 @@ #include <errno.h> #include <sys/wait.h> #include <iostream> +#include <sstream> #define GNUPGPREFIX "[GNUPG:]" #define GNUPGBADSIG "[GNUPG:] BADSIG" @@ -20,7 +21,7 @@ class GPGVMethod : public pkgAcqMethod { private: - const char *VerifyGetSigners(const char *file, const char *outfile, + string VerifyGetSigners(const char *file, const char *outfile, vector<string> &GoodSigners, vector<string> &BadSigners, vector<string> &NoPubKeySigners); @@ -32,11 +33,15 @@ class GPGVMethod : public pkgAcqMethod GPGVMethod() : pkgAcqMethod("1.0",SingleInstance | SendConfig) {}; }; -const char *GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, +string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, vector<string> &GoodSigners, vector<string> &BadSigners, vector<string> &NoPubKeySigners) { + // setup a (empty) stringstream for formating the return value + std::stringstream ret; + ret.str(""); + if (_config->FindB("Debug::Acquire::gpgv", false)) { std::cerr << "inside VerifyGetSigners" << std::endl; @@ -54,9 +59,11 @@ const char *GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, std::cerr << "Keyring path: " << pubringpath << std::endl; } - if (stat(pubringpath.c_str(), &buff) != 0) - return (string("Couldn't access keyring: ") + strerror(errno)).c_str(); - + if (stat(pubringpath.c_str(), &buff) != 0) + { + ioprintf(ret, _("Couldn't access keyring: '%s'"), strerror(errno)); + return ret.str(); + } if (pipe(fd) < 0) { return "Couldn't create pipe"; @@ -65,7 +72,7 @@ const char *GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, pid = fork(); if (pid < 0) { - return (string("Couldn't spawn new process") + strerror(errno)).c_str(); + return string("Couldn't spawn new process") + strerror(errno); } else if (pid == 0) { @@ -189,7 +196,7 @@ const char *GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, { if (GoodSigners.empty()) return _("Internal error: Good signature, but could not determine key fingerprint?!"); - return NULL; + return ""; } else if (WEXITSTATUS(status) == 1) { @@ -197,9 +204,8 @@ const char *GPGVMethod::VerifyGetSigners(const char *file, const char *outfile, } else if (WEXITSTATUS(status) == 111) { - // FIXME String concatenation considered harmful. - return (string(_("Could not execute ")) + gpgvpath + - string(_(" to verify signature (is gnupg installed?)"))).c_str(); + ioprintf(ret, _("Could not execute '%s' to verify signature (is gnupg installed?)"), gpgvpath.c_str()); + return ret.str(); } else { @@ -221,8 +227,8 @@ bool GPGVMethod::Fetch(FetchItem *Itm) URIStart(Res); // Run gpgv on file, extract contents and get the key ID of the signer - const char *msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), - GoodSigners, BadSigners, NoPubKeySigners); + string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), + GoodSigners, BadSigners, NoPubKeySigners); if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty()) { string errmsg; @@ -247,7 +253,11 @@ bool GPGVMethod::Fetch(FetchItem *Itm) errmsg += (*I + "\n"); } } - return _error->Error(errmsg.c_str()); + // this is only fatal if we have no good sigs or if we have at + // least one bad signature. good signatures and NoPubKey signatures + // happen easily when a file is signed with multiple signatures + if(GoodSigners.empty() or !BadSigners.empty()) + return _error->Error(errmsg.c_str()); } // Transfer the modification times diff --git a/methods/http.cc b/methods/http.cc index ba86aa6b6..cb63ada49 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -58,6 +58,11 @@ unsigned long PipelineDepth = 10; unsigned long TimeOut = 120; bool Debug = false; +unsigned long CircleBuf::BwReadLimit=0; +unsigned long CircleBuf::BwTickReadData=0; +struct timeval CircleBuf::BwReadTick={0,0}; +const unsigned int CircleBuf::BW_HZ=10; + // CircleBuf::CircleBuf - Circular input buffer /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -65,6 +70,8 @@ CircleBuf::CircleBuf(unsigned long Size) : Size(Size), Hash(0) { Buf = new unsigned char[Size]; Reset(); + + CircleBuf::BwReadLimit = _config->FindI("Acquire::http::Dl-Limit",0)*1024; } /*}}}*/ // CircleBuf::Reset - Reset to the default state /*{{{*/ @@ -90,16 +97,45 @@ void CircleBuf::Reset() is non-blocking.. */ bool CircleBuf::Read(int Fd) { + unsigned long BwReadMax; + while (1) { // Woops, buffer is full if (InP - OutP == Size) return true; - + + // what's left to read in this tick + BwReadMax = CircleBuf::BwReadLimit/BW_HZ; + + if(CircleBuf::BwReadLimit) { + struct timeval now; + gettimeofday(&now,0); + + unsigned long d = (now.tv_sec-CircleBuf::BwReadTick.tv_sec)*1000000 + + now.tv_usec-CircleBuf::BwReadTick.tv_usec; + if(d > 1000000/BW_HZ) { + CircleBuf::BwReadTick = now; + CircleBuf::BwTickReadData = 0; + } + + if(CircleBuf::BwTickReadData >= BwReadMax) { + usleep(1000000/BW_HZ); + return true; + } + } + // Write the buffer segment int Res; - Res = read(Fd,Buf + (InP%Size),LeftRead()); + if(CircleBuf::BwReadLimit) { + Res = read(Fd,Buf + (InP%Size), + BwReadMax > LeftRead() ? LeftRead() : BwReadMax); + } else + Res = read(Fd,Buf + (InP%Size),LeftRead()); + if(Res > 0 && BwReadLimit > 0) + CircleBuf::BwTickReadData += Res; + if (Res == 0) return false; if (Res < 0) @@ -204,28 +240,23 @@ bool CircleBuf::WriteTillEl(string &Data,bool Single) if (Buf[I%Size] != '\n') continue; ++I; - if (I < InP && Buf[I%Size] == '\r') - ++I; if (Single == false) { - if (Buf[I%Size] != '\n') - continue; - ++I; if (I < InP && Buf[I%Size] == '\r') ++I; + if (I >= InP || Buf[I%Size] != '\n') + continue; + ++I; } - if (I > InP) - I = InP; - Data = ""; while (OutP < I) { unsigned long Sz = LeftWrite(); if (Sz == 0) return false; - if (I - OutP < LeftWrite()) + if (I - OutP < Sz) Sz = I - OutP; Data += string((char *)(Buf + (OutP%Size)),Sz); OutP += Sz; @@ -787,7 +818,10 @@ bool HttpMethod::Flush(ServerState *Srv) { if (File != 0) { - SetNonBlock(File->Fd(),false); + // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking + // can't be set + if (File->Name() != "/dev/null") + SetNonBlock(File->Fd(),false); if (Srv->In.WriteSpace() == false) return true; @@ -815,7 +849,10 @@ bool HttpMethod::ServerDie(ServerState *Srv) // Dump the buffer to the file if (Srv->State == ServerState::Data) { - SetNonBlock(File->Fd(),false); + // on GNU/kFreeBSD, apt dies on /dev/null because non-blocking + // can't be set + if (File->Name() != "/dev/null") + SetNonBlock(File->Fd(),false); while (Srv->In.WriteSpace() == true) { if (Srv->In.Write(File->Fd()) == false) diff --git a/methods/http.h b/methods/http.h index c5a4d0e86..541e2952c 100644 --- a/methods/http.h +++ b/methods/http.h @@ -31,6 +31,11 @@ class CircleBuf unsigned long MaxGet; struct timeval Start; + static unsigned long BwReadLimit; + static unsigned long BwTickReadData; + static struct timeval BwReadTick; + static const unsigned int BW_HZ; + unsigned long LeftRead() { unsigned long Sz = Size - (InP - OutP); diff --git a/methods/makefile b/methods/makefile index 1e3b1ef85..03146d1bc 100644 --- a/methods/makefile +++ b/methods/makefile @@ -7,7 +7,7 @@ include ../buildlib/defaults.mak BIN := $(BIN)/methods # FIXME.. -LIB_APT_PKG_MAJOR = 3.11 +LIB_APT_PKG_MAJOR = 4.0 APT_DOMAIN := libapt-pkg$(LIB_APT_PKG_MAJOR) # The file method @@ -59,6 +59,13 @@ LIB_MAKES = apt-pkg/makefile SOURCE = ftp.cc rfc2553emu.cc connect.cc include $(PROGRAM_H) +# The rred method +PROGRAM=rred +SLIBS = -lapt-pkg $(SOCKETLIBS) +LIB_MAKES = apt-pkg/makefile +SOURCE = rred.cc +include $(PROGRAM_H) + # The rsh method PROGRAM=rsh SLIBS = -lapt-pkg diff --git a/methods/rred.cc b/methods/rred.cc new file mode 100644 index 000000000..6fa57f3a6 --- /dev/null +++ b/methods/rred.cc @@ -0,0 +1,262 @@ +#include <apt-pkg/fileutl.h> +#include <apt-pkg/error.h> +#include <apt-pkg/acquire-method.h> +#include <apt-pkg/strutl.h> +#include <apt-pkg/hashes.h> + +#include <sys/stat.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> +#include <errno.h> +#include <apti18n.h> + +/* this method implements a patch functionality similar to "patch --ed" that is + * used by the "tiffany" incremental packages download stuff. it differs from + * "ed" insofar that it is way more restricted (and therefore secure). in the + * moment only the "c", "a" and "d" commands of ed are implemented (diff + * doesn't output any other). additionally the records must be reverse sorted + * by line number and may not overlap (diff *seems* to produce this kind of + * output). + * */ + +const char *Prog; + +class RredMethod : public pkgAcqMethod +{ + bool Debug; + // the size of this doesn't really matter (except for performance) + const static int BUF_SIZE = 1024; + // the ed commands + enum Mode {MODE_CHANGED, MODE_DELETED, MODE_ADDED}; + // return values + enum State {ED_OK, ED_ORDERING, ED_PARSER, ED_FAILURE}; + // this applies a single hunk, it uses a tail recursion to + // reverse the hunks in the file + int ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line, + char *buffer, unsigned int bufsize, Hashes *hash); + // apply a patch file + int ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file, Hashes *hash); + // the methods main method + virtual bool Fetch(FetchItem *Itm); + + public: + + RredMethod() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {}; +}; + +int RredMethod::ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line, + char *buffer, unsigned int bufsize, Hashes *hash) { + int pos; + int startline; + int stopline; + int mode; + int written; + char *idx; + + /* get the current command and parse it*/ + if (fgets(buffer, bufsize, ed_cmds) == NULL) { + return line; + } + startline = strtol(buffer, &idx, 10); + if (startline < line) { + return ED_ORDERING; + } + if (*idx == ',') { + idx++; + stopline = strtol(idx, &idx, 10); + } + else { + stopline = startline; + } + if (*idx == 'c') { + mode = MODE_CHANGED; + if (Debug == true) { + std::clog << "changing from line " << startline + << " to " << stopline << std::endl; + } + } + else if (*idx == 'a') { + mode = MODE_ADDED; + if (Debug == true) { + std::clog << "adding after line " << startline << std::endl; + } + } + else if (*idx == 'd') { + mode = MODE_DELETED; + if (Debug == true) { + std::clog << "deleting from line " << startline + << " to " << stopline << std::endl; + } + } + else { + return ED_PARSER; + } + /* get the current position */ + pos = ftell(ed_cmds); + /* if this is add or change then go to the next full stop */ + if ((mode == MODE_CHANGED) || (mode == MODE_ADDED)) { + do { + fgets(buffer, bufsize, ed_cmds); + while ((strlen(buffer) == (bufsize - 1)) + && (buffer[bufsize - 2] != '\n')) { + fgets(buffer, bufsize, ed_cmds); + buffer[0] = ' '; + } + } while (strncmp(buffer, ".", 1) != 0); + } + /* do the recursive call */ + line = ed_rec(ed_cmds, in_file, out_file, line, buffer, bufsize, + hash); + /* pass on errors */ + if (line < 0) { + return line; + } + /* apply our hunk */ + fseek(ed_cmds, pos, SEEK_SET); + /* first wind to the current position */ + if (mode != MODE_ADDED) { + startline -= 1; + } + while (line < startline) { + fgets(buffer, bufsize, in_file); + written = fwrite(buffer, 1, strlen(buffer), out_file); + hash->Add((unsigned char*)buffer, written); + while ((strlen(buffer) == (bufsize - 1)) + && (buffer[bufsize - 2] != '\n')) { + fgets(buffer, bufsize, in_file); + written = fwrite(buffer, 1, strlen(buffer), out_file); + hash->Add((unsigned char*)buffer, written); + } + line++; + } + /* include from ed script */ + if ((mode == MODE_ADDED) || (mode == MODE_CHANGED)) { + do { + fgets(buffer, bufsize, ed_cmds); + if (strncmp(buffer, ".", 1) != 0) { + written = fwrite(buffer, 1, strlen(buffer), out_file); + hash->Add((unsigned char*)buffer, written); + while ((strlen(buffer) == (bufsize - 1)) + && (buffer[bufsize - 2] != '\n')) { + fgets(buffer, bufsize, ed_cmds); + written = fwrite(buffer, 1, strlen(buffer), out_file); + hash->Add((unsigned char*)buffer, written); + } + } + else { + break; + } + } while (1); + } + /* ignore the corresponding number of lines from input */ + if ((mode == MODE_DELETED) || (mode == MODE_CHANGED)) { + while (line < stopline) { + fgets(buffer, bufsize, in_file); + while ((strlen(buffer) == (bufsize - 1)) + && (buffer[bufsize - 2] != '\n')) { + fgets(buffer, bufsize, in_file); + } + line++; + } + } + return line; +} + +int RredMethod::ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file, + Hashes *hash) { + char buffer[BUF_SIZE]; + int result; + int written; + + /* we do a tail recursion to read the commands in the right order */ + result = ed_rec(ed_cmds, in_file, out_file, 0, buffer, BUF_SIZE, + hash); + + /* read the rest from infile */ + if (result > 0) { + while (fgets(buffer, BUF_SIZE, in_file) != NULL) { + written = fwrite(buffer, 1, strlen(buffer), out_file); + hash->Add((unsigned char*)buffer, written); + } + } + else { + return ED_FAILURE; + } + return ED_OK; +} + + +bool RredMethod::Fetch(FetchItem *Itm) +{ + Debug = _config->FindB("Debug::pkgAcquire::RRed",false); + URI Get = Itm->Uri; + string Path = Get.Host + Get.Path; // To account for relative paths + // Path contains the filename to patch + FetchResult Res; + Res.Filename = Itm->DestFile; + URIStart(Res); + // Res.Filename the destination filename + + if (Debug == true) + std::clog << "Patching " << Path << " with " << Path + << ".ed and putting result into " << Itm->DestFile << std::endl; + // Open the source and destination files (the d'tor of FileFd will do + // the cleanup/closing of the fds) + FileFd From(Path,FileFd::ReadOnly); + FileFd Patch(Path+".ed",FileFd::ReadOnly); + FileFd To(Itm->DestFile,FileFd::WriteEmpty); + To.EraseOnFailure(); + if (_error->PendingError() == true) + return false; + + Hashes Hash; + FILE* fFrom = fdopen(From.Fd(), "r"); + FILE* fPatch = fdopen(Patch.Fd(), "r"); + FILE* fTo = fdopen(To.Fd(), "w"); + // now do the actual patching + if (ed_file(fPatch, fFrom, fTo, &Hash) != ED_OK) { + _error->Errno("rred", _("Could not patch file")); + return false; + } + + // write out the result + fflush(fFrom); + fflush(fPatch); + fflush(fTo); + From.Close(); + Patch.Close(); + To.Close(); + + // Transfer the modification times + struct stat Buf; + if (stat(Path.c_str(),&Buf) != 0) + return _error->Errno("stat",_("Failed to stat")); + + struct utimbuf TimeBuf; + TimeBuf.actime = Buf.st_atime; + TimeBuf.modtime = Buf.st_mtime; + if (utime(Itm->DestFile.c_str(),&TimeBuf) != 0) + return _error->Errno("utime",_("Failed to set modification time")); + + if (stat(Itm->DestFile.c_str(),&Buf) != 0) + return _error->Errno("stat",_("Failed to stat")); + + // return done + Res.LastModified = Buf.st_mtime; + Res.Size = Buf.st_size; + Res.TakeHashes(Hash); + URIDone(Res); + + return true; +} + +int main(int argc, char *argv[]) +{ + RredMethod Mth; + + Prog = strrchr(argv[0],'/'); + Prog++; + + return Mth.Run(); +} |