rred.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #include <apt-pkg/fileutl.h>
  2. #include <apt-pkg/error.h>
  3. #include <apt-pkg/acquire-method.h>
  4. #include <apt-pkg/strutl.h>
  5. #include <apt-pkg/hashes.h>
  6. #include <sys/stat.h>
  7. #include <unistd.h>
  8. #include <utime.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <apti18n.h>
  12. /* this method implements a patch functionality similar to "patch --ed" that is
  13. * used by the "tiffany" incremental packages download stuff. it differs from
  14. * "ed" insofar that it is way more restricted (and therefore secure). in the
  15. * moment only the "c", "a" and "d" commands of ed are implemented (diff
  16. * doesn't output any other). additionally the records must be reverse sorted
  17. * by line number and may not overlap (diff *seems* to produce this kind of
  18. * output).
  19. * */
  20. const char *Prog;
  21. class RredMethod : public pkgAcqMethod
  22. {
  23. bool Debug;
  24. // the size of this doesn't really matter (except for performance)
  25. const static int BUF_SIZE = 1024;
  26. // the ed commands
  27. enum Mode {MODE_CHANGED, MODE_DELETED, MODE_ADDED};
  28. // return values
  29. enum State {ED_OK, ED_ORDERING, ED_PARSER, ED_FAILURE};
  30. // this applies a single hunk, it uses a tail recursion to
  31. // reverse the hunks in the file
  32. int ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
  33. char *buffer, unsigned int bufsize, Hashes *hash);
  34. // apply a patch file
  35. int ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file, Hashes *hash);
  36. // the methods main method
  37. virtual bool Fetch(FetchItem *Itm);
  38. public:
  39. RredMethod() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {};
  40. };
  41. int RredMethod::ed_rec(FILE *ed_cmds, FILE *in_file, FILE *out_file, int line,
  42. char *buffer, unsigned int bufsize, Hashes *hash) {
  43. int pos;
  44. int startline;
  45. int stopline;
  46. int mode;
  47. int written;
  48. char *idx;
  49. /* get the current command and parse it*/
  50. if (fgets(buffer, bufsize, ed_cmds) == NULL) {
  51. return line;
  52. }
  53. startline = strtol(buffer, &idx, 10);
  54. if (startline < line) {
  55. return ED_ORDERING;
  56. }
  57. if (*idx == ',') {
  58. idx++;
  59. stopline = strtol(idx, &idx, 10);
  60. }
  61. else {
  62. stopline = startline;
  63. }
  64. if (*idx == 'c') {
  65. mode = MODE_CHANGED;
  66. if (Debug == true) {
  67. std::clog << "changing from line " << startline
  68. << " to " << stopline << std::endl;
  69. }
  70. }
  71. else if (*idx == 'a') {
  72. mode = MODE_ADDED;
  73. if (Debug == true) {
  74. std::clog << "adding after line " << startline << std::endl;
  75. }
  76. }
  77. else if (*idx == 'd') {
  78. mode = MODE_DELETED;
  79. if (Debug == true) {
  80. std::clog << "deleting from line " << startline
  81. << " to " << stopline << std::endl;
  82. }
  83. }
  84. else {
  85. return ED_PARSER;
  86. }
  87. /* get the current position */
  88. pos = ftell(ed_cmds);
  89. /* if this is add or change then go to the next full stop */
  90. if ((mode == MODE_CHANGED) || (mode == MODE_ADDED)) {
  91. do {
  92. fgets(buffer, bufsize, ed_cmds);
  93. while ((strlen(buffer) == (bufsize - 1))
  94. && (buffer[bufsize - 2] != '\n')) {
  95. fgets(buffer, bufsize, ed_cmds);
  96. buffer[0] = ' ';
  97. }
  98. } while (strncmp(buffer, ".", 1) != 0);
  99. }
  100. /* do the recursive call */
  101. line = ed_rec(ed_cmds, in_file, out_file, line, buffer, bufsize,
  102. hash);
  103. /* pass on errors */
  104. if (line < 0) {
  105. return line;
  106. }
  107. /* apply our hunk */
  108. fseek(ed_cmds, pos, SEEK_SET);
  109. /* first wind to the current position */
  110. if (mode != MODE_ADDED) {
  111. startline -= 1;
  112. }
  113. while (line < startline) {
  114. fgets(buffer, bufsize, in_file);
  115. written = fwrite(buffer, 1, strlen(buffer), out_file);
  116. hash->Add((unsigned char*)buffer, written);
  117. while ((strlen(buffer) == (bufsize - 1))
  118. && (buffer[bufsize - 2] != '\n')) {
  119. fgets(buffer, bufsize, in_file);
  120. written = fwrite(buffer, 1, strlen(buffer), out_file);
  121. hash->Add((unsigned char*)buffer, written);
  122. }
  123. line++;
  124. }
  125. /* include from ed script */
  126. if ((mode == MODE_ADDED) || (mode == MODE_CHANGED)) {
  127. do {
  128. fgets(buffer, bufsize, ed_cmds);
  129. if (strncmp(buffer, ".", 1) != 0) {
  130. written = fwrite(buffer, 1, strlen(buffer), out_file);
  131. hash->Add((unsigned char*)buffer, written);
  132. while ((strlen(buffer) == (bufsize - 1))
  133. && (buffer[bufsize - 2] != '\n')) {
  134. fgets(buffer, bufsize, ed_cmds);
  135. written = fwrite(buffer, 1, strlen(buffer), out_file);
  136. hash->Add((unsigned char*)buffer, written);
  137. }
  138. }
  139. else {
  140. break;
  141. }
  142. } while (1);
  143. }
  144. /* ignore the corresponding number of lines from input */
  145. if ((mode == MODE_DELETED) || (mode == MODE_CHANGED)) {
  146. while (line < stopline) {
  147. fgets(buffer, bufsize, in_file);
  148. while ((strlen(buffer) == (bufsize - 1))
  149. && (buffer[bufsize - 2] != '\n')) {
  150. fgets(buffer, bufsize, in_file);
  151. }
  152. line++;
  153. }
  154. }
  155. return line;
  156. }
  157. int RredMethod::ed_file(FILE *ed_cmds, FILE *in_file, FILE *out_file,
  158. Hashes *hash) {
  159. char buffer[BUF_SIZE];
  160. int result;
  161. int written;
  162. /* we do a tail recursion to read the commands in the right order */
  163. result = ed_rec(ed_cmds, in_file, out_file, 0, buffer, BUF_SIZE,
  164. hash);
  165. /* read the rest from infile */
  166. if (result > 0) {
  167. while (fgets(buffer, BUF_SIZE, in_file) != NULL) {
  168. written = fwrite(buffer, 1, strlen(buffer), out_file);
  169. hash->Add((unsigned char*)buffer, written);
  170. }
  171. }
  172. else {
  173. return ED_FAILURE;
  174. }
  175. return ED_OK;
  176. }
  177. bool RredMethod::Fetch(FetchItem *Itm)
  178. {
  179. Debug = _config->FindB("Debug::pkgAcquire::RRed",false);
  180. URI Get = Itm->Uri;
  181. string Path = Get.Host + Get.Path; // To account for relative paths
  182. // Path contains the filename to patch
  183. FetchResult Res;
  184. Res.Filename = Itm->DestFile;
  185. URIStart(Res);
  186. // Res.Filename the destination filename
  187. if (Debug == true)
  188. std::clog << "Patching " << Path << " with " << Path
  189. << ".ed and putting result into " << Itm->DestFile << std::endl;
  190. // Open the source and destination files (the d'tor of FileFd will do
  191. // the cleanup/closing of the fds)
  192. FileFd From(Path,FileFd::ReadOnly);
  193. FileFd Patch(Path+".ed",FileFd::ReadOnly);
  194. FileFd To(Itm->DestFile,FileFd::WriteEmpty);
  195. To.EraseOnFailure();
  196. if (_error->PendingError() == true)
  197. return false;
  198. Hashes Hash;
  199. FILE* fFrom = fdopen(From.Fd(), "r");
  200. FILE* fPatch = fdopen(Patch.Fd(), "r");
  201. FILE* fTo = fdopen(To.Fd(), "w");
  202. // now do the actual patching
  203. if (ed_file(fPatch, fFrom, fTo, &Hash) != ED_OK) {
  204. _error->Errno("rred", _("Could not patch file"));
  205. return false;
  206. }
  207. // write out the result
  208. fflush(fFrom);
  209. fflush(fPatch);
  210. fflush(fTo);
  211. From.Close();
  212. Patch.Close();
  213. To.Close();
  214. // Transfer the modification times
  215. struct stat Buf;
  216. if (stat(Path.c_str(),&Buf) != 0)
  217. return _error->Errno("stat",_("Failed to stat"));
  218. struct utimbuf TimeBuf;
  219. TimeBuf.actime = Buf.st_atime;
  220. TimeBuf.modtime = Buf.st_mtime;
  221. if (utime(Itm->DestFile.c_str(),&TimeBuf) != 0)
  222. return _error->Errno("utime",_("Failed to set modification time"));
  223. if (stat(Itm->DestFile.c_str(),&Buf) != 0)
  224. return _error->Errno("stat",_("Failed to stat"));
  225. // return done
  226. Res.LastModified = Buf.st_mtime;
  227. Res.Size = Buf.st_size;
  228. Res.TakeHashes(Hash);
  229. URIDone(Res);
  230. return true;
  231. }
  232. int main(int argc, char *argv[])
  233. {
  234. RredMethod Mth;
  235. Prog = strrchr(argv[0],'/');
  236. Prog++;
  237. return Mth.Run();
  238. }