#
# ms-nfs41-client manual testing sequence, 2026-08-10
#
# Draft version, needs to be turned into automated tests
# if possible
#

#
# Notes:
# - Cygwin enviroment:
#   The following Cygwin packages must be installed for running the tests:
#   ---- snip ----
#   gcc-core
#   gcc-g++
#   clang
#   mingw64-i686-clang
#   mingw64-x86_64-clang
#   gdb
#   make
#   bmake
#   autoconf
#   automake
#   netpbm
#   attr
#   git
#   subversion
#   cygport
#   dos2unix
#   libiconv-devel
#   libncurses-devel
#   libgmp-devel
#   libmpfr-devel
#   libmpc-devel
#   libintl-devel
#   libisl-devel
#   flex
#   bison
#   unzip
#   pax
#   tar
#   konsole
#   nedit
#   emacs
#   cygport
#   gettext
#   gettext-devel
#   ---- snip ----
#
# - MSYS2 environment:
#   - Install the env with:
#   $ mkdir -p download && cd download
#   $ wget 'https://github.com/msys2/msys2-installer/releases/download/2025-02-21/msys2-x86_64-20250221.exe'
#   $ chmod a+x 'msys2-x86_64-20250221'
#   $ ./msys2-x86_64-20250221 --default-answer --root 'C:\msys64' install
#   - The following MSYS2 packages should be installed for running the tests with MSYS2:
#   (install with $ pacman -S --noconfirm ${name_of_package} # (use <space> to separate package names)
#   ---- snip ----
#   base-devel
#   autoconf
#   automake
#   gcc
#   clang
#   sed
#   time
#   coreutils
#   util-linux
#   grep
#   sed
#   emacs
#   gdb
#   make
#   gettext
#   gettext-devel
#   git
#   subversion
#   flex
#   bison
#   unzip
#   pax
#   tar
#   libiconv-devel
#   ncurses-devel
#   gmp-devel
#   mpfr-devel
#   mpc-devel
#   isl-devel
#   procps-ng
#   libiconv-devel
#   ---- snip ----
#
# - Benchmarking/profiling should be done with the realtime virus checker
#   disabled, e.g. disable it like this from an "Adminstrator" terminal:
#   $ powershell -Command 'Set-MpPreference -DisableRealtimeMonitoring 1' #
#
# - Microsoft Compatibility Telemetry daemon should be disabled,
#   as it can ruin profiling runs.
#   The daemon can be disabled from a Cygwin Adminstrator shell like this:
#   ---- snip ----
#   regtool -i set '/HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/DataCollection/AllowTelemetry' 0
#   ---- snip ----
#
# - A timeserver shoud be running on both Windows (NFSv4.2/NFSv4.1
#   client and NFSv4.2/NFSv4.1 server).
#   For example on Windows add timeserver 10.49.0.9 like this:
#   ---- snip ----
#   sc config w32time start=auto
#   sc start w32time
#   w32tm /config /update /manualpeerlist:10.49.0.9
#   ---- snip ----
#   (see https://stackoverflow.com/questions/22862236/how-to-sync-windows-time-from-a-ntp-time-server-in-command)
#   On Linux use xntpd or timesyncd (/etc/systemd/timesyncd.conf)
#
# - Start cygserver as admin before running tests
#   so SysV shared memory works:
#   ---- snip ----
#   cygserver-config
#   net start cygserver
#   sc query cygserver
#   ---- snip ----
#
# - Windows Kernel debugger setup:
#   1. Install "winsdksetup.exe" from
#     https://go.microsoft.com/fwlink/?linkid=2164145 on all machines
#     involved
#   2. Machine "A" runs ms-nfs41-client, and has the IPv4 address
#     10.49.202.87
#   3. Machine "B" runs the "kd" kernel debugger frontend, and has the
#     IPv4 address 10.49.202.231
#   4. Setup on machine "A":
#     $ PATH+=':/cygdrive/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/'
#     $ bcdedit /dbgsettings net hostip:10.49.202.87 port:50000 key:1.1.1.1
#     $ kdnet 10.49.202.231 50000
#   5. Run kernel debugger frontend on machine "B":
#     $ PATH+=':/cygdrive/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/'
#     $ kd -loga kdlog.log -k net:port=50000,key=1.1.1.1,target=10.49.202.87 #
#
# - Windows local crash dumps setup (for non-Cygwin apps)
#   (see https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps):
#   ---- snip -----
#   # enable crash dumps globally
#   regtool add '/HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/Windows Error Reporting/LocalDumps'
#   regtool -i set '/HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/Windows Error Reporting/LocalDumps/DumpType' 2
#   mkdir -p "$(cygpath -u "$(echo $LOCALAPPDATA)")/CrashDumps"
#   # list generated crash dumps
#   ls -la "$(cygpath -u "$(echo $LOCALAPPDATA)")/CrashDumps"
#   # get stack trace for crash dump "link.exe.1640.dmp"
#   PATH+=':/cygdrive/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/'
#   cdb -z "${LOCALAPPDATA}\CrashDumps\link.exe.1640.dmp" -c '!analyze -v ; q'
#   ---- snip -----
#
# - Testing with Windows driver verifier:
#   1. Docs see https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/driver-verifier
#   2. Enable verifier for "nfs41_driver.sys":
#       $ /cygdrive/c/Windows/system32/verifier /standard /driver nfs41_driver.sys #
#       <reboot>
#   3. Query verifier status:
#       $ /cygdrive/c/Windows/system32/verifier /query
#   4. Disable verifier:
#       $ verifier /reset
#
# - Testing NFSv4.* with non-standard TCP ports:
#   1. Run this on the NFS server itself to forward accesses to TCP/2050
#   to TCP/2049 on the same server machine:
#   $ ssh -L '*:2050:localhost:2049' root@localhost 'printf "# forwarding...\n" ; sleep $((60*60*24*366))'
#   2. Connect to the NFS server via nfs://servername1:2050//path/to/nfsexport
#

#
# "cthon04" test suite:
#
# mount a NFSv4 filesystem, and then execute this
# on this filesystem
mkdir nfsv4cthontest1 && cd nfsv4cthontest1
git clone https://github.com/kofemann/ms-nfs41-client.git
git clone git://git.linux-nfs.org/projects/steved/cthon04.git
cd cthon04/
git config --global --add safe.directory "$PWD"
git checkout 6c9abfd6907b2b43036af111cc600ab60ef804e5
git am ../ms-nfs41-client/tests/cthon04/*.patch
(make CC="gcc -std=gnu17 -Wno-implicit-int -Wno-implicit-function-declaration -Wno-incompatible-pointer-types -DNATIVE64=1" 2>&1 | tee buildlog.log)
(rm -Rf testdir1 && mkdir testdir1 && PATH="$PATH:." ksh93 ./runtests -a -t "$PWD/testdir1" 2>&1 | tee testrun.log)


#
# "winfstest" test suite:
#
See tests/winfstest/README.txt


#
# Tests for cp -p/mv/chmod/chgrp
# Testcases, all should print *ONLY* "OK" on stdout, nothing on stderr
---- snip ----
ksh93 -c 'builtin mv ; rm -f /tmp/x y ; touch /tmp/x ; mv /tmp/x y && print OK ; true'
ksh93 -c 'builtin mv ; rm -f /tmp/x y ; touch /tmp/x ; cp -p /tmp/x y && print OK ; true'
ksh93 -c 'builtin cp ; rm -f /tmp/x y ; touch /tmp/x ; cp -p /tmp/x y && print OK ; true'
ksh93 -c 'builtin cp ; rm -f x y ; touch x ; cp -p /tmp/x y && print OK ; true'
ksh93 -c 'builtin cp ; rm -f x y ; touch x ; cp -p x y && print OK ; true'
ksh93 -c 'builtin cp ; rm -f x y ; touch x ; /usr/bin/cp -p x y && print OK ; true'
ksh93 -c 'builtin id ; rm -f x ; touch x ; chown "$(id -u -n):$(id -g -n)" x && print OK'
ksh93 -c 'builtin id ; rm -f x ; touch x ; chgrp "$(id -g -n)" x && print OK'
# Extra tests if the cygwingrp*-groups exist on a test system:
ksh93 -c 'builtin chgrp; rm -f x ; touch x ; chgrp "cygwingrp2" x && print OK'
ksh93 -c 'builtin chgrp; rm -f x ; touch x ; chgrp "$(printf "cygwingrp_\u00F6\u00E4\u00FC_4")" x && print OK'
# BUG: ksh93 builtin chgrp has issues with group names which contain <space>
ksh93 -c 'rm -f x ; touch x ; chgrp "cygwingrp withspace5" x && print OK'
---- snip ----


#
# Test for |chgrp()| used with GNU /usr/bin/patch
# (GNU patch usually fails with
# 'patch: **** Failed to set the owning group of file ./d2.oL0Iv8e : Permission denied'
# if |chgrp()| fails
#
---- snip ----
$ rm -f d1 d2 d1.diff ; printf '1\n2\n' >d1 ; cp d1 d2 ; printf '3\n' >> d2 ; diff -u d1 d2 | sed 's/d1/d2/g' >d1.diff ; patch -R -p0 <d1.diff && echo "# test OK"
---- snip ----

The test should print "# test OK"


#
# Tests for Win32 streams
#

# this test should print $'dir_line1\ndir_line2\n'
rm -Rf dir1 && mkdir dir1 && cmd /c 'echo dir_line1 >>dir1:dirstr1 & echo dir_line2 >>dir1:dirstr1 & cat <dir1:dirstr1'

# these five tests should print $'file1data\nfile1_line1\nfile1_line2\n'
rm -f file1 && echo "file1data" >file1 && cmd /c 'echo file1_line1 >>file1:filestr1 & echo file1_line2 >>file1:filestr1:$DATA & C:\cygwin64\bin\cat.exe <file1 & C:\cygwin64\bin\cat.exe <file1:filestr1'
rm -f file1 && echo "file1data" >file1 && cmd /c 'echo file1_line1 >>file1:filestr1 & echo file1_line2 >>file1:filestr1:$DATA && type file1' && powershell -Command 'Get-Content -Path .\file1 -Stream filestr1'
rm -f file1 && echo "file1data" >file1 && cmd /c 'echo file1_line1 >>file1:filestr1 & type file1' && powershell -Command 'Add-Content .\file1 -Stream filestr1 -Value "file1_line2" ; Get-Content -Path .\file1 -Stream filestr1'
rm -f file1 && echo "file1data" >file1 && cmd /c 'echo file1_line1 >>file1:filestr1 & type file1' && powershell -Command 'Add-Content .\file1:filestr1 -Value "file1_line2" ; Get-Content -Path .\file1 -Stream filestr1'
rm -f file1 && echo "file1data" >file1 && cmd /c 'echo file1_line1 >>file1:filestr1' && powershell -Command 'Add-Content .\file1:filestr1 -Value "file1_line2" ; Get-Content -Path .\file1 -Stream "$DATA" ; Get-Content -Path .\file1 -Stream filestr1'
# list all streams for file "file1" (should be "filestr1")
powershell -Command 'Get-Item -LiteralPath file1 -Stream * | Select *'

# Windows powershell bug: 'Get-Item -LiteralPath d1 -Stream *' will not report any streams for direcroties.
# Example:
# rm -Rf d1 && mkdir d1 && winstreamsutil createstream /O /t "hello world" 'd1' ':bigstream4:$DATA' && powershell -Command 'Get-Item -LiteralPath d1 -Stream *'


# FIXME: tests for stream removal, enumeration and sparse data

# NTFS-style renaming (e.g. FileRenameInformation with "relative stream name", e.g. ':mystrname1:$DATA')
# (last line should print "file1_line1")
rm -f file1 && touch file1 && cmd /c 'echo file1_line1 >>file1:mystrname1' && winstreamsutil renamestream file1 ':mystrname1' ':mystrname1_renamed' && cmd /C 'C:\cygwin64\bin\cat.exe <file1:mystrname1_renamed'


#
# Tests for Cycgwin/UWIN/SFU Nfs3Attr EA-based local uid/gid
#
# ToDO:
# - iterate over multiple groups via newgrp(1)/winsg(1)
# - iterate with bash/ksh93 (because it changes the pipeline process ordering)
#

# create file, and the uid/gid fro Nfs3Attr EA must match /bin/id -u+/bin/id -g
# output should highlight uid=/gid= in colour
---- snip ----
ksh93 -c 'rm -f test1.txt ; command exec {n}>>test1.txt ; printf "fd=%d\n" $n ; printf "x" >&$n ; command exec {n}<&- ; /bin/winfsinfo nfs3attr test1.txt | egrep --colour "(uid=$(id -u)|gid=$(id -g))" ; true'
---- snip ----


#
# Test for native mklink  && powershell New-Item -ItemType SymbolicLink
#
# Notes:
# - powershell might require admin rights depending on Windows
# registry settings)
#

# 1. cmd.exe mklink dir
mkdir mydir1
cmd /C 'mklink /D symLinkmydir1 mydir1'
cmd /C "mklink /D absoluteSymLinkmydir1 $(cygpath -w "$PWD/symLinkmydir1")"
# 2. cmd.exe mklink file
touch myfile1
cmd /C 'mklink symLinkmyfile myfile1'
cmd /C "mklink absoluteSymLinksymLinkmyfile $(cygpath -w "$PWD/myfile1")"
# 3. powershell mklink dir
mkdir mypsdir1
powershell -Command 'New-Item -Path sym_mypsdir1 -ItemType SymbolicLink -Value mypsdir1'
# 4. powershell mklink file
mkdir mypsfile1
powershell -Command 'New-Item -Path sym_mypsfile1 -ItemType SymbolicLink -Value mypsfile1'
# 5. Relative links:
mkdir targetdir1
cmd /C 'mklink /D targetdir1_sym targetdir1'
cmd /C 'mklink /D targetdir2_sym .\targetdir1'
# 6. Cygwin /dev/ symlinks, e.g. foo --> /dev/null
ln -s /dev/zero foo && ls -l foo && rm foo
# 7. cmd.exe follow sublink dir to other filesystem
# (this assumes we have a drive 'M:' with a subdir "builds")
rm -f symlink1_to_m_builds ; cmd /C 'mklink /D symlink1_to_m_builds M:\builds' ; cmd /C 'cd symlink1_to_m_builds && dir && echo test_OK'
# 8. cmd.exe follow sublink dir to other filesystem
# (this assumes we have an valid UNC path
# \\derfwnb4966_ipv6linklocal@NFS@2049\bigdisk\ with a subdir "builds")
rm -f symlink1_to_unc ; cmd /C 'mklink /D symlink1_to_unc \\derfwnb4966_ipv6linklocal@NFS@2049\bigdisk\builds\' ; cmd /C 'cd symlink1_to_unc && dir && echo test_OK'
# 9a. powershell follow sublink dir to other filesystem
# (this assumes we have an valid UNC path
# \\derfwnb4966_ipv6linklocal@NFS@2049\bigdisk\ with a subdir "builds")
rm -f symlink1_to_unc ; cmd /C 'mklink /D symlink1_to_unc \\derfwnb4966_ipv6linklocal@NFS@2049\bigdisk\builds\' ; powershell -Command 'cd symlink1_to_unc ; if ($?) { dir ; if ($?) { echo "test OK" } else { echo "dir failed" } } else { echo "cd failed" }'
# 9b. powershell follow sublink dir to other filesystem
# (this assumes we have an valid UNC path
# \\derfwpc5131_ipv4@NFS@2049\export\home2\rmainz\ with a subdir "tmp")
rm -f symlink1_to_h_tmp ; cmd /C 'mklink /D symlink1_to_h_tmp \\derfwpc5131_ipv4@NFS@2049\export\home2\rmainz\tmp' ; powershell -Command 'cd symlink1_to_h_tmp ; if ($?) { dir ; if ($?) { echo "test OK" } else { echo "dir failed" } } else { echo "cd failed" }'

#
# Tests for absolute (start from root "/") POSIX symlinks
#
# all test should print "OK test <testnum>"
# ("." is a relative symlink, just included here for more coverage)
ksh93 -c 'typeset -a a=( "/" "/usr" "/usr/" "/tmp" "/usr/bin" "$PWD" "/dev/null" "." ) ; for ((i=0 ; i < ${#a[@]} ; i++ )) ; do rm -f "syml$i" ; ln -sf "${a[$i]}" "syml$i" ; l="$(readlink "syml$i")" ; if [[ "$l" == "${a[$i]}" ]] ; then printf "OK test %d\n" i ; else printf "FAIL test %d\n" i ; fi ; done'

#
# Repeatedly create the same symlink with $ ln -f -s ... #
#
# This can cause |NFS4ERR_STALE| issues if SRVOpen collapsing is not disabled for symlinks
#
# All three tests should print $'# Test OK'
---- snip ----
bash -c 'set -o errexit ; rm -Rf symlinktest1 ; mkdir symlinktest1 ; cd symlinktest1 ; mkdir -p dir1/dir2 ; touch dir1/x ; cd dir1/dir2 ; /bin/ln -f -s ../../dir1/x . ; /bin/ln -f -s ../../dir1/x . ; /bin/ln -f -s ../../dir1/x ; readlink x ; printf "# Test OK"'
bash -c 'set -o errexit ; rm -Rf symlinktest2 ; mkdir symlinktest2 ; cd symlinktest2 ; mkdir -p dir1/dir2 ; touch dir1/x ; cd dir1/dir2 ; /bin/ln -f -s ../../dir1/x . ; /bin/ln -f -s ../../dir1/x . ; /bin/ln -f -s ../../dir1/x ; ls -l x ; printf "# Test OK"'
bash -c 'set -o errexit ; rm -Rf symlinktest3 ; mkdir symlinktest3 ; cd symlinktest3 ; mkdir -p dir1/dir2 ; touch dir1/x ; cd dir1/dir2 ; for (( i=0 ; i < 100 ; i++ )) ; do /bin/ln -f -s ../../dir1/x . ; done ; ls -l x ; printf "# Test OK"'
---- snip ----

#
# Tests for symlinks with characters outside the BMP (Basic Multilingual Plane)
# (Unicode "half moon" in this case)
#
# ToDo:
# - Use different characters with different byte lengths
# - Add more tests with relative and absolute paths
# - Add tests with subdirs with non-BMP characters
#
1. Original test:
---- snip ----
bash -c 'm="$(printf "\U0001f313")" ; echo "Unicode moon character in filename test OK" >"$m$m$m$m$m$m${m}x" && ln -sf "$m$m$m$m$m$m${m}x" "$m$m$m$m$m$m$m" && cat "$m$m$m$m$m$m${m}"'
Unicode moon character in filename test OK
---- snip ----
2. Mix of Unicode non-BMP characters and ASCII charatcers:
---- snip ----
bash -c 'set -o errexit ; m="$(printf "\U0001f313")" ; typeset filenames=( "$m$m$m$m$m$m$m" "$m$m$m$m$m$m$m$m" "x$m$m$m$m$m$m" "$m$m$m_$m$m$m" ) ; for fn in "${filenames[@]}" ; do printf "Unicode moon character in filename %q test OK\n" "$fn" >"${fn}x" && ln -sf "${fn}x" "${fn}" && cat "$fn" ; done' && ls -l && echo "## All tests OK"
---- snip ----

#
# Test whether cmd.exe can follow symlink to "/" (root), which should point to C:cygwin64\ or C:\cygwin\
# (CYGWIN='winsymlinks:nativestrict' is used so the same test works with NTFS too)
#
CYGWIN='winsymlinks:nativestrict' bash -c 'rm -f symlrootdotslash &&  ln -sf / symlrootdotslash && cmd /C "cd symlrootdotslash" && echo "Test OK"'

#
# Test script to test git symlink with Windows git
#
---- snip ----
#
# Windows git symlink test
#
set -o xtrace
set -o errexit

export PATH='/bin:/usr/bin'

builtin cat
builtin ln
builtin rm

which -a git

integer test_errs=0

rm -Rf newgitproj newgitproj_cloned

#
# Create via Cygwin git
#
mkdir newgitproj
cd newgitproj
git init --initial-branch='master'
git config --global --add safe.directory "$PWD"
git config --global --add safe.directory "$PWD/.git"

printf 'Hello, Git symlink test!\n' >'README.md'
printf '123\n' >'123.txt'
git add 'README.md' '123.txt'
git commit -m 'Add README file'

ln -s 'README.md' 'README_link.md'
ln -s '123.txt' '123_link.txt'
ln -s '123_link.txt' '123_link_link.txt'
git add 'README_link.md' '123_link.txt' '123_link_link.txt'
git commit -m 'Add symlink to README+123.txt files'
cd ..

#
# clone via Windows git
#
'/cygdrive/c/Program Files/Git/cmd/git' clone -c core.symlinks=true newgitproj newgitproj_cloned
cd newgitproj_cloned
ls -l
if [[ ! -L 'README_link.md' ]] ; then
	printf '## FAIL: README_link.md should be a symlink\n'
        (( test_errs++ ))
fi
if [[ ! -L '123_link.txt' ]] ; then
	printf '## FAIL: 123_link.txt should be a symlink\n'
        (( test_errs++ ))
fi
if [[ ! -L '123_link_link.txt' ]] ; then
	printf '## FAIL: 123_link_link.txt should be a symlink\n'
        (( test_errs++ ))
fi

if [[ "$(cat '123_link_link.txt')" != $'123\r' ]] ; then
	printf '## FAIL: 123_link_link.txt contain the string "123\\r", got %q\n' "$(cat '123_link_link.txt')"
        (( test_errs++ ))
fi

if (( test_errs > 0 )) ; then
    printf '#### FAILURE: %d test failed.\n' test_errs
    exit 1
fi

printf '#### ALL Test OK\n'
exit 0

# EOF.
---- snip ----

#
# Test for mkdir with UNC path
#
Example:
---- snip ----
# get UNC path for driver letterr 'L':
$ powershell -Command 'Get-PSDrive -Name L | Select-Object -Property DisplayRoot,CurrentLocation | Format-List' | ksh93 -c 'compound c=(typeset -A ar) ; while IFS="" read -r l ; do dummy="${l/~(Elr)(.+?)[[:space:]]*:[[:space:]](.+)\015/x}" ; [[ "${.sh.match[1]-}" == "" ]] && continue ; c.ar["${.sh.match[1]}"]="${.sh.match[2]}" ; done ; cygpath -u "$(printf "%s\\\\%s\n" "${c.ar[DisplayRoot]}" "${c.ar[CurrentLocation]}")"'
//derfwnb4966_ipv6linklocal@NFS@2049/bigdisk/builds/bash_build1
# tests
mkdir -p //derfwnb4966_ipv6linklocal@NFS@2049/bigdisk/builds/bash_build1/a1/b1/c1 || echo FAIL
mkdir //derfwnb4966_ipv6linklocal@NFS@2049/bigdisk/builds/bash_build1/a2 || echo FAIL
mkdir //derfwnb4966_ipv6linklocal@NFS@2049/bigdisk/builds/bash_build1/a2/b2 || echo FAIL
---- snip ----

#
# Tests for groups
# (Add groups { "cygwingrp1", "cygwingrp2", "cygwingrp_<oe><ae<ue>_4",
# "cygwingrp withspace5" }
# to both Linux NFSv4 server and Windows machine, after that
# $ chgrp cygwingrp2 filename # and
# $ chgrp $'cygwingrp_\u00F6\u00E4\u00FC_4' filename # should work)
#
---- snip ----
# "WINHOST1" is the Windows machine,
# "DERFWNB4966" is the Linux NFSv4 server:

# create three groups on the Windows machine
WINHOST1:~$ net localgroup cygwingrp1 /add
WINHOST1:~$ net localgroup cygwingrp2 /add
WINHOST1:~$ powershell -Command 'New-LocalGroup -Name "cygwingrp_$([char]0x00F6)$([char]0x00E4)$([char]0x00FC)_4" -Description "Cygwin test group with non-ASCII chars in name"'
WINHOST1:~$ net localgroup 'cygwingrp withspace5' /add

# add user "roland_mainz" to both new groups
# (Note that the idmapper will translate Win32 "roland_mainz" to NFS server "rmainz")
WINHOST1:~$ net localgroup cygwingrp1 roland_mainz /add
WINHOST1:~$ net localgroup cygwingrp2 roland_mainz /add
WINHOST1:~$ powershell -Command 'Add-LocalGroupMember -Group "cygwingrp_$([char]0x00F6)$([char]0x00E4)$([char]0x00FC)_4" -Member roland_mainz'
WINHOST1:~$ net localgroup 'cygwingrp withspace5' roland_mainz /add

# get gid from both groups
WINHOST1:~$ getent group cygwingrp1
cygwingrp1:S-1-5-21-3286904461-661230000-4220857270-1003:197611:
WINHOST1:~$ getent group cygwingrp2
cygwingrp2:S-1-5-21-3286904461-661230000-4220857270-1004:197612:
WINHOST1:~$ getent group $'cygwingrp_\u00F6\u00E4\u00FC_4'
cygwingrp_<oe><ae<ue>_4:S-1-5-21-1849874161-1103376535-3295315891-1008:197616:
$ getent group 'cygwingrp withspace5'
cygwingrp withspace5:S-1-5-21-1849874161-1103376535-3295315891-1010:197618:

# add the two groups to the Linux NFSv4 server, including the gids
("197611", "197612", "197616" and "197618"):
root@DERFWNB4966:~# groupadd -g 197611 cygwingrp1
root@DERFWNB4966:~# groupadd -g 197612 cygwingrp2
root@DERFWNB4966:~# printf "cygwingrp_\u00F6\u00E4\u00FC_4:x:197616:\n" >>'/etc/group'
# the idmapper script will convert Win32 'cygwingrp withspace5' to NFS server 'cygwingrp_withspace5'
root@DERFWNB4966:~# groupadd -g 197618 cygwingrp_withspace5
root@DERFWNB4966:~# usermod -a -G cygwingrp1 rmainz
root@DERFWNB4966:~# usermod -a -G cygwingrp2 rmainz
# user "rmainz" must be manually added to group "cygwingrp2_<oe><ae<ue>_4"
root@DERFWNB4966:~# usermod -a -G cygwingrp_withspace5 rmainz
---- snip ---

#
# Test for group ACLs
#
Testcase:
-------- snip --------
# cd to a NFS >= v4.1 filesystem
$ rm -f test1.txt
$ touch test1.txt
$ icacls test1.txt /grant:r 'cygwingrp1:(WDAC)' /t /c
Bearbeitete Datei: test1.txt
1 Dateien erfolgreich verarbeitet, bei 0 Dateien ist ein Verarbeitungsfehler aufgetreten.

$ icacls test1.txt /grant:r 'cygwingrp2:(WDAC)' /t /c
Bearbeitete Datei: test1.txt
1 Dateien erfolgreich verarbeitet, bei 0 Dateien ist ein Verarbeitungsfehler aufgetreten.

# expectation:
# getfact output should contain both "cygwingrp1" and "cygwingrp2":
$ getfacl test1.txt
user::r--
group::r--
group:cygwingrp1:r--
group:cygwingrp2:r--
mask::r--
other::r--
-------- snip --------

or one-liner:
-------- snip --------
# cd to a NFS >= v4.1 filesystem
# getfact output should contain both "cygwingrp1" and "cygwingrp2"
ksh93 -c 'rm -f test1.txt ; touch test1.txt ; icacls test1.txt /grant:r "cygwingrp1:(WDAC)" /grant:r "cygwingrp2:(WDAC)" /t /c ; getfacl test1.txt | grep -C 20 --colour -E "cygwingrp[12]"'
-------- snip --------

Same test with xcopy /O (requires SeBackupPrivilege/SeRestorePrivilege):
-------- snip --------
# cd to a NFS >= v4.1 filesystem
# getfact output should contain both "cygwingrp1" and "cygwingrp2"
ksh93 -c 'rm -f test1.txt subdir1/test1.txt ; touch test1.txt ; icacls test1.txt /grant:r "cygwingrp1:(WDAC)" /grant:r "cygwingrp2:(WDAC)" /t /c ; xcopy /O /I test1.txt "subdir1\\" ; getfacl subdir1/test1.txt | grep -C 20 --colour -E "cygwingrp[12]"'
-------- snip --------

Same test with icacls /save+/restore (requires SeBackupPrivilege/SeRestorePrivilege):
-------- snip --------
# * Test: Copy ACL from "test1.txt" to "test2.txt"
# * Expected result: getfacl output should contain both "cygwingrp1" and "cygwingrp2"
# * Description:
# 1. save ACL data of "test1.txt" to "aclspec.icacls"
# 2. replace the filename "test1.txt" with "test2.txt" in
#   "aclspec.icacls" and save the resulting data as
#   "aclspec2.icacls"
# 3. use $ icacls . /restore aclspec2.icacls # to set
#   the ACL on "test2.txt"
# * Usage:
#   cd to a NFS >= v4.1 filesystem and execute the one-liner below:
ksh93 -c 'rm -f test1.txt test2.txt aclspec.icacls aclspec2.icacls ; touch test1.txt ; touch test2.txt ; icacls test1.txt /grant:r "cygwingrp1:(WDAC)" /grant:r "cygwingrp2:(WDAC)" /t /c ; icacls test1.txt /save aclspec.icacls ; iconv -f UTF16LE -t UTF-8 <aclspec.icacls | { IFS="" read fname ; IFS="" read aclstr ; } ; printf "test2.txt\r\n%s\n" "$aclstr" | iconv -f UTF-8 -t UTF16LE >aclspec2.icacls ; icacls . /restore aclspec2.icacls ; getfacl test2.txt | grep -C 20 --colour -E "cygwingrp[12]"'
-------- snip --------

#
# Test for file "Generic Write" ACL:
#
$ ksh93 -c 'set -o xtrace ; rm -f test1.txt ; touch test1.txt ; icacls test1.txt /grant "siegfried_wulsch:(GW)" ; icacls test1.txt | grep --colour -E "siegfried_wulsch.+GW"'
# Expectation:
# The ACL entry for user "siegfried_wulsch" should have the "GW" flag set

#
# Test for directory "X" ACL:
#
$ ksh93 -c 'set -o xtrace ; rm -rf test1.dir ; mkdir test1.dir ; icacls test1.dir /grant "siegfried_wulsch:(W,X)" ; icacls test1.dir | grep --colour -E "siegfried_wulsch.+X"'
# Expectation:
# The ACL entry for user "siegfried_wulsch" should have the "X" flag set

#
# Test for default (inheritance) ACLs:
#
mkdir test1
cd test1
icacls . /grant "cygwingrp2:(OI)(CI)(IO)(GR)"
touch mytestfile1.txt
icacls mytestfile1.txt | grep --colour -E 'cygwingrp2.+GR'
# file "mytestfile1.txt" must have the "GR" flag set

#
# Compile each of the following package
# on a NFS >= v4.1 share, and run each build in parallel/sequence
# multiple times on one or multiple mounts
#
# ./nfs_mount -p -o sec=sys T derfwnb4966_ipv6:/net_tmpfs2
# ./nfs_mount -p -o sec=sys R derfwnb4966_ipv6:/net_tmpfs2/test2
#


#
# Test whether opening the mount point (e.g. L:) as a plain file works
# (see https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
# section "FILE_NON_DIRECTORY_FILE": "... The file being opened must not be a directory file
# or this call fails. The file object being opened can represent a data file, a logical,
# virtual, or physical device, or a *VOLUME*. ...")
#
$ fsutil.exe fsinfo volumeinfo L:
<Should print info about that filesystem>


#
# Tests for Windows EAs (Extended Attributes)
# Windows EAs are represented as NFSv4 extended attributes (XATTR)
# files, with the Windows EaName prefixed with "win32.ea." to
# avoid namespace collision with other users of NFSv4 XATTR files
# (e.g. SUN Microsystems/Solaris/Illumos/ZFS etc. use "SUNWattr_"
# as predix
#

1. One-liner:
---- snip ----
ksh93 -c 'set -o xtrace -o errexit ; rm -f x1 x1_copy1 ; touch x1 ; attr -q -s "fish5" -V "hello lake world" x1 ; [[ "$(attr -q -l x1)" == *fish5* ]] || echo FAIL ; cp --preserve=xattr x1 x1_copy1 ; [[ "$(attr -q -g "fish5" x1_copy1)" == "hello lake world" ]] || echo "FAIL" ; attr -q -r "fish5" x1 ; echo "# Test OK"'
---- snip ----

2. Detailed test:
---- snip ----
# create parent file
$ rm -f myattrfile ; touch myattrfile

# list attributes for new file (should be no EAs)
$ attr -l myattrfile

# create EA attribute "chicken3"
$ attr -s "chicken3" -V "hello world" myattrfile
Attribute "chicken3" set to a 11 byte value for myattrfile:
hello world

# create EA attribute "fish5"
$ attr -s "fish5" -V "hello lake world" myattrfile
Attribute "fish5" set to a 16 byte value for myattrfile:
hello lake world

# list attributes (should be "fish5" and "chicken3"
$ attr -l myattrfile
Attribute "fish5" has a 16 byte value for myattrfile
Attribute "chicken3" has a 11 byte value for myattrfile

# get value of "chicken3" (should be "hello world")
$ attr -g chicken3 myattrfile
Attribute "chicken3" had a 11 byte value for myattrfile:
hello world

# copy file with xattr, get value of "chicken3" from copied file (should be "hello world")
$ cp --preserve=xattr myattrfile myattrfile_copy1
$ attr -g chicken3 myattrfile_copy1
Attribute "chicken3" had a 11 byte value for myattrfile_copy1:
hello world

# remove attribute "chicken3", try to get it (should fail)
$ attr -r chicken3 myattrfile
$ attr -g chicken3 myattrfile
attr_get: No data available
Could not get "chicken3" for myattrfile
---- snip ----

3. Notes:
On the NFS server side you can observe XATTR attributes like this:
- Using runat:
$ runat "x1" "ls -la"
- Using bash cd(1) -@:
$ bash -c 'cd -@ "x1" && ls -la'


#
# Test for block cloning via |FSCTL_DUPLICATE_EXTENTS_TO_FILE|+NFSv4.2 CLONE
#

# 1. Clone 5GB file via Windows 11 xcopy.exe (xcopy.exe uses |CopyFile2()|,
# which uses |FSCTL_DUPLICATE_EXTENTS_TO_FILE| by default in Win11;
# requires NFSv4.2 server with btrfs (XFS and ZFS should work too, not
# tested yet)):
---- snip ----
# clone a 5GB file
mkdir -p dir1 dir2
rm -f dir1/mybigfile.bin dir2/mybigfile.bin
time dd if=/dev/urandom of=dir1/mybigfile.bin bs=$((5*1024*1024)) count=1024
xcopy /S /E dir1 dir2
md5sum dir1/mybigfile.bin dir2/mybigfile.bin
---- snip ----

# 2. Clone sparse file via "winclonefile", and test whether sparse file
# layout and hash sums are identical
---- snip ----
rm sparsefile2.bin sparsefile2_clone.bin
printf "hello\n" | dd of=sparsefile2.bin seek=$((64)) bs=1024
printf "world\n" | dd of=sparsefile2.bin seek=$((64*4)) bs=1024
dd if=/dev/null of=sparsefile2.bin seek=$((64*8)) bs=1024
winclonefile sparsefile2.bin sparsefile2_clone.bin
# check whether sparse file layout is the same
diff -u <(lssparse -H sparsefile2.bin) <(lssparse -H sparsefile2_clone.bin) && echo "# Test OK" || echo "# Test FAILED"
# both hash sums must be identical
md5sum --binary sparsefile2.bin sparsefile2_clone.bin
---- snip ----


#
# Network provider API tests
#
1. Test |WNetGetResourceInformationW()|, input: "\dir1\dir2\dir3", output: should be the
directory of the mount ("\dir1"), followed by the remaining path in
"out_lpSystem" ('\dir2\dir3')
---- snip ----
# this should print "# Test OK"
winfsinfo get_wnetgetresourceinformation '\\10.49.202.230@PUBNFS@2049\dir1\dir2\dir3' | ksh93 -x -c 'set -o nounset ; compound c ; read -C c ; print -v c ; [[ "${c.lpRemoteName}" == "${c.out_netresource.lpRemoteName%*\\}${c.out_lpSystem}" ]] && print "# Test OK"'
---- snip ----

2. Test |WNetGetResourceParentW()|:
Note that the returned information is about the parent dir/resource:
---- snip ----
$ winfsinfo get_wnetgetresourceparent '\\10.49.202.230@PUBNFS@2049\bigdisk\builds\aaa' 'NFS41 Network' | fgrep dwDisplayType
                dwDisplayType='RESOURCEDISPLAYTYPE_SHARE'
$ winfsinfo get_wnetgetresourceparent '\\10.49.202.230@PUBNFS@2049\bigdisk\builds' 'NFS41 Network' | fgrep dwDisplayType
                dwDisplayType='RESOURCEDISPLAYTYPE_SHARE'
$ winfsinfo get_wnetgetresourceparent '\\10.49.202.230@PUBNFS@2049\bigdisk' 'NFS41 Network' | fgrep dwDisplayType
                dwDisplayType='RESOURCEDISPLAYTYPE_SERVER'
---- snip ----


#
# Test whether TMPDIR, TEMP, TMP can be on a NFS filesystem
#
One-liner, should print two times lines with "test OK", stderr should be empty
---- snip ----
$ time ksh93 -c 'rm -Rf nfstmp1 gcc.x.strace.log x.c x.exe && mkdir nfstmp1 && chmod a+rwxt nfstmp1 ; export TMPDIR="$PWD/nfstmp1" TEMP="$PWD/nfstmp1" TMP="$PWD/nfstmp1"; rm -f x.c x.exe && printf "#include <stdio.h>\n#include <stdlib.h>\nint main(int ac, char *av[]) { puts(\"hello world\"); return EXIT_SUCCESS;}\n" >x.c && strace -b $((512*1024)) -m syscall -o gcc.x.strace.log gcc x.c -o x.exe && ./x.exe && echo "# Compile Test OK" ; n="$(grep -E "/nfstmp1/.+\.o" gcc.x.strace.log | wc -l)" ; if (( n > 0 )) ; then echo "# Temp usage test OK: used by *.o files" ; else echo "# Test *.o FAILED" ; fi'
---- snip ---

#
# ksh93 (ast-ksh)
#

#
# build ast-ksh ksh93 1.0.10 with UNC support enabled
# (see ms-nfs41-client/cygwin/cygwin_ksh93/cygwin_ksh93.readme for
# instructions how to build a Cygwin package)
#
git clone -b 'v1.0.10' https://github.com/ksh93/ksh.git
cd ksh
git config --global --add safe.directory "$PWD"
# add patch so UNC path support is enabled (e.g. "cd //derfwnb4966_ipv6linklocal@NFS@2049/net_tmpfs")
patch -p1 <'../cygwin/cygwin_ksh93/ksh_1_0_8_libast_Cygwin_set_PATH_LEADING_SLASHES_for_UNC_paths.patch'
sed -i -r 's/mkfifo.+?(-m [[:digit:]]+)/mkfifo /g' ./src/cmd/INIT/package.sh ./bin/package
# repeat:
rm -Rf arch
time bash -c 'export SHELL=/bin/bash HOSTTYPE="cygwin.i386-64"; /bin/bash ./bin/package make CC="/usr/bin/cc -std=gnu17" CCFLAGS="-Os -g" SHELL="$SHELL" HOSTTYPE="$HOSTTYPE"' 2>&1 | tee buildlog.log


#
# bash
#

# build bash with very long PWD (${#PWD} == 1053 is known to work
# if the nfs server supports path lengths > 1024)
typeset -i i
typeset longpath=''
for ((i=0 ; ${#longpath} < 1024 ; i++)) ; do
    longpath+="longpath__$i/"
done
mkdir -p "$longpath" && cd "$longpath"
printf "path_len=%d\n" "${#PWD}"

git clone https://git.savannah.gnu.org/git/bash.git
cd bash/
# Cygwin: workaround for configure using cp -p where ln -s should be used
# (this is an automake/autoconf issue, they should trust Cygwin and not use
# ancient workarounds for issues which no longer exists)
(set -o xtrace ; sed -i "s/as_ln_s='cp -pR'/as_ln_s='ln -s'/g" $(find . -name configure) )
# run configure
./configure --with-curses
# repeat:
make clean && make -j4 all


#
# ms-nfs41-client with Visual Studio 2019 on Windows 10/64bit
#
git clone https://github.com/kofemann/ms-nfs41-client.git
cd ms-nfs41-client
git config --global --add safe.directory "$PWD"

export PATH="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/MSBuild/Current/Bin/:$PATH" CERTIFICATE_THUMBPRINT="$(powershell -c 'Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object {$_.Subject -like "*WDKTestCert*"} | Select-Object -ExpandProperty Thumbprint')"
# repeat:
# clean
make -f cygwin/Makefile clean ; rm -Rfv destdir
# build
time ksh93 -c 'make -j8 -f cygwin/Makefile build && make -j8 -f cygwin/Makefile installdest && make -j8 -f cygwin/Makefile bintarball'


#
# gcc
#
# * Notes:
# - The build requires that there are at least 131 characters left in
#   the path, e.g. $ echo $((260-${#PWD} > 131)) # should be "1",
#   otherwise the buildcan "randomly" fail with internal gcc errors.
#   Note that PWD might be the UNC path.
#   ---- snip ----
#   $ cd gcc
#   $ ksh93 -c 'integer pnl pnl_max ; find . | while read pn ; do pnl=${#pn} ; (( pnl_max=fmax(pnl, pnl_max) )) ; done ; printf "max_path_length=%d\n" pnl_max'
#   max_path_length=131
#   ---- snip ----
# - Full build can easily take ~~16 hours minutes
# - Option "--disable-libstdcxx-pch" is used due to bug
#   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114381 ("(Sporadic)
#   crash generating x86_64-pc-cygwin/bits/extc++.h.gch/O2g.gch")
#
git clone -b 'releases/gcc-13.2.0' git://gcc.gnu.org/git/gcc.git
cd gcc/
# Cygwin: workaround for configure using cp -p where ln -s should be used
# (this is an automake/autoconf issue, they should trust Cygwin and not use
# ancient workarounds for issues which no longer exists)
(set -o xtrace ; sed -i "s/as_ln_s='cp -pR'/as_ln_s='ln -s'/g" $(find . -name configure) )
# run configure
./configure --disable-libstdcxx-pch
# repeat:
make -j4 clean
(yes | make -j32 all)


#
# libnfs
#

# get sources and patch them
git clone https://github.com/sahlberg/libnfs.git
# fix "nfs_v4.c:436:29: error: array subscript has type 'char' [-Werror=char-subscripts]"
sed -i -E 's/if \(isdigit\(\*buf\)\) \{/if (isdigit((int)*buf)) {/' libnfs/lib/nfs_v4.c

cd libnfs
git config --global --add safe.directory "$PWD"
autoupdate
ln -s /usr/share/libtool/build-aux/ltmain.sh .
autoreconf || true
automake --add-missing
autoreconf
./configure --without-libkrb5 --enable-utils
make -j8 all


#
# Run Cygwin installer from NFS >= v4.1 share
#
wget 'https://www.cygwin.com/setup-x86_64.exe'
chmod a+rx setup-x86_64.exe
# check whether this will open the Cygwin installer window
./setup-x86_64 --no-admin


#
# Subversion checkout:
#
# Test:
svn checkout https://svn.FreeBSD.org/base/head/share/man


#
# NATIVE Windows git clone (NOT Cygwin /usr/bin/git!)
#
# This can fail with older Linux nfsd like Linux 5.10,
# but works with Linux 6.6.32-rt32 nfsd
#
# Test:
'/cygdrive/c/Program Files/Git/cmd/git' clone https://github.com/kofemann/ms-nfs41-client.git


#
# Run parallel make job on NFS >= v4.1 filesystem (/cygdrive/n/xxx/)
#
cd /cygdrive/n/xxx/
time ksh93 $msnfs41clientgitroot/tests/fstest_make_numtree1/fstest_make_numtree1.ksh93 all


#
# Run DrMemory with log dir on NFS >= v4.1 filesystem
#
cd /cygdrive/n/xxx/
drmemory -batch -check_uninit_all -strict_bitops -logdir "$(cygpath -w "$PWD")" -- "$(cygpath -w /sbin/nfs_mount)"

#
# Run Windows tar (/cygdrive/c/Windows/system32/tar) tests
# on NFS >= v4.1 filesystem
#
# Notes:
# - Win10 /cygdrive/c/Windows/system32/tar uses write-only handles
#   which should turn-off write caching. If we do not turn off
#   write caching in this case "wintartest_seq001.bash" will fail
#   like this (might require a few hundred cycles, and only fails
#   on a freshly booted machine):
#   -------- snip --------
#   #### Test cycle 11 (usingbzip=true,tarfileonlocaldisk=true):
#   x 1seq.txt
#   x 100seq.txt
#   x 1040seq.txt
#   x 5000seq.txt
#   x 10000seq.txt
#   x 12000seq.txt
#   #### Test cycle 12 (usingbzip=true,tarfileonlocaldisk=true):
#   x 1seq.txt
#   x 100seq.txt
#   x 1040seq.txt
#   x 5000seq.txt
#   x 10000seq.txt
#   x 12000seq.txt
#   ## ERROR: Zero byte in plain /usr/bin/seq output 10000seq.txt found:
#   ---- snip ----
#   000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#   000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#   000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#   000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#   000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#   ---- snip ----
#   -------- snip --------
#
#

cd /cygdrive/n/xxx/
bash /usr/share/msnfs41client/tests/misc/wintartests/wintartest_seq001.bash

cd /cygdrive/n/xxx/
bash /usr/share/msnfs41client/tests/misc/wintartest_comparewinvsgnu001.bash mytarfile.tar.bz2


#
# Test "silly rename"
#
# Deleing a file which is still in use (i.e. open fd) causes a NFS client
# to do a "silly rename", e.g. "foo" gets renamed to ".nfsXXXX" until
# the client does not use it any more.
#
# The NFS server has a weekly cron job removing files called ".nfs*" which
# are more than a week old to deal with .nfs* files from NFS clients which
# crashed while having deleted files open.
#
#

# The following two tests should print "# test OK"" and come in two
# variations, one using "rm myfile" (which triggers
# Cygwin "silly rename" (see newlib-cygwin/winsup/cygwin/syscalls.cc,
# function |try_to_bin()|), and one using $ cmd /C "del myfile" #, which
# bypasses the Cygwin code
bash -c 'set -o errexit ; rm -Rf sillytestdir ; mkdir sillytestdir ; cd sillytestdir ; touch sillytest ; ( command exec {n}<"sillytest" ; printf "fd=%d\n" $n ; sleep 10) & sleep 1 ; ls -la ; cmd /C "del sillytest" ; ls -la ; if [[ "$(ls -1 .nfs*)" != "" ]] ; then echo "# test OK" ; else echo "# test FAILED" ; fi ; wait'
bash -c 'set -o errexit ; rm -Rf sillytestdir ; mkdir sillytestdir ; cd sillytestdir ; touch sillytest ; ( command exec {n}<"sillytest" ; printf "fd=%d\n" $n ; sleep 10) & sleep 1 ; ls -la ; rm -f sillytest ; ls -la ; if [[ "$(ls -1 .nfs*)" != "" ]] ; then echo "# test OK" ; else echo "# test FAILED" ; fi ; wait'

# The following test should print $'# Test 1 OK\n# Test 2 OK\n'
$ ksh93 -c 'set -o errexit ; rm -Rf sillytestdir1 ; mkdir sillytestdir1 ; cd sillytestdir1 ; touch x ; command exec 0<>x ; echo "# Test 1 OK" >&0 ; rm x ; ls -la ; exec <#((0)) ; cat <&0 ; [ -f .nfs* ] && echo "# Test 2 OK" ; true'


#
# Test rm on read-only dir
#
---- snip ----
$ ksh93 -c 'mkdir d1 && touch d1/f1 && chmod -R a-w d1 && if rm -Rf d1 ; then echo "# Test failed, rm on readonly dir should return error" ; else echo "# Test OK" ; fi'
rm: cannot remove 'd1': Permission denied
# Test OK
---- snip ----

#
# WSL install for testing
#

# install WSL
# (from Admin powershell)
wsl --install --web-download -d Debian

# Update Debian Linux
# wsl # (enter WSL environment)

$ sudo root

# update /etc/apt/sources.list to include "contrib"+"non-free"
$ cat /etc/apt/sources.list
deb http://ftp.gwdg.de/debian/ bullseye main contrib non-free
deb-src http://ftp.gwdg.de/debian/ bullseye main contrib non-free

deb http://security.debian.org/debian-security bullseye-security main contrib non-free
deb-src http://security.debian.org/debian-security bullseye-security main contrib non-free

# update package lists+update packages
$ apt-get update
$ apt-get upgrade

# install packages for testing
$ apt-get install clang gcc gdb nedit emacs vim x11-apps xterm ksh traceroute strace ddd mesa-utils tk xpdf xmpuzzles mwm xutils-dev valgrind crash libhugetlbfs-bin wireguard xtron x11-xserver-utils sunclock moreutils iproute2 inetutils-tools build-essential linux-source libncurses5-dev xvkbd ethtool tshark xmldiff krb5-user krb5-kdc libkrb5-dev keyutils info bc kmod cpio flex libncurses5-dev libelf-dev libssl-dev inkscape xdmx xdmx-tools twm mwm sbuild autoconf automake openbsd-inetd rwho rwhod finger fingerd cronutils at nfs-kernel-server nfs-common nfs4-acl-tools autofs openjdk-17-jdk openjdk-17-demo python talk talkd libcurl4 libc6-dbg sysvbanner powertop iftop acpidump linux-perf ltrace locales task-german task-japanese schroot groff squashfs-tools dpkg-dev devscripts kernel-wedge sbsigntool git-svn apt-file module-assistant dwarves tree net-tools bridge-utils xnest uml-utilities inxi libxaw7-dev whois extrace kexec-tools dos2unix pkg-config libglib2.0-dev libpixman-1-dev qemu qemu-utils qemu-system-\* qemu-system-gui libsixel-bin w3m-img sharutils freerdp2-x11 nscd debconf-utils iotop 'manpages-posix*' konsole lsof

#
# sparse file (data+hole) tests
#
ksh93 tests/sparsefiles/testsparsefile1.ksh


#
# MSI installation from network driver
#

# cd /cygdrive/l/download/
# from https://github.com/DynamoRIO/drmemory/releases/tag/cronbuild-2.6.20282
wget 'https://github.com/DynamoRIO/drmemory/releases/download/cronbuild-2.6.20282/DrMemory-Windows-2.6.20282.msi'
msiexec /i DrMemory-Windows-2.6.20282.msi


#
# test Cygwin 32bit on 64bit Windows on a NFS share
# (64bit Windows only)
#
cd /cygdrive/l/tmp # cwd to NFS driver
mkdir /cygdrive/l/tmp/cygwin32nfstest
cd /cygdrive/l/tmp/cygwin32nfstest
curl --remote-name "https://www.cygwin.com/setup-x86.exe"
./setup-x86 -q --no-write-registry --no-admin --root "$(cygpath -w "$PWD")" --no-desktop --allow-unsupported-windows -q --no-verify --site "http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/2022/11/23/063457"
PATH="$PWD/bin" /cygdrive/c/Windows/system32/cmd.exe /C $'.\\bin\\bash.exe -x -c \'[[ "$(./bin/readlink.exe /proc/self/exe)" =~ $PWD/.*bin/.+ ]] && echo Test OK || echo Test failed'


#
# MariaDB testing
#

#### Setup:
1. Install MariaDB from https://archive.mariadb.org//mariadb-11.8.2/winx64-packages/mariadb-11.8.2-winx64.msi

2. Create user "MariaDB" and group "MariaDB entries in Cygwin /etc/passwd and /etc/group.
This is needed because the current ms-nfs41-client idmapper cannot lookup accounts from the "NT SERVICE" domain
$ getent passwd "NT SERVICE+MariaDB" | sed -E 's/NT SERVICE\+//' >>'/etc/passwd'
$ getent group "NT SERVICE+MariaDB" | sed -E 's/NT SERVICE\+// '>>'/etc/group'

3. Create user "MariaDB" and group "MariaDB" on the NFS server, based on the Cygwin /etc/passwd and /etc/group entries

4. Create GLOBAL mount N: (e.g. via $ msnfs41client sys_terminal #)

5. Create MariaDB data dir on the NFSv4.2 share
$ mkdir '/cygdrive/n/mariadbtest'

6. Edit '/cygdrive/c/Program Files/MariaDB 11.8/data/my.ini' and change "datadir" to "L:\mariadbtest".
Example:
---- snip ----
[mysqld]
datadir=N:\mariadbtest
port=3306
innodb_buffer_pool_size=1535M
character-set-server=utf8mb4
[client]
port=3306
plugin-dir=C:\Program Files\MariaDB 11.8/lib/plugin
---- snip ----

7. Copy contents of '/cygdrive/c/Program Files/MariaDB 11.8/data' to '/cygdrive/n/mariadbtest'
$ (cd '/cygdrive/c/Program Files/MariaDB 11.8/data' && tar --sparse -cvf - .) | (cd '/cygdrive/n/mariadbtest' && tar -xf -)

8. $ chown -R "MariaDB:MariaDB" mariadbtest # on the NFS server!!

#### Testing sparse database file:
1. Test script
---- snip ----
$ cat testdbsparse1.ksh
integer line_num=0
typeset line
typeset escaped_line

cat <<EOF
SELECT VERSION();

DROP DATABASE IF EXISTS alphabet_test4;
CREATE DATABASE alphabet_test4;
USE alphabet_test4;

# create table using InnoDB with PAGE_COMPRESSED=1 (which will use punch hole to free space)
# see https://dev.mysql.com/doc/refman/8.4/en/innodb-page-compression.html
CREATE TABLE file_data (
    line_number INT PRIMARY KEY,
    line_text TEXT
) ENGINE=InnoDB PAGE_COMPRESSED=1;
EOF

while IFS= read -r line ; do
    (( line_num++ ))
    # Escape single quotes in the line to prevent SQL injection issues
    # This is a basic escape, for more complex data, consider prepared statements
    escaped_line="${line//\'/\'\'}"

    # Insert the line number and text into the table
    printf $'INSERT INTO file_data (line_number, line_text) VALUES (%d, \'%s\');\n' line_num "$escaped_line"
done

# force rebuild, including punching holes
printf 'OPTIMIZE TABLE file_data;'

print -u2 '#Done;\n#Verify with\nUSE alphabet_test4; SELECT * FROM file_data ORDER BY line_number;\n'
# EOF.
---- snip ----

2. Run this test script with a large text file as input:
---- snip ----
time ksh93 -c 'for ((i=0 ; i < 10 ; i++ )) ; do cat /usr/share/doc/mingw64-x86_64-gcc/NEWS ; done | ksh93 testdbsparse1.ksh | /cygdrive/c/Program\ Files/MariaDB\ 11.8/bin/mariadb -u root -p'
---- snip ----

3. Check whether the database file now has holes:
$ lssparse -H /cygdrive/n/mariadbtest/alphabet_test4/file_data.ibd

#### Notes:
- MariaDB runs in a Windows "NT Service" account ("NT SERVICE\MariaDB"), which neither Cygwin
  $ /usrbin/getent passwd # nor $ net localusers # will list
- Thanks to MariaDB's Kristian Nielsen <knielsen@knielsen-hq.org> for helping with this


#
# Windows NTVDM
# (Windows NT Virtual DOS Maschine, emulator for 16bit DOS binaries)
#

#### Setup:
- Mount case-insensitive filesystem
- Enable NTVDM in a Windows command-line Window as admin:
---- snip ----
DISM /online /enable-feature /all /featurename:NTVDM #
---- snip ----

#### Tests:
## Test1 ("DOSNIX"):
---- snip ----
wget 'http://www.retroarchive.org/garbo/pc/unix/dosnx23b.zip'
mkdir dosnx23b && cd dosnx23b
unzip ../dosnx23b.zip
chmod a+x *.EXE
./HEAD.EXE WHATS.NEW
./head.exe WHATS.NEW
---- snip ----

## Test 2 (DOS zip 16bit):
---- snip ----
# download and unpack DOS zip
wget 'https://github.com/DankRank/ftp.info-zip.org/raw/refs/heads/master/ftp.info-zip.org/pub/infozip/msdos/zip232x.zip'
mkdir zip232x
cd zip232x
unzip ../zip232x.zip
chmod a+x *.exe
./zip16 -h
# generate new zip archive with DOS zip and test it with Cygwin unzip
seq 7000000 >seq7.txt
./zip16 x.zip MANUAL README seq7.txt
unzip -t x.zip
(mkdir tmp1 && cd tmp1 && unzip ../x.zip seq7.txt)
diff -u seq7.txt tmp1/seq7.txt
---- snip ----

## Test 3 (Total Commander 16bit):
---- snip ----
# see https://www.ghisler.com/wcmd16.htm
wget 'https://plugins.ghisler.com/tcmd16/tc16v658.zip'
mkdir tc16v658 && cd tc16v658
unzip ../tc16v658.zip
unzip INSTALL.BIN
chmod a+x *.EXE
./TCMD16.EXE
---- snip ----

## Test 4 (Turbo C):
---- snip ----
mkdir turboc && cd turboc/
wget 'https://web.archive.org/web/20040401174842/http://bdn.borland.com/article/images/20841/tc201.zip'
unzip tc201.zip
mkdir tc
mv Disk*/* tc/.
cd tc
chmod a+x *.EXE
# *.c sources MUST have <CR><LF> endings in Turbo C!!
 printf '#include <stdio.h>\nint main(int ac, char *av[])\n{\n\tprintf("hello DOS TCC world\\n");\n\treturn 0;\n}\n' | unix2dos >'x.c'
./X.EXE
hello DOS TCC world
---- snip ----

#### ToDo:
- Test more binaries from http://www.retroarchive.org/garbo/pc/unix/

#### Notes:
- NTVDM is only available on Windows 32bit
- Only works on case-insensitive filesystems, i.e.
$ /usr/lib/csih/getVolInfo.exe . | fgrep FILE_CASE_SENSITIVE_SEARCH # must be "FALSE"!!
- Path names must be <= 8+3 characters long, or files cannot be opened/executed
- ms-nfs41-client does not support alternative DOS "short names"
- References:
1. https://learn.microsoft.com/en-us/windows/compatibility/ntvdm-and-16-bit-app-support


#
# DISKSPD storage load generator / performance test tool
# (https://github.com/microsoft/diskspd/)
# Mainly used to test Win32 async read/write
#

#
# Download&&unpack
#
wget 'https://github.com/microsoft/diskspd/releases/download/v2.2/DiskSpd.ZIP'
mkdir DiskSpd && cd DiskSpd
unzip ../DiskSpd.ZIP

#
# run tests
#

# prep
mkdir testruns && cd testruns

# async IO tests
../DiskSpd/amd64/diskspd.exe -c512M -b4K -t32 -r -o64 -d120 -Sh -w50 testfile1.dat testfile2.dat
../DiskSpd/amd64/diskspd.exe -c512M -b8K -t32 -r -o64 -d120 -Sh -w50 testfile1.dat testfile2.dat
../DiskSpd/amd64/diskspd.exe -c512M -b32K -t32 -r -o64 -d120 -Sh -w50 testfile1.dat testfile2.dat
../DiskSpd/amd64/diskspd.exe -c512M -b32K -t32 -r -o64 -d120 -Sb -w50 testfile1.dat testfile2.dat
../DiskSpd/amd64/diskspd.exe -c512M -b1M -t32 -r -o64 -d120 -Sh -w50 testfile1.dat testfile2.dat
# memory-mapped IO (not async)
../DiskSpd/amd64/diskspd.exe -c512M -b1M -t32 -r -o64 -d120 -Sm -Nv -w50 testfile1.dat


#
# FileDisk
# (use plain files as disks, disk/CDROM images)
#
# Original version is 'https://www.accum.se/~bosse/filedisk/filedisk-22.zip',
# we use our fork which supports punching sparse file holes and minor other
# tweaks
#

#
# build
#
export PATH="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2019/Community/MSBuild/Current/Bin/:$PATH"
git clone https://github.com/gisburn/filedisk-sparse.git
cd filedisk-sparse/
MSBuild.exe filedisk.sln -t:Build -p:Configuration=Debug -p:Platform=x64

#
# install
#
cp ./sys/Debug/x64/filedisk.sys /cygdrive/c/Windows/System32/drivers/.
cp ./sys/Debug/x64/filedisk.pdb /cygdrive/c/Windows/System32/drivers/.
cp ./exe/Debug/x64/filedisk.exe /cygdrive/c/Windows/.
chmod a+rx /cygdrive/c/Windows/filedisk.exe
sc create filedisk binPath='C:\Windows\System32\drivers\filedisk.sys' type=kernel

#
# mounting a CD image
#
net start filedisk
filedisk /mount 1 'N:\download\debian-12.4.0-amd64-DVD-1.iso' /cd V:
cd /cygdrive/v
cat README.txt
cd ~
filedisk /umount V:

#
# Create NTFS filesystem and use it
#
rm -f /cygdrive/n/winntfs_filedisk_001.img
filedisk /mount 2 'N:\winntfs_filedisk_001.img' 1T U:
format.com U: /FS:NTFS /A:8192 "/V:NTFSonNFS4_001" /Q /X
mkdir /cygdrive/u/test1 && cd /cygdrive/u/test1
rm -Rfv bash && time nice ksh93 /home/roland_mainz/work/msnfs41_uidmapping/ms-nfs41-client/tests/nfsbuildtest/nfsbuildtest.ksh93 bash build.parallel
cd
filedisk /umount U:
lssparse /cygdrive/n/winntfs_filedisk_001.img
rm /cygdrive/n/winntfs_filedisk_001.img


#
# OFSMount - mount files containing disk images as physical disks
#
# See https://www.osforensics.com/tools/mount-disk-images.html
#
---- snip ----
# create sparse 128GB file
# (2TB does not work with ofsmount v3.3.1000 from 11 Aug 2026)
truncate -s $((128*1024*1024*1024)) x8.img
chmod a+rw x8.img

# mount sparse file "x8.img" as backing store as physical hd disk read/write
'/cygdrive/c/Program Files/OSFMount/osfmount.com' -a -t file -f "$(cygpath -w "$PWD/x8.img")" -o rw -o physical -o hd

# use the driver number ("8" in this example) to add a GPT header to the disk:
powershell -Command 'Initialize-Disk -Number 8 -PartitionStyle GPT -PassThru'

# use the driver number ("8" in this example) to format the drive with NTFS:
powershell -Command 'Get-Disk 8 | Clear-Disk -RemoveData -Confirm:$false -PassThru | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize -DriveLetter O | Format-Volume -FileSystem NTFS -NewFileSystemLabel "DATA3" -Confirm:$false'
---- snip ----


#
# OpenZFS - create ZFS filesystem with NFSv4.2 sparse file as storage
#

General:
- All following commands assume that the NFS filesystem
has been mounted via nfs_globalmount.exe (global filesystem)
- Instructions are for OpenZFS version
https://github.com/openzfsonwindows/openzfs/releases/tag/zfswin-2.4.1rc12

#### OpenZFS Installation
---- snip ----
wget 'https://github.com/openzfsonwindows/openzfs/releases/download/zfswin-2.4.1rc12/OpenZFSOnWindows-debug-2.4.1rc12.exe'
chmod a+x OpenZFSOnWindows-debug-2.4.1rc12.exe
./OpenZFSOnWindows-debug-2.4.1rc12
---- snip ----


#### Create ZFS pool on NFS filesystem

1. Create ZFS pool and assign it the drive letter "U:":
---- snip ----
# cd into NFS subdir
truncate -s $((128*1024*1024*1024)) poolfile1.bin
chmod a+rw poolfile1.bin
# Create pool:
# - ashift=15 defines 32k blocks
# - autotrim=on makes sure that we use "poolfile1.bin" as sparse file
# - casesensitivity=insensitive defines a case-insensitive filesystem
# - driveletter=U sets the fs mount point to U:
# - optionally you can add -O utf8only=on
zpool.exe create -o ashift=15 -o autotrim=on -O xattr=dir -O casesensitivity=insensitive -O driveletter=U nfspool1 "\\\\?\\$(cygpath -w "$PWD/poolfile1.bin")"
---- snip ----

2. Unmount+remove ZFS pool:
---- snip ----
zpool export nfspool1
---- snip ----

3. Import ZFS pool and mount it again:
---- snip ----
# cd into NFS subdir
zpool.exe import -d "$(cygpath -w "$PWD")" nfspool1
---- snip ----


#### Create ZFS virtual disk device on NFS filesystem
1. Create zpool "nfspool1" as described above
2. Create new virtual disk device "nfspool1/myblock1":
---- snip ----
zfs create -V 32G nfspool1/myblock1
---- snip ----
3. List all ZFS virtual disk devices (which should include "nfspool1/myblock1"):
---- snip ----
$ powershell -Command 'Get-PhysicalDisk -Manufacturer "OpenZFS"'

Number FriendlyName    SerialNumber      MediaType   CanPool OperationalStatus HealthStatus Usage       Size
------ ------------    ------------      ---------   ------- ----------------- ------------ -----       ----
1      OpenZFS WinZVOL nfspool1/myblock1 Unspecified False   OK                Healthy      Auto-Select 32 GB
---- snip ----
4. The virtual disk "nfspool1/myblock1" (Disk 1) can now be formatted with NTFS/ReFS, used for a HyperV VM disk etc.


#
# WinBTRFS - create BTRFS filesystem with NFSv4.2 sparse file as storage via filedisk
#
# Notes:
# - Requires filedisk/filedisk-sparse
# - Tested with WinBTRFS from https://github.com/maharmstone/btrfs/releases/tag/v1.9
#

---- snip ----
# cd nfsdir
rm winext4fs.img
truncate -s 1G winbtrfs001.img
filedisk /mount 8 "$(cygpath -w "$PWD/winbtrfs001.img")" X:
mkbtrfs X:
mount
cd /cygdrive/x
# ...
cd ~
filedisk /umount X:
---- snip ----


#
# Build ReactOS
# (free Windows NT clone)
#

#### Setup:
- Mount case-insensitive filesystem
- Install RosBE (ReactOS Build Environment)
$ curl --remote-name 'https://downloads.sourceforge.net/reactos/RosBE-2.2.1.exe'
$ chmod a+x RosBE-2.2.1.exe
$ ./RosBE-2.2.1.exe

#### Build ReactOS/i386 (green terminal, $ cmd.exe /t:0B /k "C:\RosBE\RosBE.cmd" #):
<inside RosBE command prompt>
git clone https://github.com/reactos/reactos
cd reactos
configure.cmd
cd output-MinGW-i386
ninja bootcd livecd

#### Build ReactOS/AMD64 (blue terminal, $ cmd.exe /t:0B /k "C:\RosBE\RosBE.cmd" amd64 #):
<inside RosBE command prompt>
git clone https://github.com/reactos/reactos
cd reactos
configure.cmd -DARCH=amd64
cd output-MinGW-amd64
ninja bootcd livecd


#
# Windows IIS FTP server
# (see also https://learn.microsoft.com/en-us/iis/publish/using-the-ftp-service/scenario-build-an-ftp-site-on-iis)
#

#### Notes:
- Anon write access requires user='IUSR'/group='IUSR' permissions on that directory
- The setup requires a "global mount" (e.g. mounted via $ /sbin/nfs_globalmount ... #),
  to ensure that drive F: is visible to all logons on a system

#### Setup&Config cmd.exe script:
---- snip ----
::
:: Create anon FTP site on NFSv4.1 filesystem
::

@echo off

dism /online /Enable-Feature /FeatureName:IIS-FTPServer /FeatureName:IIS-FTPSvc /FeatureName:IIS-FTPExtensibility /All /NoRestart

sc config ftpsvc start= auto
sc start ftpsvc

set "FTP_PATH=F:\test2\ftpdata"
set "APPCMD=%windir%\system32\inetsrv\appcmd.exe"

if not exist "%FTP_PATH%" mkdir "%FTP_PATH%"
icacls "%FTP_PATH%" /grant "IUSR:(OI)(CI)(M)" /Q

:: delete old site
%APPCMD% delete site "nfs41_anon_ftp_server001"

:: setup new FTP site
%APPCMD% add site /name:"nfs41_anon_ftp_server001" /bindings:ftp://*:21 /physicalPath:"%FTP_PATH%"
%APPCMD% set site "nfs41_anon_ftp_server001" -ftpServer.security.authentication.anonymousAuthentication.enabled:"True"
%APPCMD% set site "nfs41_anon_ftp_server001" -ftpServer.security.ssl.controlChannelPolicy:"SslAllow" -ftpServer.security.ssl.dataChannelPolicy:"SslAllow"
%APPCMD% set config "nfs41_anon_ftp_server001" -section:system.ftpServer/security/authorization /+"[accessType='Allow',users='?',permissions='Read, Write']" /commit:apphost

:: restart service
net stop ftpsvc
net start ftpsvc

echo "FTP Site "nfs41_anon_ftp_server001" successfully created and configured!"

:: EOF.
---- snip ----

Run script with $ cmd /C nfs41_iis_ftp_site_setup.bat #, then
test whether $ /cygdrive/c/Windows/system32/ftp.exe -A localhost # can access
that FTP site.


#
# EOF.
