Page 1 of 1

wimlib incompatibility with dism when some reparse points are present?

Posted: Thu Feb 20, 2025 8:09 pm
by nomeara
Summary:

I think wimlib has a bug when capturing a symlink with a path starting with \\?\. These seem like valid paths that don't involve the edge cases listed in the wimlib documentation for limitations when capturing from a unix block device (dism doesn't appear to dereference or fix them up either). The wim files created by wimlib-imagex capture when these symlinks are present are unreadable by dism.

Background:

I have used wimlib in the past to capture sysprepped windows systems (win10 and win11) and then later use Dism in WinPE to apply those WIM images to new systems, but I noticed that if I have installed Office in the source system before capturing the WIM with wimlib, dism fails to apply the WIM with the error:

Code: Select all

Error: 4392

The data present in the reparse point buffer is invalid.
(Full DISM log attached)

I notice that is dying on this specific file:
C:\Program Files\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft Office\Office16\C2R32.dll

That file looks like this when running dir /a against it in windows:

Code: Select all

 Volume in drive C has no label.
 Volume Serial Number is 68AB-E012

 Directory of C:\Program Files\Microsoft Office\root\vfs\ProgramFilesX86\Microsoft Office\Office16

02/19/2025  02:02 PM    <SYMLINK>      C2R32.dll [\\?\C:\Program Files\Common Files\Microsoft Shared\ClickToRun\C2R32.dll]
               1 File(s)              0 bytes
               0 Dir(s)  117,932,900,352 bytes free
(There are several symlinks to this same file in various places within the Office install directory).

I decided to try to create a minimal reproduction:
New NTFS volume
Create X:\Temp
Create X:\Temp\test.txt with a little bit of text data
Create symlink X:\Templink -> X:\Temp (mklink X:\templink X:\temp)
Create symlink X:\temp\testlink.txt -> test.txt (pushd X:\temp; mklink testlink.txt test.txt)
Create symlink X:\temp\testlink2.txt -> X:\temp\test.txt (pushd X:\temp; mklink testlink2.txt X:\temp\test.txt)
Create symlink X:\temp\testlink3.txt -> \\?\X:\temp\test.txt (pushd X:\temp; mklink testlink3.txt \\?\X:\temp\test.txt)
I also tested that all these links 'work' (reading them reads the contents of the target file)
(no other files on this volume at all, other than system volume information)

And then booted to a linux environment, and captured these using "wimlib-imagex capture /dev/sda4 /mnt/share/file.wim". There are no warnings during the capture, but I get the same error 4392 when trying to apply this minimal WIM to a new empty NTFS partition with dism /apply-image.

I have read through the documentation on wimlib's limitations around reparse points, and found another thread regarding how reparse points are further limited when capturing from a block device using the ntfs-3g library, but it doesn't sound like any of those limitations apply to this case.

From the microsoft docs, it appears that the point of the \\?\ prefix is to disable string expansion and allow use of otherwise restricted characters, or to bypass path length restrictions, so I don't know why using it would be incompatible with the way wimlib stores reparse points/symlinks when doing a block device capture.

Also since I can replicate it with a very simple mklink command that only differs from a working one by the use of the \\?\ prefix, I don't think this is related to dedupe or other weirdness inherent to the Office 365 Click-to-Run app itself.

So, is this a wimlib bug, or an inherent limitation of ntfs-3g, or something else? I could provide a WIM file created from this minimal partition created with wimlib and with dism, if it would help. All my testing was done with wimlib 1.14.4 compiled locally on ubuntu 24.04.

I did a simple test of applying this minimal WIM to an empty NTFS partition (via block device) with wimlib-imagex apply, and while it reported success, the files do not appear to have been restored at all.

And yes, I have tested capturing these same systems with dism /capture-image, and they restore correctly with dism /apply-image. However, one thing I noticed when doing it with dism:
The link from "testlink2.txt -> C:\temp\test.txt" updates its drive letter to match the drive letter that the WIM was restored to*. However, the link from "testlink3.txt -> \\?\C:\temp\test.txt" kept the original drive letter from when the link was created.

*Caveats: When the link was resolveable at the time of the dism capture, dism apply fixed up the path. So in my case, it was targeting E:\temp\test.txt. If the volume was mounted to E when captured with dism, a restore to C would change this path to c:\temp\test.txt, UNLESS I passed /norpfix to the dism apply command. If the volume was mounted to another letter when captured with dism, this fixup was not done during the dism apply, and a restore to C would leave this link broken and pointing to E:\temp\test.txt.
I think retaining the exact path is fine behavior, and I understand that that would be the only option when using wimlib-imagex to capture from a block device.

wimlib-imagex output:

Code: Select all

Capturing WIM image from NTFS filesystem on "/dev/sda4"
Scanning "/dev/sda4"

Excluding "/System Volume Information" from capture

4 bytes scanned (1 files, 0 directories)    
272 bytes scanned (5 files, 2 directories)    
Using LZX compression with 1 thread

Archiving file data: 0 bytes of 272 bytes (0%) done
Archiving file data: 92 bytes of 272 bytes (33%) done
Archiving file data: 176 bytes of 272 bytes (64%) done
Archiving file data: 220 bytes of 272 bytes (80%) done
Archiving file data: 224 bytes of 272 bytes (82%) done
Archiving file data: 272 bytes of 272 bytes (100%) done

dism.log:

Code: Select all

2025-02-20 19:18:26, Info                  DISM   PID=1260 TID=876 Scratch directory set to 'X:\windows\SystemTemp\'. - CDISMManager::put_ScratchDir
2025-02-20 19:18:26, Info                  DISM   PID=1260 TID=876 DismCore.dll version: 10.0.26100.1150 - CDISMManager::FinalConstruct
2025-02-20 19:18:26, Info                  DISM   Initialized Panther logging at X:\windows\Logs\DISM\dism.log
2025-02-20 19:18:26, Info                  DISM   PID=1260 TID=876 Successfully loaded the ImageSession at "X:\sources" - CDISMManager::LoadLocalImageSession
2025-02-20 19:18:26, Info                  DISM   Initialized Panther logging at X:\windows\Logs\DISM\dism.log
2025-02-20 19:18:26, Info                  DISM   DISM Provider Store: PID=1260 TID=876 Found and Initialized the DISM Logger. - CDISMProviderStore::Internal_InitializeLogger
2025-02-20 19:18:26, Info                  DISM   Initialized Panther logging at X:\windows\Logs\DISM\dism.log
2025-02-20 19:18:26, Info                  DISM   DISM Manager: PID=1260 TID=876 Successfully created the local image session and provider store. - CDISMManager::CreateLocalImageSession
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: 
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: <----- Starting Dism.exe session ----->
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: 
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: Host machine information: OS Version=10.0.26100, Running architecture=amd64, Number of processors=2
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: Instance information: Parent process=X:\Windows\System32\cmd.exe, Parent process PID=884
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: Dism.exe version: 10.0.26100.1150
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: Executing command line: dism  /apply-image /index:1 /applydir:C:\ /imagefile:Z:\MinimalCaptureWithSymlink.wim
[1260.436] [0x80071128] RestoreReparsePoint:(1835): The data present in the reparse point buffer is invalid.
[1260.436] [0xc144012e] 
2025-02-20 19:18:26, Error                 DISM   DISM WIM Provider: PID=1260 [RestoreReparsePoint:(1836) -> ioctl: setting reparse point tag failed] C:\temp\testlink3.txt (HRESULT=0x80071128) - CWimManager::WimProviderMsgLogCallback
[1260.436] [0x80071128] RestoreFileData:(3186): The data present in the reparse point buffer is invalid.
[1260.436] [0x80071128] RestoreRefNode:(1870): The data present in the reparse point buffer is invalid.
[1260.436] [0xc144012e] 
2025-02-20 19:18:26, Error                 DISM   DISM WIM Provider: PID=1260 C:\temp\testlink3.txt (HRESULT=0x80071128) - CWimManager::WimProviderMsgLogCallback
[1260.436] [0x80071128] RestoreRefNodeCallback:(2098): The data present in the reparse point buffer is invalid.
[1260.436] [0x80071128] ProcessWimQueueNode:(98): The data present in the reparse point buffer is invalid.
[1260.436] [0x80071128] DequeueWimData:(304): The data present in the reparse point buffer is invalid.
[1260.436] [0x80071128] ImageWorkerThread:(250): The data present in the reparse point buffer is invalid.
[1260.576] [0x80071128] GetImageErrorCode:(8174): The data present in the reparse point buffer is invalid.
[1260.576] [0x80071128] ImageWorkerThread:(198): The data present in the reparse point buffer is invalid.
[1260.876] [0x80071128] GetImageErrorCode:(8174): The data present in the reparse point buffer is invalid.
[1260.876] [0x80071128] RestoreAllFiles:(4191): The data present in the reparse point buffer is invalid.
[1260.876] [0x80071128] RestoreAllData:(1303): The data present in the reparse point buffer is invalid.
[1260.876] [0x80071128] WIMApplyImageInternal:(954): The data present in the reparse point buffer is invalid.
2025-02-20 19:18:26, Error                 DISM   DISM WIM Provider: PID=1260 TID=876 onecore\base\ntsetup\opktools\dism\providers\wimprovider\dll\wimmanager.cpp:998 - CWimManager::Apply(hr:0x80071128)
2025-02-20 19:18:26, Error                 DISM   DISM Imaging Provider: PID=1260 TID=876 onecore\base\ntsetup\opktools\dism\providers\imagingprovider\dll\genericimagingmanager.cpp:2847 - CGenericImagingManager::InternalCmdWimApply(hr:0x80071128)
2025-02-20 19:18:26, Error                 DISM   DISM Imaging Provider: PID=1260 TID=876 onecore\base\ntsetup\opktools\dism\providers\imagingprovider\dll\genericimagingmanager.cpp:538 - CGenericImagingManager::ExecuteCmdLine(hr:0x80071128)
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: Image session has been closed. Reboot required=no.
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: 
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: <----- Ending Dism.exe session ----->
2025-02-20 19:18:26, Info                  DISM   DISM.EXE: 

Re: wimlib incompatibility with dism when some reparse points are present?

Posted: Fri Feb 21, 2025 4:34 am
by synchronicity
This seems to be caused by wimlib's NTFS-3G capture mode always marking absolute symlink targets as "fixed", despite not modifying the reparse data at all. DISM then has trouble interpreting them properly when doing the reverse fixup, since apparently that code in DISM makes some assumptions that are true for symlinks that have run through the "fixup" code but not unmodified ones -- even when logically the fixup would be a no-op. (Windows symlinks are complex, and there are multiple ways to represent what is essentially the same thing.)

wimlib has had this behavior for over 10 years, and unfortunately I don't remember the exact rationale for why I did it this way.

It does seem wrong -- wimlib should not mark symlink targets as fixed when it does not fix them.

If you can build from source, can you try out https://wimlib.net/git/?p=wimlib;a=comm ... 916547bf10 which does this? It's currently on the "dev" branch which you can get with:

Code: Select all

git clone https://wimlib.net/git/wimlib; cd wimlib; git checkout dev
I suppose the downside is that if you apply the image on Windows to a volume with a different drive letter from the original one, then the drive letter will no longer be fixed for absolute symlinks that originally pointed into the same volume. But then again, wimlib had no way to know whether those symlinks actually pointed into the same volume in the first place, given that the drive letter is unknown in NTFS-3G capture mode.

Re: wimlib incompatibility with dism when some reparse points are present?

Posted: Mon Feb 24, 2025 7:46 pm
by synchronicity
Now merged to the master branch. Let me know how it works for you. Thanks!

Re: wimlib incompatibility with dism when some reparse points are present?

Posted: Tue Feb 25, 2025 8:10 pm
by nomeara
Just did a couple tests, no more error in Dism, and I can successfully boot the system I captured with wimlib.

I think I'm going to retry my minimal test with a changed drive letter and see exactly what behavior I get.
I suppose the downside is that if you apply the image on Windows to a volume with a different drive letter from the original one, then the drive letter will no longer be fixed for absolute symlinks that originally pointed into the same volume.
For the \\?\<letter>: symlinks, that already was the case with dism->dism workflow, so it may not be a large difference from dism captures. I'll report back after more testing.

Re: wimlib incompatibility with dism when some reparse points are present?

Posted: Tue Feb 25, 2025 8:52 pm
by nomeara
And my minimal test works as is likely expected:

if the symlinks were on E:\ when created, then I reboot to ubuntu and capture with wimlib in block device/ntfs-3g mode, and then restore to a bare NTFS partition on C:\, they're all still pointing to E:\, and thus 'broken'... but in the expected way. So I think this solves the issue for me, my use case should usually involve the drive letters staying the same.

Thanks for the fast fix.