X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/0ce20d50de158a45a588062754673014675ac993..2853201580944cb4e5a2a8a810e800e89e9a0475:/methods/rred.cc?ds=inline diff --git a/methods/rred.cc b/methods/rred.cc index 313166160..79ab8cb52 100644 --- a/methods/rred.cc +++ b/methods/rred.cc @@ -7,25 +7,28 @@ #include +#include #include -#include #include #include #include #include #include +#include "aptmethod.h" +#include +#include #include #include #include -#include #include +#include #include #include #include #include -#include +#include #include @@ -35,13 +38,11 @@ class MemBlock { char *start; size_t size; char *free; - struct MemBlock *next; + MemBlock *next; - MemBlock(size_t size) + explicit MemBlock(size_t size) : size(size), next(NULL) { free = start = new char[size]; - size = size; - next = NULL; } size_t avail(void) { return size - (free - start); } @@ -118,7 +119,7 @@ struct Change { size_t add_len; /* bytes */ char *add; - Change(int off) + explicit Change(size_t off) { offset = off; del_cnt = add_cnt = add_len = 0; @@ -152,12 +153,12 @@ class FileChanges { std::list::iterator where; size_t pos; // line number is as far left of iterator as possible - bool pos_is_okay(void) + bool pos_is_okay(void) const { #ifdef POSDEBUG size_t cpos = 0; - std::list::iterator x; - for (x = changes.begin(); x != where; x++) { + std::list::const_iterator x; + for (x = changes.begin(); x != where; ++x) { assert(x != changes.end()); cpos += x->offset + x->add_cnt; } @@ -208,7 +209,7 @@ class FileChanges { left(); } std::list::iterator next = where; - next++; + ++next; while (next != changes.end() && next->offset == 0) { where->del_cnt += next->del_cnt; @@ -221,7 +222,7 @@ class FileChanges { where->add_cnt = next->add_cnt; next = changes.erase(next); } else { - next++; + ++next; } } } @@ -261,7 +262,7 @@ class FileChanges { if (where != changes.end()) where->offset -= offset; changes.insert(where, Change(offset)); - where--; + --where; assert(pos_is_okay()); } @@ -284,24 +285,8 @@ class FileChanges { before.add_len -= where->add_len; changes.insert(where, before); - where--; - assert(pos_is_okay()); - } - - size_t check_next_offset(size_t max) - { + --where; assert(pos_is_okay()); - if (max > 0) - { - where++; - if (where != changes.end()) { - if (where->offset < max) - max = where->offset; - } - where--; - assert(pos_is_okay()); - } - return max; } void delete_lines(size_t cnt) @@ -317,7 +302,7 @@ class FileChanges { x->skip_lines(del); cnt -= del; - x++; + ++x; if (x == changes.end()) { del = cnt; } else { @@ -334,7 +319,7 @@ class FileChanges { void left(void) { assert(pos_is_okay()); - where--; + --where; pos -= where->offset + where->add_cnt; assert(pos_is_okay()); } @@ -342,7 +327,7 @@ class FileChanges { void right(void) { assert(pos_is_okay()); pos += where->offset + where->add_cnt; - where++; + ++where; assert(pos_is_okay()); } }; @@ -351,84 +336,87 @@ class Patch { FileChanges filechanges; MemBlock add_text; - static void dump_rest(FILE *o, FILE *i, Hashes *hash) + static bool retry_fwrite(char *b, size_t l, FileFd &f, Hashes *hash) + { + if (f.Write(b, l) == false) + return false; + if (hash) + hash->Add((unsigned char*)b, l); + return true; + } + + static void dump_rest(FileFd &o, FileFd &i, Hashes *hash) { char buffer[BLOCK_SIZE]; - size_t l; - while (0 < (l = fread(buffer, 1, sizeof(buffer), i))) { - fwrite(buffer, 1, l, o); - if (hash) - hash->Add((unsigned char*)buffer, l); + unsigned long long l = 0; + while (i.Read(buffer, sizeof(buffer), &l)) { + if (l ==0 || !retry_fwrite(buffer, l, o, hash)) + break; } } - static void dump_lines(FILE *o, FILE *i, size_t n, Hashes *hash) + static void dump_lines(FileFd &o, FileFd &i, size_t n, Hashes *hash) { char buffer[BLOCK_SIZE]; - size_t l; while (n > 0) { - if (fgets(buffer, sizeof(buffer), i) == 0) + if (i.ReadLine(buffer, sizeof(buffer)) == NULL) buffer[0] = '\0'; - l = strlen(buffer); + size_t const l = strlen(buffer); if (l == 0 || buffer[l-1] == '\n') n--; - fwrite(buffer, 1, l, o); - - if (hash) - hash->Add((unsigned char*)buffer, l); + retry_fwrite(buffer, l, o, hash); } } - static void skip_lines(FILE *i, int n) + static void skip_lines(FileFd &i, int n) { char buffer[BLOCK_SIZE]; - size_t l; while (n > 0) { - if (fgets(buffer, sizeof(buffer), i) == 0) + if (i.ReadLine(buffer, sizeof(buffer)) == NULL) buffer[0] = '\0'; - l = strlen(buffer); + size_t const l = strlen(buffer); if (l == 0 || buffer[l-1] == '\n') n--; } } - static bool dump_mem(FILE *o, char *p, size_t s, Hashes *hash) { - size_t r; - while (s > 0) { - r = fwrite(p, 1, s, o); - if (hash) - hash->Add((unsigned char*)p, s); - s -= r; - p += r; - if (r == 0) return false; - } - return true; + static void dump_mem(FileFd &o, char *p, size_t s, Hashes *hash) { + retry_fwrite(p, s, o, hash); } public: - void read_diff(FileFd &f) + bool read_diff(FileFd &f, Hashes * const h) { char buffer[BLOCK_SIZE]; bool cmdwanted = true; - Change ch(0); - while(f.ReadLine(buffer, sizeof(buffer))) - { + Change ch(std::numeric_limits::max()); + if (f.ReadLine(buffer, sizeof(buffer)) == NULL) + return _error->Error("Reading first line of patchfile %s failed", f.Name().c_str()); + do { + if (h != NULL) + h->Add(buffer); if (cmdwanted) { char *m, *c; size_t s, e; - s = strtol(buffer, &m, 10); - if (m == buffer) { - s = e = ch.offset + ch.add_cnt; - c = buffer; - } else if (*m == ',') { - m++; + errno = 0; + s = strtoul(buffer, &m, 10); + if (unlikely(m == buffer || s == std::numeric_limits::max() || errno != 0)) + return _error->Error("Parsing patchfile %s failed: Expected an effected line start", f.Name().c_str()); + else if (*m == ',') { + ++m; e = strtol(m, &c, 10); + if (unlikely(m == c || e == std::numeric_limits::max() || errno != 0)) + return _error->Error("Parsing patchfile %s failed: Expected an effected line end", f.Name().c_str()); + if (unlikely(e < s)) + return _error->Error("Parsing patchfile %s failed: Effected lines end %lu is before start %lu", f.Name().c_str(), e, s); } else { e = s; c = m; } + if (s > ch.offset) + return _error->Error("Parsing patchfile %s failed: Effected line is after previous effected line", f.Name().c_str()); switch(*c) { case 'a': cmdwanted = false; @@ -439,6 +427,8 @@ class Patch { ch.del_cnt = 0; break; case 'c': + if (unlikely(s == 0)) + return _error->Error("Parsing patchfile %s failed: Change command can't effect line zero", f.Name().c_str()); cmdwanted = false; ch.add = NULL; ch.add_cnt = 0; @@ -447,6 +437,8 @@ class Patch { ch.del_cnt = e - s + 1; break; case 'd': + if (unlikely(s == 0)) + return _error->Error("Parsing patchfile %s failed: Delete command can't effect line zero", f.Name().c_str()); ch.offset = s - 1; ch.del_cnt = e - s + 1; ch.add = NULL; @@ -454,9 +446,11 @@ class Patch { ch.add_len = 0; filechanges.add_change(ch); break; + default: + return _error->Error("Parsing patchfile %s failed: Unknown command", f.Name().c_str()); } - } else { /* !cmdwaanted */ - if (buffer[0] == '.' && buffer[1] == '\n') { + } else { /* !cmdwanted */ + if (strcmp(buffer, ".\n") == 0) { cmdwanted = true; filechanges.add_change(ch); } else { @@ -482,64 +476,96 @@ class Patch { } } } - } + } while(f.ReadLine(buffer, sizeof(buffer))); + return true; } - void write_diff(FILE *f) + void write_diff(FileFd &f) { - size_t line = 0; + unsigned long long line = 0; std::list::reverse_iterator ch; - for (ch = filechanges.rbegin(); ch != filechanges.rend(); ch++) { + for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) { line += ch->offset + ch->del_cnt; } - for (ch = filechanges.rbegin(); ch != filechanges.rend(); ch++) { + for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) { std::list::reverse_iterator mg_i, mg_e = ch; while (ch->del_cnt == 0 && ch->offset == 0) - ch++; + { + ++ch; + if (unlikely(ch == filechanges.rend())) + return; + } line -= ch->del_cnt; + std::string buf; if (ch->add_cnt > 0) { if (ch->del_cnt == 0) { - fprintf(f, "%lua\n", line); + strprintf(buf, "%llua\n", line); } else if (ch->del_cnt == 1) { - fprintf(f, "%luc\n", line+1); + strprintf(buf, "%lluc\n", line+1); } else { - fprintf(f, "%lu,%luc\n", line+1, line+ch->del_cnt); + strprintf(buf, "%llu,%lluc\n", line+1, line+ch->del_cnt); } + f.Write(buf.c_str(), buf.length()); mg_i = ch; do { dump_mem(f, mg_i->add, mg_i->add_len, NULL); } while (mg_i-- != mg_e); - fprintf(f, ".\n"); + buf = ".\n"; + f.Write(buf.c_str(), buf.length()); } else if (ch->del_cnt == 1) { - fprintf(f, "%lud\n", line+1); + strprintf(buf, "%llud\n", line+1); + f.Write(buf.c_str(), buf.length()); } else if (ch->del_cnt > 1) { - fprintf(f, "%lu,%lud\n", line+1, line+ch->del_cnt); + strprintf(buf, "%llu,%llud\n", line+1, line+ch->del_cnt); + f.Write(buf.c_str(), buf.length()); } line -= ch->offset; } } - void apply_against_file(FILE *out, FILE *in, Hashes *hash = NULL) + void apply_against_file(FileFd &out, FileFd &in, Hashes *hash = NULL) { std::list::iterator ch; - for (ch = filechanges.begin(); ch != filechanges.end(); ch++) { + for (ch = filechanges.begin(); ch != filechanges.end(); ++ch) { dump_lines(out, in, ch->offset, hash); skip_lines(in, ch->del_cnt); dump_mem(out, ch->add, ch->add_len, hash); } dump_rest(out, in, hash); + out.Flush(); } }; -class RredMethod : public pkgAcqMethod { +class RredMethod : public aptMethod { private: bool Debug; + struct PDiffFile { + std::string FileName; + HashStringList ExpectedHashes; + PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes) : + FileName(FileName), ExpectedHashes(ExpectedHashes) {} + }; + + HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message) + { + HashStringList ExpectedHashes; + for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) + { + std::string tagname; + strprintf(tagname, "Patch-%d-%s-Hash", patch, *type); + std::string const hashsum = LookupTag(Message, tagname.c_str()); + if (hashsum.empty() == false) + ExpectedHashes.push_back(HashString(*type, hashsum)); + } + return ExpectedHashes; + } + protected: - virtual bool Fetch(FetchItem *Itm) { + virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE { Debug = _config->FindB("Debug::pkgAcquire::RRed", false); URI Get = Itm->Uri; std::string Path = Get.Host + Get.Path; // rred:/path - no host @@ -553,11 +579,17 @@ class RredMethod : public pkgAcqMethod { } else URIStart(Res); - std::vector patchpaths; + std::vector patchfiles; Patch patch; if (FileExists(Path + ".ed") == true) - patchpaths.push_back(Path + ".ed"); + { + HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(0, Message); + std::string const FileName = Path + ".ed"; + if (ExpectedHashes.usable() == false) + return _error->Error("No hashes found for uncompressed patch: %s", FileName.c_str()); + patchfiles.push_back(PDiffFile(FileName, ExpectedHashes)); + } else { _error->PushToStack(); @@ -565,31 +597,44 @@ class RredMethod : public pkgAcqMethod { _error->RevertToStack(); std::string const baseName = Path + ".ed."; + unsigned int seen_patches = 0; for (std::vector::const_iterator p = patches.begin(); p != patches.end(); ++p) + { if (p->compare(0, baseName.length(), baseName) == 0) - patchpaths.push_back(*p); + { + HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(seen_patches, Message); + if (ExpectedHashes.usable() == false) + return _error->Error("No hashes found for uncompressed patch %d: %s", seen_patches, p->c_str()); + patchfiles.push_back(PDiffFile(*p, ExpectedHashes)); + ++seen_patches; + } + } } std::string patch_name; - for (std::vector::iterator I = patchpaths.begin(); - I != patchpaths.end(); - I++) + for (std::vector::iterator I = patchfiles.begin(); + I != patchfiles.end(); + ++I) { - patch_name = *I; + patch_name = I->FileName; if (Debug == true) std::clog << "Patching " << Path << " with " << patch_name << std::endl; FileFd p; + Hashes patch_hash(I->ExpectedHashes); // all patches are compressed, even if the name doesn't reflect it - if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false) { - std::cerr << "Could not open patch file " << patch_name << std::endl; - _error->DumpErrors(std::cerr); - abort(); + if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false || + patch.read_diff(p, &patch_hash) == false) + { + _error->DumpErrors(std::cerr, GlobalError::DEBUG, false); + return false; } - patch.read_diff(p); p.Close(); + HashStringList const hsl = patch_hash.GetHashStringList(); + if (hsl != I->ExpectedHashes) + return _error->Error("Hash Sum mismatch for uncompressed patch %s", patch_name.c_str()); } if (Debug == true) @@ -597,15 +642,28 @@ class RredMethod : public pkgAcqMethod { << " and writing results to " << Itm->DestFile << std::endl; - FILE *inp = fopen(Path.c_str(), "r"); - FILE *out = fopen(Itm->DestFile.c_str(), "w"); - - Hashes hash; + FileFd inp, out; + if (inp.Open(Path, FileFd::ReadOnly, FileFd::Extension) == false) + { + std::cerr << "FAILED to open inp " << Path << std::endl; + return _error->Error("Failed to open inp %s", Path.c_str()); + } + if (out.Open(Itm->DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite, FileFd::Extension) == false) + { + std::cerr << "FAILED to open out " << Itm->DestFile << std::endl; + return _error->Error("Failed to open out %s", Itm->DestFile.c_str()); + } + Hashes hash(Itm->ExpectedHashes); patch.apply_against_file(out, inp, &hash); - fclose(out); - fclose(inp); + out.Close(); + inp.Close(); + + if (_error->PendingError() == true) { + std::cerr << "FAILED to read or write files" << std::endl; + return false; + } if (Debug == true) { std::clog << "rred: finished file patching of " << Path << "." << std::endl; @@ -614,16 +672,17 @@ class RredMethod : public pkgAcqMethod { struct stat bufbase, bufpatch; if (stat(Path.c_str(), &bufbase) != 0 || stat(patch_name.c_str(), &bufpatch) != 0) - return _error->Errno("stat", _("Failed to stat")); + return _error->Errno("stat", _("Failed to stat %s"), Path.c_str()); - struct utimbuf timebuf; - timebuf.actime = bufbase.st_atime; - timebuf.modtime = bufpatch.st_mtime; - if (utime(Itm->DestFile.c_str(), &timebuf) != 0) - return _error->Errno("utime", _("Failed to set modification time")); + struct timeval times[2]; + times[0].tv_sec = bufbase.st_atime; + times[1].tv_sec = bufpatch.st_mtime; + times[0].tv_usec = times[1].tv_usec = 0; + if (utimes(Itm->DestFile.c_str(), times) != 0) + return _error->Errno("utimes",_("Failed to set modification time")); if (stat(Itm->DestFile.c_str(), &bufbase) != 0) - return _error->Errno("stat", _("Failed to stat")); + return _error->Errno("stat", _("Failed to stat %s"), Itm->DestFile.c_str()); Res.LastModified = bufbase.st_mtime; Res.Size = bufbase.st_size; @@ -634,13 +693,14 @@ class RredMethod : public pkgAcqMethod { } public: - RredMethod() : pkgAcqMethod("2.0",SingleInstance | SendConfig) {} + RredMethod() : aptMethod("rred", "2.0", SendConfig), Debug(false) {} }; int main(int argc, char **argv) { int i; bool just_diff = true; + bool test = false; Patch patch; if (argc <= 1) { @@ -648,7 +708,14 @@ int main(int argc, char **argv) return Mth.Run(); } - if (argc > 1 && strcmp(argv[1], "-f") == 0) { + // Usage: rred -t input output diff ... + if (argc > 1 && strcmp(argv[1], "-t") == 0) { + // Read config files so we see compressors. + pkgInitConfig(*_config); + just_diff = false; + test = true; + i = 4; + } else if (argc > 1 && strcmp(argv[1], "-f") == 0) { just_diff = false; i = 2; } else { @@ -661,17 +728,31 @@ int main(int argc, char **argv) _error->DumpErrors(std::cerr); exit(1); } - patch.read_diff(p); + if (patch.read_diff(p, NULL) == false) + { + _error->DumpErrors(std::cerr); + exit(2); + } } - if (just_diff) { - patch.write_diff(stdout); + if (test) { + FileFd out, inp; + std::cerr << "Patching " << argv[2] << " into " << argv[3] << "\n"; + inp.Open(argv[2], FileFd::ReadOnly,FileFd::Extension); + out.Open(argv[3], FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite, FileFd::Extension); + patch.apply_against_file(out, inp); + out.Close(); + } else if (just_diff) { + FileFd out; + out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create); + patch.write_diff(out); + out.Close(); } else { - FILE *out, *inp; - out = stdout; - inp = stdin; - + FileFd out, inp; + out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite); + inp.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly); patch.apply_against_file(out, inp); + out.Close(); } return 0; }