rred.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  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. #include <config.h>
  8. #include <apt-pkg/fileutl.h>
  9. #include <apt-pkg/error.h>
  10. #include <apt-pkg/acquire-method.h>
  11. #include <apt-pkg/strutl.h>
  12. #include <apt-pkg/hashes.h>
  13. #include <apt-pkg/configuration.h>
  14. #include <stddef.h>
  15. #include <iostream>
  16. #include <string>
  17. #include <list>
  18. #include <vector>
  19. #include <assert.h>
  20. #include <errno.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/stat.h>
  25. #include <sys/time.h>
  26. #include <apti18n.h>
  27. #define BLOCK_SIZE (512*1024)
  28. class MemBlock {
  29. char *start;
  30. size_t size;
  31. char *free;
  32. MemBlock *next;
  33. MemBlock(size_t size) : size(size), next(NULL)
  34. {
  35. free = start = new char[size];
  36. }
  37. size_t avail(void) { return size - (free - start); }
  38. public:
  39. MemBlock(void) {
  40. free = start = new char[BLOCK_SIZE];
  41. size = BLOCK_SIZE;
  42. next = NULL;
  43. }
  44. ~MemBlock() {
  45. delete [] start;
  46. delete next;
  47. }
  48. void clear(void) {
  49. free = start;
  50. if (next)
  51. next->clear();
  52. }
  53. char *add_easy(char *src, size_t len, char *last)
  54. {
  55. if (last) {
  56. for (MemBlock *k = this; k; k = k->next) {
  57. if (k->free == last) {
  58. if (len <= k->avail()) {
  59. char *n = k->add(src, len);
  60. assert(last == n);
  61. if (last == n)
  62. return NULL;
  63. return n;
  64. } else {
  65. break;
  66. }
  67. } else if (last >= start && last < free) {
  68. break;
  69. }
  70. }
  71. }
  72. return add(src, len);
  73. }
  74. char *add(char *src, size_t len) {
  75. if (len > avail()) {
  76. if (!next) {
  77. if (len > BLOCK_SIZE) {
  78. next = new MemBlock(len);
  79. } else {
  80. next = new MemBlock;
  81. }
  82. }
  83. return next->add(src, len);
  84. }
  85. char *dst = free;
  86. free += len;
  87. memcpy(dst, src, len);
  88. return dst;
  89. }
  90. };
  91. struct Change {
  92. /* Ordering:
  93. *
  94. * 1. write out <offset> lines unchanged
  95. * 2. skip <del_cnt> lines from source
  96. * 3. write out <add_cnt> lines (<add>/<add_len>)
  97. */
  98. size_t offset;
  99. size_t del_cnt;
  100. size_t add_cnt; /* lines */
  101. size_t add_len; /* bytes */
  102. char *add;
  103. Change(size_t off)
  104. {
  105. offset = off;
  106. del_cnt = add_cnt = add_len = 0;
  107. add = NULL;
  108. }
  109. /* actually, don't write <lines> lines from <add> */
  110. void skip_lines(size_t lines)
  111. {
  112. while (lines > 0) {
  113. char *s = (char*) memchr(add, '\n', add_len);
  114. assert(s != NULL);
  115. s++;
  116. add_len -= (s - add);
  117. add_cnt--;
  118. lines--;
  119. if (add_len == 0) {
  120. add = NULL;
  121. assert(add_cnt == 0);
  122. assert(lines == 0);
  123. } else {
  124. add = s;
  125. assert(add_cnt > 0);
  126. }
  127. }
  128. }
  129. };
  130. class FileChanges {
  131. std::list<struct Change> changes;
  132. std::list<struct Change>::iterator where;
  133. size_t pos; // line number is as far left of iterator as possible
  134. bool pos_is_okay(void) const
  135. {
  136. #ifdef POSDEBUG
  137. size_t cpos = 0;
  138. std::list<struct Change>::const_iterator x;
  139. for (x = changes.begin(); x != where; ++x) {
  140. assert(x != changes.end());
  141. cpos += x->offset + x->add_cnt;
  142. }
  143. return cpos == pos;
  144. #else
  145. return true;
  146. #endif
  147. }
  148. public:
  149. FileChanges() {
  150. where = changes.end();
  151. pos = 0;
  152. }
  153. std::list<struct Change>::iterator begin(void) { return changes.begin(); }
  154. std::list<struct Change>::iterator end(void) { return changes.end(); }
  155. std::list<struct Change>::reverse_iterator rbegin(void) { return changes.rbegin(); }
  156. std::list<struct Change>::reverse_iterator rend(void) { return changes.rend(); }
  157. void add_change(Change c) {
  158. assert(pos_is_okay());
  159. go_to_change_for(c.offset);
  160. assert(pos + where->offset == c.offset);
  161. if (c.del_cnt > 0)
  162. delete_lines(c.del_cnt);
  163. assert(pos + where->offset == c.offset);
  164. if (c.add_len > 0) {
  165. assert(pos_is_okay());
  166. if (where->add_len > 0)
  167. new_change();
  168. assert(where->add_len == 0 && where->add_cnt == 0);
  169. where->add_len = c.add_len;
  170. where->add_cnt = c.add_cnt;
  171. where->add = c.add;
  172. }
  173. assert(pos_is_okay());
  174. merge();
  175. assert(pos_is_okay());
  176. }
  177. private:
  178. void merge(void)
  179. {
  180. while (where->offset == 0 && where != changes.begin()) {
  181. left();
  182. }
  183. std::list<struct Change>::iterator next = where;
  184. ++next;
  185. while (next != changes.end() && next->offset == 0) {
  186. where->del_cnt += next->del_cnt;
  187. next->del_cnt = 0;
  188. if (next->add == NULL) {
  189. next = changes.erase(next);
  190. } else if (where->add == NULL) {
  191. where->add = next->add;
  192. where->add_len = next->add_len;
  193. where->add_cnt = next->add_cnt;
  194. next = changes.erase(next);
  195. } else {
  196. ++next;
  197. }
  198. }
  199. }
  200. void go_to_change_for(size_t line)
  201. {
  202. while(where != changes.end()) {
  203. if (line < pos) {
  204. left();
  205. continue;
  206. }
  207. if (pos + where->offset + where->add_cnt <= line) {
  208. right();
  209. continue;
  210. }
  211. // line is somewhere in this slot
  212. if (line < pos + where->offset) {
  213. break;
  214. } else if (line == pos + where->offset) {
  215. return;
  216. } else {
  217. split(line - pos);
  218. right();
  219. return;
  220. }
  221. }
  222. /* it goes before this patch */
  223. insert(line-pos);
  224. }
  225. void new_change(void) { insert(where->offset); }
  226. void insert(size_t offset)
  227. {
  228. assert(pos_is_okay());
  229. assert(where == changes.end() || offset <= where->offset);
  230. if (where != changes.end())
  231. where->offset -= offset;
  232. changes.insert(where, Change(offset));
  233. --where;
  234. assert(pos_is_okay());
  235. }
  236. void split(size_t offset)
  237. {
  238. assert(pos_is_okay());
  239. assert(where->offset < offset);
  240. assert(offset < where->offset + where->add_cnt);
  241. size_t keep_lines = offset - where->offset;
  242. Change before(*where);
  243. where->del_cnt = 0;
  244. where->offset = 0;
  245. where->skip_lines(keep_lines);
  246. before.add_cnt = keep_lines;
  247. before.add_len -= where->add_len;
  248. changes.insert(where, before);
  249. --where;
  250. assert(pos_is_okay());
  251. }
  252. void delete_lines(size_t cnt)
  253. {
  254. std::list<struct Change>::iterator x = where;
  255. assert(pos_is_okay());
  256. while (cnt > 0)
  257. {
  258. size_t del;
  259. del = x->add_cnt;
  260. if (del > cnt)
  261. del = cnt;
  262. x->skip_lines(del);
  263. cnt -= del;
  264. ++x;
  265. if (x == changes.end()) {
  266. del = cnt;
  267. } else {
  268. del = x->offset;
  269. if (del > cnt)
  270. del = cnt;
  271. x->offset -= del;
  272. }
  273. where->del_cnt += del;
  274. cnt -= del;
  275. }
  276. assert(pos_is_okay());
  277. }
  278. void left(void) {
  279. assert(pos_is_okay());
  280. --where;
  281. pos -= where->offset + where->add_cnt;
  282. assert(pos_is_okay());
  283. }
  284. void right(void) {
  285. assert(pos_is_okay());
  286. pos += where->offset + where->add_cnt;
  287. ++where;
  288. assert(pos_is_okay());
  289. }
  290. };
  291. class Patch {
  292. FileChanges filechanges;
  293. MemBlock add_text;
  294. static bool retry_fwrite(char *b, size_t l, FILE *f, Hashes *hash)
  295. {
  296. size_t r = 1;
  297. while (r > 0 && l > 0)
  298. {
  299. r = fwrite(b, 1, l, f);
  300. if (hash)
  301. hash->Add((unsigned char*)b, r);
  302. l -= r;
  303. b += r;
  304. }
  305. return l == 0;
  306. }
  307. static void dump_rest(FILE *o, FILE *i, Hashes *hash)
  308. {
  309. char buffer[BLOCK_SIZE];
  310. size_t l;
  311. while (0 < (l = fread(buffer, 1, sizeof(buffer), i))) {
  312. if (!retry_fwrite(buffer, l, o, hash))
  313. break;
  314. }
  315. }
  316. static void dump_lines(FILE *o, FILE *i, size_t n, Hashes *hash)
  317. {
  318. char buffer[BLOCK_SIZE];
  319. while (n > 0) {
  320. if (fgets(buffer, sizeof(buffer), i) == 0)
  321. buffer[0] = '\0';
  322. size_t const l = strlen(buffer);
  323. if (l == 0 || buffer[l-1] == '\n')
  324. n--;
  325. retry_fwrite(buffer, l, o, hash);
  326. }
  327. }
  328. static void skip_lines(FILE *i, int n)
  329. {
  330. char buffer[BLOCK_SIZE];
  331. while (n > 0) {
  332. if (fgets(buffer, sizeof(buffer), i) == 0)
  333. buffer[0] = '\0';
  334. size_t const l = strlen(buffer);
  335. if (l == 0 || buffer[l-1] == '\n')
  336. n--;
  337. }
  338. }
  339. static void dump_mem(FILE *o, char *p, size_t s, Hashes *hash) {
  340. retry_fwrite(p, s, o, hash);
  341. }
  342. public:
  343. bool read_diff(FileFd &f, Hashes * const h)
  344. {
  345. char buffer[BLOCK_SIZE];
  346. bool cmdwanted = true;
  347. Change ch(std::numeric_limits<size_t>::max());
  348. if (f.ReadLine(buffer, sizeof(buffer)) == NULL)
  349. return _error->Error("Reading first line of patchfile %s failed", f.Name().c_str());
  350. do {
  351. if (h != NULL)
  352. h->Add(buffer);
  353. if (cmdwanted) {
  354. char *m, *c;
  355. size_t s, e;
  356. errno = 0;
  357. s = strtoul(buffer, &m, 10);
  358. if (unlikely(m == buffer || s == std::numeric_limits<unsigned long>::max() || errno != 0))
  359. return _error->Error("Parsing patchfile %s failed: Expected an effected line start", f.Name().c_str());
  360. else if (*m == ',') {
  361. ++m;
  362. e = strtol(m, &c, 10);
  363. if (unlikely(m == c || e == std::numeric_limits<unsigned long>::max() || errno != 0))
  364. return _error->Error("Parsing patchfile %s failed: Expected an effected line end", f.Name().c_str());
  365. if (unlikely(e < s))
  366. return _error->Error("Parsing patchfile %s failed: Effected lines end %lu is before start %lu", f.Name().c_str(), e, s);
  367. } else {
  368. e = s;
  369. c = m;
  370. }
  371. if (s > ch.offset)
  372. return _error->Error("Parsing patchfile %s failed: Effected line is after previous effected line", f.Name().c_str());
  373. switch(*c) {
  374. case 'a':
  375. cmdwanted = false;
  376. ch.add = NULL;
  377. ch.add_cnt = 0;
  378. ch.add_len = 0;
  379. ch.offset = s;
  380. ch.del_cnt = 0;
  381. break;
  382. case 'c':
  383. if (unlikely(s == 0))
  384. return _error->Error("Parsing patchfile %s failed: Change command can't effect line zero", f.Name().c_str());
  385. cmdwanted = false;
  386. ch.add = NULL;
  387. ch.add_cnt = 0;
  388. ch.add_len = 0;
  389. ch.offset = s - 1;
  390. ch.del_cnt = e - s + 1;
  391. break;
  392. case 'd':
  393. if (unlikely(s == 0))
  394. return _error->Error("Parsing patchfile %s failed: Delete command can't effect line zero", f.Name().c_str());
  395. ch.offset = s - 1;
  396. ch.del_cnt = e - s + 1;
  397. ch.add = NULL;
  398. ch.add_cnt = 0;
  399. ch.add_len = 0;
  400. filechanges.add_change(ch);
  401. break;
  402. default:
  403. return _error->Error("Parsing patchfile %s failed: Unknown command", f.Name().c_str());
  404. }
  405. } else { /* !cmdwanted */
  406. if (strcmp(buffer, ".\n") == 0) {
  407. cmdwanted = true;
  408. filechanges.add_change(ch);
  409. } else {
  410. char *last = NULL;
  411. char *add;
  412. size_t l;
  413. if (ch.add)
  414. last = ch.add + ch.add_len;
  415. l = strlen(buffer);
  416. add = add_text.add_easy(buffer, l, last);
  417. if (!add) {
  418. ch.add_len += l;
  419. ch.add_cnt++;
  420. } else {
  421. if (ch.add) {
  422. filechanges.add_change(ch);
  423. ch.del_cnt = 0;
  424. }
  425. ch.offset += ch.add_cnt;
  426. ch.add = add;
  427. ch.add_len = l;
  428. ch.add_cnt = 1;
  429. }
  430. }
  431. }
  432. } while(f.ReadLine(buffer, sizeof(buffer)));
  433. return true;
  434. }
  435. void write_diff(FILE *f)
  436. {
  437. unsigned long long line = 0;
  438. std::list<struct Change>::reverse_iterator ch;
  439. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  440. line += ch->offset + ch->del_cnt;
  441. }
  442. for (ch = filechanges.rbegin(); ch != filechanges.rend(); ++ch) {
  443. std::list<struct Change>::reverse_iterator mg_i, mg_e = ch;
  444. while (ch->del_cnt == 0 && ch->offset == 0)
  445. ++ch;
  446. line -= ch->del_cnt;
  447. if (ch->add_cnt > 0) {
  448. if (ch->del_cnt == 0) {
  449. fprintf(f, "%llua\n", line);
  450. } else if (ch->del_cnt == 1) {
  451. fprintf(f, "%lluc\n", line+1);
  452. } else {
  453. fprintf(f, "%llu,%lluc\n", line+1, line+ch->del_cnt);
  454. }
  455. mg_i = ch;
  456. do {
  457. dump_mem(f, mg_i->add, mg_i->add_len, NULL);
  458. } while (mg_i-- != mg_e);
  459. fprintf(f, ".\n");
  460. } else if (ch->del_cnt == 1) {
  461. fprintf(f, "%llud\n", line+1);
  462. } else if (ch->del_cnt > 1) {
  463. fprintf(f, "%llu,%llud\n", line+1, line+ch->del_cnt);
  464. }
  465. line -= ch->offset;
  466. }
  467. }
  468. void apply_against_file(FILE *out, FILE *in, Hashes *hash = NULL)
  469. {
  470. std::list<struct Change>::iterator ch;
  471. for (ch = filechanges.begin(); ch != filechanges.end(); ++ch) {
  472. dump_lines(out, in, ch->offset, hash);
  473. skip_lines(in, ch->del_cnt);
  474. dump_mem(out, ch->add, ch->add_len, hash);
  475. }
  476. dump_rest(out, in, hash);
  477. }
  478. };
  479. class RredMethod : public pkgAcqMethod {
  480. private:
  481. bool Debug;
  482. struct PDiffFile {
  483. std::string FileName;
  484. HashStringList ExpectedHashes;
  485. PDiffFile(std::string const &FileName, HashStringList const &ExpectedHashes) :
  486. FileName(FileName), ExpectedHashes(ExpectedHashes) {}
  487. };
  488. HashStringList ReadExpectedHashesForPatch(unsigned int const patch, std::string const &Message)
  489. {
  490. HashStringList ExpectedHashes;
  491. for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
  492. {
  493. std::string tagname;
  494. strprintf(tagname, "Patch-%d-%s-Hash", patch, *type);
  495. std::string const hashsum = LookupTag(Message, tagname.c_str());
  496. if (hashsum.empty() == false)
  497. ExpectedHashes.push_back(HashString(*type, hashsum));
  498. }
  499. return ExpectedHashes;
  500. }
  501. protected:
  502. virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) {
  503. Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
  504. URI Get = Itm->Uri;
  505. std::string Path = Get.Host + Get.Path; // rred:/path - no host
  506. FetchResult Res;
  507. Res.Filename = Itm->DestFile;
  508. if (Itm->Uri.empty())
  509. {
  510. Path = Itm->DestFile;
  511. Itm->DestFile.append(".result");
  512. } else
  513. URIStart(Res);
  514. std::vector<PDiffFile> patchfiles;
  515. Patch patch;
  516. if (FileExists(Path + ".ed") == true)
  517. {
  518. HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(0, Message);
  519. std::string const FileName = Path + ".ed";
  520. if (ExpectedHashes.usable() == false)
  521. return _error->Error("No hashes found for uncompressed patch: %s", FileName.c_str());
  522. patchfiles.push_back(PDiffFile(FileName, ExpectedHashes));
  523. }
  524. else
  525. {
  526. _error->PushToStack();
  527. std::vector<std::string> patches = GetListOfFilesInDir(flNotFile(Path), "gz", true, false);
  528. _error->RevertToStack();
  529. std::string const baseName = Path + ".ed.";
  530. unsigned int seen_patches = 0;
  531. for (std::vector<std::string>::const_iterator p = patches.begin();
  532. p != patches.end(); ++p)
  533. {
  534. if (p->compare(0, baseName.length(), baseName) == 0)
  535. {
  536. HashStringList const ExpectedHashes = ReadExpectedHashesForPatch(seen_patches, Message);
  537. if (ExpectedHashes.usable() == false)
  538. return _error->Error("No hashes found for uncompressed patch %d: %s", seen_patches, p->c_str());
  539. patchfiles.push_back(PDiffFile(*p, ExpectedHashes));
  540. ++seen_patches;
  541. }
  542. }
  543. }
  544. std::string patch_name;
  545. for (std::vector<PDiffFile>::iterator I = patchfiles.begin();
  546. I != patchfiles.end();
  547. ++I)
  548. {
  549. patch_name = I->FileName;
  550. if (Debug == true)
  551. std::clog << "Patching " << Path << " with " << patch_name
  552. << std::endl;
  553. FileFd p;
  554. Hashes patch_hash(I->ExpectedHashes);
  555. // all patches are compressed, even if the name doesn't reflect it
  556. if (p.Open(patch_name, FileFd::ReadOnly, FileFd::Gzip) == false ||
  557. patch.read_diff(p, &patch_hash) == false)
  558. {
  559. _error->DumpErrors(std::cerr);
  560. return false;
  561. }
  562. p.Close();
  563. HashStringList const hsl = patch_hash.GetHashStringList();
  564. if (hsl != I->ExpectedHashes)
  565. return _error->Error("Hash Sum mismatch for uncompressed patch %s", patch_name.c_str());
  566. }
  567. if (Debug == true)
  568. std::clog << "Applying patches against " << Path
  569. << " and writing results to " << Itm->DestFile
  570. << std::endl;
  571. FILE *inp = fopen(Path.c_str(), "r");
  572. FILE *out = fopen(Itm->DestFile.c_str(), "w");
  573. Hashes hash(Itm->ExpectedHashes);
  574. patch.apply_against_file(out, inp, &hash);
  575. fclose(out);
  576. fclose(inp);
  577. if (Debug == true) {
  578. std::clog << "rred: finished file patching of " << Path << "." << std::endl;
  579. }
  580. struct stat bufbase, bufpatch;
  581. if (stat(Path.c_str(), &bufbase) != 0 ||
  582. stat(patch_name.c_str(), &bufpatch) != 0)
  583. return _error->Errno("stat", _("Failed to stat"));
  584. struct timeval times[2];
  585. times[0].tv_sec = bufbase.st_atime;
  586. times[1].tv_sec = bufpatch.st_mtime;
  587. times[0].tv_usec = times[1].tv_usec = 0;
  588. if (utimes(Itm->DestFile.c_str(), times) != 0)
  589. return _error->Errno("utimes",_("Failed to set modification time"));
  590. if (stat(Itm->DestFile.c_str(), &bufbase) != 0)
  591. return _error->Errno("stat", _("Failed to stat"));
  592. Res.LastModified = bufbase.st_mtime;
  593. Res.Size = bufbase.st_size;
  594. Res.TakeHashes(hash);
  595. URIDone(Res);
  596. return true;
  597. }
  598. bool Configuration(std::string Message)
  599. {
  600. if (pkgAcqMethod::Configuration(Message) == false)
  601. return false;
  602. DropPrivsOrDie();
  603. return true;
  604. }
  605. public:
  606. RredMethod() : pkgAcqMethod("2.0",SingleInstance | SendConfig), Debug(false) {}
  607. };
  608. int main(int argc, char **argv)
  609. {
  610. int i;
  611. bool just_diff = true;
  612. Patch patch;
  613. if (argc <= 1) {
  614. RredMethod Mth;
  615. return Mth.Run();
  616. }
  617. if (argc > 1 && strcmp(argv[1], "-f") == 0) {
  618. just_diff = false;
  619. i = 2;
  620. } else {
  621. i = 1;
  622. }
  623. for (; i < argc; i++) {
  624. FileFd p;
  625. if (p.Open(argv[i], FileFd::ReadOnly) == false) {
  626. _error->DumpErrors(std::cerr);
  627. exit(1);
  628. }
  629. if (patch.read_diff(p, NULL) == false)
  630. {
  631. _error->DumpErrors(std::cerr);
  632. exit(2);
  633. }
  634. }
  635. if (just_diff) {
  636. patch.write_diff(stdout);
  637. } else {
  638. FILE *out, *inp;
  639. out = stdout;
  640. inp = stdin;
  641. patch.apply_against_file(out, inp);
  642. }
  643. return 0;
  644. }