apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

rred.cc
Go to the documentation of this file.
1 // Copyright (c) 2014 Anthony Towns
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or
6 // (at your option) any later version.
7 
8 #include <config.h>
9 
10 #ifndef APT_EXCLUDE_RRED_METHOD_CODE
11 #include "aptmethod.h"
12 #include <apt-pkg/configuration.h>
13 #include <apt-pkg/init.h>
14 #endif
15 
16 #include <apt-pkg/error.h>
17 #include <apt-pkg/fileutl.h>
18 #include <apt-pkg/hashes.h>
19 #include <apt-pkg/strutl.h>
20 
22 
23 #include <iostream>
24 #include <list>
25 #include <string>
26 #include <vector>
27 #include <stddef.h>
28 
29 #include <cassert>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 
37 #include <apti18n.h>
38 
39 #ifndef APT_MEMBLOCK_SIZE
40 #define APT_MEMBLOCK_SIZE (512*1024)
41 #endif
42 
43 static bool ShowHelp(CommandLine &)
44 {
45  std::cout <<
46  "Usage: rred [options] -t input output patch-1 … patch-N\n"
47  " rred [options] -f patch-1 … patch-N < input > output\n"
48  " rred [options] patch-1 … patch-N > merged-patch\n"
49  "\n"
50  "The main use of this binary is by APTs acquire system, a mode reached\n"
51  "by calling it without any arguments and driven via messages on stdin.\n"
52  "\n"
53  "For the propose of testing as well as simpler direct usage the above\n"
54  "mentioned modes to work with \"reversed restricted ed\" patches as well.\n"
55  "\n"
56  "The arguments used above are:\n"
57  "* input: denotes a file you want to patch.\n"
58  "* output: a file you want to store the patched content in.\n"
59  "* patch-1 … patch-N: One or more files containing a patch.\n"
60  "* merged-patch: All changes by patch-1 … patch-N in one patch.\n"
61  "\n"
62  "This rred supports the commands 'a', 'c' and 'd', both single as well\n"
63  "as multi line. Other commands are not supported (hence 'restricted').\n"
64  "The command to patch the last line must appear first in the patch\n"
65  "(hence 'reversed'). Such a patch can e.g. be produced with 'diff --ed'.\n"
66  ;
67  return true;
68 }
69 
70 class MemBlock {
71  char *start;
72  size_t size;
73  char *free;
74  MemBlock *next = nullptr;
75 
76  explicit MemBlock(size_t size) : size(size)
77  {
78  free = start = new char[size];
79  }
80 
81  size_t avail(void) { return size - (free - start); }
82 
83  public:
84 
86 
88  delete [] start;
89  delete next;
90  }
91 
92  void clear(void) {
93  free = start;
94  if (next)
95  next->clear();
96  }
97 
98  char *add_easy(char *src, size_t len, char *last)
99  {
100  if (last) {
101  for (MemBlock *k = this; k; k = k->next) {
102  if (k->free == last) {
103  if (len <= k->avail()) {
104  char * const n = k->add(src, len);
105  assert(last == n); // we checked already that the block is big enough, so a new one shouldn't be used
106  return (last == n) ? nullptr : n;
107  } else {
108  break;
109  }
110  } else if (last >= start && last < free) {
111  break;
112  }
113  }
114  }
115  return add(src, len);
116  }
117 
118  char *add(char *src, size_t len) {
119  if (len > avail()) {
120  if (!next) {
121  if (len > APT_MEMBLOCK_SIZE) {
122  next = new MemBlock(len);
123  } else {
124  next = new MemBlock();
125  }
126  }
127  return next->add(src, len);
128  }
129  char *dst = free;
130  free += len;
131  memcpy(dst, src, len);
132  return dst;
133  }
134 };
135 
136 struct Change {
137  /* Ordering:
138  *
139  * 1. write out <offset> lines unchanged
140  * 2. skip <del_cnt> lines from source
141  * 3. write out <add_cnt> lines (<add>/<add_len>)
142  */
143  size_t offset;
144  size_t del_cnt;
145  size_t add_cnt; /* lines */
146  size_t add_len; /* bytes */
147  char *add;
148 
149  explicit Change(size_t off)
150  {
151  offset = off;
152  del_cnt = add_cnt = add_len = 0;
153  add = NULL;
154  }
155 
156  /* actually, don't write <lines> lines from <add> */
157  bool skip_lines(size_t lines)
158  {
159  while (lines > 0) {
160  char *s = (char*) memchr(add, '\n', add_len);
161  if (s == nullptr)
162  return _error->Error("No line left in add_len data to skip (1)");
163  s++;
164  add_len -= (s - add);
165  add_cnt--;
166  lines--;
167  if (add_len == 0) {
168  add = nullptr;
169  if (add_cnt != 0 || lines != 0)
170  return _error->Error("No line left in add_len data to skip (2)");
171  } else {
172  add = s;
173  if (add_cnt == 0)
174  return _error->Error("No line left in add_len data to skip (3)");
175  }
176  }
177  return true;
178  }
179 };
180 
181 class FileChanges {
182  std::list<struct Change> changes;
183  std::list<struct Change>::iterator where;
184  size_t pos; // line number is as far left of iterator as possible
185 
186  bool pos_is_okay(void) const
187  {
188 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
189  // this isn't unsafe, it is just a moderately expensive check we want to avoid normally
190  size_t cpos = 0;
191  std::list<struct Change>::const_iterator x;
192  for (x = changes.begin(); x != where; ++x) {
193  assert(x != changes.end());
194  cpos += x->offset + x->add_cnt;
195  }
196  return cpos == pos;
197 #else
198  return true;
199 #endif
200  }
201 
202  public:
204  where = changes.end();
205  pos = 0;
206  }
207 
208  std::list<struct Change>::iterator begin(void) { return changes.begin(); }
209  std::list<struct Change>::iterator end(void) { return changes.end(); }
210 
211  std::list<struct Change>::reverse_iterator rbegin(void) { return changes.rbegin(); }
212  std::list<struct Change>::reverse_iterator rend(void) { return changes.rend(); }
213 
214  bool add_change(Change c) {
215  assert(pos_is_okay());
216  if (not go_to_change_for(c.offset) ||
217  pos + where->offset != c.offset)
218  return false;
219  if (c.del_cnt > 0)
220  if (not delete_lines(c.del_cnt))
221  return false;
222  if (pos + where->offset != c.offset)
223  return false;
224  if (c.add_len > 0) {
225  assert(pos_is_okay());
226  if (where->add_len > 0)
227  if (not new_change())
228  return false;
229  if (where->add_len != 0 || where->add_cnt != 0)
230  return false;
231 
232  where->add_len = c.add_len;
233  where->add_cnt = c.add_cnt;
234  where->add = c.add;
235  }
236  assert(pos_is_okay());
237  if (not merge())
238  return false;
239  return pos_is_okay();
240  }
241 
242  private:
243  bool merge(void)
244  {
245  while (where->offset == 0 && where != changes.begin()) {
246  if (not left())
247  return false;
248  }
249  std::list<struct Change>::iterator next = where;
250  ++next;
251 
252  while (next != changes.end() && next->offset == 0) {
253  where->del_cnt += next->del_cnt;
254  next->del_cnt = 0;
255  if (next->add == NULL) {
256  next = changes.erase(next);
257  } else if (where->add == NULL) {
258  where->add = next->add;
259  where->add_len = next->add_len;
260  where->add_cnt = next->add_cnt;
261  next = changes.erase(next);
262  } else {
263  ++next;
264  }
265  }
266  return true;
267  }
268 
269  bool go_to_change_for(size_t line)
270  {
271  while(where != changes.end()) {
272  if (line < pos) {
273  if (not left())
274  return false;
275  continue;
276  }
277  if (pos + where->offset + where->add_cnt <= line) {
278  if (not right())
279  return false;
280  continue;
281  }
282  // line is somewhere in this slot
283  if (line < pos + where->offset) {
284  break;
285  } else if (line == pos + where->offset) {
286  return true;
287  } else {
288  if (not split(line - pos))
289  return false;
290  return right();
291  }
292  }
293  /* it goes before this patch */
294  return insert(line-pos);
295  }
296 
297  bool new_change(void) { return insert(where->offset); }
298 
299  bool insert(size_t offset)
300  {
301  assert(pos_is_okay());
302  if (where != changes.end() && offset > where->offset)
303  return false;
304  if (where != changes.end())
305  where->offset -= offset;
306  changes.insert(where, Change(offset));
307  --where;
308  return pos_is_okay();
309  }
310 
311  bool split(size_t offset)
312  {
313  assert(pos_is_okay());
314  if (where->offset >= offset || offset >= where->offset + where->add_cnt)
315  return false;
316 
317  size_t keep_lines = offset - where->offset;
318 
319  Change before(*where);
320 
321  where->del_cnt = 0;
322  where->offset = 0;
323  if (not where->skip_lines(keep_lines))
324  return false;
325 
326  before.add_cnt = keep_lines;
327  before.add_len -= where->add_len;
328 
329  changes.insert(where, before);
330  --where;
331  return pos_is_okay();
332  }
333 
334  bool delete_lines(size_t cnt)
335  {
336  assert(pos_is_okay());
337  std::list<struct Change>::iterator x = where;
338  while (cnt > 0)
339  {
340  size_t del;
341  del = x->add_cnt;
342  if (del > cnt)
343  del = cnt;
344  if (not x->skip_lines(del))
345  return false;
346  cnt -= del;
347 
348  ++x;
349  if (x == changes.end()) {
350  del = cnt;
351  } else {
352  del = x->offset;
353  if (del > cnt)
354  del = cnt;
355  x->offset -= del;
356  }
357  where->del_cnt += del;
358  cnt -= del;
359  }
360  return pos_is_okay();
361  }
362 
363  bool left(void) {
364  assert(pos_is_okay());
365  --where;
366  pos -= where->offset + where->add_cnt;
367  return pos_is_okay();
368  }
369 
370  bool right(void) {
371  assert(pos_is_okay());
372  pos += where->offset + where->add_cnt;
373  ++where;
374  return pos_is_okay();
375  }
376 };
377 
378 class Patch {
381 
382  static bool retry_fwrite(char *b, size_t l, FileFd &f, Hashes * const start_hash, Hashes * const end_hash = nullptr) APT_NONNULL(1)
383  {
384  if (f.Write(b, l) == false)
385  return false;
386  if (start_hash)
387  start_hash->Add((unsigned char*)b, l);
388  if (end_hash)
389  end_hash->Add((unsigned char*)b, l);
390  return true;
391  }
392 
393  static void dump_rest(FileFd &o, FileFd &i,
394  Hashes * const start_hash, Hashes * const end_hash)
395  {
396  char buffer[APT_MEMBLOCK_SIZE];
397  unsigned long long l = 0;
398  while (i.Read(buffer, sizeof(buffer), &l)) {
399  if (l ==0 || !retry_fwrite(buffer, l, o, start_hash, end_hash))
400  break;
401  }
402  }
403 
404  static void dump_lines(FileFd &o, FileFd &i, size_t n,
405  Hashes * const start_hash, Hashes * const end_hash)
406  {
407  char buffer[APT_MEMBLOCK_SIZE];
408  while (n > 0) {
409  if (i.ReadLine(buffer, sizeof(buffer)) == NULL)
410  buffer[0] = '\0';
411  size_t const l = strlen(buffer);
412  if (l == 0 || buffer[l-1] == '\n')
413  n--;
414  retry_fwrite(buffer, l, o, start_hash, end_hash);
415  }
416  }
417 
418  static void skip_lines(FileFd &i, int n, Hashes * const start_hash)
419  {
420  char buffer[APT_MEMBLOCK_SIZE];
421  while (n > 0) {
422  if (i.ReadLine(buffer, sizeof(buffer)) == NULL)
423  buffer[0] = '\0';
424  size_t const l = strlen(buffer);
425  if (l == 0 || buffer[l-1] == '\n')
426  n--;
427  if (start_hash)
428  start_hash->Add((unsigned char*)buffer, l);
429  }
430  }
431 
432  static void dump_mem(FileFd &o, char *p, size_t s, Hashes *hash) APT_NONNULL(2) {
433  retry_fwrite(p, s, o, nullptr, hash);
434  }
435 
436  public:
437 
438  bool read_diff(FileFd &f, Hashes * const h)
439  {
440  char buffer[APT_MEMBLOCK_SIZE];
441  bool cmdwanted = true;
442 
443  Change ch(std::numeric_limits<size_t>::max());
444  if (f.ReadLine(buffer, sizeof(buffer)) == nullptr)
445  {
446  if (f.Eof())
447  return true;
448  return _error->Error("Reading first line of patchfile %s failed", f.Name().c_str());
449  }
450  do {
451  if (h != NULL)
452  h->Add(buffer);
453  if (cmdwanted) {
454  char *m, *c;
455  size_t s, e;
456  errno = 0;
457  s = strtoul(buffer, &m, 10);
458  if (unlikely(m == buffer || s == std::numeric_limits<unsigned long>::max() || errno != 0))
459  return _error->Error("Parsing patchfile %s failed: Expected an effected line start", f.Name().c_str());
460  else if (*m == ',') {
461  ++m;
462  e = strtol(m, &c, 10);
463  if (unlikely(m == c || e == std::numeric_limits<unsigned long>::max() || errno != 0))
464  return _error->Error("Parsing patchfile %s failed: Expected an effected line end", f.Name().c_str());
465  if (unlikely(e < s))
466  return _error->Error("Parsing patchfile %s failed: Effected lines end %lu is before start %lu", f.Name().c_str(), e, s);
467  } else {
468  e = s;
469  c = m;
470  }
471  if (s > ch.offset)
472  return _error->Error("Parsing patchfile %s failed: Effected line is after previous effected line", f.Name().c_str());
473  switch(*c) {
474  case 'a':
475  cmdwanted = false;
476  ch.add = NULL;
477  ch.add_cnt = 0;
478  ch.add_len = 0;
479  ch.offset = s;
480  ch.del_cnt = 0;
481  break;
482  case 'c':
483  if (unlikely(s == 0))
484  return _error->Error("Parsing patchfile %s failed: Change command can't effect line zero", f.Name().c_str());
485  cmdwanted = false;
486  ch.add = NULL;
487  ch.add_cnt = 0;
488  ch.add_len = 0;
489  ch.offset = s - 1;
490  ch.del_cnt = e - s + 1;
491  break;
492  case 'd':
493  if (unlikely(s == 0))
494  return _error->Error("Parsing patchfile %s failed: Delete command can't effect line zero", f.Name().c_str());
495  ch.offset = s - 1;
496  ch.del_cnt = e - s + 1;
497  ch.add = NULL;
498  ch.add_cnt = 0;
499  ch.add_len = 0;
500  if (not filechanges.add_change(ch))
501  return _error->Error("Parsing patchfile %s failed: Delete command could not be added to changes", f.Name().c_str());
502  break;
503  default:
504  return _error->Error("Parsing patchfile %s failed: Unknown command", f.Name().c_str());
505  }
506  } else { /* !cmdwanted */
507  if (strcmp(buffer, ".\n") == 0) {
508  cmdwanted = true;
509  if (not filechanges.add_change(ch))
510  return _error->Error("Parsing patchfile %s failed: Data couldn't be added for command (1)", f.Name().c_str());
511  } else {
512  char *last = NULL;
513  char *add;
514  size_t l;
515  if (ch.add)
516  last = ch.add + ch.add_len;
517  l = strlen(buffer);
518  add = add_text.add_easy(buffer, l, last);
519  if (!add) {
520  ch.add_len += l;
521  ch.add_cnt++;
522  } else {
523  if (ch.add) {
524  if (not filechanges.add_change(ch))
525  return _error->Error("Parsing patchfile %s failed: Data couldn't be added for command (2)", f.Name().c_str());
526  ch.del_cnt = 0;
527  }
528  ch.offset += ch.add_cnt;
529  ch.add = add;
530  ch.add_len = l;
531  ch.add_cnt = 1;
532  }
533  }
534  }
535  } while(f.ReadLine(buffer, sizeof(buffer)));
536  return true;
537  }
538 
540  {
541  unsigned long long line = 0;
542  std::list<struct Change>::reverse_iterator ch;
543  for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
544  line += ch->offset + ch->del_cnt;
545  }
546 
547  for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
548  std::list<struct Change>::reverse_iterator mg_i, mg_e = ch;
549  while (ch->del_cnt == 0 && ch->offset == 0)
550  {
551  ++ch;
552  if (unlikely(ch == filechanges.rend()))
553  return;
554  }
555  line -= ch->del_cnt;
556  std::string buf;
557  if (ch->add_cnt > 0) {
558  if (ch->del_cnt == 0) {
559  strprintf(buf, "%llua\n", line);
560  } else if (ch->del_cnt == 1) {
561  strprintf(buf, "%lluc\n", line+1);
562  } else {
563  strprintf(buf, "%llu,%lluc\n", line+1, line+ch->del_cnt);
564  }
565  f.Write(buf.c_str(), buf.length());
566 
567  mg_i = ch;
568  do {
569  dump_mem(f, mg_i->add, mg_i->add_len, NULL);
570  } while (mg_i-- != mg_e);
571 
572  buf = ".\n";
573  f.Write(buf.c_str(), buf.length());
574  } else if (ch->del_cnt == 1) {
575  strprintf(buf, "%llud\n", line+1);
576  f.Write(buf.c_str(), buf.length());
577  } else if (ch->del_cnt > 1) {
578  strprintf(buf, "%llu,%llud\n", line+1, line+ch->del_cnt);
579  f.Write(buf.c_str(), buf.length());
580  }
581  line -= ch->offset;
582  }
583  }
584 
586  Hashes * const start_hash = nullptr, Hashes * const end_hash = nullptr)
587  {
588  std::list<struct Change>::iterator ch;
589  for (ch = filechanges.begin(); ch != filechanges.end(); ++ch) {
590  dump_lines(out, in, ch->offset, start_hash, end_hash);
591  skip_lines(in, ch->del_cnt, start_hash);
592  if (ch->add_len != 0)
593  dump_mem(out, ch->add, ch->add_len, end_hash);
594  }
595  dump_rest(out, in, start_hash, end_hash);
596  out.Flush();
597  }
598 };
599 
600 #ifndef APT_EXCLUDE_RRED_METHOD_CODE
601 class RredMethod : public aptMethod {
602  private:
603  bool Debug;
604 
605  struct PDiffFile {
606  std::string FileName;
608  PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes) :
610  };
611 
612  HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message)
613  {
614  HashStringList ExpectedHashes;
615  for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
616  {
617  std::string tagname;
618  strprintf(tagname, "Patch-%d-%s-Hash", patch, *type);
619  std::string const hashsum = LookupTag(Message, tagname.c_str());
620  if (hashsum.empty() == false)
621  ExpectedHashes.push_back(HashString(*type, hashsum));
622  }
623  return ExpectedHashes;
624  }
625 
626  protected:
627  virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE {
628  Debug = DebugEnabled();
629  URI Get(Itm->Uri);
630  std::string Path = DecodeSendURI(Get.Host + Get.Path); // rred:/path - no host
631 
632  FetchResult Res;
633  Res.Filename = Itm->DestFile;
634  if (Itm->Uri.empty())
635  {
636  Path = Itm->DestFile;
637  Itm->DestFile.append(".result");
638  } else
639  URIStart(Res);
640 
641  std::vector<PDiffFile> patchfiles;
642  Patch patch;
643 
644  HashStringList StartHashes;
645  for (char const * const * type = HashString::SupportedHashes(); *type != nullptr; ++type)
646  {
647  std::string tagname;
648  strprintf(tagname, "Start-%s-Hash", *type);
649  std::string const hashsum = LookupTag(Message, tagname.c_str());
650  if (hashsum.empty() == false)
651  StartHashes.push_back(HashString(*type, hashsum));
652  }
653 
654  if (FileExists(Path + ".ed") == true)
655  {
656  HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(0, Message);
657  std::string const FileName = Path + ".ed";
658  if (ExpectedHashes.usable() == false)
659  return _error->Error("No hashes found for uncompressed patch: %s", FileName.c_str());
660  patchfiles.push_back(PDiffFile(FileName, ExpectedHashes));
661  }
662  else
663  {
664  _error->PushToStack();
665  std::vector<std::string> patches = GetListOfFilesInDir(flNotFile(Path), "gz", true, false);
666  _error->RevertToStack();
667 
668  std::string const baseName = Path + ".ed.";
669  unsigned int seen_patches = 0;
670  for (std::vector<std::string>::const_iterator p = patches.begin();
671  p != patches.end(); ++p)
672  {
673  if (p->compare(0, baseName.length(), baseName) == 0)
674  {
675  HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(seen_patches, Message);
676  if (ExpectedHashes.usable() == false)
677  return _error->Error("No hashes found for uncompressed patch %d: %s", seen_patches, p->c_str());
678  patchfiles.push_back(PDiffFile(*p, ExpectedHashes));
679  ++seen_patches;
680  }
681  }
682  }
683 
684  std::string patch_name;
685  for (std::vector<PDiffFile>::iterator I = patchfiles.begin();
686  I != patchfiles.end();
687  ++I)
688  {
689  patch_name = I->FileName;
690  if (Debug == true)
691  std::clog << "Patching " << Path << " with " << patch_name
692  << std::endl;
693 
694  FileFd p;
695  Hashes patch_hash(I->ExpectedHashes);
696  // all patches are compressed, even if the name doesn't reflect it
697  if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false ||
698  patch.read_diff(p, &patch_hash) == false)
699  {
700  _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
701  return false;
702  }
703  p.Close();
704  HashStringList const hsl = patch_hash.GetHashStringList();
705  if (hsl != I->ExpectedHashes)
706  return _error->Error("Hash Sum mismatch for uncompressed patch %s", patch_name.c_str());
707  }
708 
709  if (Debug == true)
710  std::clog << "Applying patches against " << Path
711  << " and writing results to " << Itm->DestFile
712  << std::endl;
713 
714  FileFd inp, out;
715  if (inp.Open(Path, FileFd::ReadOnly, FileFd::Extension) == false)
716  {
717  if (Debug == true)
718  std::clog << "FAILED to open inp " << Path << std::endl;
719  return _error->Error("Failed to open inp %s", Path.c_str());
720  }
722  {
723  if (Debug == true)
724  std::clog << "FAILED to open out " << Itm->DestFile << std::endl;
725  return _error->Error("Failed to open out %s", Itm->DestFile.c_str());
726  }
727 
728  Hashes end_hash(Itm->ExpectedHashes);
729  if (StartHashes.usable())
730  {
731  Hashes start_hash(StartHashes);
732  patch.apply_against_file(out, inp, &start_hash, &end_hash);
733  if (start_hash.GetHashStringList() != StartHashes)
734  _error->Error("The input file hadn't the expected hash!");
735  }
736  else
737  patch.apply_against_file(out, inp, nullptr, &end_hash);
738 
739  out.Close();
740  inp.Close();
741 
742  if (_error->PendingError() == true) {
743  if (Debug == true)
744  std::clog << "FAILED to read or write files" << std::endl;
745  return false;
746  }
747 
748  if (Debug == true) {
749  std::clog << "rred: finished file patching of " << Path << "." << std::endl;
750  }
751 
752  struct stat bufbase, bufpatch;
753  if (stat(Path.c_str(), &bufbase) != 0 ||
754  stat(patch_name.c_str(), &bufpatch) != 0)
755  return _error->Errno("stat", _("Failed to stat %s"), Path.c_str());
756 
757  struct timeval times[2];
758  times[0].tv_sec = bufbase.st_atime;
759  times[1].tv_sec = bufpatch.st_mtime;
760  times[0].tv_usec = times[1].tv_usec = 0;
761  if (utimes(Itm->DestFile.c_str(), times) != 0)
762  return _error->Errno("utimes",_("Failed to set modification time"));
763 
764  if (stat(Itm->DestFile.c_str(), &bufbase) != 0)
765  return _error->Errno("stat", _("Failed to stat %s"), Itm->DestFile.c_str());
766 
767  Res.LastModified = bufbase.st_mtime;
768  Res.Size = bufbase.st_size;
769  Res.TakeHashes(end_hash);
770  URIDone(Res);
771 
772  return true;
773  }
774 
775  public:
777  {
779  }
780 };
781 
782 static const APT::Configuration::Compressor *FindCompressor(std::vector<APT::Configuration::Compressor> const &compressors, std::string const &name) /*{{{*/
783 {
784  APT::Configuration::Compressor const * compressor = nullptr;
785  for (auto const & c : compressors)
786  {
787  if (compressor != nullptr && c.Cost >= compressor->Cost)
788  continue;
789  if (c.Name == name || c.Extension == name || (!c.Extension.empty() && c.Extension.substr(1) == name))
790  compressor = &c;
791  }
792  return compressor;
793 }
794  /*}}}*/
795 static std::vector<aptDispatchWithHelp> GetCommands()
796 {
797  return {{nullptr, nullptr, nullptr}};
798 }
799 int main(int argc, const char *argv[])
800 {
801  if (argc <= 1)
802  return RredMethod().Run();
803 
804  CommandLine CmdL;
805  auto const Cmds = ParseCommandLine(CmdL, APT_CMD::RRED, &_config, nullptr, argc, argv, &ShowHelp, &GetCommands);
806 
807  FileFd input, output;
808  unsigned int argi = 0;
809  auto const argmax = CmdL.FileSize();
810  bool const quiet = _config->FindI("quiet", 0) >= 2;
811 
812  std::string const compressorName = _config->Find("Rred::Compress", "");
813  auto const compressors = APT::Configuration::getCompressors();
814  APT::Configuration::Compressor const * compressor = nullptr;
815  if (not compressorName.empty())
816  {
817  compressor = FindCompressor(compressors, compressorName);
818  if (compressor == nullptr)
819  {
820  std::cerr << "E: Could not find compressor: " << compressorName << '\n';
821  return 101;
822  }
823  }
824 
825  bool just_diff = false;
826  if (_config->FindB("Rred::T", false))
827  {
828  if (argmax < 3)
829  {
830  std::cerr << "E: Not enough filenames given on the command line for mode 't'\n";
831  return 101;
832  }
833  if (not quiet)
834  std::clog << "Patching " << CmdL.FileList[0] << " into " << CmdL.FileList[1] << "\n";
836  if (compressor == nullptr)
838  else
840  argi = 2;
841  }
842  else
843  {
844  if (compressor == nullptr)
846  else
847  output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite, *compressor);
848  if (_config->FindB("Rred::F", false))
849  input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly);
850  else
851  just_diff = true;
852  }
853 
854  if (argi + 1 > argmax)
855  {
856  std::cerr << "E: At least one patch needs to be given on the command line\n";
857  return 101;
858  }
859 
860  Patch merged_patch;
861  for (; argi < argmax; ++argi)
862  {
863  FileFd patch;
864  if (not patch.Open(CmdL.FileList[argi], FileFd::ReadOnly, FileFd::Extension))
865  {
866  _error->DumpErrors(std::cerr);
867  return 1;
868  }
869  if (not merged_patch.read_diff(patch, nullptr))
870  {
871  _error->DumpErrors(std::cerr);
872  return 2;
873  }
874  }
875 
876  if (just_diff)
877  merged_patch.write_diff(output);
878  else
879  merged_patch.apply_against_file(output, input);
880 
881  output.Close();
882  input.Close();
883  return DispatchCommandLine(CmdL, {});
884 }
885 #endif
strprintf(m, msg, repo.c_str())
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
return false
const char ** FileList
Definition: cmndline.h:78
unsigned int FileSize() const APT_PURE
Definition: cmndline.cc:353
int FindI(const char *Name, int const &Default=0) const
std::string Find(const char *Name, const char *Default=0) const
bool FindB(const char *Name, bool const &Default=false) const
std::list< struct Change >::iterator begin(void)
Definition: rred.cc:208
std::list< struct Change >::reverse_iterator rbegin(void)
Definition: rred.cc:211
std::list< struct Change >::iterator where
Definition: rred.cc:183
bool delete_lines(size_t cnt)
Definition: rred.cc:334
bool merge(void)
Definition: rred.cc:243
bool pos_is_okay(void) const
Definition: rred.cc:186
size_t pos
Definition: rred.cc:184
bool left(void)
Definition: rred.cc:363
bool insert(size_t offset)
Definition: rred.cc:299
std::list< struct Change > changes
Definition: rred.cc:182
bool go_to_change_for(size_t line)
Definition: rred.cc:269
std::list< struct Change >::reverse_iterator rend(void)
Definition: rred.cc:212
std::list< struct Change >::iterator end(void)
Definition: rred.cc:209
bool split(size_t offset)
Definition: rred.cc:311
bool add_change(Change c)
Definition: rred.cc:214
bool right(void)
Definition: rred.cc:370
bool new_change(void)
Definition: rred.cc:297
FileChanges()
Definition: rred.cc:203
Definition: fileutl.h:39
bool OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose=false)
Definition: fileutl.cc:2572
@ Extension
Definition: fileutl.h:80
@ Gzip
Definition: fileutl.h:81
@ WriteOnly
Definition: fileutl.h:60
@ BufferedWrite
Definition: fileutl.h:67
@ Empty
Definition: fileutl.h:66
@ Create
Definition: fileutl.h:63
@ ReadOnly
Definition: fileutl.h:59
bool Flush()
Definition: fileutl.cc:2808
bool Write(const void *From, unsigned long long Size)
Definition: fileutl.cc:2819
char * ReadLine(char *To, unsigned long long const Size)
Definition: fileutl.cc:2776
bool Eof()
Definition: fileutl.h:154
bool Open(std::string FileName, unsigned int const Mode, CompressMode Compress, unsigned long const AccessMode=0666)
Definition: fileutl.cc:2415
bool Read(void *To, unsigned long long Size, bool AllowEof)
Definition: fileutl.h:89
bool Close()
Definition: fileutl.cc:2977
std::string & Name()
Definition: fileutl.h:156
@ DEBUG
for developers only in areas it is hard to print something directly
Definition: error.h:66
bool usable() const
Definition: hashes.cc:178
bool push_back(const HashString &hashString)
Definition: hashes.cc:232
static APT_PURE const char ** SupportedHashes()
Definition: hashes.cc:135
Definition: hashes.h:170
HashStringList GetHashStringList()
Definition: hashes.cc:431
bool Add(const unsigned char *const Data, unsigned long long const Size) APT_NONNULL(2)
Definition: hashes.cc:353
Definition: rred.cc:70
MemBlock * next
Definition: rred.cc:74
size_t size
Definition: rred.cc:72
char * free
Definition: rred.cc:73
char * add_easy(char *src, size_t len, char *last)
Definition: rred.cc:98
MemBlock()
Definition: rred.cc:85
size_t avail(void)
Definition: rred.cc:81
char * start
Definition: rred.cc:71
void clear(void)
Definition: rred.cc:92
char * add(char *src, size_t len)
Definition: rred.cc:118
MemBlock(size_t size)
Definition: rred.cc:76
~MemBlock()
Definition: rred.cc:87
Definition: rred.cc:378
void write_diff(FileFd &f)
Definition: rred.cc:539
void apply_against_file(FileFd &out, FileFd &in, Hashes *const start_hash=nullptr, Hashes *const end_hash=nullptr)
Definition: rred.cc:585
static void dump_mem(FileFd &o, char *p, size_t s, Hashes *hash) APT_NONNULL(2)
Definition: rred.cc:432
static void dump_lines(FileFd &o, FileFd &i, size_t n, Hashes *const start_hash, Hashes *const end_hash)
Definition: rred.cc:404
MemBlock add_text
Definition: rred.cc:380
static void skip_lines(FileFd &i, int n, Hashes *const start_hash)
Definition: rred.cc:418
static bool retry_fwrite(char *b, size_t l, FileFd &f, Hashes *const start_hash, Hashes *const end_hash=nullptr) APT_NONNULL(1)
Definition: rred.cc:382
static void dump_rest(FileFd &o, FileFd &i, Hashes *const start_hash, Hashes *const end_hash)
Definition: rred.cc:393
FileChanges filechanges
Definition: rred.cc:379
bool read_diff(FileFd &f, Hashes *const h)
Definition: rred.cc:438
HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message)
Definition: rred.cc:612
virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE
Definition: rred.cc:627
RredMethod()
Definition: rred.cc:776
bool Debug
Definition: rred.cc:603
Definition: strutl.h:193
std::string Path
Definition: strutl.h:202
std::string Host
Definition: strutl.h:201
bool DebugEnabled() const
Definition: aptmethod.h:409
unsigned long SeccompFlags
Definition: aptmethod.h:50
struct timeval times[2]
Definition: aptmethod.h:469
@ DIRECTORY
Definition: aptmethod.h:55
static std::string DecodeSendURI(std::string const &part)
Definition: aptmethod.h:493
virtual void URIStart(FetchResult &Res)
int Run(bool Single=false)
virtual void URIDone(FetchResult &Res, FetchResult *Alt=0)
Configuration * _config
string flNotFile(string File)
Definition: fileutl.cc:676
bool FileExists(string File)
Definition: fileutl.cc:326
std::vector< string > GetListOfFilesInDir(string const &Dir, string const &Ext, bool const &SortList, bool const &AllowNoExt)
Definition: fileutl.cc:421
#define APT_OVERRIDE
Definition: macros.h:111
#define APT_NONNULL(...)
Definition: macros.h:67
APT_PUBLIC std::vector< Compressor > const getCompressors(bool const Cached=true)
Return a vector of Compressors supported for data.tar's.
std::vector< CommandLine::Dispatch > ParseCommandLine(CommandLine &CmdL, APT_CMD const Binary, Configuration *const *const Cnf, pkgSystem **const Sys, int const argc, const char *argv[], bool(*ShowHelp)(CommandLine &), std::vector< aptDispatchWithHelp >(*GetCommands)(void))
unsigned short DispatchCommandLine(CommandLine &CmdL, std::vector< CommandLine::Dispatch > const &Cmds)
static bool ShowHelp(CommandLine &)
Definition: rred.cc:43
static std::vector< aptDispatchWithHelp > GetCommands()
Definition: rred.cc:795
#define APT_MEMBLOCK_SIZE
Definition: rred.cc:40
static const APT::Configuration::Compressor * FindCompressor(std::vector< APT::Configuration::Compressor > const &compressors, std::string const &name)
Definition: rred.cc:782
int main(int argc, const char *argv[])
Definition: rred.cc:799
Representation of supported compressors.
Definition: rred.cc:136
size_t add_len
Definition: rred.cc:146
Change(size_t off)
Definition: rred.cc:149
char * add
Definition: rred.cc:147
size_t del_cnt
Definition: rred.cc:144
bool skip_lines(size_t lines)
Definition: rred.cc:157
size_t offset
Definition: rred.cc:143
size_t add_cnt
Definition: rred.cc:145
std::string FileName
Definition: rred.cc:606
HashStringList ExpectedHashes
Definition: rred.cc:607
PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes)
Definition: rred.cc:608
unsigned long long Size
void TakeHashes(class Hashes &Hash)
std::string LookupTag(const std::string &Message, const char *TagC, const char *Default)
Definition: strutl.cc:742