int rpmGenDigest(Header sigh, const char * file, rpmTagVal sigTag) { struct stat st; uint8_t * pkt = NULL; size_t pktlen; int ret = -1; /* assume failure. */ switch (sigTag) { case RPMSIGTAG_SIZE: { rpm_off_t size; if (stat(file, &st) != 0) break; size = st.st_size; if (!sighdrPut(sigh, sigTag, RPM_INT32_TYPE, &size, 1)) break; ret = 0; } break; case RPMSIGTAG_LONGSIZE: { rpm_loff_t size; if (stat(file, &st) != 0) break; size = st.st_size; if (!sighdrPut(sigh, sigTag, RPM_INT64_TYPE, &size, 1)) break; ret = 0; } break; case RPMSIGTAG_MD5: pktlen = 16; pkt = xcalloc(pktlen, sizeof(*pkt)); if (rpmDoDigest(PGPHASHALGO_MD5, file, 0, pkt, NULL) || !sighdrPut(sigh, sigTag, RPM_BIN_TYPE, pkt, pktlen)) break; ret = 0; break; case RPMSIGTAG_SHA1: ret = makeHDRDigest(sigh, file, sigTag); break; default: break; } free(pkt); return ret; }
int rpmfiConfigConflict(const rpmfi fi) { const char * fn = rpmfiFN(fi); rpmfileAttrs flags = rpmfiFFlags(fi); char buffer[1024]; rpmFileTypes newWhat, diskWhat; struct stat sb; if (!(flags & RPMFILE_CONFIG) || lstat(fn, &sb)) { return 0; } diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); newWhat = rpmfiWhatis(rpmfiFMode(fi)); if (newWhat != LINK && newWhat != REG) return 1; if (diskWhat != newWhat) return 1; memset(buffer, 0, sizeof(buffer)); if (newWhat == REG) { int algo; size_t diglen; const unsigned char *ndigest = rpmfiFDigest(fi, &algo, &diglen); if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL)) return 0; /* assume file has been removed */ if (ndigest && !memcmp(ndigest, buffer, diglen)) return 0; /* unmodified config file */ } else /* newWhat == LINK */ { const char * nFLink; ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1); if (link_len == -1) return 0; /* assume file has been removed */ buffer[link_len] = '\0'; nFLink = rpmfiFLink(fi); if (nFLink && rstreq(nFLink, buffer)) return 0; /* unmodified config file */ } return 1; }
rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing) { const char * fn = rpmfiFN(nfi); rpmfileAttrs newFlags = rpmfiFFlags(nfi); char buffer[1024]; rpmFileTypes dbWhat, newWhat, diskWhat; struct stat sb; int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE; if (lstat(fn, &sb)) { /* * The file doesn't exist on the disk. Create it unless the new * package has marked it as missingok, or allfiles is requested. */ if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) { rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n", fn); return FA_SKIP; } else { return FA_CREATE; } } diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); dbWhat = rpmfiWhatis(rpmfiFMode(ofi)); newWhat = rpmfiWhatis(rpmfiFMode(nfi)); /* * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore * them in older packages as well. */ if (newWhat == XDIR) return FA_CREATE; if (diskWhat != newWhat && dbWhat != REG && dbWhat != LINK) return save; else if (newWhat != dbWhat && diskWhat != dbWhat) return save; else if (dbWhat != newWhat) return FA_CREATE; else if (dbWhat != LINK && dbWhat != REG) return FA_CREATE; /* * This order matters - we'd prefer to CREATE the file if at all * possible in case something else (like the timestamp) has changed. */ memset(buffer, 0, sizeof(buffer)); if (dbWhat == REG) { pgpHashAlgo oalgo, nalgo; size_t odiglen, ndiglen; const unsigned char * odigest, * ndigest; odigest = rpmfiFDigest(ofi, &oalgo, &odiglen); if (diskWhat == REG) { if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL)) return FA_CREATE; /* assume file has been removed */ if (odigest && !memcmp(odigest, buffer, odiglen)) return FA_CREATE; /* unmodified config file, replace. */ } ndigest = rpmfiFDigest(nfi, &nalgo, &ndiglen); /* Can't compare different hash types, backup to avoid data loss */ if (oalgo != nalgo || odiglen != ndiglen) return save; if (odigest && ndigest && !memcmp(odigest, ndigest, odiglen)) return FA_SKIP; /* identical file, don't bother. */ } else /* dbWhat == LINK */ { const char * oFLink, * nFLink; oFLink = rpmfiFLink(ofi); if (diskWhat == LINK) { if (readlink(fn, buffer, sizeof(buffer) - 1) == -1) return FA_CREATE; /* assume file has been removed */ if (oFLink && rstreq(oFLink, buffer)) return FA_CREATE; /* unmodified config file, replace. */ } nFLink = rpmfiFLink(nfi); if (oFLink && nFLink && rstreq(oFLink, nFLink)) return FA_SKIP; /* identical file, don't bother. */ } /* * The config file on the disk has been modified, but * the ones in the two packages are different. It would * be nice if RPM was smart enough to at least try and * merge the difference ala CVS, but... */ return save; }
/* XXX only ts->{probs,di} modified */ static void handleOverlappedFiles(const rpmts ts, const rpmte p, rpmfi fi) { rpm_loff_t fixupSize = 0; rpmps ps; const char * fn; int i, j; rpm_color_t tscolor = rpmtsColor(ts); rpm_color_t prefcolor = rpmtsPrefColor(ts); rpmfs fs = rpmteGetFileStates(p); rpmfs otherFs; ps = rpmtsProblems(ts); fi = rpmfiInit(fi, 0); if (fi != NULL) while ((i = rpmfiNext(fi)) >= 0) { rpm_color_t oFColor, FColor; struct fingerPrint_s * fiFps; int otherPkgNum, otherFileNum; rpmfi otherFi; rpmte otherTe; rpmfileAttrs FFlags; rpm_mode_t FMode; struct rpmffi_s * recs; int numRecs; if (XFA_SKIPPING(rpmfsGetAction(fs, i))) continue; fn = rpmfiFN(fi); fiFps = rpmfiFpsIndex(fi, i); FFlags = rpmfiFFlags(fi); FMode = rpmfiFMode(fi); FColor = rpmfiFColor(fi); FColor &= tscolor; 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. */ (void) rpmFpHashGetEntry(ts->ht, fiFps, &recs, &numRecs, NULL); /* * 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 occurence of an overlapped * file in the transaction set. * */ /* Locate this overlapped file in the set of added/removed packages. */ for (j = 0; j < numRecs && recs[j].p != p; 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; otherFi = rpmteFI(otherTe); 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; (void) rpmfiSetFX(otherFi, otherFileNum); /* XXX Happens iff fingerprint for incomplete package install. */ if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN); break; } oFColor = rpmfiFColor(otherFi); oFColor &= tscolor; switch (rpmteType(p)) { case TR_ADDED: { int reportConflicts = !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES); int done = 0; if (otherPkgNum < 0) { /* XXX is this test still necessary? */ rpmFileAction action; if (rpmfsGetAction(fs, i) != FA_UNKNOWN) break; if (rpmfiConfigConflict(fi)) { /* 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 (rpmfiCompare(otherFi, fi)) { int rConflicts; rConflicts = reportConflicts; /* Resolve file conflicts to prefer Elf64 (if not forced) ... */ if (tscolor != 0) { if (FColor & prefcolor) { /* ... last file of preferred colour is installed ... */ if (!XFA_SKIPPING(rpmfsGetAction(fs, i))) { /* XXX static helpers are order dependent. Ick. */ if (strcmp(fn, "/usr/sbin/libgcc_post_upgrade") && strcmp(fn, "/usr/sbin/glibc_post_upgrade")) rpmfsSetAction(otherFs, otherFileNum, FA_SKIPCOLOR); } rpmfsSetAction(fs, i, FA_CREATE); rConflicts = 0; } else if (oFColor & prefcolor) { /* ... first file of preferred colour is installed ... */ if (XFA_SKIPPING(rpmfsGetAction(fs, i))) rpmfsSetAction(otherFs, otherFileNum, FA_CREATE); rpmfsSetAction(fs, i, FA_SKIPCOLOR); rConflicts = 0; } done = 1; } if (rConflicts) { rpmpsAppend(ps, RPMPROB_NEW_FILE_CONFLICT, rpmteNEVRA(p), rpmteKey(p), fn, NULL, rpmteNEVRA(otherTe), 0); } } /* Try to get the disk accounting correct even if a conflict. */ fixupSize = rpmfiFSize(otherFi); if (rpmfiConfigConflict(fi)) { /* Here is an overlapped pre-existing config file. */ rpmFileAction action; action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP; rpmfsSetAction(fs, i, action); } else { if (!done) 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 (rpmfiFState(fi) != RPMFILE_STATE_NORMAL) break; if (!(S_ISREG(FMode) && (FFlags & RPMFILE_CONFIG))) { rpmfsSetAction(fs, i, FA_ERASE); break; } /* Here is a pre-existing modified config file that needs saving. */ { pgpHashAlgo algo = 0; size_t diglen = 0; const unsigned char *digest; if ((digest = rpmfiFDigest(fi, &algo, &diglen))) { unsigned char fdigest[diglen]; if (!rpmDoDigest(algo, fn, 0, fdigest, NULL) && memcmp(digest, fdigest, diglen)) { rpmfsSetAction(fs, i, FA_BACKUP); break; } } } rpmfsSetAction(fs, i, FA_ERASE); break; } /* Update disk space info for a file. */ rpmtsUpdateDSI(ts, fiFps->entry->dev, rpmfiFSize(fi), rpmfiFReplacedSize(fi), fixupSize, rpmfsGetAction(fs, i)); } ps = rpmpsFree(ps); }
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; }
int rpmVerifyFile(const rpmts ts, const rpmfi fi, rpmVerifyAttrs * res, rpmVerifyAttrs omitMask) { rpm_mode_t fmode = rpmfiFMode(fi); rpmfileAttrs fileAttrs = rpmfiFFlags(fi); rpmVerifyAttrs flags = rpmfiVFlags(fi); const char * fn = rpmfiFN(fi); struct stat sb; int rc; *res = RPMVERIFY_NONE; /* * Check to see if the file was installed - if not pretend all is OK. */ switch (rpmfiFState(fi)) { case RPMFILE_STATE_NETSHARED: case RPMFILE_STATE_REPLACED: case RPMFILE_STATE_NOTINSTALLED: case RPMFILE_STATE_WRONGCOLOR: return 0; break; case RPMFILE_STATE_NORMAL: break; } if (fn == NULL || lstat(fn, &sb) != 0) { *res |= RPMVERIFY_LSTATFAIL; return 1; } /* * Not all attributes of non-regular files can be verified. */ if (S_ISDIR(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISLNK(sb.st_mode)) { flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_MODE | RPMVERIFY_CAPS); #if CHOWN_FOLLOWS_SYMLINK flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP); #endif } else if (S_ISFIFO(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISCHR(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else if (S_ISBLK(sb.st_mode)) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO | RPMVERIFY_CAPS); else flags &= ~(RPMVERIFY_LINKTO); /* * Content checks of %ghost files are meaningless. */ if (fileAttrs & RPMFILE_GHOST) flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | RPMVERIFY_LINKTO); /* * Don't verify any features in omitMask. */ flags &= ~(omitMask | RPMVERIFY_FAILURES); if (flags & RPMVERIFY_MD5) { const unsigned char *digest; pgpHashAlgo algo; size_t diglen; /* XXX If --nomd5, then prelinked library sizes are not corrected. */ if ((digest = rpmfiFDigest(fi, &algo, &diglen))) { unsigned char fdigest[diglen]; rpm_loff_t fsize; rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize); sb.st_size = fsize; if (rc) { *res |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5); } else if (memcmp(fdigest, digest, diglen)) { *res |= RPMVERIFY_MD5; } } else { *res |= RPMVERIFY_MD5; } } if (flags & RPMVERIFY_LINKTO) { char linkto[1024+1]; int size = 0; if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1) *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO); else { const char * flink = rpmfiFLink(fi); linkto[size] = '\0'; if (flink == NULL || strcmp(linkto, flink)) *res |= RPMVERIFY_LINKTO; } } if (flags & RPMVERIFY_FILESIZE) { if (sb.st_size != rpmfiFSize(fi)) *res |= RPMVERIFY_FILESIZE; } if (flags & RPMVERIFY_MODE) { rpm_mode_t metamode = fmode; rpm_mode_t filemode; /* * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t) * need the (rpm_mode_t) cast here. */ filemode = (rpm_mode_t)sb.st_mode; /* * Comparing the type of %ghost files is meaningless, but perms are OK. */ if (fileAttrs & RPMFILE_GHOST) { metamode &= ~0xf000; filemode &= ~0xf000; } if (metamode != filemode) *res |= 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) { *res |= RPMVERIFY_MODE; } acl_free(facl); } #endif } if (flags & RPMVERIFY_RDEV) { if (S_ISCHR(fmode) != S_ISCHR(sb.st_mode) || S_ISBLK(fmode) != S_ISBLK(sb.st_mode)) { *res |= RPMVERIFY_RDEV; } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) { rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff); rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff); if (st_rdev != frdev) *res |= 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(rpmfiFCaps(fi)); if (!cap) { cap = cap_from_text("="); } fcap = cap_get_file(fn); if (!fcap) { fcap = cap_from_text("="); } if (cap_compare(cap, fcap) != 0) *res |= RPMVERIFY_CAPS; cap_free(fcap); cap_free(cap); } #endif if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) { /* Filter out timestamp differences of shared files */ rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0); if (rpmdbGetIteratorCount(mi) < 2) *res |= RPMVERIFY_MTIME; rpmdbFreeIterator(mi); } if (flags & RPMVERIFY_USER) { const char * name = uidToUname(sb.st_uid); const char * fuser = rpmfiFUser(fi); if (name == NULL || fuser == NULL || strcmp(name, fuser)) *res |= RPMVERIFY_USER; } if (flags & RPMVERIFY_GROUP) { const char * name = gidToGname(sb.st_gid); const char * fgroup = rpmfiFGroup(fi); if (name == NULL || fgroup == NULL || strcmp(name, fgroup)) *res |= RPMVERIFY_GROUP; } return 0; }
int rpmfiConfigConflictIndex(rpmfi fi, int ix) { char * fn = NULL; rpmfileAttrs flags = rpmfiFFlagsIndex(fi, ix); char buffer[1024]; rpmFileTypes newWhat, diskWhat; struct stat sb; int rc = 0; /* Non-configs are not config conflicts. */ if (!(flags & RPMFILE_CONFIG)) return 0; /* Only links and regular files can be %config, this is kinda moot */ /* XXX: Why are we returning 1 here? */ newWhat = rpmfiWhatis(rpmfiFModeIndex(fi, ix)); if (newWhat != LINK && newWhat != REG) return 1; /* If it's not on disk, there's nothing to be saved */ fn = rpmfiFNIndex(fi, ix); if (lstat(fn, &sb)) goto exit; /* * Preserve legacy behavior: an existing %ghost %config is considered * "modified" but unlike regular %config, its never removed and * never backed up. Whether this actually makes sense is a whole * another question, but this is very long-standing behavior that * people might be depending on. The resulting FA_ALTNAME etc action * is special-cased in FSM to avoid actually creating backups on ghosts. */ if (flags & RPMFILE_GHOST) { rc = 1; goto exit; } /* Files of different types obviously are not identical */ diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); if (diskWhat != newWhat) { rc = 1; goto exit; } /* Files of different sizes obviously are not identical */ if (rpmfiFSizeIndex(fi, ix) != sb.st_size) { rc = 1; goto exit; } memset(buffer, 0, sizeof(buffer)); if (newWhat == REG) { int algo; size_t diglen; const unsigned char *ndigest = rpmfiFDigestIndex(fi,ix, &algo, &diglen); if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL)) goto exit; /* assume file has been removed */ if (ndigest && memcmp(ndigest, buffer, diglen) == 0) goto exit; /* unmodified config file */ } else /* newWhat == LINK */ { const char * nFLink; ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1); if (link_len == -1) goto exit; /* assume file has been removed */ buffer[link_len] = '\0'; nFLink = rpmfiFLinkIndex(fi, ix); if (nFLink && rstreq(nFLink, buffer)) goto exit; /* unmodified config file */ } rc = 1; exit: free(fn); return rc; }
rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix, int skipMissing) { char * fn = rpmfiFNIndex(nfi, nix); rpmfileAttrs newFlags = rpmfiFFlagsIndex(nfi, nix); char buffer[1024]; rpmFileTypes dbWhat, newWhat, diskWhat; struct stat sb; int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE; int action = FA_CREATE; /* assume we can create */ /* If the new file is a ghost, leave whatever might be on disk alone. */ if (newFlags & RPMFILE_GHOST) { action = FA_SKIP; goto exit; } if (lstat(fn, &sb)) { /* * The file doesn't exist on the disk. Create it unless the new * package has marked it as missingok, or allfiles is requested. */ if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) { rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n", fn); action = FA_SKIP; goto exit; } else { goto exit; } } diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode); dbWhat = rpmfiWhatis(rpmfiFModeIndex(ofi, oix)); newWhat = rpmfiWhatis(rpmfiFModeIndex(nfi, nix)); /* * This order matters - we'd prefer to CREATE the file if at all * possible in case something else (like the timestamp) has changed. * Only regular files and symlinks might need a backup, everything * else falls through here with FA_CREATE. */ memset(buffer, 0, sizeof(buffer)); if (dbWhat == REG) { int oalgo, nalgo; size_t odiglen, ndiglen; const unsigned char * odigest, * ndigest; /* See if the file on disk is identical to the one in old pkg */ odigest = rpmfiFDigestIndex(ofi, oix, &oalgo, &odiglen); if (diskWhat == REG) { if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL)) goto exit; /* assume file has been removed */ if (odigest && memcmp(odigest, buffer, odiglen) == 0) goto exit; /* unmodified config file, replace. */ } /* See if the file on disk is identical to the one in new pkg */ ndigest = rpmfiFDigestIndex(nfi, nix, &nalgo, &ndiglen); if (diskWhat == REG && newWhat == REG) { /* hash algo changed in new, recalculate digest */ if (oalgo != nalgo) if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL)) goto exit; /* assume file has been removed */ if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0) goto exit; /* file identical in new, replace. */ } /* If file can be determined identical in old and new pkg, let it be */ if (newWhat == REG && oalgo == nalgo && odiglen == ndiglen) { if (odigest && ndigest && memcmp(odigest, ndigest, odiglen) == 0) { action = FA_SKIP; /* identical file, dont bother */ goto exit; } } /* ...but otherwise a backup will be needed */ action = save; } else if (dbWhat == LINK) { const char * oFLink, * nFLink; /* See if the link on disk is identical to the one in old pkg */ oFLink = rpmfiFLinkIndex(ofi, oix); if (diskWhat == LINK) { ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1); if (link_len == -1) goto exit; /* assume file has been removed */ buffer[link_len] = '\0'; if (oFLink && rstreq(oFLink, buffer)) goto exit; /* unmodified config file, replace. */ } /* See if the link on disk is identical to the one in new pkg */ nFLink = rpmfiFLinkIndex(nfi, nix); if (diskWhat == LINK && newWhat == LINK) { if (nFLink && rstreq(nFLink, buffer)) goto exit; /* unmodified config file, replace. */ } /* If link is identical in old and new pkg, let it be */ if (newWhat == LINK && oFLink && nFLink && rstreq(oFLink, nFLink)) { action = FA_SKIP; /* identical file, don't bother. */ goto exit; } /* ...but otherwise a backup will be needed */ action = save; } exit: free(fn); return action; }