Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg-split - splitting and joining of multipart *.deb archives | |
3 | * info.c - information about split archives | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2008-2012 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 <string.h> | |
30 | #include <unistd.h> | |
31 | #include <ar.h> | |
32 | #include <inttypes.h> | |
33 | #include <stdint.h> | |
34 | #include <stdlib.h> | |
35 | #include <stdio.h> | |
36 | ||
37 | #include <dpkg/i18n.h> | |
38 | #include <dpkg/c-ctype.h> | |
39 | #include <dpkg/dpkg.h> | |
40 | #include <dpkg/dpkg-db.h> | |
41 | #include <dpkg/fdio.h> | |
42 | #include <dpkg/ar.h> | |
43 | #include <dpkg/options.h> | |
44 | ||
45 | #include "dpkg-split.h" | |
46 | ||
47 | static intmax_t | |
48 | parse_intmax(const char *value, const char *fn, const char *what) | |
49 | { | |
50 | intmax_t r; | |
51 | char *endp; | |
52 | ||
53 | errno = 0; | |
54 | r = strtoimax(value, &endp, 10); | |
55 | if (value == endp || *endp) | |
56 | ohshit(_("file '%.250s' is corrupt - bad digit (code %d) in %s"), | |
57 | fn, *endp, what); | |
58 | if (r < 0 || errno == ERANGE) | |
59 | ohshit(_("file '%s' is corrupt; out of range integer in %s"), fn, what); | |
60 | return r; | |
61 | } | |
62 | ||
63 | static char *nextline(char **ripp, const char *fn, const char *what) { | |
64 | char *newline, *rip; | |
65 | ||
66 | rip= *ripp; | |
67 | if (!rip) | |
68 | ohshit(_("file '%.250s' is corrupt - %.250s missing"), fn, what); | |
69 | newline= strchr(rip,'\n'); | |
70 | if (!newline) | |
71 | ohshit(_("file '%.250s' is corrupt - missing newline after %.250s"), | |
72 | fn, what); | |
73 | *ripp= newline+1; | |
74 | while (newline > rip && c_isspace(newline[-1])) | |
75 | newline--; | |
76 | *newline = '\0'; | |
77 | return rip; | |
78 | } | |
79 | ||
80 | /** | |
81 | * Read a deb-split part archive. | |
82 | * | |
83 | * @return Part info (nfmalloc'd) if was an archive part and we read it, | |
84 | * NULL if it wasn't. | |
85 | */ | |
86 | struct partinfo * | |
87 | read_info(struct dpkg_ar *ar, struct partinfo *ir) | |
88 | { | |
89 | static char *readinfobuf= NULL; | |
90 | static size_t readinfobuflen= 0; | |
91 | ||
92 | size_t thisilen; | |
93 | intmax_t templong; | |
94 | char magicbuf[sizeof(DPKG_AR_MAGIC) - 1], *rip, *partnums, *slash; | |
95 | const char *err; | |
96 | struct dpkg_ar_hdr arh; | |
97 | ssize_t rc; | |
98 | ||
99 | rc = fd_read(ar->fd, magicbuf, sizeof(magicbuf)); | |
100 | if (rc != sizeof(magicbuf)) { | |
101 | if (rc < 0) | |
102 | ohshite(_("error reading %.250s"), ar->name); | |
103 | else | |
104 | return NULL; | |
105 | } | |
106 | if (memcmp(magicbuf, DPKG_AR_MAGIC, sizeof(magicbuf))) | |
107 | return NULL; | |
108 | ||
109 | rc = fd_read(ar->fd, &arh, sizeof(arh)); | |
110 | if (rc != sizeof(arh)) | |
111 | read_fail(rc, ar->name, "ar header"); | |
112 | ||
113 | dpkg_ar_normalize_name(&arh); | |
114 | ||
115 | if (strncmp(arh.ar_name, PARTMAGIC, sizeof(arh.ar_name)) != 0) | |
116 | return NULL; | |
117 | if (dpkg_ar_member_is_illegal(&arh)) | |
118 | ohshit(_("file '%.250s' is corrupt - bad magic at end of first header"), | |
119 | ar->name); | |
120 | thisilen = dpkg_ar_member_get_size(ar, &arh); | |
121 | if (thisilen >= readinfobuflen) { | |
122 | readinfobuflen = thisilen + 2; | |
123 | readinfobuf= m_realloc(readinfobuf,readinfobuflen); | |
124 | } | |
125 | rc = fd_read(ar->fd, readinfobuf, thisilen + (thisilen & 1)); | |
126 | if (rc != (ssize_t)(thisilen + (thisilen & 1))) | |
127 | read_fail(rc, ar->name, "reading header member"); | |
128 | if (thisilen & 1) { | |
129 | int c = readinfobuf[thisilen + 1]; | |
130 | ||
131 | if (c != '\n') | |
132 | ohshit(_("file '%.250s' is corrupt - bad padding character (code %d)"), | |
133 | ar->name, c); | |
134 | } | |
135 | readinfobuf[thisilen] = '\0'; | |
136 | if (memchr(readinfobuf,0,thisilen)) | |
137 | ohshit(_("file '%.250s' is corrupt - nulls in info section"), ar->name); | |
138 | ||
139 | ir->filename = ar->name; | |
140 | ||
141 | rip= readinfobuf; | |
142 | err = deb_version_parse(&ir->fmtversion, | |
143 | nextline(&rip, ar->name, _("format version number"))); | |
144 | if (err) | |
145 | ohshit(_("file '%.250s' has invalid format version: %s"), ar->name, err); | |
146 | if (ir->fmtversion.major != 2) | |
147 | ohshit(_("file '%.250s' is format version %d.%d; get a newer dpkg-split"), | |
148 | ar->name, ir->fmtversion.major, ir->fmtversion.minor); | |
149 | ||
150 | ir->package = nfstrsave(nextline(&rip, ar->name, _("package name"))); | |
151 | ir->version = nfstrsave(nextline(&rip, ar->name, _("package version number"))); | |
152 | ir->md5sum = nfstrsave(nextline(&rip, ar->name, _("package file MD5 checksum"))); | |
153 | if (strlen(ir->md5sum) != MD5HASHLEN || | |
154 | strspn(ir->md5sum, "0123456789abcdef") != MD5HASHLEN) | |
155 | ohshit(_("file '%.250s' is corrupt - bad MD5 checksum '%.250s'"), | |
156 | ar->name, ir->md5sum); | |
157 | ||
158 | ir->orglength = parse_intmax(nextline(&rip, ar->name, _("archive total size")), | |
159 | ar->name, _("archive total size")); | |
160 | ir->maxpartlen = parse_intmax(nextline(&rip, ar->name, _("archive part offset")), | |
161 | ar->name, _("archive part offset")); | |
162 | ||
163 | partnums = nextline(&rip, ar->name, _("archive part numbers")); | |
164 | slash= strchr(partnums,'/'); | |
165 | if (!slash) | |
166 | ohshit(_("file '%.250s' is corrupt - no slash between archive part numbers"), ar->name); | |
167 | *slash++ = '\0'; | |
168 | ||
169 | templong = parse_intmax(slash, ar->name, _("number of archive parts")); | |
170 | if (templong <= 0 || templong > INT_MAX) | |
171 | ohshit(_("file '%.250s' is corrupt - bad number of archive parts"), ar->name); | |
172 | ir->maxpartn= templong; | |
173 | templong = parse_intmax(partnums, ar->name, _("archive parts number")); | |
174 | if (templong <= 0 || templong > ir->maxpartn) | |
175 | ohshit(_("file '%.250s' is corrupt - bad archive part number"), ar->name); | |
176 | ir->thispartn= templong; | |
177 | ||
178 | /* If the package was created with dpkg 1.16.1 or later it will include | |
179 | * the architecture. */ | |
180 | if (*rip != '\0') | |
181 | ir->arch = nfstrsave(nextline(&rip, ar->name, _("package architecture"))); | |
182 | else | |
183 | ir->arch = NULL; | |
184 | ||
185 | rc = fd_read(ar->fd, &arh, sizeof(arh)); | |
186 | if (rc != sizeof(arh)) | |
187 | read_fail(rc, ar->name, "reading data part member ar header"); | |
188 | ||
189 | dpkg_ar_normalize_name(&arh); | |
190 | ||
191 | if (dpkg_ar_member_is_illegal(&arh)) | |
192 | ohshit(_("file '%.250s' is corrupt - bad magic at end of second header"), | |
193 | ar->name); | |
194 | if (strncmp(arh.ar_name,"data",4)) | |
195 | ohshit(_("file '%.250s' is corrupt - second member is not data member"), | |
196 | ar->name); | |
197 | ||
198 | ir->thispartlen = dpkg_ar_member_get_size(ar, &arh); | |
199 | ir->thispartoffset= (ir->thispartn-1)*ir->maxpartlen; | |
200 | ||
201 | if (ir->maxpartn != (ir->orglength+ir->maxpartlen-1)/ir->maxpartlen) | |
202 | ohshit(_("file '%.250s' is corrupt - wrong number of parts for quoted sizes"), | |
203 | ar->name); | |
204 | if (ir->thispartlen != | |
205 | (ir->thispartn == ir->maxpartn | |
206 | ? ir->orglength - ir->thispartoffset : ir->maxpartlen)) | |
207 | ohshit(_("file '%.250s' is corrupt - size is wrong for quoted part number"), | |
208 | ar->name); | |
209 | ||
210 | ir->filesize = (strlen(DPKG_AR_MAGIC) + | |
211 | sizeof(arh) + thisilen + (thisilen & 1) + | |
212 | sizeof(arh) + ir->thispartlen + (ir->thispartlen & 1)); | |
213 | ||
214 | if (S_ISREG(ar->mode)) { | |
215 | /* Don't do this check if it's coming from a pipe or something. It's | |
216 | * only an extra sanity check anyway. */ | |
217 | if (ar->size < ir->filesize) | |
218 | ohshit(_("file '%.250s' is corrupt - too short"), ar->name); | |
219 | } | |
220 | ||
221 | ir->headerlen = strlen(DPKG_AR_MAGIC) + | |
222 | sizeof(arh) + thisilen + (thisilen & 1) + sizeof(arh); | |
223 | ||
224 | return ir; | |
225 | } | |
226 | ||
227 | void mustgetpartinfo(const char *filename, struct partinfo *ri) { | |
228 | struct dpkg_ar *part; | |
229 | ||
230 | part = dpkg_ar_open(filename); | |
231 | if (!part) | |
232 | ohshite(_("cannot open archive part file '%.250s'"), filename); | |
233 | if (!read_info(part, ri)) | |
234 | ohshite(_("file '%.250s' is not an archive part"), filename); | |
235 | dpkg_ar_close(part); | |
236 | } | |
237 | ||
238 | void print_info(const struct partinfo *pi) { | |
239 | printf(_("%s:\n" | |
240 | " Part format version: %d.%d\n" | |
241 | " Part of package: %s\n" | |
242 | " ... version: %s\n" | |
243 | " ... architecture: %s\n" | |
244 | " ... MD5 checksum: %s\n" | |
245 | " ... length: %jd bytes\n" | |
246 | " ... split every: %jd bytes\n" | |
247 | " Part number: %d/%d\n" | |
248 | " Part length: %jd bytes\n" | |
249 | " Part offset: %jd bytes\n" | |
250 | " Part file size (used portion): %jd bytes\n\n"), | |
251 | pi->filename, | |
252 | pi->fmtversion.major, pi->fmtversion.minor, | |
253 | pi->package, | |
254 | pi->version, | |
255 | pi->arch ? pi->arch : C_("architecture", "<unknown>"), | |
256 | pi->md5sum, | |
257 | (intmax_t)pi->orglength, | |
258 | (intmax_t)pi->maxpartlen, | |
259 | pi->thispartn, | |
260 | pi->maxpartn, | |
261 | (intmax_t)pi->thispartlen, | |
262 | (intmax_t)pi->thispartoffset, | |
263 | (intmax_t)pi->filesize); | |
264 | } | |
265 | ||
266 | int | |
267 | do_info(const char *const *argv) | |
268 | { | |
269 | const char *thisarg; | |
270 | struct partinfo *pi, ps; | |
271 | struct dpkg_ar *part; | |
272 | ||
273 | if (!*argv) | |
274 | badusage(_("--%s requires one or more part file arguments"), | |
275 | cipaction->olong); | |
276 | ||
277 | while ((thisarg= *argv++)) { | |
278 | part = dpkg_ar_open(thisarg); | |
279 | if (!part) | |
280 | ohshite(_("cannot open archive part file '%.250s'"), thisarg); | |
281 | pi = read_info(part, &ps); | |
282 | dpkg_ar_close(part); | |
283 | if (pi) { | |
284 | print_info(pi); | |
285 | } else { | |
286 | printf(_("file '%s' is not an archive part\n"), thisarg); | |
287 | } | |
288 | m_output(stdout, _("<standard output>")); | |
289 | } | |
290 | ||
291 | return 0; | |
292 | } |