Bug 2406164 - pretrans scripts are no performed in dry-run transaction verification, breaking symlink-to-directory workarounds
Summary: pretrans scripts are no performed in dry-run transaction verification, breaki...
Keywords:
Status: NEW
Alias: None
Product: Fedora
Classification: Fedora
Component: dnf5
Version: 42
Hardware: x86_64
OS: Linux
unspecified
medium
Target Milestone: ---
Assignee: rpm-software-management
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2025-10-24 02:43 UTC by Michael Cronenworth
Modified: 2025-11-05 14:44 UTC (History)
6 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Type: ---
Embargoed:


Attachments (Terms of Use)

Description Michael Cronenworth 2025-10-24 02:43:46 UTC
Wine added symlinks to cover its cross-arch DLL support.

$ ls -l /usr/lib64/wine/
total 68
lrwxrwxrwx. 1 root root    23 Sep 16 19:00 i386-unix -> /usr/lib/wine/i386-unix
lrwxrwxrwx. 1 root root    26 Sep 16 19:00 i386-windows -> /usr/lib/wine/i386-windows
drwxr-xr-x. 2 root root  4096 Oct 16 16:36 x86_64-unix
drwxr-xr-x. 2 root root 61440 Oct 16 16:36 x86_64-windows

$ ls -l /usr/lib/wine/
total 72
drwxr-xr-x. 2 root root  4096 Oct 16 16:36 i386-unix
drwxr-xr-x. 2 root root 65536 Oct 16 16:36 i386-windows
lrwxrwxrwx. 1 root root    27 Sep 16 19:00 x86_64-unix -> /usr/lib64/wine/x86_64-unix
lrwxrwxrwx. 1 root root    30 Sep 16 19:00 x86_64-windows -> /usr/lib64/wine/x86_64-windows

As of Wine 10.12 in Fedora 43 this directory structure with symlinks is no longer needed. Unfortunately trying to add a %pretrans scriptlet to remove the symlinks does not help upgrades from Fedora 42 to 43. Running the "rpm" tool manually on the wine packages successfully runs the %pretrans scriptlets and the "rpm" tool upgrades packages without complaint.

Reproducible: Always

Steps to Reproduce:
1. Install Fedora 42
2. Install wine
3. Upgrade to Fedora 43
4. Build wine from the rawhide branch and use dnf upgrade ./*.rpm
Actual Results:
Running transaction
Transaction failed: Rpm transaction failed.
Warning: skipped OpenPGP checks for 34 packages from repository: @commandline
  - file /usr/lib/wine/i386-windows/ddraw.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/kernelbase.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/shell32.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/urlmon.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/windows.devices.bluetooth.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/wined3d.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/winexinput.sys conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/wininet.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/winmm.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/wintrust.dll conflicts between attempted installs of wine-core-10.16-1.fc42.i686 and wine-core-10.16-1.fc42.x86_64

Expected Results:
pretrans scriptlets run, symlinks deleted.

Additional Information:
The pretrans scriptlets are not built yet and live in the rawhide branch.

https://src.fedoraproject.org/rpms/wine/c/39b67234f34e01832dc332196c7836fcfc1a31db?branch=rawhide

Comment 1 Petr Pisar 2025-10-24 08:44:13 UTC
> Running the "rpm" tool manually on the wine packages successfully runs the %pretrans scriptlets and the "rpm" tool upgrades packages without complaint

That's hard to believe because DNF5 calls RPM to install the packages, including performing RPM scritplets. I worry your testing did not have completely identical environment.

> 1. Install Fedora 42
> 2. Install wine
> 3. Upgrade to Fedora 43

Is that really necessary? Could a direct upgrade from Fedora 42 to your not-yet-existing F44 build be enough?

> 4. Build wine from the rawhide branch and use dnf upgrade ./*.rpm
> The pretrans scriptlets are not built yet and live in the rawhide branch.

Please provide the built wine packages which can reproduce the bug. E.g. a link to scratch build in Koji.

Comment 2 Petr Pisar 2025-10-24 10:09:41 UTC
I tested your pretrans script let in a separate package and "dnf reinstall" successfully removes the symlink:

root@fedora-44:~ # stat /usr/lib/wine/x86_64-unix
  File: /usr/lib/wine/x86_64-unix -> foo
  Size: 3               Blocks: 0          IO Block: 4096   symbolic link
Device: 252,0   Inode: 260076      Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Context: unconfined_u:object_r:lib_t:s0
Access: 2025-10-24 12:01:18.212000000 +0200
Modify: 2025-10-24 12:01:18.212000000 +0200
Change: 2025-10-24 12:01:18.212000000 +0200
 Birth: 2025-10-24 12:01:18.212000000 +0200
root@fedora-44:~ # rpm -q -p --scripts ~test/rpmbuild/RPMS/noarch/test-0-0.fc44.noarch.rpm
pretrans scriptlet (using <lua>):
pathA = "/usr/lib/wine/x86_64-unix"
pathB = "/usr/lib/wine/x86_64-windows"
stA = posix.stat(pathA)
stB = posix.stat(pathB)
if stA and stA.type == "link" then
  os.remove(pathA)
end
if stB and stB.type == "link" then
  os.remove(pathB)
end
root@fedora-44:~ # dnf -q -y reinstall ~test/rpmbuild/RPMS/noarch/test-0-0.fc44.noarch.rpm
Package                         Arch      Version                          Repository              Size
Reinstalling:
 test                           noarch    0-0.fc44                         @commandline         0.0   B
   replacing test               noarch    0-0.fc44                         @commandline         0.0   B

Transaction Summary:
 Reinstalling:       1 package
 Replacing:          1 package

[1/4] Verify package files                                     100% |  90.0   B/s |   1.0   B |  00m00s
[2/4] Prepare transaction                                      100% |  21.0   B/s |   2.0   B |  00m00s
[3/4] Reinstalling test-0:0-0.fc44.noarch                      100% |  10.1 KiB/s | 124.0   B |  00m00s
[4/4] Removing test-0:0-0.fc44.noarch                          100% | 467.0   B/s | 100.0   B |  00m00s
Warning: skipped OpenPGP checks for 1 package from repository: @commandline
root@fedora-44:~ # stat /usr/lib/wine/x86_64-unix
stat: cannot statx '/usr/lib/wine/x86_64-unix': No such file or directory

I recommend you checking /var/log/dnf5.log. If the pretrans script finishes, you should see an entry like this:

2025-10-24T10:01:51+0000 [2345] INFO RPM callback open file "/home/test/rpmbuild/RPMS/noarch/test-0-0.fc44.noarch.rpm"
2025-10-24T10:01:51+0000 [2345] INFO RPM callback start %pretrans scriptlet "test-0:0-0.fc44.noarch"
2025-10-24T10:01:51+0000 [2345] INFO RPM callback stop %pretrans scriptlet "test-0:0-0.fc44.noarch" return code 0
2025-10-24T10:01:51+0000 [2345] INFO RPM callback close file


By the way the two %ifarch branches in the scriptlet in your wine.spec are identical, you might want to write a single "%ifarch %{ix86} x86_64" condition. I also recommends to place the %pretrans directive into the conditional block, so that you don't produce empty %pretrans scripts for other architectures.

Comment 3 Michael Cronenworth 2025-10-28 15:39:05 UTC
> That's hard to believe because DNF5 calls RPM to install the packages, including performing RPM scritplets. I worry your testing did not have completely identical environment.
I could take a video? I've been scratching my head for weeks.

(In reply to Petr Pisar from comment #2)
> I tested your pretrans script let in a separate package and "dnf reinstall"
> successfully removes the symlink:

Sure, but that isn't the same. Did your separate package contain files living in /usr/lib/wine/x86_64-unix ?

> I recommend you checking /var/log/dnf5.log. If the pretrans script finishes, you should see an entry like this:

No '%pretrans' line appears in my dnf5.log. That seems like a problem.

> By the way the two %ifarch branches in the scriptlet in your wine.spec are
> identical, you might want to write a single "%ifarch %{ix86} x86_64"
> condition. I also recommends to place the %pretrans directive into the
> conditional block, so that you don't produce empty %pretrans scripts for
> other architectures.

The committed version was wrong and I've since corrected it and tried even different ways to move files out of the way. Here's a scratch build:
https://koji.fedoraproject.org/koji/taskinfo?taskID=138507391

You will need to start with a version of wine that has symlinks and then dnf upgrade to the scratch build. 10.4-6 was the first version with symlinks, or I can scratch build 10.15 with the "old" wow64 support.

10.4-6.fc43: https://koji.fedoraproject.org/koji/buildinfo?buildID=2744162

I promise you I'm not making this up. I know you and I have worked with RPM files for a very long time. Thanks for your time.

Comment 4 Petr Pisar 2025-10-29 10:01:24 UTC
I enhanced my mock pakage to package the symlink in the old version and it still works for me.

I installed wine-10.4-6.fc43.x86_64 from <https://koji.fedoraproject.org/koji/buildinfo?buildID=2744162> on F44. I checked that the /usr/lib64/wine/i386-unix symlink exists and is owned by wine-core.x86_64. Then I upgraded to your scratchbuild with developmental DNF5 version. RPM library correctly warned that /usr/lib64/wine/i386-unix could not been removed because it does not exist. dnf5.log correctly reported that the %pretrans scriptlet of wine-core-0:10.16-1.fc44.x86_64 was successfully executed (the %postrans too). And at the end, the symlink has gone from the file system.

I will retry it with stable DNF5, and also on F43, but I worry I'll get the same success.

Comment 5 Petr Pisar 2025-10-29 13:00:08 UTC
I tried you original procedure: I took an up-to-date Fedora 42 system. I installed wine-10.15-1.fc42.x86_64. It created the symlinks:

root@fedora-42:~ # ls -l /usr/lib64/wine
total 40
lrwxrwxrwx. 1 root root    23 Sep 17 02:00 i386-unix -> /usr/lib/wine/i386-unix
lrwxrwxrwx. 1 root root    26 Sep 17 02:00 i386-windows -> /usr/lib/wine/i386-windows
drwxr-xr-x. 2 root root  4096 Oct 29 12:44 x86_64-unix
drwxr-xr-x. 2 root root 36864 Oct 29 12:44 x86_64-windows
root@fedora-42:~ # ls -l /usr/lib/wine
total 40
drwxr-xr-x. 2 root root  4096 Oct 29 12:44 i386-unix
drwxr-xr-x. 2 root root 36864 Oct 29 12:45 i386-windows
lrwxrwxrwx. 1 root root    27 Sep 17 02:00 x86_64-unix -> /usr/lib64/wine/x86_64-unix
lrwxrwxrwx. 1 root root    30 Sep 17 02:00 x86_64-windows -> /usr/lib64/wine/x86_64-windows

but no package owns the directories, the targets of the symlinks:

# rpm -qf /usr/lib64/wine/*
wine-core-10.15-1.fc42.x86_64
wine-core-10.15-1.fc42.x86_64
file /usr/lib64/wine/x86_64-unix is not owned by any package
file /usr/lib64/wine/x86_64-windows is not owned by any package
root@fedora-42:~ # rpm -qf /usr/lib/wine/*
file /usr/lib/wine/i386-unix is not owned by any package
file /usr/lib/wine/i386-windows is not owned by any package
wine-core-10.15-1.fc42.i686
wine-core-10.15-1.fc42.i686

Then I commenced "dnf --releasever 43 upgrade". It failed:

Transaction failed: Rpm transaction failed.
  - file /usr/lib64/wine/i386-windows from install of wine-core-10.15-1.fc43.x86_64 conflicts with file from package wine-core-10.15-1.fc42.x86_64
  - file /usr/lib/wine/i386-windows/ddraw.dll conflicts between attempted installs of wine-core-10.15-1.fc43.i686 and wine-core-10.15-1.fc43.x86_64
  - file /usr/lib/wine/i386-windows/urlmon.dll conflicts between attempted installs of wine-core-10.15-1.fc43.i686 and wine-core-10.15-1.fc43.x86_64
  - file /usr/lib/wine/i386-windows/windows.devices.bluetooth.dll conflicts between attempted installs of wine-core-10.15-1.fc43.i686 and wine-core-10.15-1.fc43.x86_64
  - file /usr/lib/wine/i386-windows/wined3d.dll conflicts between attempted installs of wine-core-10.15-1.fc43.i686 and wine-core-10.15-1.fc43.x86_64
  - file /usr/lib/wine/i386-windows/winmm.dll conflicts between attempted installs of wine-core-10.15-1.fc43.i686 and wine-core-10.15-1.fc43.x86_64

So the problem has already existed with stable wine-core-10.15-1.fc43.x86_64.

Then I tried upgrading in that F42 system to your new wine-0:10.16-1.fc44 scratch build and it failed similarly:

Warning: skipped OpenPGP checks for 35 packages from repository: wine
  - file /usr/lib/wine/i386-windows/ddraw.dll conflicts between attempted installs of wine-core-10.16-1.fc44.i686 and wine-core-10.16-1.fc44.x86_64
  - file /usr/lib/wine/i386-windows/urlmon.dll conflicts between attempted installs of wine-core-10.16-1.fc44.i686 and wine-core-10.16-1.fc44.x86_64
  - file /usr/lib/wine/i386-windows/windows.devices.bluetooth.dll conflicts between attempted installs of wine-core-10.16-1.fc44.i686 and wine-core-10.16-1.fc44.x86_64
  - file /usr/lib/wine/i386-windows/wined3d.dll conflicts between attempted installs of wine-core-10.16-1.fc44.i686 and wine-core-10.16-1.fc44.x86_64
  - file /usr/lib/wine/i386-windows/winmm.dll conflicts between attempted installs of wine-core-10.16-1.fc44.i686 and wine-core-10.16-1.fc44.x86_64

Reading the dnf5.log indeed does not show any %pretrans script. The reason is that what fails is not the transaction, but a dry-run transaction verification which is performed before the transaction by DNF5. It exists to catch incompatibilities between DNF5 and RPM dependency solver and to catch file conflicts which DNF5 has no access to. The drawback is that %pretrans scripts are not executed in dry-run transaction because they could have side effects on the file system. But here in your case the script is abused to actually have a side effect.

The reason why this happens with wine and not with my mocked packages is that in your case you at the same time swap files between x86_64 and i686 packages and their path prefix conflicts with the symlinks (before being removed by the scriptlet). If I rename the symlinks, then DNF5 can upgrade.

So the question is whether DNF5 can and should perform the potentially destructive %pretrans scripts on the dry-run transaction verification. I'm for doing it. But I need to involve more experienced DNF5 developers as I worry that not performing the script is a property of librpm and not DNF5.

Affected packages:

dnf5-5.2.16.0-1.fc42.x86_64
rpm-libs-4.20.1-1.fc42.x86_64

Comment 6 Michael Cronenworth 2025-10-29 13:03:06 UTC
(In reply to Petr Pisar from comment #5)
> The reason why this happens with wine and not with my mocked packages is
> that in your case you at the same time swap files between x86_64 and i686
> packages and their path prefix conflicts with the symlinks (before being
> removed by the scriptlet). If I rename the symlinks, then DNF5 can upgrade.

I suspected a dry-run check. Thank you for your time in confirming.

> So the question is whether DNF5 can and should perform the potentially
> destructive %pretrans scripts on the dry-run transaction verification. I'm
> for doing it. But I need to involve more experienced DNF5 developers as I
> worry that not performing the script is a property of librpm and not DNF5.

My feelings won't be hurt if this is too invasive. The symlinks were a one-off solution for wine and should (tm) never be used again.

Comment 7 Petr Pisar 2025-10-29 13:14:56 UTC
As a workaround, could wine get rid of the symlinks within a new F42 update? Then the symlink removal and the file swap between the packages would be split into multiple transactions and this deficiency of DNF5 would not manifest.

Comment 8 Michael Cronenworth 2025-10-29 15:57:58 UTC
(In reply to Petr Pisar from comment #7)
> As a workaround, could wine get rid of the symlinks within a new F42 update?
> Then the symlink removal and the file swap between the packages would be
> split into multiple transactions and this deficiency of DNF5 would not
> manifest.

I was seeing this problem on my F42 system. It has since upgraded to F43, but I was building wine without the symlinks to test upgrading on F42 (F42 Wine 10.15 w/links to F42 Wine 10.16 w/o links).

For context: The symlinks were created for the "1.0" version of WoW64 in wine. A few versions back Wine supported a "2.0" WoW64 feature by no longer needing 32-bit binaries to run 32-bit executables. One of the wine co-maintainers blocked this 2.0 feature off for F43 and higher only.

Comment 9 Petr Pisar 2025-11-04 14:00:14 UTC
Panu, DNF5 does in Transaction::Impl::_run() in libdnf5/base/transaction.cpp effectively this:

rpmtsSetFlags(ts, RPMTRANS_FLAG_TEST);
rpmtsRun(ts, nullptr, ...)

for testing a transaction.

Is that a reason why RPM does not execute the wine pretrans script for the test transaction?

I found in RPM's handleInstInstalledFile() this:

    /*
     * 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;
        }
    }

In this bug report the pretrans script is defined in the same package, wine-core, which triggers the file conflict.

Is there anything DNF5 could do differently to let RPM to perform the pretrans script during the test transaction?

Otherwise, DNF5 could provide an option to skip testing the transaction. That of course would decrease resilience of the upgrade process and should not be default.

Comment 10 Panu Matilainen 2025-11-04 14:28:43 UTC
Well, it's not a test-transaction anymore if scripts are executed.

The whole test-transaction as inherited from yum is a bit of an anti-pattern. It basically tests for things that would prevent the transaction from taking place anyhow. The only thing it's good for is avoiding changes to the file system from %pretrans if there are dir<->symlink file conflicts. But doing that test-transaction prevents those %pretrans hacks from resolving them conflicts. And here we are.

The quoted hack in rpm is an attempt to let it work but there are cases where it doesn't, and it's not really fixable.

Comment 11 Petr Pisar 2025-11-04 15:46:02 UTC
When does RPM check for file conflicts in case of non-test transaction? Is it before installing all packages for all of them, or is it per-package activity just before installing that particular package?

DNF5 uses the test transaction to discover file conflicts soon. In the past people complained that RPM installed many packages and then a file conflict was reported, leaving the transaction unfinished (with installed both old and new packages), or spending long time rolling back the installed packages to undo the transaction.

I ponder whether it still makes sense to do the test transaction to prevent that, or whether its safe now not to do it and remove that code from DNF5.

Comment 12 Panu Matilainen 2025-11-05 07:30:22 UTC
Rpm checks for file-conflicts before the transaction, of course. That's one of the main things that happen during the "Preparing..." stage, regardless of whether it's a test-transaction or not. Old rpm versions (rpm < 4.11) did not detect those dir<->symlink (and some similar) issues at that stage though, and those are what could cause a mid-transaction failure. 

The gotcha is that %pretrans runs before those file conflicts checks. It has to, in order to be able to fix those dir<->symlink issues. And if you run a test-transaction that by definition does not execute anything, those scripts can't do what they intend to do, and thus the conflicts remain, and prevent the transaction. It's an oxymoron that only exists in the yum family line of depsolvers. Rpm itself does not do a separate test-transaction, and neither do any of the other depsolvers.

Of course the real flaw is rpm having such a horribly broken mechanism in the first place. AIUI nothing like that exists in dpkg although it shares the same issue of symlink<->directory replacement, and they're better for it.

Comment 13 Petr Pisar 2025-11-05 10:28:48 UTC
(In reply to Panu Matilainen from comment #12)
> Rpm checks for file-conflicts before the transaction, of course. That's one
> of the main things that happen during the "Preparing..." stage, regardless
> of whether it's a test-transaction or not. Old rpm versions (rpm < 4.11) did
> not detect those dir<->symlink (and some similar) issues at that stage
> though, and those are what could cause a mid-transaction failure. 
> 

That's what I needed to know. Thanks.

For interactive invocations it is clear that DNF should not test the transaction.

Question is what to do with systemd off-line updates: When a user schedules an off-line update, he believes that everything is ready for the update and nothing can fail. The user commence a reboot, the update quickly fails on a file conflict, systemd reboots the machine again, and then the user needs to check whether the update passed or failed and why. It is more or less a problem of a good user interface to dig the error from journald. But still then the user can ask: Can DNF do more to catch the file conflict before rebooting the machine?

And we have again the problem of testing the transaction on the stage.

One could say that a transaction can fail for whatever reason, not only because a file conflict. That's true. But in distributions with multilib packages, IMHO, the most common cause is file conflicts.

One resolution could be keep testing the transaction and allow the user to override the detected file conflicts. Like this one:

Transaction test failed: Rpm transaction failed.
  - file /usr/lib64/wine/i386-windows from install of wine-core-10.15-1.fc43.x86_64 conflicts with file from package wine-core-10.15-1.fc42.x86_64
The requested transaction is expected to fail. Proceed anyway [N/y]?

Comment 14 Panu Matilainen 2025-11-05 14:44:41 UTC
No, don't offer the option to continue, that's nothing but a footgun that will come back to haunt us.

The proper resolution is to uninstall the package. It's typically reinstallable afterwards.


Note You need to log in before you can comment on or make changes to this bug.