Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg-deb - construction and deconstruction of *.deb archives | |
3 | * build.c - building archives | |
4 | * | |
5 | * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> | |
7 | * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> | |
8 | * | |
9 | * This is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <compat.h> | |
25 | ||
26 | #include <sys/types.h> | |
27 | #include <sys/stat.h> | |
28 | #include <sys/wait.h> | |
29 | ||
30 | #include <errno.h> | |
31 | #include <limits.h> | |
32 | #include <string.h> | |
33 | #include <time.h> | |
34 | #include <dirent.h> | |
35 | #include <fcntl.h> | |
36 | #include <unistd.h> | |
37 | #include <stdbool.h> | |
38 | #include <stdint.h> | |
39 | #include <stdlib.h> | |
40 | #include <stdio.h> | |
41 | ||
42 | #include <dpkg/i18n.h> | |
43 | #include <dpkg/c-ctype.h> | |
44 | #include <dpkg/dpkg.h> | |
45 | #include <dpkg/dpkg-db.h> | |
46 | #include <dpkg/path.h> | |
47 | #include <dpkg/treewalk.h> | |
48 | #include <dpkg/varbuf.h> | |
49 | #include <dpkg/fdio.h> | |
50 | #include <dpkg/buffer.h> | |
51 | #include <dpkg/subproc.h> | |
52 | #include <dpkg/command.h> | |
53 | #include <dpkg/compress.h> | |
54 | #include <dpkg/ar.h> | |
55 | #include <dpkg/options.h> | |
56 | ||
57 | #include "dpkg-deb.h" | |
58 | ||
59 | static void | |
60 | control_treewalk_feed(const char *dir, int fd_out) | |
61 | { | |
62 | struct treeroot *tree; | |
63 | struct treenode *node; | |
64 | ||
65 | tree = treewalk_open(dir, TREEWALK_NONE, NULL); | |
66 | for (node = treewalk_node(tree); node; node = treewalk_next(tree)) { | |
67 | char *nodename; | |
68 | ||
69 | nodename = str_fmt("./%s", treenode_get_virtname(node)); | |
70 | if (fd_write(fd_out, nodename, strlen(nodename) + 1) < 0) | |
71 | ohshite(_("failed to write filename to tar pipe (%s)"), | |
72 | _("control member")); | |
73 | free(nodename); | |
74 | } | |
75 | treewalk_close(tree); | |
76 | } | |
77 | ||
78 | /** | |
79 | * Simple structure to store information about a file. | |
80 | */ | |
81 | struct file_info { | |
82 | struct file_info *next; | |
83 | char *fn; | |
84 | }; | |
85 | ||
86 | static struct file_info * | |
87 | file_info_new(const char *filename) | |
88 | { | |
89 | struct file_info *fi; | |
90 | ||
91 | fi = m_malloc(sizeof(*fi)); | |
92 | fi->fn = m_strdup(filename); | |
93 | fi->next = NULL; | |
94 | ||
95 | return fi; | |
96 | } | |
97 | ||
98 | static void | |
99 | file_info_free(struct file_info *fi) | |
100 | { | |
101 | free(fi->fn); | |
102 | free(fi); | |
103 | } | |
104 | ||
105 | static struct file_info * | |
106 | file_info_find_name(struct file_info *list, const char *filename) | |
107 | { | |
108 | struct file_info *node; | |
109 | ||
110 | for (node = list; node; node = node->next) | |
111 | if (strcmp(node->fn, filename) == 0) | |
112 | return node; | |
113 | ||
114 | return NULL; | |
115 | } | |
116 | ||
117 | /** | |
118 | * Add a new file_info struct to a single linked list of file_info structs. | |
119 | * | |
120 | * We perform a slight optimization to work around a ‘feature’ in tar: tar | |
121 | * always recurses into subdirectories if you list a subdirectory. So if an | |
122 | * entry is added and the previous entry in the list is its subdirectory we | |
123 | * remove the subdirectory. | |
124 | * | |
125 | * After a file_info struct is added to a list it may no longer be freed, we | |
126 | * assume full responsibility for its memory. | |
127 | */ | |
128 | static void | |
129 | file_info_list_append(struct file_info **head, struct file_info **tail, | |
130 | struct file_info *fi) | |
131 | { | |
132 | if (*head == NULL) | |
133 | *head = *tail = fi; | |
134 | else | |
135 | *tail = (*tail)->next =fi; | |
136 | } | |
137 | ||
138 | /** | |
139 | * Free the memory for all entries in a list of file_info structs. | |
140 | */ | |
141 | static void | |
142 | file_info_list_free(struct file_info *fi) | |
143 | { | |
144 | while (fi) { | |
145 | struct file_info *fl; | |
146 | ||
147 | fl=fi; fi=fi->next; | |
148 | file_info_free(fl); | |
149 | } | |
150 | } | |
151 | ||
152 | static void | |
153 | file_treewalk_feed(const char *dir, int fd_out) | |
154 | { | |
155 | struct treeroot *tree; | |
156 | struct treenode *node; | |
157 | struct file_info *fi; | |
158 | struct file_info *symlist = NULL; | |
159 | struct file_info *symlist_end = NULL; | |
160 | ||
161 | tree = treewalk_open(dir, TREEWALK_NONE, NULL); | |
162 | for (node = treewalk_node(tree); node ; node = treewalk_next(tree)) { | |
163 | const char *virtname = treenode_get_virtname(node); | |
164 | char *nodename; | |
165 | ||
166 | if (strncmp(virtname, BUILDCONTROLDIR, strlen(BUILDCONTROLDIR)) == 0) | |
167 | continue; | |
168 | ||
169 | nodename = str_fmt("./%s", virtname); | |
170 | ||
171 | if (strchr(nodename, '\n')) | |
172 | ohshit(_("newline not allowed in pathname '%s'"), nodename); | |
173 | ||
174 | /* We need to reorder the files so we can make sure that symlinks | |
175 | * will not appear before their target. */ | |
176 | if (S_ISLNK(treenode_get_mode(node))) { | |
177 | fi = file_info_new(nodename); | |
178 | file_info_list_append(&symlist, &symlist_end, fi); | |
179 | } else { | |
180 | if (fd_write(fd_out, nodename, strlen(nodename) + 1) < 0) | |
181 | ohshite(_("failed to write filename to tar pipe (%s)"), | |
182 | _("data member")); | |
183 | } | |
184 | ||
185 | free(nodename); | |
186 | } | |
187 | treewalk_close(tree); | |
188 | ||
189 | for (fi = symlist; fi; fi = fi->next) | |
190 | if (fd_write(fd_out, fi->fn, strlen(fi->fn) + 1) < 0) | |
191 | ohshite(_("failed to write filename to tar pipe (%s)"), _("data member")); | |
192 | ||
193 | file_info_list_free(symlist); | |
194 | } | |
195 | ||
196 | static const char *const maintainerscripts[] = { | |
197 | PREINSTFILE, | |
198 | POSTINSTFILE, | |
199 | PRERMFILE, | |
200 | POSTRMFILE, | |
201 | NULL, | |
202 | }; | |
203 | ||
204 | /** | |
205 | * Check control directory and file permissions. | |
206 | */ | |
207 | static void | |
208 | check_file_perms(const char *ctrldir) | |
209 | { | |
210 | struct varbuf path = VARBUF_INIT; | |
211 | const char *const *mscriptp; | |
212 | struct stat mscriptstab; | |
213 | ||
214 | varbuf_printf(&path, "%s/", ctrldir); | |
215 | if (lstat(path.buf, &mscriptstab)) | |
216 | ohshite(_("unable to stat control directory")); | |
217 | if (!S_ISDIR(mscriptstab.st_mode)) | |
218 | ohshit(_("control directory is not a directory")); | |
219 | if ((mscriptstab.st_mode & 07757) != 0755) | |
220 | ohshit(_("control directory has bad permissions %03lo " | |
221 | "(must be >=0755 and <=0775)"), | |
222 | (unsigned long)(mscriptstab.st_mode & 07777)); | |
223 | ||
224 | for (mscriptp = maintainerscripts; *mscriptp; mscriptp++) { | |
225 | varbuf_reset(&path); | |
226 | varbuf_printf(&path, "%s/%s", ctrldir, *mscriptp); | |
227 | if (!lstat(path.buf, &mscriptstab)) { | |
228 | if (S_ISLNK(mscriptstab.st_mode)) | |
229 | continue; | |
230 | if (!S_ISREG(mscriptstab.st_mode)) | |
231 | ohshit(_("maintainer script '%.50s' is not a plain file or symlink"), | |
232 | *mscriptp); | |
233 | if ((mscriptstab.st_mode & 07557) != 0555) | |
234 | ohshit(_("maintainer script '%.50s' has bad permissions %03lo " | |
235 | "(must be >=0555 and <=0775)"), | |
236 | *mscriptp, (unsigned long)(mscriptstab.st_mode & 07777)); | |
237 | } else if (errno != ENOENT) { | |
238 | ohshite(_("maintainer script '%.50s' is not stattable"), *mscriptp); | |
239 | } | |
240 | } | |
241 | ||
242 | varbuf_destroy(&path); | |
243 | } | |
244 | ||
245 | /** | |
246 | * Check if conffiles contains sane information. | |
247 | */ | |
248 | static void | |
249 | check_conffiles(const char *ctrldir, const char *rootdir) | |
250 | { | |
251 | FILE *cf; | |
252 | struct varbuf controlfile = VARBUF_INIT; | |
253 | char conffilename[MAXCONFFILENAME + 1]; | |
254 | struct file_info *conffiles_head = NULL; | |
255 | struct file_info *conffiles_tail = NULL; | |
256 | ||
257 | varbuf_printf(&controlfile, "%s/%s", ctrldir, CONFFILESFILE); | |
258 | ||
259 | cf = fopen(controlfile.buf, "r"); | |
260 | if (cf == NULL) { | |
261 | if (errno == ENOENT) | |
262 | return; | |
263 | ||
264 | ohshite(_("error opening conffiles file")); | |
265 | } | |
266 | ||
267 | while (fgets(conffilename, MAXCONFFILENAME + 1, cf)) { | |
268 | struct stat controlstab; | |
269 | int n; | |
270 | ||
271 | n = strlen(conffilename); | |
272 | if (!n) | |
273 | ohshite(_("empty string from fgets reading conffiles")); | |
274 | ||
275 | if (conffilename[n - 1] != '\n') | |
276 | ohshit(_("conffile name '%s' is too long, or missing final newline"), | |
277 | conffilename); | |
278 | ||
279 | conffilename[n - 1] = '\0'; | |
280 | varbuf_reset(&controlfile); | |
281 | varbuf_printf(&controlfile, "%s/%s", rootdir, conffilename); | |
282 | if (lstat(controlfile.buf, &controlstab)) { | |
283 | if (errno == ENOENT) { | |
284 | if ((n > 1) && c_isspace(conffilename[n - 2])) | |
285 | warning(_("conffile filename '%s' contains trailing white spaces"), | |
286 | conffilename); | |
287 | ohshit(_("conffile '%.250s' does not appear in package"), conffilename); | |
288 | } else | |
289 | ohshite(_("conffile '%.250s' is not stattable"), conffilename); | |
290 | } else if (!S_ISREG(controlstab.st_mode)) { | |
291 | warning(_("conffile '%s' is not a plain file"), conffilename); | |
292 | } | |
293 | ||
294 | if (file_info_find_name(conffiles_head, conffilename)) { | |
295 | warning(_("conffile name '%s' is duplicated"), conffilename); | |
296 | } else { | |
297 | struct file_info *conffile; | |
298 | ||
299 | conffile = file_info_new(conffilename); | |
300 | file_info_list_append(&conffiles_head, &conffiles_tail, conffile); | |
301 | } | |
302 | } | |
303 | ||
304 | file_info_list_free(conffiles_head); | |
305 | varbuf_destroy(&controlfile); | |
306 | ||
307 | if (ferror(cf)) | |
308 | ohshite(_("error reading conffiles file")); | |
309 | fclose(cf); | |
310 | } | |
311 | ||
312 | /** | |
313 | * Check the control file. | |
314 | * | |
315 | * @param dir The directory from where to build the binary package. | |
316 | * @return The pkginfo struct from the parsed control file. | |
317 | */ | |
318 | static struct pkginfo * | |
319 | check_control_file(const char *ctrldir) | |
320 | { | |
321 | struct pkginfo *pkg; | |
322 | char *controlfile; | |
323 | ||
324 | controlfile = str_fmt("%s/%s", ctrldir, CONTROLFILE); | |
325 | parsedb(controlfile, pdb_parse_binary, &pkg); | |
326 | ||
327 | if (strspn(pkg->set->name, "abcdefghijklmnopqrstuvwxyz0123456789+-.") != | |
328 | strlen(pkg->set->name)) | |
329 | ohshit(_("package name has characters that aren't lowercase alphanums or '-+.'")); | |
330 | if (pkg->available.arch->type == DPKG_ARCH_NONE || | |
331 | pkg->available.arch->type == DPKG_ARCH_EMPTY) | |
332 | ohshit(_("package architecture is missing or empty")); | |
333 | if (pkg->priority == PKG_PRIO_OTHER) | |
334 | warning(_("'%s' contains user-defined Priority value '%s'"), | |
335 | controlfile, pkg->otherpriority); | |
336 | ||
337 | free(controlfile); | |
338 | ||
339 | return pkg; | |
340 | } | |
341 | ||
342 | /** | |
343 | * Perform some sanity checks on the to-be-built package control area. | |
344 | * | |
345 | * @param dir The directory from where to build the binary package. | |
346 | * @return The pkginfo struct from the parsed control file. | |
347 | */ | |
348 | static struct pkginfo * | |
349 | check_control_area(const char *ctrldir, const char *rootdir) | |
350 | { | |
351 | struct pkginfo *pkg; | |
352 | int warns; | |
353 | ||
354 | /* Start by reading in the control file so we can check its contents. */ | |
355 | pkg = check_control_file(ctrldir); | |
356 | check_file_perms(ctrldir); | |
357 | check_conffiles(ctrldir, rootdir); | |
358 | ||
359 | warns = warning_get_count(); | |
360 | if (warns) | |
361 | warning(P_("ignoring %d warning about the control file(s)", | |
362 | "ignoring %d warnings about the control file(s)", warns), | |
363 | warns); | |
364 | ||
365 | return pkg; | |
366 | } | |
367 | ||
368 | /** | |
369 | * Generate the pathname for the destination binary package. | |
370 | * | |
371 | * If the pathname cannot be computed, because the destination is a directory, | |
372 | * then NULL will be returned. | |
373 | * | |
374 | * @param dir The directory from where to build the binary package. | |
375 | * @param dest The destination name, either a file or directory name. | |
376 | * @return The pathname for the package being built. | |
377 | */ | |
378 | static char * | |
379 | gen_dest_pathname(const char *dir, const char *dest) | |
380 | { | |
381 | if (dest) { | |
382 | struct stat dest_stab; | |
383 | ||
384 | if (stat(dest, &dest_stab)) { | |
385 | if (errno != ENOENT) | |
386 | ohshite(_("unable to check for existence of archive '%.250s'"), dest); | |
387 | } else if (S_ISDIR(dest_stab.st_mode)) { | |
388 | /* Need to compute the destination name from the package control file. */ | |
389 | return NULL; | |
390 | } | |
391 | ||
392 | return m_strdup(dest); | |
393 | } else { | |
394 | char *pathname; | |
395 | ||
396 | pathname = m_malloc(strlen(dir) + sizeof(DEBEXT)); | |
397 | strcpy(pathname, dir); | |
398 | path_trim_slash_slashdot(pathname); | |
399 | strcat(pathname, DEBEXT); | |
400 | ||
401 | return pathname; | |
402 | } | |
403 | } | |
404 | ||
405 | /** | |
406 | * Generate the pathname for the destination binary package from control file. | |
407 | * | |
408 | * @return The pathname for the package being built. | |
409 | */ | |
410 | static char * | |
411 | gen_dest_pathname_from_pkg(const char *dir, struct pkginfo *pkg) | |
412 | { | |
413 | return str_fmt("%s/%s_%s_%s%s", dir, pkg->set->name, | |
414 | versiondescribe(&pkg->available.version, vdew_never), | |
415 | pkg->available.arch->name, DEBEXT); | |
416 | } | |
417 | ||
418 | typedef void filenames_feed_func(const char *dir, int fd_out); | |
419 | ||
420 | /** | |
421 | * Pack the contents of a directory into a tarball. | |
422 | */ | |
423 | static void | |
424 | tarball_pack(const char *dir, filenames_feed_func *tar_filenames_feeder, | |
425 | time_t timestamp, const char *mode, | |
426 | struct compress_params *tar_compress_params, int fd_out) | |
427 | { | |
428 | int pipe_filenames[2], pipe_tarball[2]; | |
429 | pid_t pid_tar, pid_comp; | |
430 | ||
431 | /* Fork off a tar. We will feed it a list of filenames on stdin later. */ | |
432 | m_pipe(pipe_filenames); | |
433 | m_pipe(pipe_tarball); | |
434 | pid_tar = subproc_fork(); | |
435 | if (pid_tar == 0) { | |
436 | struct command cmd; | |
437 | char mtime[50]; | |
438 | ||
439 | m_dup2(pipe_filenames[0], 0); | |
440 | close(pipe_filenames[0]); | |
441 | close(pipe_filenames[1]); | |
442 | m_dup2(pipe_tarball[1], 1); | |
443 | close(pipe_tarball[0]); | |
444 | close(pipe_tarball[1]); | |
445 | ||
446 | if (chdir(dir)) | |
447 | ohshite(_("failed to chdir to '%.255s'"), dir); | |
448 | ||
449 | snprintf(mtime, sizeof(mtime), "@%ld", timestamp); | |
450 | ||
451 | command_init(&cmd, TAR, "tar -cf"); | |
452 | command_add_args(&cmd, "tar", "-cf", "-", "--format=gnu", | |
453 | "--mtime", mtime, "--clamp-mtime", NULL); | |
454 | /* Mode might become a positional argument, pass it before -T. */ | |
455 | if (mode) | |
456 | command_add_args(&cmd, "--mode", mode, NULL); | |
457 | command_add_args(&cmd, "--null", "--no-unquote", "--no-recursion", | |
458 | "-T", "-", NULL); | |
459 | command_exec(&cmd); | |
460 | } | |
461 | close(pipe_filenames[0]); | |
462 | close(pipe_tarball[1]); | |
463 | ||
464 | /* Of course we should not forget to compress the archive as well. */ | |
465 | pid_comp = subproc_fork(); | |
466 | if (pid_comp == 0) { | |
467 | close(pipe_filenames[1]); | |
468 | compress_filter(tar_compress_params, pipe_tarball[0], fd_out, | |
469 | _("compressing tar member")); | |
470 | exit(0); | |
471 | } | |
472 | close(pipe_tarball[0]); | |
473 | ||
474 | /* All the pipes are set, now lets start feeding filenames to tar. */ | |
475 | tar_filenames_feeder(dir, pipe_filenames[1]); | |
476 | ||
477 | /* All done, clean up wait for tar and <compress> to finish their job. */ | |
478 | close(pipe_filenames[1]); | |
479 | subproc_reap(pid_comp, _("<compress> from tar -cf"), 0); | |
480 | subproc_reap(pid_tar, "tar -cf", 0); | |
481 | } | |
482 | ||
483 | static time_t | |
484 | parse_timestamp(const char *value) | |
485 | { | |
486 | time_t timestamp; | |
487 | char *end; | |
488 | ||
489 | errno = 0; | |
490 | timestamp = strtol(value, &end, 10); | |
491 | if (value == end || *end || errno != 0) | |
492 | ohshite(_("unable to parse timestamp '%.255s'"), value); | |
493 | ||
494 | return timestamp; | |
495 | } | |
496 | ||
497 | /** | |
498 | * Overly complex function that builds a .deb file. | |
499 | */ | |
500 | int | |
501 | do_build(const char *const *argv) | |
502 | { | |
503 | struct compress_params control_compress_params; | |
504 | struct dpkg_error err; | |
505 | struct dpkg_ar *ar; | |
506 | time_t timestamp; | |
507 | const char *timestamp_str; | |
508 | const char *dir, *dest; | |
509 | char *ctrldir; | |
510 | char *debar; | |
511 | char *tfbuf; | |
512 | int gzfd; | |
513 | ||
514 | /* Decode our arguments. */ | |
515 | dir = *argv++; | |
516 | if (!dir) | |
517 | badusage(_("--%s needs a <directory> argument"), cipaction->olong); | |
518 | ||
519 | dest = *argv++; | |
520 | if (dest && *argv) | |
521 | badusage(_("--%s takes at most two arguments"), cipaction->olong); | |
522 | ||
523 | debar = gen_dest_pathname(dir, dest); | |
524 | ctrldir = str_fmt("%s/%s", dir, BUILDCONTROLDIR); | |
525 | ||
526 | /* Perform some sanity checks on the to-be-build package. */ | |
527 | if (nocheckflag) { | |
528 | if (debar == NULL) | |
529 | ohshit(_("target is directory - cannot skip control file check")); | |
530 | warning(_("not checking contents of control area")); | |
531 | info(_("building an unknown package in '%s'."), debar); | |
532 | } else { | |
533 | struct pkginfo *pkg; | |
534 | ||
535 | pkg = check_control_area(ctrldir, dir); | |
536 | if (debar == NULL) | |
537 | debar = gen_dest_pathname_from_pkg(dest, pkg); | |
538 | info(_("building package '%s' in '%s'."), pkg->set->name, debar); | |
539 | } | |
540 | m_output(stdout, _("<standard output>")); | |
541 | ||
542 | timestamp_str = getenv("SOURCE_DATE_EPOCH"); | |
543 | if (timestamp_str) | |
544 | timestamp = parse_timestamp(timestamp_str); | |
545 | else | |
546 | timestamp = time(NULL); | |
547 | ||
548 | /* Now that we have verified everything it is time to actually | |
549 | * build something. Let's start by making the ar-wrapper. */ | |
550 | ar = dpkg_ar_create(debar, 0644); | |
551 | ||
552 | dpkg_ar_set_mtime(ar, timestamp); | |
553 | ||
554 | unsetenv("TAR_OPTIONS"); | |
555 | ||
556 | /* Create a temporary file to store the control data in. Immediately | |
557 | * unlink our temporary file so others can't mess with it. */ | |
558 | tfbuf = path_make_temp_template("dpkg-deb"); | |
559 | gzfd = mkstemp(tfbuf); | |
560 | if (gzfd == -1) | |
561 | ohshite(_("failed to make temporary file (%s)"), _("control member")); | |
562 | /* Make sure it's gone, the fd will remain until we close it. */ | |
563 | if (unlink(tfbuf)) | |
564 | ohshit(_("failed to unlink temporary file (%s), %s"), _("control member"), | |
565 | tfbuf); | |
566 | free(tfbuf); | |
567 | ||
568 | /* Select the compressor to use for our control archive. */ | |
569 | if (opt_uniform_compression) { | |
570 | control_compress_params = compress_params; | |
571 | } else { | |
572 | control_compress_params.type = COMPRESSOR_TYPE_GZIP; | |
573 | control_compress_params.strategy = COMPRESSOR_STRATEGY_NONE; | |
574 | control_compress_params.level = -1; | |
575 | if (!compressor_check_params(&control_compress_params, &err)) | |
576 | internerr("invalid control member compressor params: %s", err.str); | |
577 | } | |
578 | ||
579 | /* Fork a tar to package the control-section of the package. */ | |
580 | tarball_pack(ctrldir, control_treewalk_feed, timestamp, "u+rw,go=rX", | |
581 | &control_compress_params, gzfd); | |
582 | ||
583 | free(ctrldir); | |
584 | ||
585 | if (lseek(gzfd, 0, SEEK_SET)) | |
586 | ohshite(_("failed to rewind temporary file (%s)"), _("control member")); | |
587 | ||
588 | /* We have our first file for the ar-archive. Write a header for it | |
589 | * to the package and insert it. */ | |
590 | if (deb_format.major == 0) { | |
591 | struct stat controlstab; | |
592 | char versionbuf[40]; | |
593 | ||
594 | if (fstat(gzfd, &controlstab)) | |
595 | ohshite(_("failed to stat temporary file (%s)"), _("control member")); | |
596 | sprintf(versionbuf, "%-8s\n%jd\n", OLDARCHIVEVERSION, | |
597 | (intmax_t)controlstab.st_size); | |
598 | if (fd_write(ar->fd, versionbuf, strlen(versionbuf)) < 0) | |
599 | ohshite(_("error writing '%s'"), debar); | |
600 | if (fd_fd_copy(gzfd, ar->fd, -1, &err) < 0) | |
601 | ohshit(_("cannot copy '%s' into archive '%s': %s"), _("control member"), | |
602 | ar->name, err.str); | |
603 | } else if (deb_format.major == 2) { | |
604 | const char deb_magic[] = ARCHIVEVERSION "\n"; | |
605 | char adminmember[16 + 1]; | |
606 | ||
607 | sprintf(adminmember, "%s%s", ADMINMEMBER, | |
608 | compressor_get_extension(control_compress_params.type)); | |
609 | ||
610 | dpkg_ar_put_magic(ar); | |
611 | dpkg_ar_member_put_mem(ar, DEBMAGIC, deb_magic, strlen(deb_magic)); | |
612 | dpkg_ar_member_put_file(ar, adminmember, gzfd, -1); | |
613 | } else { | |
614 | internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor); | |
615 | } | |
616 | ||
617 | close(gzfd); | |
618 | ||
619 | /* Control is done, now we need to archive the data. */ | |
620 | if (deb_format.major == 0) { | |
621 | /* In old format, the data member is just concatenated after the | |
622 | * control member, so we do not need a temporary file and can use | |
623 | * the compression file descriptor. */ | |
624 | gzfd = ar->fd; | |
625 | } else if (deb_format.major == 2) { | |
626 | /* Start by creating a new temporary file. Immediately unlink the | |
627 | * temporary file so others can't mess with it. */ | |
628 | tfbuf = path_make_temp_template("dpkg-deb"); | |
629 | gzfd = mkstemp(tfbuf); | |
630 | if (gzfd == -1) | |
631 | ohshite(_("failed to make temporary file (%s)"), _("data member")); | |
632 | /* Make sure it's gone, the fd will remain until we close it. */ | |
633 | if (unlink(tfbuf)) | |
634 | ohshit(_("failed to unlink temporary file (%s), %s"), _("data member"), | |
635 | tfbuf); | |
636 | free(tfbuf); | |
637 | } else { | |
638 | internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor); | |
639 | } | |
640 | ||
641 | /* Pack the directory into a tarball, feeding files from the callback. */ | |
642 | tarball_pack(dir, file_treewalk_feed, timestamp, NULL, &compress_params, gzfd); | |
643 | ||
644 | /* Okay, we have data.tar as well now, add it to the ar wrapper. */ | |
645 | if (deb_format.major == 2) { | |
646 | char datamember[16 + 1]; | |
647 | ||
648 | sprintf(datamember, "%s%s", DATAMEMBER, | |
649 | compressor_get_extension(compress_params.type)); | |
650 | ||
651 | if (lseek(gzfd, 0, SEEK_SET)) | |
652 | ohshite(_("failed to rewind temporary file (%s)"), _("data member")); | |
653 | ||
654 | dpkg_ar_member_put_file(ar, datamember, gzfd, -1); | |
655 | ||
656 | close(gzfd); | |
657 | } | |
658 | if (fsync(ar->fd)) | |
659 | ohshite(_("unable to sync file '%s'"), ar->name); | |
660 | ||
661 | dpkg_ar_close(ar); | |
662 | ||
663 | free(debar); | |
664 | ||
665 | return 0; | |
666 | } |