Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg-split - splitting and joining of multipart *.deb archives | |
3 | * queue.c - queue management | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
23 | #include <compat.h> | |
24 | ||
25 | #include <sys/stat.h> | |
26 | ||
27 | #include <errno.h> | |
28 | #include <limits.h> | |
29 | #include <inttypes.h> | |
30 | #include <string.h> | |
31 | #include <fcntl.h> | |
32 | #include <dirent.h> | |
33 | #include <unistd.h> | |
34 | #include <stdint.h> | |
35 | #include <stdlib.h> | |
36 | #include <stdio.h> | |
37 | ||
38 | #include <dpkg/i18n.h> | |
39 | #include <dpkg/dpkg.h> | |
40 | #include <dpkg/dpkg-db.h> | |
41 | #include <dpkg/dir.h> | |
42 | #include <dpkg/buffer.h> | |
43 | #include <dpkg/options.h> | |
44 | ||
45 | #include "dpkg-split.h" | |
46 | ||
47 | /* | |
48 | * The queue, by default located in /var/lib/dpkg/parts/, is a plain | |
49 | * directory with one file per part. | |
50 | * | |
51 | * Each part is named “<md5sum>.<maxpartlen>.<thispartn>.<maxpartn>”, | |
52 | * with all numbers in hex. | |
53 | */ | |
54 | ||
55 | ||
56 | static bool | |
57 | decompose_filename(const char *filename, struct partqueue *pq) | |
58 | { | |
59 | const char *p; | |
60 | char *q; | |
61 | ||
62 | if (strspn(filename, "0123456789abcdef") != MD5HASHLEN || | |
63 | filename[MD5HASHLEN] != '.') | |
64 | return false; | |
65 | q = nfmalloc(MD5HASHLEN + 1); | |
66 | memcpy(q, filename, MD5HASHLEN); | |
67 | q[MD5HASHLEN] = '\0'; | |
68 | pq->info.md5sum= q; | |
69 | p = filename + MD5HASHLEN + 1; | |
70 | errno = 0; | |
71 | pq->info.maxpartlen = strtoimax(p, &q, 16); | |
72 | if (q == p || *q++ != '.' || errno != 0) | |
73 | return false; | |
74 | p = q; | |
75 | pq->info.thispartn = (int)strtol(p, &q, 16); | |
76 | if (q == p || *q++ != '.' || errno != 0) | |
77 | return false; | |
78 | p = q; | |
79 | pq->info.maxpartn = (int)strtol(p, &q, 16); | |
80 | if (q == p || *q || errno != 0) | |
81 | return false; | |
82 | return true; | |
83 | } | |
84 | ||
85 | static struct partqueue * | |
86 | scandepot(void) | |
87 | { | |
88 | DIR *depot; | |
89 | struct dirent *de; | |
90 | struct partqueue *queue = NULL; | |
91 | ||
92 | depot = opendir(opt_depotdir); | |
93 | if (!depot) | |
94 | ohshite(_("unable to read depot directory '%.250s'"), opt_depotdir); | |
95 | while ((de= readdir(depot))) { | |
96 | struct partqueue *pq; | |
97 | char *p; | |
98 | ||
99 | if (de->d_name[0] == '.') continue; | |
100 | pq= nfmalloc(sizeof(struct partqueue)); | |
101 | pq->info.fmtversion.major = 0; | |
102 | pq->info.fmtversion.minor = 0; | |
103 | pq->info.package = NULL; | |
104 | pq->info.version = NULL; | |
105 | pq->info.arch = NULL; | |
106 | pq->info.orglength= pq->info.thispartoffset= pq->info.thispartlen= 0; | |
107 | pq->info.headerlen= 0; | |
108 | p = nfmalloc(strlen(opt_depotdir) + 1 + strlen(de->d_name) + 1); | |
109 | sprintf(p, "%s/%s", opt_depotdir, de->d_name); | |
110 | pq->info.filename= p; | |
111 | if (!decompose_filename(de->d_name,pq)) { | |
112 | pq->info.md5sum= NULL; | |
113 | pq->info.maxpartlen= pq->info.thispartn= pq->info.maxpartn= 0; | |
114 | } | |
115 | pq->nextinqueue= queue; | |
116 | queue= pq; | |
117 | } | |
118 | closedir(depot); | |
119 | ||
120 | return queue; | |
121 | } | |
122 | ||
123 | static bool | |
124 | partmatches(struct partinfo *pi, struct partinfo *refi) | |
125 | { | |
126 | return (pi->md5sum && | |
127 | strcmp(pi->md5sum, refi->md5sum) == 0 && | |
128 | pi->maxpartn == refi->maxpartn && | |
129 | pi->maxpartlen == refi->maxpartlen); | |
130 | } | |
131 | ||
132 | int | |
133 | do_auto(const char *const *argv) | |
134 | { | |
135 | const char *partfile; | |
136 | struct partinfo *refi, **partlist, *otherthispart; | |
137 | struct partqueue *queue; | |
138 | struct partqueue *pq; | |
139 | struct dpkg_ar *part; | |
140 | unsigned int i; | |
141 | int j; | |
142 | ||
143 | if (!opt_outputfile) | |
144 | badusage(_("--auto requires the use of the --output option")); | |
145 | partfile = *argv++; | |
146 | if (partfile == NULL || *argv) | |
147 | badusage(_("--auto requires exactly one part file argument")); | |
148 | ||
149 | refi= nfmalloc(sizeof(struct partqueue)); | |
150 | part = dpkg_ar_open(partfile); | |
151 | if (!part) | |
152 | ohshite(_("unable to read part file '%.250s'"), partfile); | |
153 | if (!read_info(part, refi)) { | |
154 | if (!opt_npquiet) | |
155 | printf(_("File '%.250s' is not part of a multipart archive.\n"), partfile); | |
156 | m_output(stdout, _("<standard output>")); | |
157 | return 1; | |
158 | } | |
159 | dpkg_ar_close(part); | |
160 | ||
161 | queue = scandepot(); | |
162 | partlist= nfmalloc(sizeof(struct partinfo*)*refi->maxpartn); | |
163 | for (i = 0; i < refi->maxpartn; i++) | |
164 | partlist[i] = NULL; | |
165 | for (pq= queue; pq; pq= pq->nextinqueue) { | |
166 | struct partinfo *npi, *pi = &pq->info; | |
167 | ||
168 | if (!partmatches(pi,refi)) continue; | |
169 | npi= nfmalloc(sizeof(struct partinfo)); | |
170 | mustgetpartinfo(pi->filename,npi); | |
171 | addtopartlist(partlist,npi,refi); | |
172 | } | |
173 | /* If we already have a copy of this version we ignore it and prefer the | |
174 | * new one, but we still want to delete the one in the depot, so we | |
175 | * save its partinfo (with the filename) for later. This also prevents | |
176 | * us from accidentally deleting the source file. */ | |
177 | otherthispart= partlist[refi->thispartn-1]; | |
178 | partlist[refi->thispartn-1]= refi; | |
179 | for (j=refi->maxpartn-1; j>=0 && partlist[j]; j--); | |
180 | ||
181 | if (j>=0) { | |
182 | struct dpkg_error err; | |
183 | int fd_src, fd_dst; | |
184 | int ap; | |
185 | char *p, *q; | |
186 | ||
187 | p = str_fmt("%s/t.%lx", opt_depotdir, (long)getpid()); | |
188 | q = str_fmt("%s/%s.%jx.%x.%x", opt_depotdir, refi->md5sum, | |
189 | (intmax_t)refi->maxpartlen, refi->thispartn, refi->maxpartn); | |
190 | ||
191 | fd_src = open(partfile, O_RDONLY); | |
192 | if (fd_src < 0) | |
193 | ohshite(_("unable to reopen part file '%.250s'"), partfile); | |
194 | fd_dst = creat(p, 0644); | |
195 | if (fd_dst < 0) | |
196 | ohshite(_("unable to open new depot file '%.250s'"), p); | |
197 | ||
198 | if (fd_fd_copy(fd_src, fd_dst, refi->filesize, &err) < 0) | |
199 | ohshit(_("cannot extract split package part '%s': %s"), | |
200 | partfile, err.str); | |
201 | ||
202 | if (fsync(fd_dst)) | |
203 | ohshite(_("unable to sync file '%s'"), p); | |
204 | if (close(fd_dst)) | |
205 | ohshite(_("unable to close file '%s'"), p); | |
206 | close(fd_src); | |
207 | ||
208 | if (rename(p, q)) | |
209 | ohshite(_("unable to rename new depot file '%.250s' to '%.250s'"), p, q); | |
210 | free(q); | |
211 | free(p); | |
212 | ||
213 | printf(_("Part %d of package %s filed (still want "),refi->thispartn,refi->package); | |
214 | /* There are still some parts missing. */ | |
215 | for (i=0, ap=0; i<refi->maxpartn; i++) | |
216 | if (!partlist[i]) | |
217 | printf("%s%d", !ap++ ? "" : i == (unsigned int)j ? _(" and ") : ", ", i + 1); | |
218 | printf(").\n"); | |
219 | ||
220 | dir_sync_path(opt_depotdir); | |
221 | } else { | |
222 | ||
223 | /* We have all the parts. */ | |
224 | reassemble(partlist, opt_outputfile); | |
225 | ||
226 | /* OK, delete all the parts (except the new one, which we never copied). */ | |
227 | partlist[refi->thispartn-1]= otherthispart; | |
228 | for (i=0; i<refi->maxpartn; i++) | |
229 | if (partlist[i]) | |
230 | if (unlink(partlist[i]->filename)) | |
231 | ohshite(_("unable to delete used-up depot file '%.250s'"), | |
232 | partlist[i]->filename); | |
233 | ||
234 | } | |
235 | ||
236 | m_output(stderr, _("<standard error>")); | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | int | |
242 | do_queue(const char *const *argv) | |
243 | { | |
244 | struct partqueue *queue; | |
245 | struct partqueue *pq; | |
246 | const char *head; | |
247 | struct stat stab; | |
248 | off_t bytes; | |
249 | ||
250 | if (*argv) | |
251 | badusage(_("--%s takes no arguments"), cipaction->olong); | |
252 | ||
253 | queue = scandepot(); | |
254 | ||
255 | head= N_("Junk files left around in the depot directory:\n"); | |
256 | for (pq= queue; pq; pq= pq->nextinqueue) { | |
257 | if (pq->info.md5sum) continue; | |
258 | fputs(gettext(head),stdout); head= ""; | |
259 | if (lstat(pq->info.filename,&stab)) | |
260 | ohshit(_("unable to stat '%.250s'"), pq->info.filename); | |
261 | if (S_ISREG(stab.st_mode)) { | |
262 | bytes= stab.st_size; | |
263 | printf(_(" %s (%jd bytes)\n"), pq->info.filename, (intmax_t)bytes); | |
264 | } else { | |
265 | printf(_(" %s (not a plain file)\n"),pq->info.filename); | |
266 | } | |
267 | } | |
268 | if (!*head) putchar('\n'); | |
269 | ||
270 | head= N_("Packages not yet reassembled:\n"); | |
271 | for (pq= queue; pq; pq= pq->nextinqueue) { | |
272 | struct partinfo ti; | |
273 | unsigned int i; | |
274 | ||
275 | if (!pq->info.md5sum) continue; | |
276 | mustgetpartinfo(pq->info.filename,&ti); | |
277 | fputs(gettext(head),stdout); head= ""; | |
278 | printf(_(" Package %s: part(s) "), ti.package); | |
279 | bytes= 0; | |
280 | for (i=0; i<ti.maxpartn; i++) { | |
281 | struct partqueue *qq; | |
282 | ||
283 | for (qq= pq; | |
284 | qq && !(partmatches(&qq->info,&ti) && qq->info.thispartn == i+1); | |
285 | qq= qq->nextinqueue); | |
286 | if (qq) { | |
287 | printf("%d ",i+1); | |
288 | if (lstat(qq->info.filename,&stab)) | |
289 | ohshite(_("unable to stat '%.250s'"), qq->info.filename); | |
290 | if (!S_ISREG(stab.st_mode)) | |
291 | ohshit(_("part file '%.250s' is not a plain file"), qq->info.filename); | |
292 | bytes+= stab.st_size; | |
293 | ||
294 | /* Don't find this package again. */ | |
295 | qq->info.md5sum = NULL; | |
296 | } | |
297 | } | |
298 | printf(_("(total %jd bytes)\n"), (intmax_t)bytes); | |
299 | } | |
300 | m_output(stdout, _("<standard output>")); | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | enum discard_which { | |
306 | DISCARD_PART_JUNK, | |
307 | DISCARD_PART_PACKAGE, | |
308 | DISCARD_PART_ALL, | |
309 | }; | |
310 | ||
311 | static void | |
312 | discard_parts(struct partqueue *queue, enum discard_which which, | |
313 | const char *package) | |
314 | { | |
315 | struct partqueue *pq; | |
316 | ||
317 | for (pq= queue; pq; pq= pq->nextinqueue) { | |
318 | switch (which) { | |
319 | case DISCARD_PART_JUNK: | |
320 | if (pq->info.md5sum) continue; | |
321 | break; | |
322 | case DISCARD_PART_PACKAGE: | |
323 | if (!pq->info.md5sum || strcasecmp(pq->info.package,package)) continue; | |
324 | break; | |
325 | case DISCARD_PART_ALL: | |
326 | break; | |
327 | default: | |
328 | internerr("unknown discard_which '%d'", which); | |
329 | } | |
330 | if (unlink(pq->info.filename)) | |
331 | ohshite(_("unable to discard '%.250s'"), pq->info.filename); | |
332 | printf(_("Deleted %s.\n"),pq->info.filename); | |
333 | } | |
334 | } | |
335 | ||
336 | int | |
337 | do_discard(const char *const *argv) | |
338 | { | |
339 | const char *thisarg; | |
340 | struct partqueue *queue; | |
341 | struct partqueue *pq; | |
342 | ||
343 | queue = scandepot(); | |
344 | if (*argv) { | |
345 | for (pq= queue; pq; pq= pq->nextinqueue) | |
346 | if (pq->info.md5sum) | |
347 | mustgetpartinfo(pq->info.filename,&pq->info); | |
348 | discard_parts(queue, DISCARD_PART_JUNK, NULL); | |
349 | while ((thisarg = *argv++)) | |
350 | discard_parts(queue, DISCARD_PART_PACKAGE, thisarg); | |
351 | } else { | |
352 | discard_parts(queue, DISCARD_PART_ALL, NULL); | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } |