/** * Pack the contents of a directory into a tarball. */ static void tarball_pack(const char *dir, filenames_feed_func *tar_filenames_feeder, time_t timestamp, struct compress_params *tar_compress_params, int fd_out) { int pipe_filenames[2], pipe_tarball[2]; pid_t pid_tar, pid_comp; /* Fork off a tar. We will feed it a list of filenames on stdin later. */ m_pipe(pipe_filenames); m_pipe(pipe_tarball); pid_tar = subproc_fork(); if (pid_tar == 0) { char mtime[50]; m_dup2(pipe_filenames[0], 0); close(pipe_filenames[0]); close(pipe_filenames[1]); m_dup2(pipe_tarball[1], 1); close(pipe_tarball[0]); close(pipe_tarball[1]); if (chdir(dir)) ohshite(_("failed to chdir to '%.255s'"), dir); snprintf(mtime, sizeof(mtime), "@%ld", timestamp); execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--mtime", mtime, "--clamp-mtime", "--null", "--no-unquote", "--no-recursion", "-T", "-", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(pipe_filenames[0]); close(pipe_tarball[1]); /* Of course we should not forget to compress the archive as well. */ pid_comp = subproc_fork(); if (pid_comp == 0) { close(pipe_filenames[1]); compress_filter(tar_compress_params, pipe_tarball[0], fd_out, _("compressing tar member")); exit(0); } close(pipe_tarball[0]); /* All the pipes are set, now lets start feeding filenames to tar. */ tar_filenames_feeder(dir, pipe_filenames[1]); /* All done, clean up wait for tar and <compress> to finish their job. */ close(pipe_filenames[1]); subproc_reap(pid_comp, _("<compress> from tar -cf"), 0); subproc_reap(pid_tar, "tar -cf", 0); }
static void run_status_loggers(struct invoke_hook *hook_head) { struct invoke_hook *hook; for (hook = hook_head; hook; hook = hook->next) { pid_t pid; int p[2]; m_pipe(p); pid = subproc_fork(); if (pid == 0) { /* Setup stdin and stdout. */ m_dup2(p[0], 0); close(1); close(p[0]); close(p[1]); command_shell(hook->command, _("status logger")); } close(p[0]); statusfd_add(p[1]); } }
static void DPKG_ATTR_SENTINEL fd_fd_filter(int fd_in, int fd_out, const char *desc, const char *delenv[], const char *file, ...) { va_list args; struct command cmd; pid_t pid; int i; pid = subproc_fork(); if (pid == 0) { if (fd_in != 0) { m_dup2(fd_in, 0); close(fd_in); } if (fd_out != 1) { m_dup2(fd_out, 1); close(fd_out); } for (i = 0; delenv[i]; i++) unsetenv(delenv[i]); command_init(&cmd, file, desc); command_add_arg(&cmd, file); va_start(args, file); command_add_argv(&cmd, args); va_end(args); command_exec(&cmd); } subproc_reap(pid, desc, 0); }
void ensure_pathname_nonexisting(const char *pathname) { pid_t pid; const char *u; u = path_skip_slash_dotslash(pathname); assert(*u); debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname); if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */ if (errno == ENOENT || errno == ELOOP) return; if (errno == ENOTDIR) { /* Either it's a file, or one of the path components is. If one * of the path components is this will fail again ... */ if (secure_unlink(pathname) == 0) return; /* OK, it was. */ if (errno == ENOTDIR) return; } if (errno != ENOTEMPTY && errno != EEXIST) { /* Huh? */ ohshite(_("unable to securely remove '%.255s'"), pathname); } pid = subproc_fork(); if (pid == 0) { execlp(RM, "rm", "-rf", "--", pathname, NULL); ohshite(_("unable to execute %s (%s)"), _("rm command for cleanup"), RM); } debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf"); subproc_wait_check(pid, "rm cleanup", 0); }
static int do_script(struct pkginfo *pkg, struct pkgbin *pif, struct command *cmd, struct stat *stab, int warn) { pid_t pid; int r; setexecute(cmd->filename, stab); push_cleanup(cu_post_script_tasks, ehflag_bombout, NULL, 0, 0); pid = subproc_fork(); if (pid == 0) { if (setenv("DPKG_MAINTSCRIPT_PACKAGE", pkg->set->name, 1) || setenv("DPKG_MAINTSCRIPT_ARCH", pif->arch->name, 1) || setenv("DPKG_MAINTSCRIPT_NAME", cmd->argv[0], 1) || setenv("DPKG_RUNNING_VERSION", PACKAGE_VERSION, 1)) ohshite(_("unable to setenv for maintainer script")); cmd->filename = cmd->argv[0] = preexecscript(cmd); command_exec(cmd); } subproc_signals_setup(cmd->name); /* This does a push_cleanup(). */ r = subproc_wait_check(pid, cmd->name, warn); pop_cleanup(ehflag_normaltidy); pop_cleanup(ehflag_normaltidy); return r; }
static void deb_verify(const char *filename) { struct stat stab; pid_t pid; if (stat(DEBSIGVERIFY, &stab) < 0) return; printf(_("Authenticating %s ...\n"), filename); fflush(stdout); pid = subproc_fork(); if (!pid) { execl(DEBSIGVERIFY, DEBSIGVERIFY, "-q", filename, NULL); ohshite(_("unable to execute %s (%s)"), _("package signature verification"), DEBSIGVERIFY); } else { int status; status = subproc_wait(pid, "debsig-verify"); if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { if (!fc_badverify) ohshit(_("Verification on package %s failed!"), filename); else fprintf(stderr, _("Verification on package %s failed,\n" "but installing anyway as you requested.\n"), filename); } else { printf(_("passed\n")); } } }
static void movecontrolfiles(const char *thing) { char buf[200]; pid_t pid; sprintf(buf, "mv %s/* . && rmdir %s", thing, thing); pid = subproc_fork(); if (pid == 0) { command_shell(buf, _("shell command to move files")); } subproc_reap(pid, _("shell command to move files"), 0); }
struct pager * pager_spawn(const char *desc) { struct sigaction sa; struct pager *pager; const char *exec; pager = m_calloc(1, sizeof(*pager)); pager->used = isatty(0) && isatty(1); pager->desc = desc; exec = pager_get_exec(); if (strcmp(exec, CAT) == 0) pager->used = false; if (!pager_enabled) pager->used = false; if (!pager->used) return pager; m_pipe(pager->pipe); memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigaction(SIGPIPE, &sa, &pager->sigpipe); pager->pid = subproc_fork(); if (pager->pid == 0) { /* Set better defaults for less if not already set. */ setenv("LESS", "-FRSXMQ", 0); m_dup2(pager->pipe[0], 0); close(pager->pipe[0]); close(pager->pipe[1]); command_shell(exec, desc); } pager->stdout_old = m_dup(1); m_dup2(pager->pipe[1], 1); close(pager->pipe[0]); close(pager->pipe[1]); /* Force the output to fully buffered, because originally stdout was * a tty, so it was set as line buffered. This way we send as much as * possible to the pager, which will handle the output by itself. */ setvbuf(stdout, NULL, _IOFBF, 0); return pager; }
static void movecontrolfiles(const char *thing) { char buf[200] = { '\0' }; pid_t pid; snprintf(buf, sizeof(buf) - 1, "mv %s/* . && rmdir %s", thing, thing); pid = subproc_fork(); if (pid == 0) command_shell(buf, "shell command to move files"); subproc_reap(pid, "shell command to move files", 0); }
static void file_treewalk_feed(const char *dir, int fd_out) { int pipefd[2]; pid_t pid; struct file_info *fi; struct file_info *symlist = NULL; struct file_info *symlist_end = NULL; m_pipe(pipefd); pid = subproc_fork(); if (pid == 0) { m_dup2(pipefd[1], 1); close(pipefd[0]); close(pipefd[1]); if (chdir(dir)) ohshite(_("failed to chdir to `%.255s'"), dir); execlp(FIND, "find", ".", "-path", "./" BUILDCONTROLDIR, "-prune", "-o", "-print0", NULL); ohshite(_("unable to execute %s (%s)"), "find", FIND); } close(pipefd[1]); /* We need to reorder the files so we can make sure that symlinks * will not appear before their target. */ while ((fi = file_info_get(dir, pipefd[0])) != NULL) { if (S_ISLNK(fi->st.st_mode)) { file_info_list_append(&symlist, &symlist_end, fi); } else { if (fd_write(fd_out, fi->fn, strlen(fi->fn) + 1) < 0) ohshite(_("failed to write filename to tar pipe (%s)"), _("data member")); file_info_free(fi); } } close(pipefd[0]); subproc_wait_check(pid, "find", 0); for (fi = symlist; fi; fi = fi->next) if (fd_write(fd_out, fi->fn, strlen(fi->fn) + 1) < 0) ohshite(_("failed to write filename to tar pipe (%s)"), _("data member")); file_info_list_free(symlist); }
static void cu_info_prepare(int argc, void **argv) { pid_t c1; char *directory; struct stat stab; directory= (char*)(argv[0]); if (chdir("/")) ohshite(_("failed to chdir to `/' for cleanup")); if (lstat(directory,&stab) && errno==ENOENT) return; c1 = subproc_fork(); if (!c1) { execlp(RM, "rm", "-rf", directory, NULL); ohshite(_("unable to execute %s (%s)"), _("rm command for cleanup"), RM); } subproc_wait_check(c1, _("rm command for cleanup"), 0); }
static void cu_info_prepare(int argc, void **argv) { pid_t pid; char *dir; struct stat stab; dir = argv[0]; if (lstat(dir, &stab) && errno == ENOENT) return; pid = subproc_fork(); if (pid == 0) { if (chdir("/")) ohshite(_("failed to chdir to `/' for cleanup")); execlp(RM, "rm", "-rf", dir, NULL); ohshite(_("unable to execute %s (%s)"), _("rm command for cleanup"), RM); } subproc_wait_check(pid, _("rm command for cleanup"), 0); }
static bool deb_reassemble(const char **filename, const char **pfilename) { static char *reasmbuf = NULL; struct stat stab; int status; pid_t pid; if (!reasmbuf) reasmbuf = dpkg_db_get_path(REASSEMBLETMP); if (unlink(reasmbuf) && errno != ENOENT) ohshite(_("error ensuring `%.250s' doesn't exist"), reasmbuf); push_cleanup(cu_pathname, ~0, NULL, 0, 1, (void *)reasmbuf); pid = subproc_fork(); if (!pid) { execlp(SPLITTER, SPLITTER, "-Qao", reasmbuf, *filename, NULL); ohshite(_("unable to execute %s (%s)"), _("split package reassembly"), SPLITTER); } status = subproc_wait(pid, SPLITTER); switch (WIFEXITED(status) ? WEXITSTATUS(status) : -1) { case 0: /* It was a part - is it complete? */ if (!stat(reasmbuf, &stab)) { /* Yes. */ *filename = reasmbuf; *pfilename = _("reassembled package file"); break; } else if (errno == ENOENT) { /* No. That's it, we skip it. */ return false; } case 1: /* No, it wasn't a part. */ break; default: subproc_check(status, SPLITTER, 0); } return true; }
static char * deb_field(const char *filename, const char *field) { pid_t pid; int p[2]; struct varbuf buf = VARBUF_INIT; char *end; m_pipe(p); pid = subproc_fork(); if (pid == 0) { /* Child writes to pipe. */ m_dup2(p[1], 1); close(p[0]); close(p[1]); execlp(BACKEND, BACKEND, "--field", filename, field, NULL); ohshite(_("unable to execute %s (%s)"), _("package field value extraction"), BACKEND); } close(p[1]); /* Parant reads from pipe. */ varbuf_reset(&buf); fd_vbuf_copy(p[0], &buf, -1, _("package field value extraction")); varbuf_end_str(&buf); close(p[0]); subproc_wait_check(pid, _("package field value extraction"), PROCPIPE); /* Trim down trailing junk. */ for (end = buf.buf + strlen(buf.buf) - 1; end - buf.buf >= 1; end--) if (isspace(*end)) *end = '\0'; else break; return varbuf_detach(&buf); }
void file_show(const char *filename) { pid_t pid; if (filename == NULL) internerr("file '%s' does not exist", filename); pid = subproc_fork(); if (pid == 0) { struct command cmd; const char *pager; pager = command_get_pager(); command_init(&cmd, pager, _("showing file on pager")); command_add_arg(&cmd, pager); command_add_arg(&cmd, filename); command_exec(&cmd); } subproc_wait(pid, _("showing file on pager")); }
static int run_logger(struct invoke_hook *hook, const char *name) { pid_t pid; int p[2]; m_pipe(p); pid = subproc_fork(); if (pid == 0) { /* Setup stdin and stdout. */ m_dup2(p[0], 0); close(1); close(p[0]); close(p[1]); command_shell(hook->command, name); } close(p[0]); return p[1]; }
static void test_subproc_fork(void) { pid_t pid; int ret; /* Test exit(). */ pid = subproc_fork(); if (pid == 0) exit(0); ret = subproc_reap(pid, "subproc exit pass", SUBPROC_RETERROR); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) exit(128); ret = subproc_reap(pid, "subproc exit fail", SUBPROC_RETERROR); test_pass(ret == 128); /* Test signals. */ pid = subproc_fork(); if (pid == 0) raise(SIGINT); ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGTERM); ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN | SUBPROC_NOPIPE); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN); test_pass(ret == -1); }
static void test_subproc_fork(void) { pid_t pid; int ret; /* Test exit(). */ pid = subproc_fork(); if (pid == 0) exit(0); ret = subproc_wait_check(pid, "subproc exit pass", PROCNOERR); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) exit(128); ret = subproc_wait_check(pid, "subproc exit fail", PROCNOERR); test_pass(ret == 128); /* Test signals. */ pid = subproc_fork(); if (pid == 0) raise(SIGINT); ret = subproc_wait_check(pid, "subproc signal", PROCWARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGTERM); ret = subproc_wait_check(pid, "subproc signal", PROCWARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_wait_check(pid, "subproc SIGPIPE", PROCWARN | PROCPIPE); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_wait_check(pid, "subproc SIGPIPE", PROCWARN); test_pass(ret == -1); }
void archivefiles(const char *const *argv) { const char *volatile thisarg; const char *const *volatile argp; jmp_buf ejbuf; int pi[2], fc, nfiles, c, i, r; FILE *pf; static struct varbuf findoutput; const char **arglist; char *p; trigproc_install_hooks(); modstatdb_init(admindir, f_noact ? msdbrw_readonly : cipaction->arg == act_avail ? msdbrw_write : fc_nonroot ? msdbrw_write : msdbrw_needsuperuser); checkpath(); log_message("startup archives %s", cipaction->olong); if (f_recursive) { if (!*argv) badusage(_("--%s --recursive needs at least one path argument"),cipaction->olong); m_pipe(pi); fc = subproc_fork(); if (!fc) { struct command cmd; const char *const *ap; m_dup2(pi[1],1); close(pi[0]); close(pi[1]); command_init(&cmd, FIND, _("find for dpkg --recursive")); command_add_args(&cmd, FIND, "-L", NULL); for (ap = argv; *ap; ap++) { if (strchr(FIND_EXPRSTARTCHARS,(*ap)[0])) { char *a; a= m_malloc(strlen(*ap)+10); strcpy(a,"./"); strcat(a,*ap); command_add_arg(&cmd, a); } else { command_add_arg(&cmd, (const char *)*ap); } } command_add_args(&cmd, "-name", "*.deb", "-type", "f", "-print0", NULL); command_exec(&cmd); } close(pi[1]); nfiles= 0; pf= fdopen(pi[0],"r"); if (!pf) ohshite(_("failed to fdopen find's pipe")); varbufreset(&findoutput); while ((c= fgetc(pf)) != EOF) { varbufaddc(&findoutput,c); if (!c) nfiles++; } if (ferror(pf)) ohshite(_("error reading find's pipe")); if (fclose(pf)) ohshite(_("error closing find's pipe")); r = subproc_wait_check(fc, "find", PROCNOERR); if (r != 0) ohshit(_("find for --recursive returned unhandled error %i"),r); if (!nfiles) ohshit(_("searched, but found no packages (files matching *.deb)")); varbufaddc(&findoutput,0); varbufaddc(&findoutput,0); arglist= m_malloc(sizeof(char*)*(nfiles+1)); p= findoutput.buf; i=0; while (*p) { arglist[i++]= p; while (*p++ != '\0') ; } arglist[i] = NULL; argp= arglist; } else { if (!*argv) badusage(_("--%s needs at least one package archive file argument"), cipaction->olong); argp= argv; } currenttime = time(NULL); /* Initialize fname variables contents. */ varbufreset(&fnamevb); varbufreset(&fnametmpvb); varbufreset(&fnamenewvb); varbufaddstr(&fnamevb,instdir); varbufaddc(&fnamevb,'/'); varbufaddstr(&fnametmpvb,instdir); varbufaddc(&fnametmpvb,'/'); varbufaddstr(&fnamenewvb,instdir); varbufaddc(&fnamenewvb,'/'); fnameidlu= fnamevb.used; ensure_diversions(); ensure_statoverrides(); while ((thisarg = *argp++) != NULL) { if (setjmp(ejbuf)) { pop_error_context(ehflag_bombout); if (abort_processing) break; continue; } push_error_context_jump(&ejbuf, print_error_perpackage, thisarg); process_archive(thisarg); onerr_abort++; m_output(stdout, _("<standard output>")); m_output(stderr, _("<standard error>")); onerr_abort--; pop_error_context(ehflag_normaltidy); } switch (cipaction->arg) { case act_install: case act_configure: case act_triggers: case act_remove: case act_purge: process_queue(); case act_unpack: case act_avail: break; default: internerr("unknown action '%d'", cipaction->arg); } trigproc_run_deferred(); modstatdb_shutdown(); }
void process_archive(const char *filename) { static const struct tar_operations tf = { .read = tarfileread, .extract_file = tarobject, .link = tarobject, .symlink = tarobject, .mkdir = tarobject, .mknod = tarobject, }; /* These need to be static so that we can pass their addresses to * push_cleanup as arguments to the cu_xxx routines; if an error occurs * we unwind the stack before processing the cleanup list, and these * variables had better still exist ... */ static int p1[2]; static char *cidir = NULL; static struct fileinlist *newconffiles, *newfileslist; static enum pkgstatus oldversionstatus; static struct varbuf depprobwhy; static struct tarcontext tc; int r, i; pid_t pid; struct pkgiterator *it; struct pkginfo *pkg, *otherpkg; char *cidirrest, *p; char conffilenamebuf[MAXCONFFILENAME]; char *psize; const char *pfilename; struct fileinlist *newconff, **newconffileslastp; struct fileinlist *cfile; struct reversefilelistiter rlistit; struct conffile *searchconff, **iconffileslastp, *newiconff; struct dependency *dsearch, *newdeplist, **newdeplistlastp; struct dependency *newdep, *dep, *providecheck; struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep; FILE *conff; struct filenamenode *namenode; struct stat stab, oldfs; struct pkg_deconf_list *deconpil, *deconpiltemp; struct pkginfo *fixbytrigaw; cleanup_pkg_failed= cleanup_conflictor_failed= 0; pfilename = summarize_filename(filename); if (stat(filename,&stab)) ohshite(_("cannot access archive")); /* We can't ‘tentatively-reassemble’ packages. */ if (!f_noact) { if (!deb_reassemble(&filename, &pfilename)) return; } /* Verify the package. */ if (!f_nodebsig) deb_verify(filename); /* Get the control information directory. */ cidir = get_control_dir(cidir); cidirrest = cidir + strlen(cidir); push_cleanup(cu_cidir, ~0, NULL, 0, 2, (void *)cidir, (void *)cidirrest); pid = subproc_fork(); if (pid == 0) { cidirrest[-1] = '\0'; execlp(BACKEND, BACKEND, "--control", filename, cidir, NULL); ohshite(_("unable to execute %s (%s)"), _("package control information extraction"), BACKEND); } subproc_wait_check(pid, BACKEND " --control", 0); /* We want to guarantee the extracted files are on the disk, so that the * subsequent renames to the info database do not end up with old or zero * length files in case of a system crash. As neither dpkg-deb nor tar do * explicit fsync()s, we have to do them here. * XXX: This could be avoided by switching to an internal tar extractor. */ dir_sync_contents(cidir); strcpy(cidirrest,CONTROLFILE); parsedb(cidir, pdb_parse_binary | pdb_ignorefiles | parsedb_force_flags(), &pkg); if (!pkg->files) { pkg->files= nfmalloc(sizeof(struct filedetails)); pkg->files->next = NULL; pkg->files->name = pkg->files->msdosname = pkg->files->md5sum = NULL; } /* Always nfmalloc. Otherwise, we may overwrite some other field (like * md5sum). */ psize = nfmalloc(30); sprintf(psize, "%jd", (intmax_t)stab.st_size); pkg->files->size = psize; if (cipaction->arg_int == act_avail) { printf(_("Recorded info about %s from %s.\n"), pkgbin_name(pkg, &pkg->available, pnaw_nonambig), pfilename); pop_cleanup(ehflag_normaltidy); return; } if (pkg->available.arch->type != arch_all && pkg->available.arch->type != arch_native) forcibleerr(fc_architecture, _("package architecture (%s) does not match system (%s)"), pkg->available.arch->name, dpkg_arch_get(arch_native)->name); for (deconpil= deconfigure; deconpil; deconpil= deconpiltemp) { deconpiltemp= deconpil->next; free(deconpil); } deconfigure = NULL; clear_istobes(); if (!wanttoinstall(pkg)) { pop_cleanup(ehflag_normaltidy); return; } /* Check if anything is installed that we conflict with, or not installed * that we need. */ pkg->clientdata->istobe= itb_installnew; for (dsearch= pkg->available.depends; dsearch; dsearch= dsearch->next) { switch (dsearch->type) { case dep_conflicts: /* Look for things we conflict with. */ check_conflict(dsearch, pkg, pfilename); break; case dep_breaks: /* Look for things we break. */ check_breaks(dsearch, pkg, pfilename); break; case dep_provides: /* Look for things that conflict with what we provide. */ for (psearch = dsearch->list->ed->depended.installed; psearch; psearch = psearch->rev_next) { if (psearch->up->type != dep_conflicts) continue; check_conflict(psearch->up, pkg, pfilename); } break; case dep_suggests: case dep_recommends: case dep_depends: case dep_replaces: case dep_enhances: /* Ignore these here. */ break; case dep_predepends: if (!depisok(dsearch, &depprobwhy, NULL, &fixbytrigaw, true)) { if (fixbytrigaw) { while (fixbytrigaw->trigaw.head) trigproc(fixbytrigaw->trigaw.head->pend); } else { varbuf_end_str(&depprobwhy); fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"), pfilename, pkgbin_name(pkg, &pkg->available, pnaw_nonambig), depprobwhy.buf); if (!force_depends(dsearch->list)) ohshit(_("pre-dependency problem - not installing %.250s"), pkgbin_name(pkg, &pkg->available, pnaw_nonambig)); warning(_("ignoring pre-dependency problem!")); } } } } /* Look for things that conflict with us. */ for (psearch = pkg->set->depended.installed; psearch; psearch = psearch->rev_next) { if (psearch->up->type != dep_conflicts) continue; check_conflict(psearch->up, pkg, pfilename); } ensure_allinstfiles_available(); filesdbinit(); trig_file_interests_ensure(); if (pkg->status != stat_notinstalled && pkg->status != stat_configfiles) { printf(_("Preparing to replace %s %s (using %s) ...\n"), pkg_name(pkg, pnaw_nonambig), versiondescribe(&pkg->installed.version,vdew_nonambig), pfilename); log_action("upgrade", pkg, &pkg->installed); } else { printf(_("Unpacking %s (from %s) ...\n"), pkgbin_name(pkg, &pkg->available, pnaw_nonambig), pfilename); log_action("install", pkg, &pkg->available); } if (f_noact) { pop_cleanup(ehflag_normaltidy); return; } /* * OK, we're going ahead. */ trig_activate_packageprocessing(pkg); strcpy(cidirrest, TRIGGERSCIFILE); trig_parse_ci(cidir, NULL, trig_cicb_statuschange_activate, pkg, &pkg->available); /* Read the conffiles, and copy the hashes across. */ newconffiles = NULL; newconffileslastp = &newconffiles; push_cleanup(cu_fileslist, ~0, NULL, 0, 0); strcpy(cidirrest,CONFFILESFILE); conff= fopen(cidir,"r"); if (conff) { push_cleanup(cu_closestream, ehflag_bombout, NULL, 0, 1, (void *)conff); while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) { struct filepackages_iterator *iter; p= conffilenamebuf + strlen(conffilenamebuf); assert(p != conffilenamebuf); if (p[-1] != '\n') ohshit(_("name of conffile (starting `%.250s') is too long (>%d characters)"), conffilenamebuf, MAXCONFFILENAME); while (p > conffilenamebuf && isspace(p[-1])) --p; if (p == conffilenamebuf) continue; *p = '\0'; namenode= findnamenode(conffilenamebuf, 0); namenode->oldhash= NEWCONFFILEFLAG; newconff= newconff_append(&newconffileslastp, namenode); /* Let's see if any packages have this file. If they do we * check to see if they listed it as a conffile, and if they did * we copy the hash across. Since (for plain file conffiles, * which is the only kind we are supposed to have) there will * only be one package which ‘has’ the file, this will usually * mean we only look in the package which we're installing now. * The ‘conffiles’ data in the status file is ignored when a * package isn't also listed in the file ownership database as * having that file. If several packages are listed as owning * the file we pick one at random. */ searchconff = NULL; iter = filepackages_iter_new(newconff->namenode); while ((otherpkg = filepackages_iter_next(iter))) { debug(dbg_conffdetail, "process_archive conffile `%s' in package %s - conff ?", newconff->namenode->name, pkg_name(otherpkg, pnaw_nonambig)); for (searchconff = otherpkg->installed.conffiles; searchconff && strcmp(newconff->namenode->name, searchconff->name); searchconff = searchconff->next) debug(dbg_conffdetail, "process_archive conffile `%s' in package %s - conff ? not `%s'", newconff->namenode->name, pkg_name(otherpkg, pnaw_nonambig), searchconff->name); if (searchconff) { debug(dbg_conff, "process_archive conffile `%s' package=%s %s hash=%s", newconff->namenode->name, pkg_name(otherpkg, pnaw_nonambig), otherpkg == pkg ? "same" : "different!", searchconff->hash); if (otherpkg == pkg) break; } } filepackages_iter_free(iter); if (searchconff) { newconff->namenode->oldhash= searchconff->hash; /* We don't copy ‘obsolete’; it's not obsolete in the new package. */ } else { debug(dbg_conff,"process_archive conffile `%s' no package, no hash", newconff->namenode->name); } newconff->namenode->flags |= fnnf_new_conff; } if (ferror(conff)) ohshite(_("read error in %.250s"),cidir); pop_cleanup(ehflag_normaltidy); /* conff = fopen() */ if (fclose(conff)) ohshite(_("error closing %.250s"),cidir); } else { if (errno != ENOENT) ohshite(_("error trying to open %.250s"),cidir); } /* All the old conffiles are marked with a flag, so that we don't delete * them if they seem to disappear completely. */ oldconffsetflags(pkg->installed.conffiles); for (i = 0 ; i < cflict_index ; i++) { oldconffsetflags(conflictor[i]->installed.conffiles); } oldversionstatus= pkg->status; assert(oldversionstatus <= stat_installed); debug(dbg_general,"process_archive oldversionstatus=%s", statusstrings[oldversionstatus]); if (oldversionstatus == stat_halfconfigured || oldversionstatus == stat_triggersawaited || oldversionstatus == stat_triggerspending || oldversionstatus == stat_installed) { pkg_set_eflags(pkg, eflag_reinstreq); pkg_set_status(pkg, stat_halfconfigured); modstatdb_note(pkg); push_cleanup(cu_prermupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg); if (versioncompare(&pkg->available.version, &pkg->installed.version) >= 0) /* Upgrade or reinstall */ maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest, "upgrade", "failed-upgrade"); else /* Downgrade => no fallback */ maintainer_script_installed(pkg, PRERMFILE, "pre-removal", "upgrade", versiondescribe(&pkg->available.version, vdew_nonambig), NULL); pkg_set_status(pkg, stat_unpacked); oldversionstatus= stat_unpacked; modstatdb_note(pkg); } for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) { struct pkginfo *removing = deconpil->pkg_removal; if (removing) printf(_("De-configuring %s, to allow removal of %s ...\n"), pkg_name(deconpil->pkg, pnaw_nonambig), pkg_name(removing, pnaw_nonambig)); else printf(_("De-configuring %s ...\n"), pkg_name(deconpil->pkg, pnaw_nonambig)); trig_activate_packageprocessing(deconpil->pkg); pkg_set_status(deconpil->pkg, stat_halfconfigured); modstatdb_note(deconpil->pkg); /* This means that we *either* go and run postinst abort-deconfigure, * *or* queue the package for later configure processing, depending * on which error cleanup route gets taken. */ push_cleanup(cu_prermdeconfigure, ~ehflag_normaltidy, ok_prermdeconfigure, ehflag_normaltidy, 3, (void*)deconpil->pkg, (void*)removing, (void*)pkg); if (removing) { maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal", "deconfigure", "in-favour", pkg->set->name, versiondescribe(&pkg->available.version, vdew_nonambig), "removing", removing->set->name, versiondescribe(&removing->installed.version, vdew_nonambig), NULL); } else { maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal", "deconfigure", "in-favour", pkg->set->name, versiondescribe(&pkg->available.version, vdew_nonambig), NULL); } } for (i = 0 ; i < cflict_index; i++) { if (!(conflictor[i]->status == stat_halfconfigured || conflictor[i]->status == stat_triggersawaited || conflictor[i]->status == stat_triggerspending || conflictor[i]->status == stat_installed)) continue; trig_activate_packageprocessing(conflictor[i]); pkg_set_status(conflictor[i], stat_halfconfigured); modstatdb_note(conflictor[i]); push_cleanup(cu_prerminfavour, ~ehflag_normaltidy, NULL, 0, 2,(void*)conflictor[i],(void*)pkg); maintainer_script_installed(conflictor[i], PRERMFILE, "pre-removal", "remove", "in-favour", pkg->set->name, versiondescribe(&pkg->available.version, vdew_nonambig), NULL); pkg_set_status(conflictor[i], stat_halfinstalled); modstatdb_note(conflictor[i]); } pkg_set_eflags(pkg, eflag_reinstreq); if (pkg->status == stat_notinstalled) pkg->installed.version= pkg->available.version; pkg_set_status(pkg, stat_halfinstalled); modstatdb_note(pkg); if (oldversionstatus == stat_notinstalled) { push_cleanup(cu_preinstverynew, ~ehflag_normaltidy, NULL, 0, 3,(void*)pkg,(void*)cidir,(void*)cidirrest); maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest, "install", NULL); } else if (oldversionstatus == stat_configfiles) { push_cleanup(cu_preinstnew, ~ehflag_normaltidy, NULL, 0, 3,(void*)pkg,(void*)cidir,(void*)cidirrest); maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest, "install", versiondescribe(&pkg->installed.version, vdew_nonambig), NULL); } else { push_cleanup(cu_preinstupgrade, ~ehflag_normaltidy, NULL, 0, 4,(void*)pkg,(void*)cidir,(void*)cidirrest,(void*)&oldversionstatus); maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest, "upgrade", versiondescribe(&pkg->installed.version, vdew_nonambig), NULL); printf(_("Unpacking replacement %.250s ...\n"), pkgbin_name(pkg, &pkg->available, pnaw_nonambig)); } /* * Now we unpack the archive, backing things up as we go. * For each file, we check to see if it already exists. * There are several possibilities: * * + We are trying to install a non-directory ... * - It doesn't exist. In this case we simply extract it. * - It is a plain file, device, symlink, &c. We do an ‘atomic * overwrite’ using link() and rename(), but leave a backup copy. * Later, when we delete the backup, we remove it from any other * packages' lists. * - It is a directory. In this case it depends on whether we're * trying to install a symlink or something else. * = If we're not trying to install a symlink we move the directory * aside and extract the node. Later, when we recursively remove * the backed-up directory, we remove it from any other packages' * lists. * = If we are trying to install a symlink we do nothing - ie, * dpkg will never replace a directory tree with a symlink. This * is to avoid embarrassing effects such as replacing a directory * tree with a link to a link to the original directory tree. * + We are trying to install a directory ... * - It doesn't exist. We create it with the appropriate modes. * - It exists as a directory or a symlink to one. We do nothing. * - It is a plain file or a symlink (other than to a directory). * We move it aside and create the directory. Later, when we * delete the backup, we remove it from any other packages' lists. * * Install non-dir Install symlink Install dir * Exists not X X X * File/node/symlink LXR LXR BXR * Directory BXR - - * * X: extract file/node/link/directory * LX: atomic overwrite leaving backup * B: ordinary backup * R: later remove from other packages' lists * -: do nothing * * After we've done this we go through the remaining things in the * lists of packages we're trying to remove (including the old * version of the current package). This happens in reverse order, * so that we process files before the directories (or symlinks-to- * directories) containing them. * * + If the thing is a conffile then we leave it alone for the purge * operation. * + Otherwise, there are several possibilities too: * - The listed thing does not exist. We ignore it. * - The listed thing is a directory or a symlink to a directory. * We delete it only if it isn't listed in any other package. * - The listed thing is not a directory, but was part of the package * that was upgraded, we check to make sure the files aren't the * same ones from the old package by checking dev/inode * - The listed thing is not a directory or a symlink to one (ie, * it's a plain file, device, pipe, &c, or a symlink to one, or a * dangling symlink). We delete it. * * The removed packages' list becomes empty (of course, the new * version of the package we're installing will have a new list, * which replaces the old version's list). * * If at any stage we remove a file from a package's list, and the * package isn't one we're already processing, and the package's * list becomes empty as a result, we ‘vanish’ the package. This * means that we run its postrm with the ‘disappear’ argument, and * put the package in the ‘not-installed’ state. If it had any * conffiles, their hashes and ownership will have been transferred * already, so we just ignore those and forget about them from the * point of view of the disappearing package. * * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the * files get replaced ‘as we go’. */ m_pipe(p1); push_cleanup(cu_closepipe, ehflag_bombout, NULL, 0, 1, (void *)&p1[0]); pid = subproc_fork(); if (pid == 0) { m_dup2(p1[1],1); close(p1[0]); close(p1[1]); execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, NULL); ohshite(_("unable to execute %s (%s)"), _("package filesystem archive extraction"), BACKEND); } close(p1[1]); p1[1] = -1; newfileslist = NULL; tc.newfilesp = &newfileslist; push_cleanup(cu_fileslist, ~0, NULL, 0, 0); tc.pkg= pkg; tc.backendpipe= p1[0]; r = tar_extractor(&tc, &tf); if (r) { if (errno) { ohshite(_("error reading dpkg-deb tar output")); } else { ohshit(_("corrupted filesystem tarfile - corrupted package archive")); } } fd_skip(p1[0], -1, _("dpkg-deb: zap possible trailing zeros")); close(p1[0]); p1[0] = -1; subproc_wait_check(pid, BACKEND " --fsys-tarfile", PROCPIPE); tar_deferred_extract(newfileslist, pkg); if (oldversionstatus == stat_halfinstalled || oldversionstatus == stat_unpacked) { /* Packages that were in ‘installed’ and ‘postinstfailed’ have been * reduced to ‘unpacked’ by now, by the running of the prerm script. */ pkg_set_status(pkg, stat_halfinstalled); modstatdb_note(pkg); push_cleanup(cu_postrmupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg); maintainer_script_alternative(pkg, POSTRMFILE, "post-removal", cidir, cidirrest, "upgrade", "failed-upgrade"); } /* If anything goes wrong while tidying up it's a bit late to do * anything about it. However, we don't install the new status * info yet, so that a future dpkg installation will put everything * right (we hope). * * If something does go wrong later the ‘conflictor’ package will be * left in the ‘removal_failed’ state. Removing or installing it * will be impossible if it was required because of the conflict with * the package we're installing now and (presumably) the dependency * by other packages. This means that the files it contains in * common with this package will hang around until we successfully * get this package installed, after which point we can trust the * conflicting package's file list, which will have been updated to * remove any files in this package. */ push_checkpoint(~ehflag_bombout, ehflag_normaltidy); /* Now we delete all the files that were in the old version of * the package only, except (old or new) conffiles, which we leave * alone. */ reversefilelist_init(&rlistit,pkg->clientdata->files); while ((namenode= reversefilelist_next(&rlistit))) { struct filenamenode *usenode; if ((namenode->flags & fnnf_new_conff) || (namenode->flags & fnnf_new_inarchive)) continue; usenode = namenodetouse(namenode, pkg, &pkg->installed); trig_file_activate(usenode, pkg); varbuf_trunc(&fnamevb, fnameidlu); varbuf_add_str(&fnamevb, usenode->name); varbuf_end_str(&fnamevb); if (!stat(namenode->name,&stab) && S_ISDIR(stab.st_mode)) { debug(dbg_eachfiledetail, "process_archive: %s is a directory", namenode->name); if (dir_is_used_by_others(namenode, pkg)) continue; } if (lstat(fnamevb.buf, &oldfs)) { if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR)) warning(_("could not stat old file '%.250s' so not deleting it: %s"), fnamevb.buf, strerror(errno)); continue; } if (S_ISDIR(oldfs.st_mode)) { if (rmdir(fnamevb.buf)) { warning(_("unable to delete old directory '%.250s': %s"), namenode->name, strerror(errno)); } else if ((namenode->flags & fnnf_old_conff)) { warning(_("old conffile '%.250s' was an empty directory " "(and has now been deleted)"), namenode->name); } } else { struct fileinlist *sameas = NULL; static struct stat empty_stat; struct varbuf cfilename = VARBUF_INIT; /* * Ok, it's an old file, but is it really not in the new package? * It might be known by a different name because of symlinks. * * We need to check to make sure, so we stat the file, then compare * it to the new list. If we find a dev/inode match, we assume they * are the same file, and leave it alone. NOTE: we don't check in * other packages for sanity reasons (we don't want to stat _all_ * the files on the system). * * We run down the list of _new_ files in this package. This keeps * the process a little leaner. We are only worried about new ones * since ones that stayed the same don't really apply here. */ /* If we can't stat the old or new file, or it's a directory, * we leave it up to the normal code. */ debug(dbg_eachfile, "process_archive: checking %s for same files on " "upgrade/downgrade", fnamevb.buf); for (cfile= newfileslist; cfile; cfile= cfile->next) { /* If the file has been filtered then treat it as if it didn't exist * on the file system. */ if (cfile->namenode->flags & fnnf_filtered) continue; if (!cfile->namenode->filestat) { struct stat tmp_stat; varbuf_reset(&cfilename); varbuf_add_str(&cfilename, instdir); varbuf_add_char(&cfilename, '/'); varbuf_add_str(&cfilename, cfile->namenode->name); varbuf_end_str(&cfilename); if (lstat(cfilename.buf, &tmp_stat) == 0) { cfile->namenode->filestat = nfmalloc(sizeof(struct stat)); memcpy(cfile->namenode->filestat, &tmp_stat, sizeof(struct stat)); } else { if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR)) ohshite(_("unable to stat other new file `%.250s'"), cfile->namenode->name); cfile->namenode->filestat = &empty_stat; continue; } } if (cfile->namenode->filestat == &empty_stat) continue; if (oldfs.st_dev == cfile->namenode->filestat->st_dev && oldfs.st_ino == cfile->namenode->filestat->st_ino) { if (sameas) warning(_("old file '%.250s' is the same as several new files! " "(both '%.250s' and '%.250s')"), fnamevb.buf, sameas->namenode->name, cfile->namenode->name); sameas= cfile; debug(dbg_eachfile, "process_archive: not removing %s," " since it matches %s", fnamevb.buf, cfile->namenode->name); } } varbuf_destroy(&cfilename); if ((namenode->flags & fnnf_old_conff)) { if (sameas) { if (sameas->namenode->flags & fnnf_new_conff) { if (!strcmp(sameas->namenode->oldhash, NEWCONFFILEFLAG)) { sameas->namenode->oldhash= namenode->oldhash; debug(dbg_eachfile, "process_archive: old conff %s" " is same as new conff %s, copying hash", namenode->name, sameas->namenode->name); } else { debug(dbg_eachfile, "process_archive: old conff %s" " is same as new conff %s but latter already has hash", namenode->name, sameas->namenode->name); } } } else { debug(dbg_eachfile, "process_archive: old conff %s" " is disappearing", namenode->name); namenode->flags |= fnnf_obs_conff; newconff_append(&newconffileslastp, namenode); addfiletolist(&tc, namenode); } continue; } if (sameas) continue; if (secure_unlink_statted(fnamevb.buf, &oldfs)) { warning(_("unable to securely remove old file '%.250s': %s"), namenode->name, strerror(errno)); } } /* !S_ISDIR */ } /* OK, now we can write the updated files-in-this package list, * since we've done away (hopefully) with all the old junk. */ write_filelist_except(pkg, &pkg->available, newfileslist, 0); /* Trigger interests may have changed. * Firstly we go through the old list of interests deleting them. * Then we go through the new list adding them. */ strcpy(cidirrest, TRIGGERSCIFILE); trig_parse_ci(pkgadminfile(pkg, &pkg->installed, TRIGGERSCIFILE), trig_cicb_interest_delete, NULL, pkg, &pkg->installed); trig_parse_ci(cidir, trig_cicb_interest_add, NULL, pkg, &pkg->available); trig_file_interests_save(); /* We also install the new maintainer scripts, and any other * cruft that may have come along with the package. First * we go through the existing scripts replacing or removing * them as appropriate; then we go through the new scripts * (any that are left) and install them. */ debug(dbg_general, "process_archive updating info directory"); pkg_infodb_update(pkg, cidir, cidirrest); /* * Update the status database. * * This involves copying each field across from the ‘available’ * to the ‘installed’ half of the pkg structure. * For some of the fields we have to do a complicated construction * operation; for others we can just copy the value. * We tackle the fields in the order they appear, so that * we don't miss any out :-). * At least we don't have to copy any strings that are referred * to, because these are never modified and never freed. */ /* The dependencies are the most difficult. We have to build * a whole new forward dependency tree. At least the reverse * links (linking our deppossi's into the reverse chains) * can be done by copy_dependency_links. */ newdeplist = NULL; newdeplistlastp = &newdeplist; for (dep= pkg->available.depends; dep; dep= dep->next) { newdep= nfmalloc(sizeof(struct dependency)); newdep->up= pkg; newdep->next = NULL; newdep->list = NULL; newpossilastp = &newdep->list; for (possi= dep->list; possi; possi= possi->next) { newpossi= nfmalloc(sizeof(struct deppossi)); newpossi->up= newdep; newpossi->ed= possi->ed; newpossi->next = NULL; newpossi->rev_next = newpossi->rev_prev = NULL; newpossi->arch_is_implicit = possi->arch_is_implicit; newpossi->arch = possi->arch; newpossi->verrel= possi->verrel; if (possi->verrel != dvr_none) newpossi->version= possi->version; else blankversion(&newpossi->version); newpossi->cyclebreak = false; *newpossilastp= newpossi; newpossilastp= &newpossi->next; } newdep->type= dep->type; *newdeplistlastp= newdep; newdeplistlastp= &newdep->next; } /* Right, now we've replicated the forward tree, we * get copy_dependency_links to remove all the old dependency * structures from the reverse links and add the new dependency * structures in instead. It also copies the new dependency * structure pointer for this package into the right field. */ copy_dependency_links(pkg,&pkg->installed.depends,newdeplist,0); /* We copy the text fields. */ pkg->installed.essential= pkg->available.essential; pkg->installed.multiarch = pkg->available.multiarch; pkg->installed.description= pkg->available.description; pkg->installed.maintainer= pkg->available.maintainer; pkg->installed.source= pkg->available.source; pkg->installed.arch = pkg->available.arch; pkg->installed.installedsize= pkg->available.installedsize; pkg->installed.version= pkg->available.version; pkg->installed.origin = pkg->available.origin; pkg->installed.bugs = pkg->available.bugs; /* We have to generate our own conffiles structure. */ pkg->installed.conffiles = NULL; iconffileslastp = &pkg->installed.conffiles; for (cfile= newconffiles; cfile; cfile= cfile->next) { newiconff= nfmalloc(sizeof(struct conffile)); newiconff->next = NULL; newiconff->name= nfstrsave(cfile->namenode->name); newiconff->hash= nfstrsave(cfile->namenode->oldhash); newiconff->obsolete= !!(cfile->namenode->flags & fnnf_obs_conff); *iconffileslastp= newiconff; iconffileslastp= &newiconff->next; } /* We can just copy the arbitrary fields list, because it is * never even rearranged. Phew! */ pkg->installed.arbs= pkg->available.arbs; /* Check for disappearing packages: * We go through all the packages on the system looking for ones * whose files are entirely part of the one we've just unpacked * (and which actually *have* some files!). * * Any that we find are removed - we run the postrm with ‘disappear’ * as an argument, and remove their info/... files and status info. * Conffiles are ignored (the new package had better do something * with them!). */ it = pkg_db_iter_new(); while ((otherpkg = pkg_db_iter_next_pkg(it)) != NULL) { ensure_package_clientdata(otherpkg); if (otherpkg == pkg || otherpkg->status == stat_notinstalled || otherpkg->status == stat_configfiles || otherpkg->clientdata->istobe == itb_remove || !otherpkg->clientdata->files) continue; debug(dbg_veryverbose, "process_archive checking disappearance %s", pkg_name(otherpkg, pnaw_nonambig)); assert(otherpkg->clientdata->istobe == itb_normal || otherpkg->clientdata->istobe == itb_deconfigure); for (cfile= otherpkg->clientdata->files; cfile && !strcmp(cfile->namenode->name,"/."); cfile= cfile->next); if (!cfile) { debug(dbg_stupidlyverbose, "process_archive no non-root, no disappear"); continue; } for (cfile= otherpkg->clientdata->files; cfile && !filesavespackage(cfile,otherpkg,pkg); cfile= cfile->next); if (cfile) continue; /* So dependency things will give right answers ... */ otherpkg->clientdata->istobe= itb_remove; debug(dbg_veryverbose, "process_archive disappear checking dependencies"); for (pdep = otherpkg->set->depended.installed; pdep; pdep = pdep->rev_next) { if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && pdep->up->type != dep_recommends) continue; if (depisok(pdep->up, &depprobwhy, NULL, NULL, false)) continue; varbuf_end_str(&depprobwhy); debug(dbg_veryverbose,"process_archive cannot disappear: %s",depprobwhy.buf); break; } if (!pdep) { /* If we haven't found a reason not to yet, let's look some more. */ for (providecheck= otherpkg->installed.depends; providecheck; providecheck= providecheck->next) { if (providecheck->type != dep_provides) continue; for (pdep = providecheck->list->ed->depended.installed; pdep; pdep = pdep->rev_next) { if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && pdep->up->type != dep_recommends) continue; if (depisok(pdep->up, &depprobwhy, NULL, NULL, false)) continue; varbuf_end_str(&depprobwhy); debug(dbg_veryverbose,"process_archive cannot disappear (provides %s): %s", providecheck->list->ed->name, depprobwhy.buf); goto break_from_both_loops_at_once; } } break_from_both_loops_at_once:; } otherpkg->clientdata->istobe= itb_normal; if (pdep) continue; /* No, we're disappearing it. This is the wrong time to go and * run maintainer scripts and things, as we can't back out. But * what can we do ? It has to be run this late. */ pkg_disappear(otherpkg, pkg); } /* while (otherpkg= ... */ pkg_db_iter_free(it); /* Delete files from any other packages' lists. * We have to do this before we claim this package is in any * sane kind of state, as otherwise we might delete by mistake * a file that we overwrote, when we remove the package which * had the version we overwrote. To prevent this we make * sure that we don't claim this package is OK until we * have claimed ‘ownership’ of all its files. */ for (cfile= newfileslist; cfile; cfile= cfile->next) { struct filepackages_iterator *iter; struct pkgset *divpkgset; if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue; if (cfile->namenode->divert && cfile->namenode->divert->useinstead) { divpkgset = cfile->namenode->divert->pkgset; if (divpkgset == pkg->set) { debug(dbg_eachfile, "process_archive not overwriting any `%s' (overriding, `%s')", cfile->namenode->name, cfile->namenode->divert->useinstead->name); continue; } else { debug(dbg_eachfile, "process_archive looking for overwriting `%s' (overridden by %s)", cfile->namenode->name, divpkgset ? divpkgset->name : "<local>"); } } else { divpkgset = NULL; debug(dbg_eachfile, "process_archive looking for overwriting `%s'", cfile->namenode->name); } iter = filepackages_iter_new(cfile->namenode); while ((otherpkg = filepackages_iter_next(iter))) { debug(dbg_eachfiledetail, "process_archive ... found in %s", pkg_name(otherpkg, pnaw_nonambig)); /* If !fileslistvalid then it's one of the disappeared packages above * and we don't bother with it here, clearly. */ if (otherpkg == pkg || !otherpkg->clientdata->fileslistvalid) continue; if (otherpkg->set == divpkgset) { debug(dbg_eachfiledetail, "process_archive ... diverted, skipping"); continue; } /* Found one. We delete remove the list entry for this file, * (and any others in the same package) and then mark the package * as requiring a reread. */ write_filelist_except(otherpkg, &otherpkg->installed, otherpkg->clientdata->files, fnnf_elide_other_lists); ensure_package_clientdata(otherpkg); debug(dbg_veryverbose, "process_archive overwrote from %s", pkg_name(otherpkg, pnaw_nonambig)); } filepackages_iter_free(iter); } /* Right, the package we've unpacked is now in a reasonable state. * The only thing that we have left to do with it is remove * backup files, and we can leave the user to fix that if and when * it happens (we leave the reinstall required flag, of course). */ pkg_set_status(pkg, stat_unpacked); modstatdb_note(pkg); /* Now we delete all the backup files that we made when * extracting the archive - except for files listed as conffiles * in the new package. * This time we count it as an error if something goes wrong. * * Note that we don't ever delete things that were in the old * package as a conffile and don't appear at all in the new. * They stay recorded as obsolete conffiles and will eventually * (if not taken over by another package) be forgotten. */ for (cfile= newfileslist; cfile; cfile= cfile->next) { if (cfile->namenode->flags & fnnf_new_conff) continue; varbuf_trunc(&fnametmpvb, fnameidlu); varbuf_add_str(&fnametmpvb, namenodetouse(cfile->namenode, pkg, &pkg->installed)->name); varbuf_add_str(&fnametmpvb, DPKGTEMPEXT); varbuf_end_str(&fnametmpvb); ensure_pathname_nonexisting(fnametmpvb.buf); } /* OK, we're now fully done with the main package. * This is quite a nice state, so we don't unwind past here. */ pkg_reset_eflags(pkg); modstatdb_note(pkg); push_checkpoint(~ehflag_bombout, ehflag_normaltidy); /* Only the removal of the conflictor left to do. * The files list for the conflictor is still a little inconsistent in-core, * as we have not yet updated the filename->packages mappings; however, * the package->filenames mapping is. */ for (i = 0 ; i < cflict_index ; i++) { /* We need to have the most up-to-date info about which files are * what ... */ ensure_allinstfiles_available(); removal_bulk(conflictor[i]); } if (cipaction->arg_int == act_install) add_to_queue(pkg); }
void extracthalf(const char *debar, const char *dir, enum dpkg_tar_options taroption, int admininfo) { struct dpkg_error err; const char *errstr; char versionbuf[40]; struct deb_version version; off_t ctrllennum, memberlen = 0; ssize_t r; int dummy; pid_t c1=0,c2,c3; int p1[2], p2[2]; int p2_out; int arfd; struct stat stab; char nlc; int adminmember = -1; bool header_done; enum compressor_type decompressor = COMPRESSOR_TYPE_GZIP; if (strcmp(debar, "-") == 0) arfd = STDIN_FILENO; else arfd = open(debar, O_RDONLY); if (arfd < 0) ohshite(_("failed to read archive '%.255s'"), debar); if (fstat(arfd, &stab)) ohshite(_("failed to fstat archive")); r = read_line(arfd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1); if (r < 0) read_fail(r, debar, _("archive magic version number")); if (strcmp(versionbuf, DPKG_AR_MAGIC) == 0) { ctrllennum= 0; header_done = false; for (;;) { struct ar_hdr arh; r = fd_read(arfd, &arh, sizeof(arh)); if (r != sizeof(arh)) read_fail(r, debar, _("archive member header")); dpkg_ar_normalize_name(&arh); if (dpkg_ar_member_is_illegal(&arh)) ohshit(_("file '%.250s' is corrupt - bad archive header magic"), debar); memberlen = dpkg_ar_member_get_size(debar, &arh); if (!header_done) { char *infobuf; if (strncmp(arh.ar_name, DEBMAGIC, sizeof(arh.ar_name)) != 0) ohshit(_("file '%.250s' is not a debian binary archive (try dpkg-split?)"), debar); infobuf= m_malloc(memberlen+1); r = fd_read(arfd, infobuf, memberlen + (memberlen & 1)); if (r != (memberlen + (memberlen & 1))) read_fail(r, debar, _("archive information header member")); infobuf[memberlen] = '\0'; if (strchr(infobuf, '\n') == NULL) ohshit(_("archive has no newlines in header")); errstr = deb_version_parse(&version, infobuf); if (errstr) ohshit(_("archive has invalid format version: %s"), errstr); if (version.major != 2) ohshit(_("archive is format version %d.%d; get a newer dpkg-deb"), version.major, version.minor); free(infobuf); header_done = true; } else if (arh.ar_name[0] == '_') { /* Members with ‘_’ are noncritical, and if we don't understand * them we skip them. */ if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str); } else { if (strncmp(arh.ar_name, ADMINMEMBER, strlen(ADMINMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(ADMINMEMBER); adminmember = 1; decompressor = compressor_find_by_extension(extension); if (decompressor != COMPRESSOR_TYPE_NONE && decompressor != COMPRESSOR_TYPE_GZIP && decompressor != COMPRESSOR_TYPE_XZ) ohshit(_("archive '%s' uses unknown compression for member '%.*s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name); } else { if (adminmember != 1) ohshit(_("archive '%s' has premature member '%.*s' before '%s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name, ADMINMEMBER); if (strncmp(arh.ar_name, DATAMEMBER, strlen(DATAMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(DATAMEMBER); adminmember= 0; decompressor = compressor_find_by_extension(extension); if (decompressor == COMPRESSOR_TYPE_UNKNOWN) ohshit(_("archive '%s' uses unknown compression for member '%.*s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name); } else { ohshit(_("archive '%s' has premature member '%.*s' before '%s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name, DATAMEMBER); } } if (adminmember == 1) { if (ctrllennum != 0) ohshit(_("archive '%.250s' contains two control members, giving up"), debar); ctrllennum= memberlen; } if (!adminmember != !admininfo) { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str); } else { /* Yes! - found it. */ break; } } } if (admininfo >= 2) { printf(_(" new debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd bytes.\n"), version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum); m_output(stdout, _("<standard output>")); } } else if (strncmp(versionbuf, "0.93", 4) == 0) { char ctrllenbuf[40]; int l; l = strlen(versionbuf); if (strchr(versionbuf, '\n') == NULL) ohshit(_("archive has no newlines in header")); errstr = deb_version_parse(&version, versionbuf); if (errstr) ohshit(_("archive has invalid format version: %s"), errstr); r = read_line(arfd, ctrllenbuf, 1, sizeof(ctrllenbuf) - 1); if (r < 0) read_fail(r, debar, _("archive control member size")); if (sscanf(ctrllenbuf, "%jd%c%d", &ctrllennum, &nlc, &dummy) != 2 || nlc != '\n') ohshit(_("archive has malformatted control member size '%s'"), ctrllenbuf); if (admininfo) { memberlen = ctrllennum; } else { memberlen = stab.st_size - ctrllennum - strlen(ctrllenbuf) - l; if (fd_skip(arfd, ctrllennum, &err) < 0) ohshit(_("cannot skip archive control member from '%s': %s"), debar, err.str); } if (admininfo >= 2) { printf(_(" old debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd, main archive=%jd.\n"), version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum, (intmax_t)(stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)); m_output(stdout, _("<standard output>")); } } else { if (strncmp(versionbuf, "!<arch>", 7) == 0) { notice(_("file looks like it might be an archive which has been\n" " corrupted by being downloaded in ASCII mode")); } ohshit(_("'%.255s' is not a debian format archive"), debar); } m_pipe(p1); c1 = subproc_fork(); if (!c1) { close(p1[0]); if (fd_fd_copy(arfd, p1[1], memberlen, &err) < 0) ohshit(_("cannot copy archive member from '%s' to decompressor pipe: %s"), debar, err.str); if (close(p1[1])) ohshite(_("cannot close decompressor pipe")); exit(0); } close(p1[1]); if (taroption) { m_pipe(p2); p2_out = p2[1]; } else { p2_out = 1; } c2 = subproc_fork(); if (!c2) { if (taroption) close(p2[0]); decompress_filter(decompressor, p1[0], p2_out, _("decompressing archive member")); exit(0); } close(p1[0]); close(arfd); if (taroption) close(p2[1]); if (taroption) { c3 = subproc_fork(); if (!c3) { struct command cmd; command_init(&cmd, TAR, "tar"); command_add_arg(&cmd, "tar"); if ((taroption & DPKG_TAR_LIST) && (taroption & DPKG_TAR_EXTRACT)) command_add_arg(&cmd, "-xv"); else if (taroption & DPKG_TAR_EXTRACT) command_add_arg(&cmd, "-x"); else if (taroption & DPKG_TAR_LIST) command_add_arg(&cmd, "-tv"); else internerr("unknown or missing tar action '%d'", taroption); if (taroption & DPKG_TAR_PERMS) command_add_arg(&cmd, "-p"); if (taroption & DPKG_TAR_NOMTIME) command_add_arg(&cmd, "-m"); command_add_arg(&cmd, "-f"); command_add_arg(&cmd, "-"); command_add_arg(&cmd, "--warning=no-timestamp"); m_dup2(p2[0],0); close(p2[0]); unsetenv("TAR_OPTIONS"); if (dir) { if (chdir(dir)) { if (errno != ENOENT) ohshite(_("failed to chdir to directory")); if (mkdir(dir, 0777)) ohshite(_("failed to create directory")); if (chdir(dir)) ohshite(_("failed to chdir to directory after creating it")); } } command_exec(&cmd); } close(p2[0]); subproc_reap(c3, "tar", 0); } subproc_reap(c2, _("<decompress>"), SUBPROC_NOPIPE); if (c1 != -1) subproc_reap(c1, _("paste"), 0); if (version.major == 0 && admininfo) { /* Handle the version as a float to preserve the behaviour of old code, * because even if the format is defined to be padded by 0's that might * not have been always true for really ancient versions... */ while (version.minor && (version.minor % 10) == 0) version.minor /= 10; if (version.minor == 931) movecontrolfiles(OLDOLDDEBDIR); else if (version.minor == 932 || version.minor == 933) movecontrolfiles(OLDDEBDIR); } }
/** * Overly complex function that builds a .deb file. */ void do_build(const char *const *argv) { static const char *const maintainerscripts[]= { PREINSTFILE, POSTINSTFILE, PRERMFILE, POSTRMFILE, NULL }; char *m; const char *debar, *directory, *const *mscriptp, *versionstring, *arch; bool subdir; char *controlfile, *tfbuf; struct pkginfo *checkedinfo; struct arbitraryfield *field; FILE *ar, *cf; int p1[2], p2[2], p3[2], warns, n, c, gzfd; pid_t c1,c2,c3; struct stat controlstab, mscriptstab, debarstab; char conffilename[MAXCONFFILENAME+1]; struct file_info *fi; struct file_info *symlist = NULL; struct file_info *symlist_end = NULL; /* Decode our arguments. */ directory = *argv++; if (!directory) badusage(_("--%s needs a <directory> argument"), cipaction->olong); subdir = false; debar = *argv++; if (debar != NULL) { if (*argv) badusage(_("--build takes at most two arguments")); if (debar) { if (stat(debar,&debarstab)) { if (errno != ENOENT) ohshite(_("unable to check for existence of archive `%.250s'"),debar); } else if (S_ISDIR(debarstab.st_mode)) { subdir = true; } } } else { m= m_malloc(strlen(directory) + sizeof(DEBEXT)); strcpy(m, directory); path_trim_slash_slashdot(m); strcat(m, DEBEXT); debar= m; } /* Perform some sanity checks on the to-be-build package. */ if (nocheckflag) { if (subdir) ohshit(_("target is directory - cannot skip control file check")); warning(_("not checking contents of control area.")); printf(_("dpkg-deb: building an unknown package in '%s'.\n"), debar); } else { controlfile= m_malloc(strlen(directory) + sizeof(BUILDCONTROLDIR) + sizeof(CONTROLFILE) + sizeof(CONFFILESFILE) + sizeof(POSTINSTFILE) + sizeof(PREINSTFILE) + sizeof(POSTRMFILE) + sizeof(PRERMFILE) + MAXCONFFILENAME + 5); /* Let's start by reading in the control-file so we can check its * contents. */ strcpy(controlfile, directory); strcat(controlfile, "/" BUILDCONTROLDIR "/" CONTROLFILE); warns = 0; parsedb(controlfile, pdb_recordavailable|pdb_rejectstatus, &checkedinfo, &warns); if (strspn(checkedinfo->name, "abcdefghijklmnopqrstuvwxyz0123456789+-.") != strlen(checkedinfo->name)) ohshit(_("package name has characters that aren't lowercase alphanums or `-+.'")); if (checkedinfo->priority == pri_other) { warning(_("'%s' contains user-defined Priority value '%s'"), controlfile, checkedinfo->otherpriority); warns++; } for (field= checkedinfo->available.arbs; field; field= field->next) { if (known_arbitrary_field(field)) continue; warning(_("'%s' contains user-defined field '%s'"), controlfile, field->name); warns++; } if (subdir) { versionstring= versiondescribe(&checkedinfo->available.version,vdew_never); arch= checkedinfo->available.architecture; if (!arch) arch= ""; m= m_malloc(sizeof(DEBEXT)+1+strlen(debar)+1+strlen(checkedinfo->name)+ strlen(versionstring)+1+strlen(arch)); sprintf(m,"%s/%s_%s%s%s" DEBEXT,debar,checkedinfo->name,versionstring, arch[0] ? "_" : "", arch); debar= m; } printf(_("dpkg-deb: building package `%s' in `%s'.\n"), checkedinfo->name, debar); /* Check file permissions. */ strcpy(controlfile, directory); strcat(controlfile, "/" BUILDCONTROLDIR "/"); if (lstat(controlfile, &mscriptstab)) ohshite(_("unable to stat control directory")); if (!S_ISDIR(mscriptstab.st_mode)) ohshit(_("control directory is not a directory")); if ((mscriptstab.st_mode & 07757) != 0755) ohshit(_("control directory has bad permissions %03lo (must be >=0755 " "and <=0775)"), (unsigned long)(mscriptstab.st_mode & 07777)); for (mscriptp= maintainerscripts; *mscriptp; mscriptp++) { strcpy(controlfile, directory); strcat(controlfile, "/" BUILDCONTROLDIR "/"); strcat(controlfile, *mscriptp); if (!lstat(controlfile,&mscriptstab)) { if (S_ISLNK(mscriptstab.st_mode)) continue; if (!S_ISREG(mscriptstab.st_mode)) ohshit(_("maintainer script `%.50s' is not a plain file or symlink"),*mscriptp); if ((mscriptstab.st_mode & 07557) != 0555) ohshit(_("maintainer script `%.50s' has bad permissions %03lo " "(must be >=0555 and <=0775)"), *mscriptp, (unsigned long)(mscriptstab.st_mode & 07777)); } else if (errno != ENOENT) { ohshite(_("maintainer script `%.50s' is not stattable"),*mscriptp); } } /* Check if conffiles contains sane information. */ strcpy(controlfile, directory); strcat(controlfile, "/" BUILDCONTROLDIR "/" CONFFILESFILE); if ((cf= fopen(controlfile,"r"))) { struct file_info *conffiles_head = NULL; struct file_info *conffiles_tail = NULL; while (fgets(conffilename,MAXCONFFILENAME+1,cf)) { n= strlen(conffilename); if (!n) ohshite(_("empty string from fgets reading conffiles")); if (conffilename[n-1] != '\n') { warning(_("conffile name '%.50s...' is too long, or missing final newline"), conffilename); warns++; while ((c= getc(cf)) != EOF && c != '\n'); continue; } conffilename[n - 1] = '\0'; strcpy(controlfile, directory); strcat(controlfile, "/"); strcat(controlfile, conffilename); if (lstat(controlfile,&controlstab)) { if (errno == ENOENT) { if((n > 1) && isspace(conffilename[n-2])) warning(_("conffile filename '%s' contains trailing white spaces"), conffilename); ohshit(_("conffile `%.250s' does not appear in package"), conffilename); } else ohshite(_("conffile `%.250s' is not stattable"), conffilename); } else if (!S_ISREG(controlstab.st_mode)) { warning(_("conffile '%s' is not a plain file"), conffilename); warns++; } if (file_info_find_name(conffiles_head, conffilename)) warning(_("conffile name '%s' is duplicated"), conffilename); else { struct file_info *conffile; conffile = file_info_new(conffilename); add_to_filist(&conffiles_head, &conffiles_tail, conffile); } } free_filist(conffiles_head); if (ferror(cf)) ohshite(_("error reading conffiles file")); fclose(cf); } else if (errno != ENOENT) { ohshite(_("error opening conffiles file")); } if (warns) warning(P_("ignoring %d warning about the control file(s)\n", "ignoring %d warnings about the control file(s)\n", warns), warns); } m_output(stdout, _("<standard output>")); /* Now that we have verified everything its time to actually * build something. Let's start by making the ar-wrapper. */ if (!(ar=fopen(debar,"wb"))) ohshite(_("unable to create `%.255s'"),debar); if (setvbuf(ar, NULL, _IONBF, 0)) ohshite(_("unable to unbuffer `%.255s'"), debar); /* Fork a tar to package the control-section of the package. */ unsetenv("TAR_OPTIONS"); m_pipe(p1); c1 = subproc_fork(); if (!c1) { m_dup2(p1[1],1); close(p1[0]); close(p1[1]); if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory); if (chdir(BUILDCONTROLDIR)) ohshite(_("failed to chdir to `%.255s'"), ".../DEBIAN"); execlp(TAR, "tar", "-cf", "-", "--format=gnu", ".", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(p1[1]); /* Create a temporary file to store the control data in. Immediately * unlink our temporary file so others can't mess with it. */ tfbuf = path_make_temp_template("dpkg-deb"); gzfd = mkstemp(tfbuf); if (gzfd == -1) ohshite(_("failed to make temporary file (%s)"), _("control member")); /* Make sure it's gone, the fd will remain until we close it. */ if (unlink(tfbuf)) ohshit(_("failed to unlink temporary file (%s), %s"), _("control member"), tfbuf); free(tfbuf); /* And run gzip to compress our control archive. */ c2 = subproc_fork(); if (!c2) { m_dup2(p1[0],0); m_dup2(gzfd,1); close(p1[0]); close(gzfd); compress_filter(&compressor_gzip, 0, 1, 9, _("control member")); } close(p1[0]); subproc_wait_check(c2, "gzip -9c", 0); subproc_wait_check(c1, "tar -cf", 0); if (lseek(gzfd, 0, SEEK_SET)) ohshite(_("failed to rewind temporary file (%s)"), _("control member")); /* We have our first file for the ar-archive. Write a header for it * to the package and insert it. */ if (oldformatflag) { if (fstat(gzfd, &controlstab)) ohshite(_("failed to stat temporary file (%s)"), _("control member")); if (fprintf(ar, "%-8s\n%ld\n", OLDARCHIVEVERSION, (long)controlstab.st_size) == EOF) werr(debar); fd_fd_copy(gzfd, fileno(ar), -1, _("control member")); } else { const char deb_magic[] = ARCHIVEVERSION "\n"; dpkg_ar_put_magic(debar, fileno(ar)); dpkg_ar_member_put_mem(debar, fileno(ar), DEBMAGIC, deb_magic, strlen(deb_magic)); dpkg_ar_member_put_file(debar, fileno(ar), ADMINMEMBER, gzfd); } /* Control is done, now we need to archive the data. Start by creating * a new temporary file. Immediately unlink the temporary file so others * can't mess with it. */ if (!oldformatflag) { close(gzfd); tfbuf = path_make_temp_template("dpkg-deb"); gzfd = mkstemp(tfbuf); if (gzfd == -1) ohshite(_("failed to make temporary file (%s)"), _("data member")); /* Make sure it's gone, the fd will remain until we close it. */ if (unlink(tfbuf)) ohshit(_("failed to unlink temporary file (%s), %s"), _("data member"), tfbuf); free(tfbuf); } /* Fork off a tar. We will feed it a list of filenames on stdin later. */ m_pipe(p1); m_pipe(p2); c1 = subproc_fork(); if (!c1) { m_dup2(p1[0],0); close(p1[0]); close(p1[1]); m_dup2(p2[1],1); close(p2[0]); close(p2[1]); if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory); execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--null", "-T", "-", "--no-recursion", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(p1[0]); close(p2[1]); /* Of course we should not forget to compress the archive as well. */ c2 = subproc_fork(); if (!c2) { close(p1[1]); m_dup2(p2[0],0); close(p2[0]); m_dup2(oldformatflag ? fileno(ar) : gzfd,1); compress_filter(compressor, 0, 1, compress_level, _("data member")); } close(p2[0]); /* All the pipes are set, now lets run find, and start feeding * filenames to tar. */ m_pipe(p3); c3 = subproc_fork(); if (!c3) { m_dup2(p3[1],1); close(p3[0]); close(p3[1]); if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory); execlp(FIND, "find", ".", "-path", "./" BUILDCONTROLDIR, "-prune", "-o", "-print0", NULL); ohshite(_("unable to execute %s (%s)"), "find", FIND); } close(p3[1]); /* We need to reorder the files so we can make sure that symlinks * will not appear before their target. */ while ((fi=getfi(directory, p3[0]))!=NULL) if (S_ISLNK(fi->st.st_mode)) add_to_filist(&symlist, &symlist_end, fi); else { if (write(p1[1], fi->fn, strlen(fi->fn)+1) ==- 1) ohshite(_("failed to write filename to tar pipe (%s)"), _("data member")); file_info_free(fi); } close(p3[0]); subproc_wait_check(c3, "find", 0); for (fi= symlist;fi;fi= fi->next) if (write(p1[1], fi->fn, strlen(fi->fn)+1) == -1) ohshite(_("failed to write filename to tar pipe (%s)"), _("data member")); /* All done, clean up wait for tar and gzip to finish their job. */ close(p1[1]); free_filist(symlist); subproc_wait_check(c2, _("<compress> from tar -cf"), 0); subproc_wait_check(c1, "tar -cf", 0); /* Okay, we have data.tar as well now, add it to the ar wrapper. */ if (!oldformatflag) { char datamember[16 + 1]; sprintf(datamember, "%s%s", DATAMEMBER, compressor->extension); if (lseek(gzfd, 0, SEEK_SET)) ohshite(_("failed to rewind temporary file (%s)"), _("data member")); dpkg_ar_member_put_file(debar, fileno(ar), datamember, gzfd); } if (fflush(ar)) ohshite(_("unable to flush file '%s'"), debar); if (fsync(fileno(ar))) ohshite(_("unable to sync file '%s'"), debar); if (fclose(ar)) werr(debar); exit(0); }
int main(int argc, char *argv[]) { char cwd[PATH_MAX] = { '\0' }; char *dir = NULL; enum dpkg_tar_options taroption = DPKG_TAR_EXTRACT | DPKG_TAR_NOMTIME; int admininfo = 0; char *debar = argv[1]; int arfd = -1; struct stat stab; char versionbuf[40] = { '\0' }; ssize_t r; off_t ctrllennum, memberlen = 0; bool header_done; char *infobuf = NULL; struct dpkg_error err; const char *errstr; struct deb_version version; int adminmember = -1; enum compressor_type decompressor = COMPRESSOR_TYPE_GZIP; char nlc; int dummy; int p1[2], p2[2]; pid_t c1 = 0, c2, c3; int p2_out; if (getcwd(cwd, PATH_MAX)) { dir = m_malloc(PATH_MAX); snprintf(dir, PATH_MAX - 1, "%s/extract", cwd); } arfd = open(debar, O_RDONLY); if (arfd < 0) ohshite("failed to read archive %s", debar); if (fstat(arfd, &stab)) ohshite("failed to fstat archive"); r = read_line(arfd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1); if (r < 0) read_fail(r, debar, "archive magic version number"); if (strcmp(versionbuf, DPKG_AR_MAGIC) == 0) { ctrllennum = 0; header_done = false; for (;;) { struct ar_hdr arh; r = fd_read(arfd, &arh, sizeof(arh)); if (r != sizeof(arh)) read_fail(r, debar, "archive member header"); dpkg_ar_normalize_name(&arh); if (dpkg_ar_member_is_illegal(&arh)) { ohshit("file '%.250s' is corrupt - bad archive header magic", debar); } memberlen = dpkg_ar_member_get_size(debar, &arh); if (!header_done) { infobuf = NULL; if (strncmp(arh.ar_name, DEBMAGIC, sizeof(arh.ar_name)) != 0) { ohshit("file '%.250s' is not a debian binary archive " "(try dpkg-split?)", debar); } infobuf = m_malloc(memberlen + 1); r = fd_read(arfd, infobuf, memberlen + (memberlen & 1)); if (r != (memberlen + (memberlen & 1))) read_fail(r, debar, "archive information header member"); infobuf[memberlen] = '\0'; if (strchr(infobuf, '\n') == NULL) ohshit("archive has no newlines in header"); errstr = deb_version_parse(&version, infobuf); if (errstr) ohshit("archive has invalid format version: %s", errstr); if (version.major != 2) { ohshit("archive is format version %d.%d; get a newer dpkg-deb", version.major, version.minor); } if (infobuf) { free(infobuf); infobuf = NULL; } header_done = true; } else if (arh.ar_name[0] == '_') { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) { ohshit("cannot skip archive member from '%s': %s", debar, err.str); } } else { if (strncmp(arh.ar_name, ADMINMEMBER, strlen(ADMINMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(ADMINMEMBER); adminmember = 1; decompressor = compressor_find_by_extension(extension); if (decompressor != COMPRESSOR_TYPE_NONE && decompressor != COMPRESSOR_TYPE_GZIP && decompressor != COMPRESSOR_TYPE_XZ) { ohshit("ERROR: archive '%s' uses unknown compression " "for member '%.*s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name); } } else { if (adminmember != 1) { ohshit("archive '%s' has premature member '%.*s' " "before '%s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name, ADMINMEMBER); } if (strncmp(arh.ar_name, DATAMEMBER, strlen(DATAMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(DATAMEMBER); adminmember = 0; decompressor = compressor_find_by_extension(extension); if (decompressor == COMPRESSOR_TYPE_UNKNOWN) { ohshit("archive '%s' uses unknown " "compression for member '%.*s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name); } } else { ohshit("archive '%s' has premature member '%.*s' " "before '%s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name, DATAMEMBER); } } if (adminmember == 1) { if (ctrllennum != 0) { ohshit("archive '%.250s' contains two control members, giving up", debar); } ctrllennum = memberlen; } if (!adminmember != !admininfo) { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) { ohshit("cannot skip archive member from '%s': %s", debar, err.str); } } else { break; } } } if (admininfo >= 2) { printf(" new debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd bytes.\n", version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum); m_output(stdout, "<standard output>"); } } else if (strncmp(versionbuf, "0.93", 4) == 0) { char ctrllenbuf[40] = { '\0' }; int l; l = strlen(versionbuf); if (strchr(versionbuf, '\n') == NULL) ohshit("archive has no newlines in header"); errstr = deb_version_parse(&version, versionbuf); if (errstr) ohshit("archive has invalid format version: %s", errstr); r = read_line(arfd, ctrllenbuf, 1, sizeof(ctrllenbuf)); if (r < 0) read_fail(r, debar, "archive control member size"); if (sscanf(ctrllenbuf, "%jd%c%d", &ctrllennum, &nlc, &dummy) != 2 || nlc != '\n') { ohshit("archive has malformatted control member size '%s'", ctrllenbuf); } if (admininfo) { memberlen = ctrllennum; } else { memberlen = stab.st_size - ctrllennum - strlen(ctrllenbuf) - l; if (fd_skip(arfd, ctrllennum, &err) < 0) { ohshit("cannot skip archive control member from '%s': %s", debar, err.str); } } if (admininfo >= 2) { printf(" old debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd, main archive=%jd.\n", version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum, (intmax_t)(stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)); m_output(stdout, "<standard output>"); } } else { if (strncmp(versionbuf, "!<arch>", 7) == 0) { notice("file looks like it might be an archive which has been\n" " corrupted by being downloaded in ASCII mode"); } ohshit("'%.255s' is not a debian format archive", debar); } m_pipe(p1); c1 = subproc_fork(); if (!c1) { close(p1[0]); if (fd_fd_copy(arfd, p1[1], memberlen, &err) < 0) ohshit("cannot copy archive member from '%s' to decompressor pipe: %s", debar, err.str); if (close(p1[1])) ohshite("cannot close decompressor pipe"); exit(0); } close(p1[1]); if (taroption) { m_pipe(p2); p2_out = p2[1]; } else { p2_out = 1; } c2 = subproc_fork(); if (!c2) { if (taroption) close(p2[0]); decompress_filter(decompressor, p1[0], p2_out, "decompressing archive member"); exit(0); } close(p1[0]); close(arfd); if (taroption) close(p2[1]); if (taroption) { c3 = subproc_fork(); if (!c3) { struct command cmd; command_init(&cmd, TAR, "tar"); command_add_arg(&cmd, "tar"); if ((taroption & DPKG_TAR_LIST) && (taroption & DPKG_TAR_EXTRACT)) command_add_arg(&cmd, "-xv"); else if (taroption & DPKG_TAR_EXTRACT) command_add_arg(&cmd, "-x"); else if (taroption & DPKG_TAR_LIST) command_add_arg(&cmd, "-tv"); else internerr("unknown or missing tar action '%d'", taroption); if (taroption & DPKG_TAR_PERMS) command_add_arg(&cmd, "-p"); if (taroption & DPKG_TAR_NOMTIME) command_add_arg(&cmd, "-m"); command_add_arg(&cmd, "-f"); command_add_arg(&cmd, "-"); command_add_arg(&cmd, "--warning=no-timestamp"); m_dup2(p2[0],0); close(p2[0]); unsetenv("TAR_OPTIONS"); if (dir) { if (chdir(dir)) { if (errno != ENOENT) ohshite("failed to chdir to directory"); if (mkdir(dir, 0777)) ohshite("failed to create directory"); if (chdir(dir)) ohshite("failed to chdir to directory after creating it"); } } command_exec(&cmd); } close(p2[0]); subproc_reap(c3, "tar", 0); } subproc_reap(c2, "<decompress>", SUBPROC_NOPIPE); if (c1 != -1) subproc_reap(c1, "paste", 0); if (version.major == 0 && admininfo) { while (version.minor && (version.minor % 10) == 0) version.minor /= 10; if (version.minor == 931) movecontrolfiles(OLDOLDDEBDIR); else if (version.minor == 932 || version.minor == 933) movecontrolfiles(OLDDEBDIR); } return 0; }
/** * Overly complex function that builds a .deb file. */ int do_build(const char *const *argv) { struct dpkg_error err; const char *debar, *dir; bool subdir; char *tfbuf; int arfd; int p1[2], p2[2], gzfd; pid_t c1, c2; /* Decode our arguments. */ dir = *argv++; if (!dir) badusage(_("--%s needs a <directory> argument"), cipaction->olong); subdir = false; debar = *argv++; if (debar != NULL) { struct stat debarstab; if (*argv) badusage(_("--%s takes at most two arguments"), cipaction->olong); if (stat(debar, &debarstab)) { if (errno != ENOENT) ohshite(_("unable to check for existence of archive `%.250s'"), debar); } else if (S_ISDIR(debarstab.st_mode)) { subdir = true; } } else { char *m; m= m_malloc(strlen(dir) + sizeof(DEBEXT)); strcpy(m, dir); path_trim_slash_slashdot(m); strcat(m, DEBEXT); debar= m; } /* Perform some sanity checks on the to-be-build package. */ if (nocheckflag) { if (subdir) ohshit(_("target is directory - cannot skip control file check")); warning(_("not checking contents of control area")); printf(_("dpkg-deb: building an unknown package in '%s'.\n"), debar); } else { struct pkginfo *pkg; pkg = check_new_pkg(dir); if (subdir) debar = pkg_get_pathname(debar, pkg); printf(_("dpkg-deb: building package `%s' in `%s'.\n"), pkg->set->name, debar); } m_output(stdout, _("<standard output>")); /* Now that we have verified everything its time to actually * build something. Let's start by making the ar-wrapper. */ arfd = creat(debar, 0644); if (arfd < 0) ohshite(_("unable to create `%.255s'"), debar); /* Fork a tar to package the control-section of the package. */ unsetenv("TAR_OPTIONS"); m_pipe(p1); c1 = subproc_fork(); if (!c1) { m_dup2(p1[1],1); close(p1[0]); close(p1[1]); if (chdir(dir)) ohshite(_("failed to chdir to `%.255s'"), dir); if (chdir(BUILDCONTROLDIR)) ohshite(_("failed to chdir to `%.255s'"), ".../DEBIAN"); execlp(TAR, "tar", "-cf", "-", "--format=gnu", ".", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(p1[1]); /* Create a temporary file to store the control data in. Immediately * unlink our temporary file so others can't mess with it. */ tfbuf = path_make_temp_template("dpkg-deb"); gzfd = mkstemp(tfbuf); if (gzfd == -1) ohshite(_("failed to make temporary file (%s)"), _("control member")); /* Make sure it's gone, the fd will remain until we close it. */ if (unlink(tfbuf)) ohshit(_("failed to unlink temporary file (%s), %s"), _("control member"), tfbuf); free(tfbuf); /* And run gzip to compress our control archive. */ c2 = subproc_fork(); if (!c2) { struct compress_params params; params.type = compressor_type_gzip; params.strategy = compressor_strategy_none; params.level = 9; compress_filter(¶ms, p1[0], gzfd, _("compressing control member")); exit(0); } close(p1[0]); subproc_wait_check(c2, "gzip -9c", 0); subproc_wait_check(c1, "tar -cf", 0); if (lseek(gzfd, 0, SEEK_SET)) ohshite(_("failed to rewind temporary file (%s)"), _("control member")); /* We have our first file for the ar-archive. Write a header for it * to the package and insert it. */ if (deb_format.major == 0) { struct stat controlstab; char versionbuf[40]; if (fstat(gzfd, &controlstab)) ohshite(_("failed to stat temporary file (%s)"), _("control member")); sprintf(versionbuf, "%-8s\n%jd\n", OLDARCHIVEVERSION, (intmax_t)controlstab.st_size); if (fd_write(arfd, versionbuf, strlen(versionbuf)) < 0) ohshite(_("error writing `%s'"), debar); if (fd_fd_copy(gzfd, arfd, -1, &err) < 0) ohshit(_("cannot copy '%s' into archive '%s': %s"), _("control member"), debar, err.str); } else if (deb_format.major == 2) { const char deb_magic[] = ARCHIVEVERSION "\n"; dpkg_ar_put_magic(debar, arfd); dpkg_ar_member_put_mem(debar, arfd, DEBMAGIC, deb_magic, strlen(deb_magic)); dpkg_ar_member_put_file(debar, arfd, ADMINMEMBER, gzfd, -1); } else { internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor); } close(gzfd); /* Control is done, now we need to archive the data. */ if (deb_format.major == 0) { /* In old format, the data member is just concatenated after the * control member, so we do not need a temporary file and can use * the compression file descriptor. */ gzfd = arfd; } else if (deb_format.major == 2) { /* Start by creating a new temporary file. Immediately unlink the * temporary file so others can't mess with it. */ tfbuf = path_make_temp_template("dpkg-deb"); gzfd = mkstemp(tfbuf); if (gzfd == -1) ohshite(_("failed to make temporary file (%s)"), _("data member")); /* Make sure it's gone, the fd will remain until we close it. */ if (unlink(tfbuf)) ohshit(_("failed to unlink temporary file (%s), %s"), _("data member"), tfbuf); free(tfbuf); } else { internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor); } /* Fork off a tar. We will feed it a list of filenames on stdin later. */ m_pipe(p1); m_pipe(p2); c1 = subproc_fork(); if (!c1) { m_dup2(p1[0],0); close(p1[0]); close(p1[1]); m_dup2(p2[1],1); close(p2[0]); close(p2[1]); if (chdir(dir)) ohshite(_("failed to chdir to `%.255s'"), dir); execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--null", "-T", "-", "--no-recursion", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(p1[0]); close(p2[1]); /* Of course we should not forget to compress the archive as well. */ c2 = subproc_fork(); if (!c2) { close(p1[1]); compress_filter(&compress_params, p2[0], gzfd, _("compressing data member")); exit(0); } close(p2[0]); /* All the pipes are set, now lets walk the tree, and start feeding * filenames to tar. */ file_treewalk_feed(dir, p1[1]); /* All done, clean up wait for tar and gzip to finish their job. */ close(p1[1]); subproc_wait_check(c2, _("<compress> from tar -cf"), 0); subproc_wait_check(c1, "tar -cf", 0); /* Okay, we have data.tar as well now, add it to the ar wrapper. */ if (deb_format.major == 2) { char datamember[16 + 1]; sprintf(datamember, "%s%s", DATAMEMBER, compressor_get_extension(compress_params.type)); if (lseek(gzfd, 0, SEEK_SET)) ohshite(_("failed to rewind temporary file (%s)"), _("data member")); dpkg_ar_member_put_file(debar, arfd, datamember, gzfd, -1); close(gzfd); } if (fsync(arfd)) ohshite(_("unable to sync file '%s'"), debar); if (close(arfd)) ohshite(_("unable to close file '%s'"), debar); return 0; }