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); }
/** * 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); }
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 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]); } }
void pager_reap(struct pager *pager) { if (!pager->used) return; m_dup2(pager->stdout_old, 1); subproc_reap(pager->pid, pager->desc, SUBPROC_NOPIPE); sigaction(SIGPIPE, &pager->sigpipe, NULL); free(pager); }
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 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); }
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]; }
void process_archive(const char *filename) { static const struct TarFunctions tf = { tarfileread, tarobject, tarobject, tarobject, tarobject, 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 cidirtmpnambuf[L_tmpnam+100]; static char *cidirbuf = NULL, *reasmbuf = NULL; static struct fileinlist *newconffiles, *newfileslist; static enum pkgstatus oldversionstatus; static struct varbuf infofnvb, fnvb, depprobwhy; static struct tarcontext tc; int c1, r, admindirlen, i, infodirlen, infodirbaseused, status; struct pkgiterator *it; struct pkginfo *pkg, *otherpkg, *divpkg; char *cidir, *cidirrest, *p; char *pfilenamebuf, conffilenamebuf[MAXCONFFILENAME]; char *psize; const char *pfilename, *newinfofilename; struct fileinlist *newconff, **newconffileslastp; struct fileinlist *cfile; struct reversefilelistiter rlistit; struct conffile *searchconff, **iconffileslastp, *newiconff; struct filepackages *packageslump; struct dependency *dsearch, *newdeplist, **newdeplistlastp; struct dependency *newdep, *dep, *providecheck; struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep; FILE *conff; DIR *dsd; struct filenamenode *namenode; struct dirent *de; struct stat stab, oldfs; struct pkg_deconf_list *deconpil, *deconpiltemp; cleanup_pkg_failed= cleanup_conflictor_failed= 0; admindirlen= strlen(admindir); for (pfilename= filename ; pfilename && strlen(pfilename) > 30 && strchr(pfilename,'/') != NULL ; pfilename++ ) pfilename= strchr(pfilename,'/'); if (pfilename && pfilename != filename) { pfilenamebuf= (char *)nfmalloc(strlen(pfilename)+5); strcpy(pfilenamebuf,".../"); strcat(pfilenamebuf,pfilename); pfilename= pfilenamebuf; } else { pfilename= filename; } if (stat(filename,&stab)) ohshite(_("cannot access archive")); if (!f_noact) { /* We can't `tentatively-reassemble' packages. */ if (!reasmbuf) { reasmbuf= m_malloc(admindirlen+sizeof(REASSEMBLETMP)+5); strcpy(reasmbuf,admindir); strcat(reasmbuf,"/" REASSEMBLETMP); } if (unlink(reasmbuf) && errno != ENOENT) ohshite(_("error ensuring `%.250s' doesn't exist"),reasmbuf); push_cleanup(cu_pathname, ~0, NULL, 0, 1, (void *)reasmbuf); c1= m_fork(); if (!c1) { execlp(SPLITTER, SPLITTER, "-Qao", reasmbuf, filename, NULL); ohshite(_("failed to exec dpkg-split to see if it's part of a multiparter")); } while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR); if (r != c1) { onerr_abort++; ohshite(_("wait for dpkg-split failed")); } 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; } case 1: /* No, it wasn't a part. */ break; default: checksubprocerr(status,SPLITTER,0); } } /* Verify the package. */ if (!f_nodebsig && (stat(DEBSIGVERIFY, &stab)==0)) { printf(_("Authenticating %s ...\n"), filename); fflush(stdout); c1 = m_fork(); if (!c1) { execl(DEBSIGVERIFY, DEBSIGVERIFY, "-q", filename, NULL); ohshite(_("failed to execl debsig-verify")); } else { int status; waitpid(c1, &status, 0); if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { if (! fc_badverify) { ohshit(_("Verification on package %s failed!"), filename); } else { fprintf(stderr, _("Verification on package %s failed,\nbut installing anyway as you requested.\n"), filename); } } else { printf(_("passed\n")); } } } if (f_noact) { cidir= cidirtmpnambuf; /* We use tmpnam here, not to get a unique filename, but to get a unique directory. */ if (!tmpnam(cidir)) ohshite(_("unable to get unique filename for control info")); strcat(cidir,"/"); } else { /* We want it to be on the same filesystem so that we can * use rename(2) to install the postinst &c. */ if (!cidirbuf) cidirbuf= m_malloc(admindirlen+sizeof(CONTROLDIRTMP)+MAXCONTROLFILENAME+10); cidir= cidirbuf; strcpy(cidir,admindir); strcat(cidir, "/" CONTROLDIRTMP); } cidirrest= cidir + strlen(cidir); assert(*cidir && cidirrest[-1] == '/'); cidirrest[-1] = '\0'; ensure_pathname_nonexisting(cidir); cidirrest[-1]= '/'; push_cleanup(cu_cidir, ~0, NULL, 0, 2, (void *)cidir, (void *)cidirrest); c1= m_fork(); if (!c1) { cidirrest[-1] = '\0'; execlp(BACKEND, BACKEND, "--control", filename, cidir, NULL); ohshite(_("failed to exec dpkg-deb to extract control information")); } waitsubproc(c1,BACKEND " --control",0); strcpy(cidirrest,CONTROLFILE); parsedb(cidir, pdb_recordavailable | pdb_rejectstatus | pdb_ignorefiles, &pkg,NULL,NULL); 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, "%lu", (unsigned long)stab.st_size); pkg->files->size = psize; if (cipaction->arg == act_avail) { printf(_("Recorded info about %s from %s.\n"),pkg->name,pfilename); pop_cleanup(ehflag_normaltidy); return; } if (pkg->available.architecture && *pkg->available.architecture && strcmp(pkg->available.architecture,"all") && strcmp(pkg->available.architecture,architecture)) forcibleerr(fc_architecture, _("package architecture (%s) does not match system (%s)"), pkg->available.architecture,architecture); if (!pkg->installed.valid) blankpackageperfile(&pkg->installed); assert(pkg->available.valid); for (deconpil= deconfigure; deconpil; deconpil= deconpiltemp) { deconpiltemp= deconpil->next; free(deconpil); } deconfigure = NULL; clear_istobes(); if (!wanttoinstall(pkg,&pkg->available.version,1)) { 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. */ if (dsearch->list->ed->installed.valid) { for (psearch= dsearch->list->ed->installed.depended; psearch; psearch= psearch->nextrev) { 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, 1)) { varbufaddc(&depprobwhy,0); fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"), pfilename, pkg->name, depprobwhy.buf); if (!force_depends(dsearch->list)) ohshit(_("pre-dependency problem - not installing %.250s"),pkg->name); warning(_("ignoring pre-dependency problem!")); } } } /* Look for things that conflict with us. */ for (psearch= pkg->installed.depended; psearch; psearch= psearch->nextrev) { 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, versiondescribe(&pkg->installed.version,vdew_nonambig), pfilename); log_action("upgrade", pkg); } else { printf(_("Unpacking %s (from %s) ...\n"),pkg->name,pfilename); log_action("install", pkg); } 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); /* 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_closefile, ehflag_bombout, NULL, 0, 1, (void *)conff); while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) { 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; for (packageslump= newconff->namenode->packages; packageslump; packageslump= packageslump->more) { for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { otherpkg= packageslump->pkgs[i]; debug(dbg_conffdetail,"process_archive conffile `%s' in package %s - conff ?", newconff->namenode->name,otherpkg->name); 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,otherpkg->name,searchconff->name); if (searchconff) { debug(dbg_conff,"process_archive conffile `%s' package=%s %s hash=%s", newconff->namenode->name,otherpkg->name, otherpkg == pkg ? "same" : "different!", searchconff->hash); if (otherpkg == pkg) goto xit_conff_hashcopy_srch; } } } xit_conff_hashcopy_srch: 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->eflag |= eflag_reinstreq; pkg->status= stat_halfconfigured; modstatdb_note(pkg); push_cleanup(cu_prermupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg); maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest, "upgrade", "failed-upgrade"); pkg->status= 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"), deconpil->pkg->name, removing->name); else printf(_("De-configuring %s ...\n"), deconpil->pkg->name); trig_activate_packageprocessing(deconpil->pkg); deconpil->pkg->status= 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->name, versiondescribe(&pkg->available.version, vdew_nonambig), "removing", removing->name, versiondescribe(&removing->installed.version, vdew_nonambig), NULL); } else { maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal", "deconfigure", "in-favour", pkg->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]); conflictor[i]->status= 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->name, versiondescribe(&pkg->available.version, vdew_nonambig), NULL); conflictor[i]->status= stat_halfinstalled; modstatdb_note(conflictor[i]); } pkg->eflag |= eflag_reinstreq; if (pkg->status == stat_notinstalled) pkg->installed.version= pkg->available.version; pkg->status= 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"),pkg->name); } /* * 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]); c1= m_fork(); if (!c1) { m_dup2(p1[1],1); close(p1[0]); close(p1[1]); execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, NULL); ohshite(_("unable to exec dpkg-deb to get filesystem archive")); } 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= TarExtractor((void*)&tc, &tf); if (r) { if (errno) { ohshite(_("error reading dpkg-deb tar output")); } else { ohshit(_("corrupted filesystem tarfile - corrupted package archive")); } } fd_null_copy(p1[0], -1, _("dpkg-deb: zap possible trailing zeros")); close(p1[0]); p1[0] = -1; waitsubproc(c1,BACKEND " --fsys-tarfile",PROCPIPE); 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->status= 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); trig_file_activate(usenode, pkg); fnamevb.used= fnameidlu; varbufaddstr(&fnamevb, usenode->name); varbufaddc(&fnamevb,0); if (!stat(namenode->name,&stab) && S_ISDIR(stab.st_mode)) { debug(dbg_eachfiledetail, "process_archive: %s is a directory", namenode->name); if (isdirectoryinuse(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 { /* 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. */ struct fileinlist *sameas = NULL; static struct stat empty_stat; struct varbuf cfilename = VARBUF_INIT; /* 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 (!cfile->namenode->filestat) { struct stat tmp_stat; varbufreset(&cfilename); varbufaddstr(&cfilename, instdir); varbufaddc(&cfilename, '/'); varbufaddstr(&cfilename, cfile->namenode->name); varbufaddc(&cfilename, '\0'); 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); } } varbuffree(&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,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, TRIGGERSCIFILE), trig_cicb_interest_delete, NULL, pkg); trig_parse_ci(cidir, trig_cicb_interest_add, NULL, pkg); 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"); varbufreset(&infofnvb); varbufaddstr(&infofnvb,admindir); varbufaddstr(&infofnvb, "/" INFODIR); infodirlen= infofnvb.used; varbufaddc(&infofnvb,0); dsd= opendir(infofnvb.buf); if (!dsd) ohshite(_("cannot read info directory")); push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd); while ((de = readdir(dsd)) != NULL) { debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); if (de->d_name[0] == '.') continue; /* ignore dotfiles, including `.' and `..' */ p= strrchr(de->d_name,'.'); if (!p) continue; /* ignore anything odd */ if (strlen(pkg->name) != (size_t)(p-de->d_name) || strncmp(de->d_name,pkg->name,p-de->d_name)) continue; debug(dbg_stupidlyverbose, "process_archive info this pkg"); /* Right do we have one ? */ p++; /* skip past the full stop */ if (!strcmp(p,LISTFILE)) continue; /* We do the list separately */ if (strlen(p) > MAXCONTROLFILENAME) ohshit(_("old version of package has overly-long info file name starting `%.250s'"), de->d_name); infofnvb.used= infodirlen; varbufaddstr(&infofnvb,de->d_name); varbufaddc(&infofnvb,0); strcpy(cidirrest,p); if (!rename(cidir,infofnvb.buf)) { debug(dbg_scripts, "process_archive info installed %s as %s", cidir, infofnvb.buf); } else if (errno == ENOENT) { /* Right, no new version. */ if (unlink(infofnvb.buf)) ohshite(_("unable to remove obsolete info file `%.250s'"),infofnvb.buf); debug(dbg_scripts, "process_archive info unlinked %s",infofnvb.buf); } else { ohshite(_("unable to install (supposed) new info file `%.250s'"),cidir); } } pop_cleanup(ehflag_normaltidy); /* closedir */ *cidirrest = '\0'; /* the directory itself */ dsd= opendir(cidir); if (!dsd) ohshite(_("unable to open temp control directory")); push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd); while ((de= readdir(dsd))) { if (strchr(de->d_name,'.')) { debug(dbg_scripts,"process_archive tmp.ci script/file `%s' contains dot", de->d_name); continue; } if (strlen(de->d_name) > MAXCONTROLFILENAME) ohshit(_("package contains overly-long control info file name (starting `%.50s')"), de->d_name); strcpy(cidirrest,de->d_name); /* First we check it's not a directory. */ if (!rmdir(cidir)) ohshit(_("package control info contained directory `%.250s'"),cidir); else if (errno != ENOTDIR) ohshite(_("package control info rmdir of `%.250s' didn't say not a dir"),de->d_name); if (!strcmp(de->d_name,CONTROLFILE)) { debug(dbg_scripts,"process_archive tmp.ci script/file `%s' is control",cidir); continue; /* ignore the control file */ } if (!strcmp(de->d_name,LISTFILE)) { warning(_("package %s contained list as info file"), pkg->name); continue; } /* Right, install it */ newinfofilename= pkgadminfile(pkg,de->d_name); if (rename(cidir,newinfofilename)) ohshite(_("unable to install new info file `%.250s' as `%.250s'"), cidir,newinfofilename); debug(dbg_scripts,"process_archive tmp.ci script/file `%s' installed as `%s'", cidir, newinfofilename); } pop_cleanup(ehflag_normaltidy); /* closedir */ /* 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->nextrev = newpossi->backrev = NULL; newpossi->verrel= possi->verrel; if (possi->verrel != dvr_none) newpossi->version= possi->version; else blankversion(&newpossi->version); newpossi->cyclebreak= 0; *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); /* The `depended' pointer in the structure doesn't represent anything * that is actually specified by this package - it's there so we * can find out what other packages refer to this one. So, * we don't copy it. We go straight on to copy the text fields. */ pkg->installed.essential= pkg->available.essential; pkg->installed.description= pkg->available.description; pkg->installed.maintainer= pkg->available.maintainer; pkg->installed.source= pkg->available.source; pkg->installed.architecture= pkg->available.architecture; 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= iterpkgstart(); while ((otherpkg = iterpkgnext(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",otherpkg->name); 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->installed.depended; pdep; pdep= pdep->nextrev) { if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && pdep->up->type != dep_recommends) continue; if (depisok(pdep->up, &depprobwhy, NULL, 0)) continue; varbufaddc(&depprobwhy,0); 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->installed.depended; pdep; pdep= pdep->nextrev) { if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && pdep->up->type != dep_recommends) continue; if (depisok(pdep->up, &depprobwhy, NULL, 0)) continue; varbufaddc(&depprobwhy,0); 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; printf(_("(Noting disappearance of %s, which has been completely replaced.)\n"), otherpkg->name); debug(dbg_general, "process_archive disappearing %s",otherpkg->name); /* 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. */ trig_activate_packageprocessing(otherpkg); maintainer_script_installed(otherpkg, POSTRMFILE, "post-removal script (for disappearance)", "disappear", pkg->name, versiondescribe(&pkg->available.version, vdew_nonambig), NULL); /* OK, now we delete all the stuff in the `info' directory .. */ varbufreset(&fnvb); varbufaddstr(&fnvb,admindir); varbufaddstr(&fnvb,"/" INFODIR); infodirbaseused= fnvb.used; varbufaddc(&fnvb,0); dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory")); push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd); debug(dbg_general, "process_archive disappear cleaning info directory"); while ((de = readdir(dsd)) != NULL) { debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); if (de->d_name[0] == '.') continue; p= strrchr(de->d_name,'.'); if (!p) continue; if (strlen(otherpkg->name) != (size_t)(p-de->d_name) || strncmp(de->d_name,otherpkg->name,p-de->d_name)) continue; debug(dbg_stupidlyverbose, "process_archive info this pkg"); fnvb.used= infodirbaseused; varbufaddstr(&fnvb,de->d_name); varbufaddc(&fnvb,0); if (unlink(fnvb.buf)) ohshite(_("unable to delete disappearing control info file `%.250s'"),fnvb.buf); debug(dbg_scripts, "process_archive info unlinked %s",fnvb.buf); } pop_cleanup(ehflag_normaltidy); /* closedir */ otherpkg->status= stat_notinstalled; otherpkg->want = want_unknown; otherpkg->eflag = eflag_ok; blankversion(&otherpkg->configversion); blankpackageperfile(&otherpkg->installed); otherpkg->clientdata->fileslistvalid= 0; modstatdb_note(otherpkg); } /* while (otherpkg= ... */ iterpkgend(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) { if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue; if (cfile->namenode->divert && cfile->namenode->divert->useinstead) { divpkg= cfile->namenode->divert->pkg; if (divpkg == pkg) { 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, divpkg ? divpkg->name : "<local>"); } } else { divpkg = NULL; debug(dbg_eachfile, "process_archive looking for overwriting `%s'", cfile->namenode->name); } for (packageslump= cfile->namenode->packages; packageslump; packageslump= packageslump->more) { for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { otherpkg= packageslump->pkgs[i]; debug(dbg_eachfiledetail, "process_archive ... found in %s\n",otherpkg->name); /* 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 == divpkg) { debug(dbg_eachfiledetail, "process_archive ... diverted, skipping\n"); 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->clientdata->files, 1); ensure_package_clientdata(otherpkg); debug(dbg_veryverbose, "process_archive overwrote from %s",otherpkg->name); } } } /* 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->status= 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; fnametmpvb.used= fnameidlu; varbufaddstr(&fnametmpvb,namenodetouse(cfile->namenode,pkg)->name); varbufaddstr(&fnametmpvb,DPKGTEMPEXT); varbufaddc(&fnametmpvb,0); 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->eflag = eflag_ok; 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 == 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); } }
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; }
/* 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; char *controlfile, *tfbuf; const char *envbuf; struct pkginfo *checkedinfo; struct arbitraryfield *field; FILE *ar, *cf; int p1[2],p2[2],p3[2], warns, errs, n, c, subdir, gzfd; pid_t c1,c2,c3; struct stat controlstab, datastab, mscriptstab, debarstab; char conffilename[MAXCONFFILENAME+1]; time_t thetime= 0; 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); /* template for our tempfiles */ if ((envbuf= getenv("TMPDIR")) == NULL) envbuf= P_tmpdir; tfbuf = m_malloc(strlen(envbuf) + 13); strcpy(tfbuf,envbuf); strcat(tfbuf,"/dpkg.XXXXXX"); subdir= 0; 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= 1; } } } else { m= m_malloc(strlen(directory) + sizeof(DEBEXT)); strcpy(m, directory); path_rtrim_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); /* Lets start by reading in the control-file so we can check its contents */ strcpy(controlfile, directory); strcat(controlfile, "/" BUILDCONTROLDIR "/" CONTROLFILE); warns= 0; errs= 0; parsedb(controlfile, pdb_recordavailable|pdb_rejectstatus, &checkedinfo, stderr, &warns); assert(checkedinfo->available.valid); 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++; } checkversion(checkedinfo->available.version.version, _("(upstream) version"), &errs); checkversion(checkedinfo->available.version.revision, _("Debian revision"), &errs); if (errs) ohshit(_("%d errors in control file"),errs); 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(_("ignoring %d warnings about the control file(s)\n"), warns); } m_output(stdout, _("<standard output>")); /* Now that we have verified everything its time to actually * build something. Lets 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); if (!(c1= m_fork())) { 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 .../DEBIAN")); execlp(TAR, "tar", "-cf", "-", "--format=gnu", ".", NULL); ohshite(_("failed to exec tar -cf")); } 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. */ if ((gzfd= mkstemp(tfbuf)) == -1) ohshite(_("failed to make tmpfile (control)")); /* make sure it's gone, the fd will remain until we close it */ if (unlink(tfbuf)) ohshit(_("failed to unlink tmpfile (control), %s"), tfbuf); /* reset this, so we can use it elsewhere */ strcpy(tfbuf,envbuf); strcat(tfbuf,"/dpkg.XXXXXX"); /* And run gzip to compress our control archive */ if (!(c2= m_fork())) { m_dup2(p1[0],0); m_dup2(gzfd,1); close(p1[0]); close(gzfd); compress_cat(compress_type_gzip, 0, 1, "9", _("control")); } close(p1[0]); waitsubproc(c2,"gzip -9c",0); waitsubproc(c1,"tar -cf",0); if (fstat(gzfd,&controlstab)) ohshite(_("failed to fstat tmpfile (control)")); /* We have our first file for the ar-archive. Write a header for it to the * package and insert it. */ if (oldformatflag) { if (fprintf(ar, "%-8s\n%ld\n", OLDARCHIVEVERSION, (long)controlstab.st_size) == EOF) werr(debar); } else { thetime = time(NULL); if (fprintf(ar, "!<arch>\n" "debian-binary %-12lu0 0 100644 %-10ld`\n" ARCHIVEVERSION "\n" "%s" ADMINMEMBER "%-12lu0 0 100644 %-10ld`\n", thetime, (long)sizeof(ARCHIVEVERSION), (sizeof(ARCHIVEVERSION)&1) ? "\n" : "", (unsigned long)thetime, (long)controlstab.st_size) == EOF) werr(debar); } if (lseek(gzfd,0,SEEK_SET)) ohshite(_("failed to rewind tmpfile (control)")); fd_fd_copy(gzfd, fileno(ar), -1, _("control")); /* 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); if ((gzfd= mkstemp(tfbuf)) == -1) ohshite(_("failed to make tmpfile (data)")); /* make sure it's gone, the fd will remain until we close it */ if (unlink(tfbuf)) ohshit(_("failed to unlink tmpfile (data), %s"), tfbuf); /* reset these, in case we want to use the later */ strcpy(tfbuf,envbuf); strcat(tfbuf,"/dpkg.XXXXXX"); } /* Fork off a tar. We will feed it a list of filenames on stdin later. */ m_pipe(p1); m_pipe(p2); if (!(c1= m_fork())) { 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(_("failed to exec tar -cf")); } close(p1[0]); close(p2[1]); /* Of course we should not forget to compress the archive as well.. */ if (!(c2= m_fork())) { close(p1[1]); m_dup2(p2[0],0); close(p2[0]); m_dup2(oldformatflag ? fileno(ar) : gzfd,1); compress_cat(compress_type, 0, 1, compression, _("data")); } close(p2[0]); /* All the pipes are set, now lets run find, and start feeding * filenames to tar. */ m_pipe(p3); if (!(c3= m_fork())) { 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(_("failed to exec 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 (data)")); } close(p3[0]); waitsubproc(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 (data)")); /* All done, clean up wait for tar and gzip to finish their job */ close(p1[1]); free_filist(symlist); waitsubproc(c2, _("<compress> from tar -cf"), 0); waitsubproc(c1,"tar -cf",0); /* Okay, we have data.tar.gz as well now, add it to the ar wrapper */ if (!oldformatflag) { const char *datamember; switch (compress_type) { case compress_type_gzip: datamember = DATAMEMBER_GZ; break; case compress_type_bzip2: datamember = DATAMEMBER_BZ2; break; case compress_type_lzma: datamember = DATAMEMBER_LZMA; break; case compress_type_cat: datamember = DATAMEMBER_CAT; break; default: internerr("unkown compress_type '%i'", compress_type); } if (fstat(gzfd, &datastab)) ohshite(_("failed to fstat tmpfile (data)")); if (fprintf(ar, "%s" "%s" "%-12lu0 0 100644 %-10ld`\n", (controlstab.st_size & 1) ? "\n" : "", datamember, (unsigned long)thetime, (long)datastab.st_size) == EOF) werr(debar); if (lseek(gzfd,0,SEEK_SET)) ohshite(_("failed to rewind tmpfile (data)")); fd_fd_copy(gzfd, fileno(ar), -1, _("cat (data)")); if (datastab.st_size & 1) if (putc('\n',ar) == EOF) werr(debar); } if (fclose(ar)) werr(debar); exit(0); }
void extracthalf(const char *debar, const char *directory, const char *taroption, int admininfo) { char versionbuf[40]; float versionnum; char ctrllenbuf[40], *infobuf; size_t ctrllennum, memberlen= 0; int dummy, l= 0; pid_t c1=0,c2,c3; void *ctrlarea = NULL; int p1[2], p2[2]; FILE *ar, *pi; struct stat stab; char nlc; char *cur; struct ar_hdr arh; int readfromfd, oldformat= 0, header_done, adminmember; enum compress_type compress_type = compress_type_gzip; ar= fopen(debar,"r"); if (!ar) ohshite(_("failed to read archive `%.255s'"),debar); if (fstat(fileno(ar),&stab)) ohshite(_("failed to fstat archive")); if (!fgets(versionbuf,sizeof(versionbuf),ar)) readfail(ar,debar,_("version number")); if (!strcmp(versionbuf,"!<arch>\n")) { oldformat= 0; ctrllennum= 0; header_done= 0; for (;;) { if (fread(&arh,1,sizeof(arh),ar) != sizeof(arh)) readfail(ar,debar,_("between members")); if (memcmp(arh.ar_fmag,ARFMAG,sizeof(arh.ar_fmag))) ohshit(_("file `%.250s' is corrupt - bad magic at end of first header"),debar); memberlen= parseheaderlength(arh.ar_size,sizeof(arh.ar_size), debar, _("member length")); if (!header_done) { if (memcmp(arh.ar_name,"debian-binary ",sizeof(arh.ar_name)) && memcmp(arh.ar_name,"debian-binary/ ",sizeof(arh.ar_name))) ohshit(_("file `%.250s' is not a debian binary archive (try dpkg-split?)"),debar); infobuf= m_malloc(memberlen+1); if (fread(infobuf,1, memberlen + (memberlen&1), ar) != memberlen + (memberlen&1)) readfail(ar,debar,_("header info member")); infobuf[memberlen] = '\0'; cur= strchr(infobuf,'\n'); if (!cur) ohshit(_("archive has no newlines in header")); *cur = '\0'; cur= strchr(infobuf,'.'); if (!cur) ohshit(_("archive has no dot in version number")); *cur = '\0'; if (strcmp(infobuf,"2")) ohshit(_("archive version %.250s not understood, get newer dpkg-deb"), infobuf); *cur= '.'; strncpy(versionbuf,infobuf,sizeof(versionbuf)); versionbuf[sizeof(versionbuf) - 1] = '\0'; header_done= 1; } else if (arh.ar_name[0] == '_') { /* Members with `_' are noncritical, and if we don't understand them * we skip them. */ stream_null_copy(ar, memberlen + (memberlen&1),_("skipped member data from %s"), debar); } else { adminmember= (!memcmp(arh.ar_name,ADMINMEMBER,sizeof(arh.ar_name)) || !memcmp(arh.ar_name,ADMINMEMBER_COMPAT,sizeof(arh.ar_name))) ? 1 : -1; if (adminmember == -1) { if (!memcmp(arh.ar_name,DATAMEMBER_GZ,sizeof(arh.ar_name)) || !memcmp(arh.ar_name,DATAMEMBER_COMPAT_GZ,sizeof(arh.ar_name))) { adminmember= 0; compress_type = compress_type_gzip; } else if (!memcmp(arh.ar_name,DATAMEMBER_BZ2,sizeof(arh.ar_name)) || !memcmp(arh.ar_name,DATAMEMBER_COMPAT_BZ2,sizeof(arh.ar_name))) { adminmember= 0; compress_type = compress_type_bzip2; } else if (!memcmp(arh.ar_name, DATAMEMBER_LZMA, sizeof(arh.ar_name)) || !memcmp(arh.ar_name, DATAMEMBER_COMPAT_LZMA, sizeof(arh.ar_name))) { adminmember = 0; compress_type = compress_type_lzma; } else if (!memcmp(arh.ar_name,DATAMEMBER_CAT,sizeof(arh.ar_name)) || !memcmp(arh.ar_name,DATAMEMBER_COMPAT_CAT,sizeof(arh.ar_name))) { adminmember= 0; compress_type = compress_type_cat; } else { ohshit(_("file `%.250s' contains ununderstood data member %.*s, giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name); } } if (adminmember == 1) { if (ctrllennum != 0) ohshit(_("file `%.250s' contains two control members, giving up"), debar); ctrllennum= memberlen; } if (!adminmember != !admininfo) { stream_null_copy(ar, memberlen + (memberlen&1),_("skipped member data from %s"), debar); } else { break; /* Yes ! - found it. */ } } } if (admininfo >= 2) { printf(_(" new debian package, version %s.\n" " size %ld bytes: control archive= %zi bytes.\n"), versionbuf, (long)stab.st_size, ctrllennum); m_output(stdout, _("<standard output>")); } } else if (!strncmp(versionbuf,"0.93",4) && sscanf(versionbuf,"%f%c%d",&versionnum,&nlc,&dummy) == 2 && nlc == '\n') { oldformat= 1; l = strlen(versionbuf); if (l && versionbuf[l - 1] == '\n') versionbuf[l - 1] = '\0'; if (!fgets(ctrllenbuf,sizeof(ctrllenbuf),ar)) readfail(ar, debar, _("control information length")); if (sscanf(ctrllenbuf,"%zi%c%d",&ctrllennum,&nlc,&dummy) !=2 || nlc != '\n') ohshit(_("archive has malformatted control length `%s'"), ctrllenbuf); if (admininfo >= 2) { printf(_(" old debian package, version %s.\n" " size %ld bytes: control archive= %zi, main archive= %ld.\n"), versionbuf, (long)stab.st_size, ctrllennum, (long) (stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)); m_output(stdout, _("<standard output>")); } ctrlarea = m_malloc(ctrllennum); errno=0; if (fread(ctrlarea,1,ctrllennum,ar) != ctrllennum) readfail(ar, debar, _("control area")); } else { if (!strncmp(versionbuf,"!<arch>",7)) { fprintf(stderr, _("dpkg-deb: file looks like it might be an archive which has been\n" "dpkg-deb: corrupted by being downloaded in ASCII mode\n")); } ohshit(_("`%.255s' is not a debian format archive"),debar); } safe_fflush(ar); if (oldformat) { if (admininfo) { m_pipe(p1); if (!(c1= m_fork())) { close(p1[0]); pi = fdopen(p1[1], "w"); if (!pi) ohshite(_("failed to open pipe descriptor `1' in paste")); errno=0; if (fwrite(ctrlarea,1,ctrllennum,pi) != ctrllennum) ohshit(_("failed to write to gzip -dc")); if (fclose(pi)) ohshit(_("failed to close gzip -dc")); exit(0); } close(p1[1]); readfromfd= p1[0]; } else { if (lseek(fileno(ar),l+strlen(ctrllenbuf)+ctrllennum,SEEK_SET) == -1) ohshite(_("failed to syscall lseek to files archive portion")); c1= -1; readfromfd= fileno(ar); } } else { m_pipe(p1); if (!(c1= m_fork())) { close(p1[0]); stream_fd_copy(ar, p1[1], memberlen, _("failed to write to pipe in copy")); if (close(p1[1]) == EOF) ohshite(_("failed to close pipe in copy")); exit(0); } close(p1[1]); readfromfd= p1[0]; } if (taroption) m_pipe(p2); if (!(c2= m_fork())) { m_dup2(readfromfd,0); if (admininfo) close(p1[0]); if (taroption) { m_dup2(p2[1],1); close(p2[0]); close(p2[1]); } decompress_cat(compress_type, 0, 1, _("data")); } if (readfromfd != fileno(ar)) close(readfromfd); if (taroption) close(p2[1]); if (taroption && directory) { if (chdir(directory)) { if (errno == ENOENT) { if (mkdir(directory,0777)) ohshite(_("failed to create directory")); if (chdir(directory)) ohshite(_("failed to chdir to directory after creating it")); } else { ohshite(_("failed to chdir to directory")); } } } if (taroption) { if (!(c3= m_fork())) { char buffer[30+2]; if (strlen(taroption) > 30) internerr("taroption is too long '%s'", taroption); strcpy(buffer, taroption); strcat(buffer, "f"); m_dup2(p2[0],0); close(p2[0]); unsetenv("TAR_OPTIONS"); execlp(TAR, "tar", buffer, "-", NULL); ohshite(_("failed to exec tar")); } close(p2[0]); waitsubproc(c3,"tar",0); } waitsubproc(c2, _("<decompress>"), PROCPIPE); if (c1 != -1) waitsubproc(c1, _("paste"), 0); if (oldformat && admininfo) { if (versionnum == 0.931F) { movecontrolfiles(OLDOLDDEBDIR); } else if (versionnum == 0.932F || versionnum == 0.933F) { movecontrolfiles(OLDDEBDIR); } } }
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 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(); }