rred.cc 7.0 KB

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