Search code examples
phpdockerwindows-10sambaddev

XSym symbolic links cannot be used in PHP on Docker for Windows


When using PHP inside a Docker container on Windows (e.g. using DDEV), symbolic links that are created inside the container (e.g. by composer) do not seem to work correctly with PHP's file streams.

Scenario

Imagine the following PHP code

<?php
mkdir('demo-base-directory');
symlink('demo-base-directory', 'demo-symbolic-link');
var_dump(glob('demo-*', GLOB_ONLYDIR));

If executed inside the container, it outputs only demo-base-directory, however demo-symbolic-link is missing (the very same example works as expected on Linux/Unix systems inside Docker containers)

array(1) {
  [0]=>
  string(19) "demo-base-directory"
}

When viewing the symbolic link in the host system (e.g. using cat demo-symbolic-link in Windows PowerShell) it shows

XSym
0019
0df68e8650ddca993c28277a5cfa3dcd
demo-base-directory

There have been other reports to Docker for Windows about symlink emulation - I could not reproduce this behavior for files using fgets or file_get_contents but for the mentioned glob invocation, see

Shared volumes are mounted in a Linux-based Docker container on a Windows host system as Samba/CIFS mount like this:

//10.0.75.1/C on /var/www/html type cifs (rw,relatime,vers=3.02,sec=ntlmsspi,cache=strict,username=olly,domain=OLIVERHADERB9D8,uid=0,noforceuid,gid=0,noforcegid,addr=10.0.75.1,file_mode=0755,dir_mode=0777,iocharset=utf8,nounix,serverino,mapposix,nobrl,mfsymlinks,noperm,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1)

Mount option mfsymlinks refers to Minshall+French symlinks

Workaround

Instead of creating symbolic links inside the container, creating them outside (directly in Windows) solves the problem.

Using plain mklink in cmd.exe

del demo-symbolic-link
mklink /d demo-symbolic-link demo-base-directory

output

symbolic link created for demo-symbolic-link <<===>> demo-base-directory

Using plain mklink via cmd in PowerShell

del demo-symbolic-link
cmd /c mklink /d demo-symbolic-link demo-base-directory

output

symbolic link created for demo-symbolic-link <<===>> demo-base-directory

side note: New-Item -ItemType SymbolicLink could not be resolved using glob(..., GLOB_ONLYDIR in PHP - using mklink /d here as well

Using Git-Bash for Windows

I had to execute the following while executing Git-Bash as administrator. Setting the environment variable with export is important here - see Enable native NTFS symbolic links for Cygwin

rm demo-symbolic-link
export MSYS=winsymlinks:nativestrict
ln -s demo-base-directory/ demo-symbolic-link

Helper tools

I've created helper tool examples that search for Samba XSym pointers in a particular directory (TYPO3 related, public/typo3conf/ext/) and "upgrade" the XSym pointers to proper symlinks - probably admin privileges are required when executing those scripts:

Results

Executing the PHP example from above again now outputs both expected items

array(2) {
  [0]=>
  string(19) "demo-base-directory"
  [1]=>
  string(18) "demo-symbolic-link"
}

Question

Working inside a Docker container and having to adjust symbolic links manually on the host system is really just a workaround - not a solution. At which level could this be enhanced and optimized in order to create proper symbolic links using the Docker container only?

It seems this could be resolved at different layers:


Updates

  • switched from mklink /j (junction) to mklink /d (symlinked directory) since removing the linked junction removed it's origin as well
  • PowerShell's symlink cmdlet New-Item -ItemType SymbolicLink (instead of previous New-Item -ItemType Junction) then again could not be resolved using glob(..., GLOB_ONLYDIR in PHP - as a result, using cmd /c mklink /d here
  • added Samba/CIFS mount information

Solution

  • Starting with ddev v1.5.0, the ddev composer command on Windows attempts to convert XSym symlinks to legitimate Windows symlinks. This feature only works if you have enabled "Developer mode" on Windows. See Windows OS and ddev composer in docs.