static int handleRemovalConflict(rpmfiles fi, int fx, rpmfiles ofi, int ofx) { int rConflicts = 0; /* Removed files don't conflict, normally */ rpmFileTypes ft = rpmfiWhatis(rpmfilesFMode(fi, fx)); rpmFileTypes oft = rpmfiWhatis(rpmfilesFMode(ofi, ofx)); struct stat sb; char *fn = NULL; if (oft == XDIR) { /* We can't handle directory changing to anything else */ if (ft != XDIR) rConflicts = 1; } else if (oft == LINK) { /* We can't correctly handle directory symlink changing to directory */ if (ft == XDIR) { fn = rpmfilesFN(fi, fx); if (stat(fn, &sb) == 0 && S_ISDIR(sb.st_mode)) rConflicts = 1; } } /* * ...but if the conflicting item is either not on disk, or has * already been changed to the new type, we should be ok afterall. */ if (rConflicts) { if (fn == NULL) fn = rpmfilesFN(fi, fx); if (lstat(fn, &sb) || rpmfiWhatis(sb.st_mode) == ft) rConflicts = 0; } free(fn); return rConflicts; }
static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files, rpmpsm psm, int nodigest, int *setmeta, int * firsthardlink, FD_t *firstlinkfile) { int rc = 0; int numHardlinks = rpmfiFNlink(fi); if (numHardlinks > 1) { /* Create first hardlinked file empty */ if (*firsthardlink < 0) { *firsthardlink = rpmfiFX(fi); rc = wfd_open(firstlinkfile, dest); } else { /* Create hard links for others */ char *fn = rpmfilesFN(files, *firsthardlink); rc = link(fn, dest); if (rc < 0) { rc = RPMERR_LINK_FAILED; } free(fn); } } /* Write normal files or fill the last hardlinked (already existing) file with content */ if (numHardlinks<=1) { if (!rc) rc = expandRegular(fi, dest, psm, nodigest); } else if (rpmfiArchiveHasContent(fi)) { if (!rc) rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); wfd_close(firstlinkfile); *firsthardlink = -1; } else { *setmeta = 0; } return rc; }
rpmRC rpmInstallSourcePackage(rpmts ts, FD_t fd, char ** specFilePtr, char ** cookie) { Header h = NULL; rpmpsm psm = NULL; rpmte te = NULL; rpmRC rpmrc; int specix = -1; rpmrc = rpmReadPackageFile(ts, fd, NULL, &h); switch (rpmrc) { case RPMRC_NOTTRUSTED: case RPMRC_NOKEY: case RPMRC_OK: break; default: goto exit; break; } if (h == NULL) goto exit; rpmrc = RPMRC_FAIL; /* assume failure */ if (!headerIsSource(h)) { rpmlog(RPMLOG_ERR, _("source package expected, binary found\n")); goto exit; } /* src.rpm install can require specific rpmlib features, check them */ if (!rpmlibDeps(h)) goto exit; specix = headerFindSpec(h); if (specix < 0) { rpmlog(RPMLOG_ERR, _("source package contains no .spec file\n")); goto exit; }; if (rpmtsAddInstallElement(ts, h, NULL, 0, NULL)) { goto exit; } te = rpmtsElement(ts, 0); if (te == NULL) { /* XXX can't happen */ goto exit; } rpmteSetFd(te, fd); rpmteSetHeader(te, h); { /* set all files to be installed */ rpmfs fs = rpmteGetFileStates(te); int fc = rpmfsFC(fs); for (int i = 0; i < fc; i++) rpmfsSetAction(fs, i, FA_CREATE); } psm = rpmpsmNew(ts, te, PKG_INSTALL); if (rpmpsmUnpack(psm) == RPMRC_OK) rpmrc = RPMRC_OK; rpmpsmFree(psm); exit: if (rpmrc == RPMRC_OK && specix >= 0) { if (cookie) *cookie = headerGetAsString(h, RPMTAG_COOKIE); if (specFilePtr) { rpmfiles files = rpmteFiles(te); *specFilePtr = rpmfilesFN(files, specix); rpmfilesFree(files); } } /* XXX nuke the added package(s). */ headerFree(h); rpmtsEmpty(ts); return rpmrc; }
/* XXX only ts->{probs,di} modified */ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfiles fi) { rpm_loff_t fixupSize = 0; int i, j; rpmfs fs = rpmteGetFileStates(p); rpmfs otherFs; rpm_count_t fc = rpmfilesFC(fi); int reportConflicts = !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES); fingerPrint * fpList = rpmfilesFps(fi); for (i = 0; i < fc; i++) { struct fingerPrint_s * fiFps; int otherPkgNum, otherFileNum; rpmfiles otherFi; rpmte otherTe; rpmfileAttrs FFlags; struct rpmffi_s * recs; int numRecs; if (XFA_SKIPPING(rpmfsGetAction(fs, i))) continue; FFlags = rpmfilesFFlags(fi, i); fixupSize = 0; /* * Retrieve all records that apply to this file. Note that the * file info records were built in the same order as the packages * will be installed and removed so the records for an overlapped * files will be sorted in exactly the same order. */ fiFps = fpCacheGetByFp(fpc, fpList, i, &recs, &numRecs); /* * If this package is being added, look only at other packages * being added -- removed packages dance to a different tune. * * If both this and the other package are being added, overlapped * files must be identical (or marked as a conflict). The * disposition of already installed config files leads to * a small amount of extra complexity. * * If this package is being removed, then there are two cases that * need to be worried about: * If the other package is being added, then skip any overlapped files * so that this package removal doesn't nuke the overlapped files * that were just installed. * If both this and the other package are being removed, then each * file removal from preceding packages needs to be skipped so that * the file removal occurs only on the last occurrence of an overlapped * file in the transaction set. * */ /* * Locate this overlapped file in the set of added/removed packages, * including the package owning it: a package can have self-conflicting * files when directory symlinks are present. Don't compare a file * with itself though... */ for (j = 0; j < numRecs && !(recs[j].p == p && recs[j].fileno == i); j++) {}; /* Find what the previous disposition of this file was. */ otherFileNum = -1; /* keep gcc quiet */ otherFi = NULL; otherTe = NULL; otherFs = NULL; for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) { otherTe = recs[otherPkgNum].p; otherFileNum = recs[otherPkgNum].fileno; otherFs = rpmteGetFileStates(otherTe); /* Added packages need only look at other added packages. */ if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED) continue; /* XXX Happens iff fingerprint for incomplete package install. */ if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN) { otherFi = rpmteFiles(otherTe); break; } } switch (rpmteType(p)) { case TR_ADDED: if (otherPkgNum < 0) { /* XXX is this test still necessary? */ rpmFileAction action; if (rpmfsGetAction(fs, i) != FA_UNKNOWN) break; if (rpmfilesConfigConflict(fi, i)) { /* Here is a non-overlapped pre-existing config file. */ action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_BACKUP; } else { action = FA_CREATE; } rpmfsSetAction(fs, i, action); break; } assert(otherFi != NULL); /* Mark added overlapped non-identical files as a conflict. */ if (rpmfilesCompare(otherFi, otherFileNum, fi, i)) { int rConflicts; /* If enabled, resolve colored conflicts to preferred type */ rConflicts = handleColorConflict(ts, fs, fi, i, otherFs, otherFi, otherFileNum); if (rConflicts && reportConflicts) { char *fn = rpmfilesFN(fi, i); rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT, rpmteNEVRA(otherTe), fn, 0); free(fn); } } else { /* Skip create on all but the first instance of a shared file */ rpmFileAction oaction = rpmfsGetAction(otherFs, otherFileNum); if (oaction != FA_UNKNOWN && !XFA_SKIPPING(oaction)) { rpmfileAttrs oflags; /* ...but ghosts aren't really created so... */ oflags = rpmfilesFFlags(otherFi, otherFileNum); if (!(oflags & RPMFILE_GHOST)) { rpmfsSetAction(fs, i, FA_SKIP); } /* if the other file is color skipped then skip this file too */ } else if (oaction == FA_SKIPCOLOR) { rpmfsSetAction(fs, i, FA_SKIPCOLOR); } } /* Skipped files dont need fixup size or backups, %config or not */ if (XFA_SKIPPING(rpmfsGetAction(fs, i))) break; /* Try to get the disk accounting correct even if a conflict. */ fixupSize = rpmfilesFSize(otherFi, otherFileNum); if (rpmfilesConfigConflict(fi, i)) { /* Here is an overlapped pre-existing config file. */ rpmFileAction action; action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP; rpmfsSetAction(fs, i, action); } else { /* If not decided yet, create it */ if (rpmfsGetAction(fs, i) == FA_UNKNOWN) rpmfsSetAction(fs, i, FA_CREATE); } break; case TR_REMOVED: if (otherPkgNum >= 0) { assert(otherFi != NULL); /* Here is an overlapped added file we don't want to nuke. */ if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) { /* On updates, don't remove files. */ rpmfsSetAction(fs, i, FA_SKIP); break; } /* Here is an overlapped removed file: skip in previous. */ rpmfsSetAction(otherFs, otherFileNum, FA_SKIP); } if (XFA_SKIPPING(rpmfsGetAction(fs, i))) break; if (rpmfilesFState(fi, i) != RPMFILE_STATE_NORMAL) break; /* Pre-existing modified config files need to be saved. */ if (rpmfilesConfigConflict(fi, i)) { rpmfsSetAction(fs, i, FA_SAVE); break; } /* Otherwise, we can just erase. */ rpmfsSetAction(fs, i, FA_ERASE); break; } rpmfilesFree(otherFi); /* Update disk space info for a file. */ rpmtsUpdateDSI(ts, fpEntryDev(fpc, fiFps), fpEntryDir(fpc, fiFps), rpmfilesFSize(fi, i), rpmfilesFReplacedSize(fi, i), fixupSize, rpmfsGetAction(fs, i)); } }
/* XXX only ts->{probs,rpmdb} modified */ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfiles fi, int fx, Header otherHeader, rpmfiles otherFi, int ofx, int beingRemoved) { rpmfs fs = rpmteGetFileStates(p); int isCfgFile = ((rpmfilesFFlags(otherFi, ofx) | rpmfilesFFlags(fi, fx)) & RPMFILE_CONFIG); if (XFA_SKIPPING(rpmfsGetAction(fs, fx))) return; if (rpmfilesCompare(otherFi, ofx, fi, fx)) { int rConflicts = 1; char rState = RPMFILE_STATE_REPLACED; /* * There are some removal conflicts we can't handle. However * if the package has a %pretrans scriptlet, it might be able to * fix the conflict. Let it through on test-transaction to allow * eg yum to get past it, if the conflict is present on the actual * transaction we'll abort. Behaving differently on test is nasty, * but its still better than barfing in middle of large transaction. */ if (beingRemoved) { rConflicts = handleRemovalConflict(fi, fx, otherFi, ofx); if (rConflicts && rpmteHaveTransScript(p, RPMTAG_PRETRANS)) { if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) rConflicts = 0; } } if (rConflicts) { /* If enabled, resolve colored conflicts to preferred type */ rConflicts = handleColorConflict(ts, fs, fi, fx, NULL, otherFi, ofx); /* If resolved, we need to adjust in-rpmdb state too */ if (rConflicts == 0 && rpmfsGetAction(fs, fx) == FA_CREATE) rState = RPMFILE_STATE_WRONGCOLOR; } /* Somebody used The Force, lets shut up... */ if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) rConflicts = 0; if (rConflicts) { char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA); char *fn = rpmfilesFN(fi, fx); rpmteAddProblem(p, RPMPROB_FILE_CONFLICT, altNEVR, fn, headerGetInstance(otherHeader)); free(fn); free(altNEVR); } /* Save file identifier to mark as state REPLACED. */ if ( !(isCfgFile || XFA_SKIPPING(rpmfsGetAction(fs, fx))) ) { if (!beingRemoved) rpmfsAddReplaced(rpmteGetFileStates(p), fx, rState, headerGetInstance(otherHeader), ofx); } } /* Determine config file disposition, skipping missing files (if any). */ if (isCfgFile) { int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1); rpmFileAction action; action = rpmfilesDecideFate(otherFi, ofx, fi, fx, skipMissing); rpmfsSetAction(fs, fx, action); } rpmfilesSetFReplacedSize(fi, fx, rpmfilesFSize(otherFi, ofx)); }
rpmVerifyAttrs rpmfilesVerify(rpmfiles fi, int ix, rpmVerifyAttrs omitMask) { rpmfileAttrs fileAttrs = rpmfilesFFlags(fi, ix); rpmVerifyAttrs flags = rpmfilesVFlags(fi, ix); const char * fn = rpmfilesFN(fi, ix); struct stat sb, fsb; rpmVerifyAttrs vfy = RPMVERIFY_NONE; /* * Check to see if the file was installed - if not pretend all is OK. */ switch (rpmfilesFState(fi, ix)) { case RPMFILE_STATE_NETSHARED: case RPMFILE_STATE_NOTINSTALLED: goto exit; break; case RPMFILE_STATE_REPLACED: /* For replaced files we can only verify if it exists at all */ flags = RPMVERIFY_LSTATFAIL; break; case RPMFILE_STATE_WRONGCOLOR: /* * Files with wrong color are supposed to share some attributes * with the actually installed file - verify what we can. */ flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_RDEV); break; case RPMFILE_STATE_NORMAL: /* File from a non-installed package, try to verify nevertheless */ case RPMFILE_STATE_MISSING: break; } if (fn == NULL || lstat(fn, &sb) != 0 || rpmfilesStat(fi, ix, 0, &fsb)) { vfy |= RPMVERIFY_LSTATFAIL; goto exit; } /* If we expected a directory but got a symlink to one, follow the link */ if (S_ISDIR(fsb.st_mode) && S_ISLNK(sb.st_mode)) { struct stat dsb; /* ...if it actually points to a directory */ if (stat(fn, &dsb) == 0 && S_ISDIR(dsb.st_mode)) { /* ...and is by a legit user, to match fsmVerify() behavior */ if (sb.st_uid == 0 || sb.st_uid == fsb.st_uid) sb = dsb; /* struct assignment */ } } /* Links have no mode, other types have no linkto */ if (S_ISLNK(sb.st_mode)) flags &= ~(RPMVERIFY_MODE); else flags &= ~(RPMVERIFY_LINKTO); /* Not all attributes of non-regular files can be verified */ if (!S_ISREG(sb.st_mode)) flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_CAPS); /* Content checks of %ghost files are meaningless. */ if (fileAttrs & RPMFILE_GHOST) flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO); /* Don't verify any features in omitMask. */ flags &= ~(omitMask | RPMVERIFY_FAILURES); if (flags & RPMVERIFY_FILEDIGEST) { const unsigned char *digest; int algo; size_t diglen; /* XXX If --nomd5, then prelinked library sizes are not corrected. */ if ((digest = rpmfilesFDigest(fi, ix, &algo, &diglen))) { unsigned char fdigest[diglen]; rpm_loff_t fsize; if (rpmDoDigest(algo, fn, 0, fdigest, &fsize)) { vfy |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST); } else { sb.st_size = fsize; if (memcmp(fdigest, digest, diglen)) vfy |= RPMVERIFY_FILEDIGEST; } } else { vfy |= RPMVERIFY_FILEDIGEST; } } if (flags & RPMVERIFY_LINKTO) { char linkto[1024+1]; int size = 0; if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1) vfy |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); else { const char * flink = rpmfilesFLink(fi, ix); linkto[size] = '\0'; if (flink == NULL || !rstreq(linkto, flink)) vfy |= RPMVERIFY_LINKTO; } } if ((flags & RPMVERIFY_FILESIZE) && (sb.st_size != fsb.st_size)) vfy |= RPMVERIFY_FILESIZE; if (flags & RPMVERIFY_MODE) { mode_t metamode = fsb.st_mode; mode_t filemode = sb.st_mode; /* * Comparing the type of %ghost files is meaningless, but perms are OK. */ if (fileAttrs & RPMFILE_GHOST) { metamode &= ~S_IFMT; filemode &= ~S_IFMT; } if (metamode != filemode) vfy |= RPMVERIFY_MODE; #if WITH_ACL /* * For now, any non-default acl's on a file is a difference as rpm * cannot have set them. */ acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS); if (facl) { if (acl_equiv_mode(facl, NULL) == 1) { vfy |= RPMVERIFY_MODE; } acl_free(facl); } #endif } if (flags & RPMVERIFY_RDEV) { if (S_ISCHR(fsb.st_mode) != S_ISCHR(sb.st_mode) || S_ISBLK(fsb.st_mode) != S_ISBLK(sb.st_mode)) { vfy |= RPMVERIFY_RDEV; } else if (S_ISDEV(fsb.st_mode) && S_ISDEV(sb.st_mode)) { rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff); rpm_rdev_t frdev = (fsb.st_rdev & 0xffff); if (st_rdev != frdev) vfy |= RPMVERIFY_RDEV; } } #if WITH_CAP if (flags & RPMVERIFY_CAPS) { /* * Empty capability set ("=") is not exactly the same as no * capabilities at all but suffices for now... */ cap_t cap, fcap; cap = cap_from_text(rpmfilesFCaps(fi, ix)); if (!cap) { cap = cap_from_text("="); } fcap = cap_get_file(fn); if (!fcap) { fcap = cap_from_text("="); } if (cap_compare(cap, fcap) != 0) vfy |= RPMVERIFY_CAPS; cap_free(fcap); cap_free(cap); } #endif if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != fsb.st_mtime)) vfy |= RPMVERIFY_MTIME; if ((flags & RPMVERIFY_USER) && (sb.st_uid != fsb.st_uid)) vfy |= RPMVERIFY_USER; if ((flags & RPMVERIFY_GROUP) && (sb.st_gid != fsb.st_gid)) vfy |= RPMVERIFY_GROUP; exit: return vfy; }
/* Check file for to be installed symlinks in their path and correct their fp */ static void fpLookupSubdir(rpmFpHash symlinks, fingerPrintCache fpc, rpmte p, int filenr) { rpmfiles fi = rpmteFiles(p); struct fingerPrint_s current_fp; const char *currentsubdir; size_t lensubDir, bnStart, bnEnd; struct rpmffi_s * recs; int numRecs; int i; fingerPrint *fp = rpmfilesFps(fi) + filenr; int symlinkcount = 0; struct rpmffi_s ffi = { p, filenr}; if (fp->subDirId == 0) { rpmFpHashAddEntry(fpc->fp, fp, ffi); goto exit; } currentsubdir = rpmstrPoolStr(fpc->pool, fp->subDirId); lensubDir = rpmstrPoolStrlen(fpc->pool, fp->subDirId); current_fp = *fp; /* Set baseName to the upper most dir */ bnStart = bnEnd = 1; while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') bnEnd++; /* no subDir for now */ current_fp.subDirId = 0; while (bnEnd < lensubDir) { char found = 0; current_fp.baseNameId = rpmstrPoolIdn(fpc->pool, currentsubdir + bnStart, bnEnd - bnStart, 1); rpmFpHashGetEntry(symlinks, ¤t_fp, &recs, &numRecs, NULL); for (i = 0; i < numRecs; i++) { rpmfiles foundfi = rpmteFiles(recs[i].p); char const *linktarget = rpmfilesFLink(foundfi, recs[i].fileno); char *link; /* Ignore already removed (by eg %pretrans) links */ if (linktarget && rpmteType(recs[i].p) == TR_REMOVED) { char *path = rpmfilesFN(foundfi, recs[i].fileno); struct stat sb; if (lstat(path, &sb) == -1) linktarget = NULL; free(path); } foundfi = rpmfilesFree(foundfi); if (linktarget && *linktarget != '\0') { const char *bn; /* this "directory" is a symlink */ link = NULL; if (*linktarget != '/') { const char *dn, *subDir = NULL; dn = rpmstrPoolStr(fpc->pool, current_fp.entry->dirId); if (current_fp.subDirId) { subDir = rpmstrPoolStr(fpc->pool, current_fp.subDirId); } rstrscat(&link, dn, subDir ? subDir : "", "/", NULL); } rstrscat(&link, linktarget, "/", NULL); if (strlen(currentsubdir + bnEnd)) { rstrscat(&link, currentsubdir + bnEnd, NULL); } bn = rpmstrPoolStr(fpc->pool, fp->baseNameId); doLookup(fpc, link, bn, fp); free(link); symlinkcount++; /* setup current_fp for the new path */ found = 1; current_fp = *fp; if (fp->subDirId == 0) { /* directory exists - no need to look for symlinks */ rpmFpHashAddEntry(fpc->fp, fp, ffi); goto exit; } currentsubdir = rpmstrPoolStr(fpc->pool, fp->subDirId); lensubDir = rpmstrPoolStrlen(fpc->pool, fp->subDirId); /* no subDir for now */ current_fp.subDirId = 0; /* Set baseName to the upper most dir */ bnStart = bnEnd = 1; while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') bnEnd++; break; } } if (symlinkcount > 50) { // found too many symlinks in the path // most likley a symlink cicle // giving up // TODO warning/error break; } if (found) { continue; // restart loop after symlink } /* Set former baseName as subDir */ bnEnd++; current_fp.subDirId = rpmstrPoolIdn(fpc->pool, currentsubdir, bnEnd, 1); /* set baseName to the next lower dir */ bnStart = bnEnd; while (bnEnd < lensubDir && currentsubdir[bnEnd] != '/') bnEnd++; } rpmFpHashAddEntry(fpc->fp, fp, ffi); exit: rpmfilesFree(fi); }