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