Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg-split - splitting and joining of multipart *.deb archives | |
3 | * split.c - splitting archives | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2008-2015 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/types.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/wait.h> | |
28 | ||
29 | #include <errno.h> | |
30 | #include <limits.h> | |
31 | #include <fcntl.h> | |
32 | #include <libgen.h> | |
33 | #include <string.h> | |
34 | #include <time.h> | |
35 | #include <unistd.h> | |
36 | #include <stdint.h> | |
37 | #include <stdlib.h> | |
38 | #include <stdio.h> | |
39 | ||
40 | #include <dpkg/i18n.h> | |
41 | #include <dpkg/c-ctype.h> | |
42 | #include <dpkg/dpkg.h> | |
43 | #include <dpkg/dpkg-db.h> | |
44 | #include <dpkg/parsedump.h> | |
45 | #include <dpkg/path.h> | |
46 | #include <dpkg/string.h> | |
47 | #include <dpkg/subproc.h> | |
48 | #include <dpkg/buffer.h> | |
49 | #include <dpkg/ar.h> | |
50 | #include <dpkg/options.h> | |
51 | ||
52 | #include "dpkg-split.h" | |
53 | ||
54 | /** | |
55 | * Parse the control file from a .deb package into a struct pkginfo. | |
56 | */ | |
57 | static struct pkginfo * | |
58 | deb_parse_control(const char *filename) | |
59 | { | |
60 | struct parsedb_state *ps; | |
61 | struct pkginfo *pkg; | |
62 | pid_t pid; | |
63 | int p[2]; | |
64 | ||
65 | m_pipe(p); | |
66 | ||
67 | pid = subproc_fork(); | |
68 | if (pid == 0) { | |
69 | /* Child writes to pipe. */ | |
70 | m_dup2(p[1], 1); | |
71 | close(p[0]); | |
72 | close(p[1]); | |
73 | ||
74 | execlp(BACKEND, BACKEND, "--info", filename, "control", NULL); | |
75 | ohshite(_("unable to execute %s (%s)"), | |
76 | _("package field value extraction"), BACKEND); | |
77 | } | |
78 | close(p[1]); | |
79 | ||
80 | /* Parent reads from pipe. */ | |
81 | ps = parsedb_new(_("<dpkg-deb --info pipe>"), p[0], pdb_parse_binary); | |
82 | parsedb_load(ps); | |
83 | parsedb_parse(ps, &pkg); | |
84 | parsedb_close(ps); | |
85 | ||
86 | close(p[0]); | |
87 | ||
88 | subproc_reap(pid, _("package field value extraction"), SUBPROC_NOPIPE); | |
89 | ||
90 | return pkg; | |
91 | } | |
92 | ||
93 | static time_t | |
94 | parse_timestamp(const char *value) | |
95 | { | |
96 | time_t timestamp; | |
97 | char *end; | |
98 | ||
99 | errno = 0; | |
100 | timestamp = strtol(value, &end, 10); | |
101 | if (value == end || *end || errno != 0) | |
102 | ohshite(_("unable to parse timestamp '%.255s'"), value); | |
103 | ||
104 | return timestamp; | |
105 | } | |
106 | ||
107 | /* Cleanup filename for use in crippled msdos systems. */ | |
108 | static char * | |
109 | clean_msdos_filename(char *filename) | |
110 | { | |
111 | char *d, *s; | |
112 | ||
113 | for (s = d = filename; *s; d++, s++) { | |
114 | if (*s == '+') | |
115 | *d = 'x'; | |
116 | else if (c_isupper(*s)) | |
117 | *d = c_tolower(*s); | |
118 | else if (c_islower(*s) || c_isdigit(*s)) | |
119 | *d = *s; | |
120 | else | |
121 | s++; | |
122 | } | |
123 | ||
124 | return filename; | |
125 | } | |
126 | ||
127 | static int | |
128 | mksplit(const char *file_src, const char *prefix, off_t maxpartsize, | |
129 | bool msdos) | |
130 | { | |
131 | struct pkginfo *pkg; | |
132 | struct dpkg_error err; | |
133 | int fd_src; | |
134 | struct stat st; | |
135 | time_t timestamp; | |
136 | const char *timestamp_str; | |
137 | const char *version; | |
138 | char hash[MD5HASHLEN + 1]; | |
139 | int nparts, curpart; | |
140 | off_t partsize; | |
141 | off_t cur_partsize, last_partsize; | |
142 | char *prefixdir = NULL, *msdos_prefix = NULL; | |
143 | struct varbuf file_dst = VARBUF_INIT; | |
144 | struct varbuf partmagic = VARBUF_INIT; | |
145 | struct varbuf partname = VARBUF_INIT; | |
146 | ||
147 | fd_src = open(file_src, O_RDONLY); | |
148 | if (fd_src < 0) | |
149 | ohshite(_("unable to open source file '%.250s'"), file_src); | |
150 | if (fstat(fd_src, &st)) | |
151 | ohshite(_("unable to fstat source file")); | |
152 | if (!S_ISREG(st.st_mode)) | |
153 | ohshit(_("source file '%.250s' not a plain file"), file_src); | |
154 | ||
155 | if (fd_md5(fd_src, hash, -1, &err) < 0) | |
156 | ohshit(_("cannot compute MD5 hash for file '%s': %s"), | |
157 | file_src, err.str); | |
158 | lseek(fd_src, 0, SEEK_SET); | |
159 | ||
160 | pkg = deb_parse_control(file_src); | |
161 | version = versiondescribe(&pkg->available.version, vdew_nonambig); | |
162 | ||
163 | timestamp_str = getenv("SOURCE_DATE_EPOCH"); | |
164 | if (timestamp_str) | |
165 | timestamp = parse_timestamp(timestamp_str); | |
166 | else | |
167 | timestamp = time(NULL); | |
168 | ||
169 | partsize = maxpartsize - HEADERALLOWANCE; | |
170 | last_partsize = st.st_size % partsize; | |
171 | if (last_partsize == 0) | |
172 | last_partsize = partsize; | |
173 | nparts = (st.st_size + partsize - 1) / partsize; | |
174 | ||
175 | printf(P_("Splitting package %s into %d part: ", | |
176 | "Splitting package %s into %d parts: ", nparts), | |
177 | pkg->set->name, nparts); | |
178 | ||
179 | if (msdos) { | |
180 | char *t; | |
181 | ||
182 | t = m_strdup(prefix); | |
183 | prefixdir = m_strdup(dirname(t)); | |
184 | free(t); | |
185 | ||
186 | msdos_prefix = m_strdup(path_basename(prefix)); | |
187 | prefix = clean_msdos_filename(msdos_prefix); | |
188 | } | |
189 | ||
190 | for (curpart = 1; curpart <= nparts; curpart++) { | |
191 | struct dpkg_ar *ar; | |
192 | ||
193 | varbuf_reset(&file_dst); | |
194 | /* Generate output filename. */ | |
195 | if (msdos) { | |
196 | char *refname; | |
197 | int prefix_max; | |
198 | ||
199 | refname = str_fmt("%dof%d", curpart, nparts); | |
200 | prefix_max = max(8 - strlen(refname), 0); | |
201 | varbuf_printf(&file_dst, "%s/%.*s%.8s.deb", | |
202 | prefixdir, prefix_max, prefix, refname); | |
203 | free(refname); | |
204 | } else { | |
205 | varbuf_printf(&file_dst, "%s.%dof%d.deb", | |
206 | prefix, curpart, nparts); | |
207 | } | |
208 | ||
209 | if (curpart == nparts) | |
210 | cur_partsize = last_partsize; | |
211 | else | |
212 | cur_partsize = partsize; | |
213 | ||
214 | if (cur_partsize > maxpartsize) { | |
215 | ohshit(_("header is too long, making part too long; " | |
216 | "the package name or version\n" | |
217 | "numbers must be extraordinarily long, " | |
218 | "or something; giving up")); | |
219 | } | |
220 | ||
221 | /* Split the data. */ | |
222 | ar = dpkg_ar_create(file_dst.buf, 0644); | |
223 | dpkg_ar_set_mtime(ar, timestamp); | |
224 | ||
225 | /* Write the ar header. */ | |
226 | dpkg_ar_put_magic(ar); | |
227 | ||
228 | /* Write the debian-split part. */ | |
229 | varbuf_printf(&partmagic, | |
230 | "%s\n%s\n%s\n%s\n%jd\n%jd\n%d/%d\n%s\n", | |
231 | SPLITVERSION, pkg->set->name, version, hash, | |
232 | (intmax_t)st.st_size, (intmax_t)partsize, | |
233 | curpart, nparts, pkg->available.arch->name); | |
234 | dpkg_ar_member_put_mem(ar, PARTMAGIC, | |
235 | partmagic.buf, partmagic.used); | |
236 | varbuf_reset(&partmagic); | |
237 | ||
238 | /* Write the data part. */ | |
239 | varbuf_printf(&partname, "data.%d", curpart); | |
240 | dpkg_ar_member_put_file(ar, partname.buf, | |
241 | fd_src, cur_partsize); | |
242 | varbuf_reset(&partname); | |
243 | ||
244 | dpkg_ar_close(ar); | |
245 | ||
246 | printf("%d ", curpart); | |
247 | } | |
248 | ||
249 | varbuf_destroy(&file_dst); | |
250 | varbuf_destroy(&partname); | |
251 | varbuf_destroy(&partmagic); | |
252 | ||
253 | free(prefixdir); | |
254 | free(msdos_prefix); | |
255 | ||
256 | close(fd_src); | |
257 | ||
258 | printf(_("done\n")); | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | int | |
264 | do_split(const char *const *argv) | |
265 | { | |
266 | const char *sourcefile, *prefix; | |
267 | ||
268 | sourcefile = *argv++; | |
269 | if (!sourcefile) | |
270 | badusage(_("--split needs a source filename argument")); | |
271 | prefix = *argv++; | |
272 | if (prefix && *argv) | |
273 | badusage(_("--split takes at most a source filename and destination prefix")); | |
274 | if (!prefix) { | |
275 | size_t sourcefile_len = strlen(sourcefile); | |
276 | ||
277 | if (str_match_end(sourcefile, DEBEXT)) | |
278 | sourcefile_len -= strlen(DEBEXT); | |
279 | ||
280 | prefix = nfstrnsave(sourcefile, sourcefile_len); | |
281 | } | |
282 | ||
283 | mksplit(sourcefile, prefix, opt_maxpartsize, opt_msdos); | |
284 | ||
285 | return 0; | |
286 | } |