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;
}
Beispiel #2
0
Datei: fsm.c Projekt: nforro/rpm
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;
}
Beispiel #3
0
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));
}
Beispiel #6
0
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;
}
Beispiel #7
0
/* 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, &current_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);
}